quokka-admin 0.1.0

An admin panel for quokka
Documentation
use std::marker::PhantomData;

use axum::http::Method;
use quokka::{
    handler::html::TemplateDataLoader,
    state::{FromState, ProvideState},
};

use crate::{
    data::Severity,
    service::{AdminCreateForm, FormBuilder, FormProperties},
};

use super::{AdminPageData, AdminPageLoader};

pub struct AdminCreateFormPageLoader<S, F> {
    form_builder: FormBuilder<S>,
    admin_page_loader: AdminPageLoader,
    state: S,
    _form: PhantomData<F>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct AdminFormCreatePageData {
    form: FormProperties,
}

impl<S, F> TemplateDataLoader<S> for AdminCreateFormPageLoader<S, F>
where
    S: ProvideState<AdminPageLoader>,
    S: Send + Sync + Clone + 'static,
    F: Send + Sync + 'static,
    F: AdminCreateForm<S> + 'static,
{
    type Args = (Method, <AdminPageLoader as TemplateDataLoader<S>>::Args);

    type Data = AdminPageData<AdminFormCreatePageData>;

    #[tracing::instrument(
        skip_all,
        target = "quokka_admin::service::page_loader::admin_create_form_page_loader::AdminCreateFormPageLoader"
    )]
    async fn load_data(&self, (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!(
                create_form = std::any::type_name::<F>(),
                "Received POST method in AdminCreateFormPageLoader::load_data, this indicates a success for us",
            );

            base = base.message(
                Severity::Success.message("Entity Created", "Entity created successfully"),
            );
        }

        let form = self
            .form_builder
            .construct_form_data(F::get_form())
            .await
            .inspect_err(|error| {
                tracing::error!(
                    ?error,
                    create_form = std::any::type_name::<F>(),
                    "Unable to construct form data in create form",
                );
            })?;

        Ok(base.page(AdminFormCreatePageData { form }))
    }

    #[tracing::instrument(
        skip_all,
        target = "quokka_admin::service::page_loader::admin_create_form_page_loader::AdminCreateFormPageLoader"
    )]
    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> FromState<S> for AdminCreateFormPageLoader<S, F>
where
    S: Clone,
    S: ProvideState<FormBuilder<S>>,
    S: ProvideState<AdminPageLoader>,
{
    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 AdminCreateFormPageLoader<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,
        }
    }
}