Skip to main content

quokka_admin/service/page_loader/
admin_update_form_page_loader.rs

1use 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}