use std::marker::PhantomData;
use axum::routing::get;
use quokka::{
handler::html::{DataTemplate, FormTemplate},
state::{ProvideState, Templating},
};
use crate::{
controller::{entity_create::post_create_entity, entity_delete::post_delete_entity},
data::{AdminNavigationGroup, NavigationItem},
service::{
page_loader::{
AdminCreateFormPageLoader, AdminEntityDeleteConfirmPageLoader,
AdminEntityListingPageLoader, AdminPageLoader, AdminUpdateFormPageLoader,
},
AdminCreateForm, AdminListing, AdminUpdateForm, FormBuilder,
},
};
pub const ENTITY_LISTING_TEMPLATE: &str = "quokka-admin/page/listing/listing.html.hbs";
pub const FORM_CREATE_TEMPLATE: &str = "quokka-admin/page/form/create.html.hbs";
pub const FORM_UPDATE_TEMPLATE: &str = "quokka-admin/page/form/update.html.hbs";
pub const DELETE_ENTITY_CONFIRM_PAGE: &str =
"quokka-admin/page/listing/delete_entity_confirm.html.hbs";
#[derive(Clone, Debug)]
pub struct EntityHandler<State, CreateForm, UpdateForm, Listing> {
_types: PhantomData<(State, CreateForm, UpdateForm, Listing)>,
pub navigation_group: String,
pub navigation_title: String,
}
#[doc(hidden)]
pub trait TypeErasedEntityHandler<S> {
fn get_router(&self) -> axum::Router<S>;
fn get_navigation(&self) -> AdminNavigationGroup;
}
impl<S, C, U, L: AdminListing<S>> EntityHandler<S, C, U, L> {
pub fn new(navigation_group: impl ToString) -> Self {
Self {
_types: PhantomData,
navigation_group: navigation_group.to_string(),
navigation_title: format!("List {}", L::entity_name()),
}
}
}
impl<S, C, U, L> TypeErasedEntityHandler<S> for EntityHandler<S, C, U, L>
where
S: ProvideState<Templating>,
S: ProvideState<FormBuilder<S>>,
S: ProvideState<AdminCreateFormPageLoader<S, C>>,
S: ProvideState<AdminUpdateFormPageLoader<S, U>>,
S: ProvideState<AdminPageLoader>,
S: quokka::state::State + 'static,
C: AdminCreateForm<S>
+ serde::de::DeserializeOwned
+ serde::Serialize
+ std::fmt::Debug
+ Send
+ Sync
+ 'static,
U: AdminUpdateForm<S>
+ serde::de::DeserializeOwned
+ serde::Serialize
+ std::fmt::Debug
+ Send
+ Sync
+ 'static,
U::PrimaryKeys: serde::de::DeserializeOwned + Send + Sync + 'static,
L: AdminListing<S> + Clone + Send + Sync + serde::Serialize + 'static,
L::PrimaryKeys: serde::de::DeserializeOwned + Send + Sync + 'static,
L::Entity:
serde::Serialize + serde::de::DeserializeOwned + Clone + std::fmt::Debug + Send + 'static,
{
fn get_router(&self) -> axum::Router<S> {
axum::Router::new()
.route(
&format!("/admin/entity/{}/create", C::entity_name()),
get(DataTemplate::<AdminCreateFormPageLoader<S, C>, S>::new(
FORM_CREATE_TEMPLATE,
))
.post(post_create_entity::<S, C>),
)
.route(
&format!("/admin/entity/{}/update/{{*pks}}", U::entity_name()),
FormTemplate::<AdminUpdateFormPageLoader<S, U>, S>::new(FORM_UPDATE_TEMPLATE)
.into(),
)
.route(
&format!("/admin/entity/{}", L::entity_name()),
DataTemplate::<AdminEntityListingPageLoader<S, L>, S>::new(ENTITY_LISTING_TEMPLATE)
.into(),
)
.route(
&format!("/admin/entity/{}/delete/{{*pks}}", L::entity_name()),
get(
DataTemplate::<AdminEntityDeleteConfirmPageLoader<S, L>, S>::new(
DELETE_ENTITY_CONFIRM_PAGE,
),
)
.post(post_delete_entity::<S, L>),
)
}
fn get_navigation(&self) -> AdminNavigationGroup {
AdminNavigationGroup::new(&self.navigation_group).add(
NavigationItem::new(
format!("entity_{}", C::entity_name()),
&self.navigation_title,
)
.link(format!("/admin/entity/{}", C::entity_name())),
)
}
}