//! Admin management of the built-in delivery options (Packeta, DPD). //! //! The options themselves are fixed and seeded by `initializers::shipping_seeder` //! — they cannot be added or removed here. The admin only sets each one's price //! and toggles whether it is offered at checkout. use axum_extra::extract::cookie::CookieJar; use loco_rs::prelude::*; use sea_orm::{ActiveModelTrait, EntityTrait, QueryOrder, Set}; use serde::Deserialize; use serde_json::json; use crate::{ models::shipping_methods, controllers::i18n::current_lang, shared::{ guard, money::{format_price, parse_price_to_cents}, }, }; #[derive(Debug, Deserialize)] struct ShippingForm { price: String, enabled: Option, } fn is_checked(value: &Option) -> bool { matches!(value.as_deref(), Some("on" | "true" | "1")) } #[debug_handler] async fn index( auth: auth::JWT, jar: CookieJar, ViewEngine(v): ViewEngine, State(ctx): State, ) -> Result { guard::current_admin(auth, &ctx).await?; let methods = shipping_methods::Entity::find() .order_by_asc(shipping_methods::Column::Position) .all(&ctx.db) .await?; let rows: Vec = methods .iter() .map(|m| { json!({ "id": m.id, "code": m.code, "name": m.name, "price": format_price(m.price_cents), "carrier": m.carrier, "requires_pickup_point": m.requires_pickup_point, "enabled": m.enabled, }) }) .collect(); format::view( &v, "admin/shipping/index.html", json!({ "methods": rows, "lang": current_lang(&jar) }), ) } #[debug_handler] async fn update( auth: auth::JWT, Path(id): Path, State(ctx): State, Form(form): Form, ) -> Result { guard::current_admin(auth, &ctx).await?; let method = shipping_methods::Entity::find_by_id(id) .one(&ctx.db) .await? .ok_or_else(|| Error::NotFound)?; let mut active = method.into_active_model(); active.price_cents = Set(parse_price_to_cents(&form.price)?); active.enabled = Set(is_checked(&form.enabled)); active.update(&ctx.db).await?; format::redirect("/admin/shipping") } pub fn routes() -> Routes { Routes::new() .add("/admin/shipping", get(index)) .add("/admin/shipping/{id}", post(update)) }