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#[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#[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}