//! The Askama entry point for the two described git-repository removals. //! //! Shape 8 (`b279b9eb`), through the glue-module ruling `27d5e5b8`. Both //! destroy an association rather than a repository, and both ask, because a //! creator cannot tell the two apart from the button alone. //! //! Each prompt names what it acts on, so the two are separate calls rather than //! one with a label parameter: the sentence differs, not just the address. //! //! `DELETE /api/projects/{id}/repos/{name}` and //! `DELETE /api/repos/{id}/collaborators/{user}` are plain API routes the //! description layer does not serve. Both answer the whole section 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 section both acts redraw. const REGION: &str = "git-repos-section"; /// Take a repository off a project. #[must_use] pub fn unlink_html(project_id: impl fmt::Display, repo_name: &str) -> String { fragment( Act::new( "Unlink", Action::delete(format!("/api/projects/{project_id}/repos/{repo_name}")) .replacing(REGION), ) .tone(Tone::Danger) .confirm(format!("Unlink '{repo_name}' from this project?")), ) } /// Take a collaborator off a repository. #[must_use] pub fn remove_collaborator_html( repo_id: impl fmt::Display, repo_name: &str, user_id: impl fmt::Display, username: &str, ) -> String { fragment( Act::new( "Remove", Action::delete(format!("/api/repos/{repo_id}/collaborators/{user_id}")) .replacing(REGION), ) .tone(Tone::Danger) .confirm(format!("Remove @{username} from {repo_name}?")), ) } /// No shell and no region: this lands inside a document Askama has already /// built, which is what every glue module here does. fn fragment(act: Act) -> String { use quasi_axum::Serves as _; quasi_webview::Webview::new().fragment(&Node::Act(act)) } #[cfg(test)] mod tests { #[test] fn an_unlink_names_its_route_its_region_and_the_repository_it_asks_about() { let html = super::unlink_html("p1", "synth"); assert!( html.contains(r#"hx-delete="/api/projects/p1/repos/synth""#), "{html}" ); assert!( html.contains(r##"hx-target="#git-repos-section""##), "{html}" ); assert!( html.contains(r#"hx-confirm="Unlink 'synth' from this project?""#), "{html}" ); } #[test] fn removing_a_collaborator_asks_about_the_person_and_the_repository() { let html = super::remove_collaborator_html("r1", "synth", "u9", "ada"); assert!( html.contains(r#"hx-delete="/api/repos/r1/collaborators/u9""#), "{html}" ); assert!( html.contains(r#"hx-confirm="Remove @ada from synth?""#), "{html}" ); } #[test] fn both_removals_say_they_are_dangerous() { assert!(super::unlink_html("p1", "synth").contains(r#"data-tone="danger""#)); assert!( super::remove_collaborator_html("r1", "synth", "u9", "ada") .contains(r#"data-tone="danger""#) ); } }