use std::marker::PhantomData;
use axum::{extract::Path, http::Method};
use quokka::{
handler::html::{FormDataStore, FormResponse, TemplateDataLoader},
state::{FromState, ProvideState},
};
use crate::{
data::Severity,
service::{AdminUpdateForm, FormBuilder, FormProperties},
};
use super::{AdminPageData, AdminPageLoader};
pub struct AdminUpdateFormPageLoader<S, F> {
form_builder: FormBuilder<S>,
admin_page_loader: AdminPageLoader,
state: S,
_form: PhantomData<F>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct AdminFormUpdatePageData<F> {
form: FormProperties,
data: F,
}
impl<S, F> TemplateDataLoader<S> for AdminUpdateFormPageLoader<S, F>
where
S: ProvideState<AdminPageLoader>,
S: Send + Sync + Clone + 'static,
F: AdminUpdateForm<S>
+ Send
+ Sync
+ std::fmt::Debug
+ serde::de::DeserializeOwned
+ serde::Serialize
+ 'static,
F::PrimaryKeys: Send + Sync + serde::de::DeserializeOwned + 'static,
{
type Args = (
Path<F::PrimaryKeys>,
Method,
<AdminPageLoader as TemplateDataLoader<S>>::Args,
);
type Data = AdminPageData<AdminFormUpdatePageData<F>>;
#[tracing::instrument(
skip_all,
target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
)]
async fn load_data(
&self,
(path, method, base_params): Self::Args,
) -> quokka::Result<Self::Data> {
let mut base = <AdminPageLoader as TemplateDataLoader<S>>::load_data(
&self.admin_page_loader,
base_params,
)
.await?;
if method == Method::POST {
tracing::debug!(
update_form = std::any::type_name::<F>(),
"Received POST method in AdminUpdateFormPageLoader::load_data, this indicates a success for us",
);
base = base.message(
Severity::Success.message("Entity Updated", "Entity updated successfully"),
);
}
let form = self
.form_builder
.construct_form_data(F::get_form())
.await
.inspect_err(|error| {
tracing::error!(
?error,
update_form = std::any::type_name::<F>(),
"Unable to construct form data in update form",
);
})?;
Ok(base.page(AdminFormUpdatePageData {
form,
data: F::get_query(&self.state, path.0)
.await
.inspect_err(|error| {
tracing::error!(
?error,
update_form = std::any::type_name::<F>(),
"Unable to get entity data for update form",
);
})?,
}))
}
#[tracing::instrument(
skip_all,
target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
)]
async fn render_error(&self, error: quokka::Error) -> impl axum::response::IntoResponse {
<AdminPageLoader as TemplateDataLoader<S>>::render_error(&self.admin_page_loader, error)
.await
}
}
impl<S, F> FormDataStore<S> for AdminUpdateFormPageLoader<S, F>
where
S: ProvideState<AdminPageLoader>,
S: Send + Sync + Clone + 'static,
F: AdminUpdateForm<S>
+ Send
+ Sync
+ std::fmt::Debug
+ serde::de::DeserializeOwned
+ serde::Serialize
+ 'static,
F::PrimaryKeys: Send + Sync + serde::de::DeserializeOwned + 'static,
{
type StoreArgs = ();
type AdditionalResponse = ();
type Body = axum::Form<F>;
#[tracing::instrument(
skip_all,
target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
)]
async fn store_data(
&self,
_: Self::StoreArgs,
body: Self::Body,
) -> quokka::Result<FormResponse<Self::AdditionalResponse>> {
body.0
.update_query(&self.state)
.await
.inspect_err(|error| {
tracing::error!(
?error,
update_form = std::any::type_name::<F>(),
"Unable to update entity data in update form",
);
})?;
Ok(FormResponse::Empty)
}
}
impl<S, F> FromState<S> for AdminUpdateFormPageLoader<S, F>
where
S: ProvideState<FormBuilder<S>>,
S: ProvideState<AdminPageLoader>,
S: Clone,
{
fn from_state(state: &S) -> Self {
Self {
form_builder: state.provide(),
admin_page_loader: state.provide(),
state: state.clone(),
_form: PhantomData,
}
}
}
impl<S: Clone, F> Clone for AdminUpdateFormPageLoader<S, F> {
fn clone(&self) -> Self {
Self {
form_builder: self.form_builder.clone(),
admin_page_loader: self.admin_page_loader.clone(),
state: self.state.clone(),
_form: PhantomData,
}
}
}