//! The Askama entry point for the described "revoke a license key" button. //! //! Shape 8 (`b279b9eb`), through the glue-module ruling `27d5e5b8`. //! //! Destructive and irreversible in a way a deleted row is not: every activation //! standing against the key stops working, which is the reason the prompt says //! more than the label does. Both marks, as the ruling asks. //! //! `POST /api/keys/{id}/revoke` is a plain API route the description layer does //! not serve. It answers the whole list rebuilt, so the control says where that //! lands. See [`Action::replaces`]. //! //! Ids arrive as `impl Display` rather than `&str`: the caller is Askama and //! the server's ids are typed newtypes over `Uuid`, so asking for a string //! would push a `to_string()` into every call site to say what `format!` //! already says here. use std::fmt; use makeover_layout::Tone; use quasi_router::screen::Act; use quasi_router::{Action, Node}; /// The revoke button for one key's row. #[must_use] pub fn html(key_id: impl fmt::Display) -> String { use quasi_axum::Serves as _; let act = Act::new( "Revoke", Action::post(format!("/api/keys/{key_id}/revoke")).replacing("license-keys-list"), ) .tone(Tone::Danger) .confirm("Revoke this license key? All activations will be deactivated."); quasi_webview::Webview::new().fragment(&Node::Act(act)) } #[cfg(test)] mod tests { #[test] fn a_revoke_names_its_route_its_region_and_its_prompt() { let html = super::html("k1"); assert!(html.contains(r#"hx-post="/api/keys/k1/revoke""#), "{html}"); assert!( html.contains(r##"hx-target="#license-keys-list""##), "{html}" ); assert!( html.contains( r#"hx-confirm="Revoke this license key? All activations will be deactivated.""# ), "{html}" ); } #[test] fn a_revoke_says_it_is_dangerous() { assert!(super::html("k1").contains(r#"data-tone="danger""#)); } }