1use axum::routing::get;
2use full::{
3 auth::{TestAuthProvider, TestLoginProvider},
4 ui::test::{TestEntityCreateForm, TestEntityListing, TestEntityUpdateForm},
5};
6use quokka::{
7 bundle::Pouch,
8 state::{
9 Database, ProvideState, ProvideStateRef, Resources, Scripting, State, Styling, Templating,
10 },
11};
12use quokka_admin::{
13 data::AdminDashboardWidget,
14 handler::EntityHandler,
15 service::page_loader::{AdminNavigationGroup, AdminSidebarWidget, NavigationItem},
16 state::AdminState,
17 AdminBundle,
18};
19
20mod full;
21
22pub const EXAMPLE_SUPER_USER_GROUP: &str = "super_example_user";
23
24#[derive(Clone, State, ProvideState)]
25pub struct ExampleState {
26 templating: Templating,
27 database: Database,
28 styling: Styling,
29 scripting: Scripting,
30 resources: Resources,
31 admin_state: AdminState<Self>,
32}
33
34struct ExampleBundle;
35
36async fn test_widget() -> String {
37 String::from("Example widget")
38}
39
40async fn time_widget() -> String {
41 format!(
42 "Current Server Time: {}",
43 time::OffsetDateTime::now_local().unwrap_or_else(|_| time::OffsetDateTime::now_utc())
44 )
45}
46
47impl<S> Pouch<S> for ExampleBundle
48where
49 S: ProvideState<Database>,
50 S: ProvideState<Templating>,
51 S: ProvideStateRef<AdminState<S>>,
52 S: State + 'static,
53{
54 fn from_config(_: &quokka::config::Config) -> quokka::Result<Self>
55 where
56 Self: Sized,
57 {
58 Ok(Self)
59 }
60
61 fn get_router(&mut self, _state: &S) -> axum::Router<S> {
62 axum::Router::new()
63 .route("/example/admin/test_widget", get(test_widget))
64 .route("/example/admin/time_widget", get(time_widget))
65 }
66
67 fn configure_state(&mut self, state: &mut S) -> quokka::Result<()> {
68 let admin: &mut AdminState<_> = state.provide_mut();
69
70 admin.super_admin_group = EXAMPLE_SUPER_USER_GROUP.to_string().into(); admin.add_auth_provider(TestAuthProvider);
72 admin.add_login_provider(TestLoginProvider);
73
74 admin.add_navigation(
75 AdminNavigationGroup::new("Example").add(
76 NavigationItem::new("example_page", "Example Page").link("/admin/example/page"),
77 ),
78 );
79
80 admin.add_sidebar_widget(AdminSidebarWidget::htmx("/example/admin/test_widget"));
81 admin.add_sidebar_widget(AdminSidebarWidget::htmx("/example/admin/time_widget")); admin.add_dashboard_widget(AdminDashboardWidget::htmx("/example/admin/test_widget"));
84 admin.add_dashboard_widget(AdminDashboardWidget::htmx("/example/admin/test_widget"));
85 admin.add_dashboard_widget(AdminDashboardWidget::htmx("/example/admin/test_widget"));
86 admin.add_dashboard_widget(AdminDashboardWidget::htmx("/example/admin/test_widget"));
87
88 admin.register_entity_handler(EntityHandler::<
89 S,
90 TestEntityCreateForm,
91 TestEntityUpdateForm,
92 TestEntityListing,
93 >::new("Example"));
94
95 Ok(())
96 }
97
98 fn get_migrations(&self) -> Option<sqlx::migrate::Migrator> {
99 Some(sqlx::migrate::Migrator {
100 migrations: vec![sqlx::migrate::Migration {
101 version: 1,
102 description: "Test migration".into(),
103 migration_type: sqlx::migrate::MigrationType::Simple,
104 sql: r#"CREATE TABLE IF NOT EXISTS "test" (
105 id SERIAL NOT NULL PRIMARY KEY,
106 name VARCHAR(255) NOT NULL,
107 age INTEGER NOT NULL,
108 created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
109 updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW())"#
110 .into(),
111 checksum: vec![1, 2, 3, 4, 5, 6].into(),
112 no_tx: true,
113 }]
114 .into(),
115 ignore_missing: true,
116 locking: true,
117 no_tx: true,
118 })
119 }
120}
121
122#[tokio::main]
123async fn main() -> quokka::Result<()> {
124 quokka::Quokka::<ExampleState>::try_default()?
125 .load::<ExampleBundle>()?
126 .load::<AdminBundle>()?
127 .serve()
128 .await
129}