312ff7e21ba3e5bddfa5be7ef696712f31211b51 / frogue / src / views / invites.rs
Raw
use actix_web::HttpResponse;
use maud::{Markup, Render, html};
use super::{Page, components::Complaint};
/// An invite as the list renders it.
pub struct InviteSummary {
pub uuid: String,
pub link: String,
pub created_at: String,
pub expires_at: String,
pub used: Option<String>,
}
pub(super) struct InvitesBody<'a> {
owner: &'a str,
invites: &'a [InviteSummary],
remaining: u64,
error: Option<&'a str>,
mode_note: Option<&'a str>,
}
impl Render for InvitesBody<'_> {
fn render(&self) -> Markup {
html! {
h2 { "Invites" }
@if let Some(note) = self.mode_note {
p class="notice" { (note) }
}
p {
"You can invite others to register. Each invite is single-use and expires after 48 hours."
}
p {
strong { (self.remaining) " invites remaining today" }
}
@if self.invites.is_empty() {
p { "No invites yet." }
} @else {
ul class="invites" {
@for invite in self.invites {
li {
a href=(invite.link) { (invite.uuid) }
" created " (invite.created_at)
", expires " (invite.expires_at)
@if let Some(used_by) = &invite.used {
span class="used" { " — used by " (used_by) }
} @else {
span class="unused" { " — unused" }
}
}
}
}
}
@if let Some(error) = self.error {
(Complaint(Some(error)))
}
h3 { "Create an invite" }
form method="post" action=(format!("/~{}/invites", self.owner)) {
button type="submit" { "Generate invite link" }
}
}
}
}
/// The page listing `owner`'s invites, and the form for creating one.
pub fn invites(
viewer: Option<&str>,
owner: &str,
invites: &[InviteSummary],
remaining: u64,
error: Option<&str>,
mode_note: Option<&str>,
) -> HttpResponse {
Page {
title: format!("Frogue - {owner}'s invites"),
viewer: viewer.map(str::to_string),
extra_head: None,
body: InvitesBody {
owner,
invites,
remaining,
error,
mode_note,
},
}
.into_response()
}