quokka_admin/service/page_loader/
admin_update_form_page_loader.rs1use std::marker::PhantomData;
2
3use axum::{extract::Path, http::Method};
4use quokka::{
5 handler::html::{FormDataStore, FormResponse, TemplateDataLoader},
6 state::{FromState, ProvideState},
7};
8
9use crate::{
10 data::Severity,
11 service::{AdminUpdateForm, FormBuilder, FormProperties},
12};
13
14use super::{AdminPageData, AdminPageLoader};
15
16pub struct AdminUpdateFormPageLoader<S, F> {
17 form_builder: FormBuilder<S>,
18 admin_page_loader: AdminPageLoader,
19 state: S,
20 _form: PhantomData<F>,
21}
22
23#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
24pub struct AdminFormUpdatePageData<F> {
25 form: FormProperties,
26 data: F,
27}
28
29impl<S, F> TemplateDataLoader<S> for AdminUpdateFormPageLoader<S, F>
30where
31 S: ProvideState<AdminPageLoader>,
32 S: Send + Sync + Clone + 'static,
33 F: AdminUpdateForm<S>
34 + Send
35 + Sync
36 + std::fmt::Debug
37 + serde::de::DeserializeOwned
38 + serde::Serialize
39 + 'static,
40 F::PrimaryKeys: Send + Sync + serde::de::DeserializeOwned + 'static,
41{
42 type Args = (
43 Path<F::PrimaryKeys>,
44 Method,
45 <AdminPageLoader as TemplateDataLoader<S>>::Args,
46 );
47
48 type Data = AdminPageData<AdminFormUpdatePageData<F>>;
49
50 #[tracing::instrument(
51 skip_all,
52 target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
53 )]
54 async fn load_data(
55 &self,
56 (path, method, base_params): Self::Args,
57 ) -> quokka::Result<Self::Data> {
58 let mut base = <AdminPageLoader as TemplateDataLoader<S>>::load_data(
59 &self.admin_page_loader,
60 base_params,
61 )
62 .await?;
63
64 if method == Method::POST {
65 tracing::debug!(
66 update_form = std::any::type_name::<F>(),
67 "Received POST method in AdminUpdateFormPageLoader::load_data, this indicates a success for us",
68 );
69
70 base = base.message(
71 Severity::Success.message("Entity Updated", "Entity updated successfully"),
72 );
73 }
74
75 let form = self
76 .form_builder
77 .construct_form_data(F::get_form())
78 .await
79 .inspect_err(|error| {
80 tracing::error!(
81 ?error,
82 update_form = std::any::type_name::<F>(),
83 "Unable to construct form data in update form",
84 );
85 })?;
86
87 Ok(base.page(AdminFormUpdatePageData {
88 form,
89 data: F::get_query(&self.state, path.0)
90 .await
91 .inspect_err(|error| {
92 tracing::error!(
93 ?error,
94 update_form = std::any::type_name::<F>(),
95 "Unable to get entity data for update form",
96 );
97 })?,
98 }))
99 }
100
101 #[tracing::instrument(
102 skip_all,
103 target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
104 )]
105 async fn render_error(&self, error: quokka::Error) -> impl axum::response::IntoResponse {
106 <AdminPageLoader as TemplateDataLoader<S>>::render_error(&self.admin_page_loader, error)
107 .await
108 }
109}
110
111impl<S, F> FormDataStore<S> for AdminUpdateFormPageLoader<S, F>
112where
113 S: ProvideState<AdminPageLoader>,
114 S: Send + Sync + Clone + 'static,
115 F: AdminUpdateForm<S>
116 + Send
117 + Sync
118 + std::fmt::Debug
119 + serde::de::DeserializeOwned
120 + serde::Serialize
121 + 'static,
122 F::PrimaryKeys: Send + Sync + serde::de::DeserializeOwned + 'static,
123{
124 type StoreArgs = ();
125
126 type AdditionalResponse = ();
127
128 type Body = axum::Form<F>;
129
130 #[tracing::instrument(
131 skip_all,
132 target = "quokka_admin::service::page_loader::admin_update_form_page_loader::AdminUpdateFormPageLoader"
133 )]
134 async fn store_data(
135 &self,
136 _: Self::StoreArgs,
137 body: Self::Body,
138 ) -> quokka::Result<FormResponse<Self::AdditionalResponse>> {
139 body.0
140 .update_query(&self.state)
141 .await
142 .inspect_err(|error| {
143 tracing::error!(
144 ?error,
145 update_form = std::any::type_name::<F>(),
146 "Unable to update entity data in update form",
147 );
148 })?;
149
150 Ok(FormResponse::Empty)
151 }
152}
153
154impl<S, F> FromState<S> for AdminUpdateFormPageLoader<S, F>
155where
156 S: ProvideState<FormBuilder<S>>,
157 S: ProvideState<AdminPageLoader>,
158 S: Clone,
159{
160 fn from_state(state: &S) -> Self {
161 Self {
162 form_builder: state.provide(),
163 admin_page_loader: state.provide(),
164 state: state.clone(),
165 _form: PhantomData,
166 }
167 }
168}
169
170impl<S: Clone, F> Clone for AdminUpdateFormPageLoader<S, F> {
171 fn clone(&self) -> Self {
172 Self {
173 form_builder: self.form_builder.clone(),
174 admin_page_loader: self.admin_page_loader.clone(),
175 state: self.state.clone(),
176 _form: PhantomData,
177 }
178 }
179}