fabrica

hanna/fabrica

style(web): show invite status as a leading used/unused pill

85fc506 · hanna committed on 2026-07-26

The invite's redemption state moves from a meta line under the note to a
coloured pill at the start of the row — green "unused" while open, muted
"used" once redeemed. Any expiry stays on the meta line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
2 files changed · +19 −4UnifiedSplit
assets/base.css +9 −0
@@ -1421,6 +1421,15 @@ label.checkbox input {
14211421 color: var(--fb-warning);
14221422 border-color: var(--fb-warning);
14231423 }
1424+/* Sign-up invite status: open (unused) reads as available, spent (used) muted. */
1425+.badge-unused {
1426+ color: var(--fb-success);
1427+ border-color: var(--fb-success);
1428+}
1429+.badge-used {
1430+ color: var(--fb-fg-muted);
1431+ border-color: var(--fb-fg-muted);
1432+}
14241433
14251434 /* Inline search row: input grows, button sits to its right with margin. */
14261435 .search-row {
crates/web/src/admin.rs +10 −4
@@ -590,10 +590,16 @@ pub async fn invites(
590590 @for iv in &invites {
591591 li class="entry-row" {
592592 div class="entry-row-body" {
593- span class="entry-row-title" { (iv.note.clone().unwrap_or_else(|| "(no note)".to_string())) }
594- div class="entry-row-meta muted" {
595- @if iv.used_at.is_some() { "used" } @else { "unused" }
596- @if let Some(exp) = iv.expires_at { " · expires " (crate::repo::fmt_date(exp)) }
593+ span class="entry-row-title" {
594+ @if iv.used_at.is_some() {
595+ span class="badge badge-used" { "used" }
596+ } @else {
597+ span class="badge badge-unused" { "unused" }
598+ }
599+ " " (iv.note.clone().unwrap_or_else(|| "(no note)".to_string()))
600+ }
601+ @if let Some(exp) = iv.expires_at {
602+ div class="entry-row-meta muted" { "expires " (crate::repo::fmt_date(exp)) }
597603 }
598604 }
599605 div class="entry-row-actions admin-actions" {