Skip to main content

quokka_admin/handler/
mod.rs

1use std::marker::PhantomData;
2
3use axum::routing::get;
4use quokka::{
5    handler::html::{DataTemplate, FormTemplate},
6    state::{ProvideState, Templating},
7};
8
9use crate::{
10    controller::{entity_create::post_create_entity, entity_delete::post_delete_entity},
11    data::{AdminNavigationGroup, NavigationItem},
12    service::{
13        page_loader::{
14            AdminCreateFormPageLoader, AdminEntityDeleteConfirmPageLoader,
15            AdminEntityListingPageLoader, AdminPageLoader, AdminUpdateFormPageLoader,
16        },
17        AdminCreateForm, AdminListing, AdminUpdateForm, FormBuilder,
18    },
19};
20pub const ENTITY_LISTING_TEMPLATE: &str = "quokka-admin/page/listing/listing.html.hbs";
21pub const FORM_CREATE_TEMPLATE: &str = "quokka-admin/page/form/create.html.hbs";
22pub const FORM_UPDATE_TEMPLATE: &str = "quokka-admin/page/form/update.html.hbs";
23pub const DELETE_ENTITY_CONFIRM_PAGE: &str =
24    "quokka-admin/page/listing/delete_entity_confirm.html.hbs";
25
26///
27/// This is the main item for presenting your custom entity to the admin-ui. It requires a [AdminCreateForm], [AdminUpdateForm] and
28/// [AdminListing].
29///
30#[derive(Clone, Debug)]
31pub struct EntityHandler<State, CreateForm, UpdateForm, Listing> {
32    _types: PhantomData<(State, CreateForm, UpdateForm, Listing)>,
33    pub navigation_group: String,
34    pub navigation_title: String,
35}
36
37///
38/// In internal, type-erased version of the [EntityHandler]
39///
40#[doc(hidden)]
41pub trait TypeErasedEntityHandler<S> {
42    fn get_router(&self) -> axum::Router<S>;
43
44    fn get_navigation(&self) -> AdminNavigationGroup;
45}
46
47impl<S, C, U, L: AdminListing<S>> EntityHandler<S, C, U, L> {
48    pub fn new(navigation_group: impl ToString) -> Self {
49        Self {
50            _types: PhantomData,
51            navigation_group: navigation_group.to_string(),
52            navigation_title: format!("List {}", L::entity_name()),
53        }
54    }
55}
56
57impl<S, C, U, L> TypeErasedEntityHandler<S> for EntityHandler<S, C, U, L>
58where
59    S: ProvideState<Templating>,
60    S: ProvideState<FormBuilder<S>>,
61    S: ProvideState<AdminCreateFormPageLoader<S, C>>,
62    S: ProvideState<AdminUpdateFormPageLoader<S, U>>,
63    S: ProvideState<AdminPageLoader>,
64    S: quokka::state::State + 'static,
65    C: AdminCreateForm<S>
66        + serde::de::DeserializeOwned
67        + serde::Serialize
68        + std::fmt::Debug
69        + Send
70        + Sync
71        + 'static,
72    U: AdminUpdateForm<S>
73        + serde::de::DeserializeOwned
74        + serde::Serialize
75        + std::fmt::Debug
76        + Send
77        + Sync
78        + 'static,
79    U::PrimaryKeys: serde::de::DeserializeOwned + Send + Sync + 'static,
80    L: AdminListing<S> + Clone + Send + Sync + serde::Serialize + 'static,
81    L::PrimaryKeys: serde::de::DeserializeOwned + Send + Sync + 'static,
82    L::Entity:
83        serde::Serialize + serde::de::DeserializeOwned + Clone + std::fmt::Debug + Send + 'static,
84{
85    fn get_router(&self) -> axum::Router<S> {
86        axum::Router::new()
87            .route(
88                &format!("/admin/entity/{}/create", C::entity_name()),
89                get(DataTemplate::<AdminCreateFormPageLoader<S, C>, S>::new(
90                    FORM_CREATE_TEMPLATE,
91                ))
92                .post(post_create_entity::<S, C>),
93            )
94            .route(
95                &format!("/admin/entity/{}/update/{{*pks}}", U::entity_name()),
96                FormTemplate::<AdminUpdateFormPageLoader<S, U>, S>::new(FORM_UPDATE_TEMPLATE)
97                    .into(),
98            )
99            .route(
100                &format!("/admin/entity/{}", L::entity_name()),
101                DataTemplate::<AdminEntityListingPageLoader<S, L>, S>::new(ENTITY_LISTING_TEMPLATE)
102                    .into(),
103            )
104            .route(
105                &format!("/admin/entity/{}/delete/{{*pks}}", L::entity_name()),
106                get(
107                    DataTemplate::<AdminEntityDeleteConfirmPageLoader<S, L>, S>::new(
108                        DELETE_ENTITY_CONFIRM_PAGE,
109                    ),
110                )
111                .post(post_delete_entity::<S, L>),
112            )
113    }
114
115    fn get_navigation(&self) -> AdminNavigationGroup {
116        AdminNavigationGroup::new(&self.navigation_group).add(
117            NavigationItem::new(
118                format!("entity_{}", C::entity_name()),
119                &self.navigation_title,
120            )
121            .link(format!("/admin/entity/{}", C::entity_name())),
122        )
123    }
124}