From 2ba4d4407dda83cb4c4f5766218190d25b07e77a Mon Sep 17 00:00:00 2001 From: Pen Anderson Date: Wed, 4 Mar 2026 20:52:12 -0600 Subject: [PATCH] Updated Dashboard and clarified default states. --- frontend/src/pages/Dashboard.svelte | 179 ++++++++++++++++++++---- frontend/src/pages/Departments.svelte | 2 +- frontend/src/pages/ScheduleBoard.svelte | 4 +- frontend/src/pages/Users.svelte | 14 +- frontend/src/pages/Volunteers.svelte | 10 +- 5 files changed, 174 insertions(+), 35 deletions(-) diff --git a/frontend/src/pages/Dashboard.svelte b/frontend/src/pages/Dashboard.svelte index c1ae495..6b44455 100644 --- a/frontend/src/pages/Dashboard.svelte +++ b/frontend/src/pages/Dashboard.svelte @@ -4,13 +4,54 @@ let { session } = $props() - const attendees = liveQuery(() => db.attendees.toArray()) - const event = liveQuery(() => db.event.get(1)) + const role = $derived(session?.user?.role ?? '') + const myDeptIDs = $derived(session?.user?.department_ids ?? []) + const isTicketing = $derived(['admin', 'ticketing'].includes(role)) + const isStaffing = $derived(['admin', 'ticketing', 'staffing'].includes(role)) + const isColead = $derived(role === 'colead') - const total = $derived(($attendees ?? []).length) - const checkedIn = $derived(($attendees ?? []).filter(a => a.checked_in).length) - const remaining = $derived(total - checkedIn) - const pct = $derived(total > 0 ? Math.round((checkedIn / total) * 100) : 0) + const event = liveQuery(() => db.event.get(1)) + const allTickets = liveQuery(() => db.tickets.toArray()) + const allVolunteers = liveQuery(() => db.volunteers.filter(v => !v.deleted_at).toArray()) + const allShifts = liveQuery(() => db.shifts.filter(s => !s.deleted_at).toArray()) + const allDepts = liveQuery(() => db.departments.filter(d => !d.deleted_at).toArray()) + const allVS = liveQuery(() => db.volunteer_shifts.toArray()) + + // Ticket stats + const tickets = $derived($allTickets ?? []) + const ticketTotal = $derived(tickets.length) + const ticketCheckedIn = $derived(tickets.filter(t => t.checked_in_at).length) + const ticketRemaining = $derived(ticketTotal - ticketCheckedIn) + const ticketPct = $derived(ticketTotal > 0 ? Math.round((ticketCheckedIn / ticketTotal) * 100) : 0) + + // Volunteer stats (scoped for colead) + const volunteers = $derived.by(() => { + const vols = $allVolunteers ?? [] + if (isColead) return vols.filter(v => myDeptIDs.includes(v.department_id)) + return vols + }) + const volTotal = $derived(volunteers.length) + const volCheckedIn = $derived(volunteers.filter(v => v.checked_in).length) + const volLeads = $derived(volunteers.filter(v => v.is_lead).length) + + // Shift stats (scoped for colead) + const shifts = $derived.by(() => { + const all = $allShifts ?? [] + if (isColead) return all.filter(s => myDeptIDs.includes(s.department_id)) + return all + }) + const shiftTotal = $derived(shifts.length) + const shiftsFilled = $derived.by(() => { + const vs = $allVS ?? [] + return shifts.filter(s => vs.some(a => a.shift_id === s.id)).length + }) + const shiftFillPct = $derived(shiftTotal > 0 ? Math.round((shiftsFilled / shiftTotal) * 100) : 0) + + // Department names for colead header + const myDeptNames = $derived.by(() => { + const depts = $allDepts ?? [] + return myDeptIDs.map(id => depts.find(d => d.id === id)?.name).filter(Boolean) + })
@@ -28,35 +69,115 @@

{/if} -
-
-
Total
-
{total}
-
-
-
Checked in
-
{checkedIn}
-
-
-
Remaining
-
{remaining}
-
-
-
Progress
-
{pct}%
-
-
+ {#if isColead && myDeptNames.length > 0} +

+ Your department{myDeptNames.length > 1 ? 's' : ''}: + {myDeptNames.join(', ')} +

+ {/if} - {#if total > 0} -
-
-
+ + {#if isTicketing} +

Ticket Check-in

+
+
+
Total tickets
+
{ticketTotal}
+
+
+
Checked in
+
{ticketCheckedIn}
+
+
+
Remaining
+
{ticketRemaining}
+
+
+
Progress
+
{ticketPct}%
+
+
+ + {#if ticketTotal > 0} +
+
+
+
+
+ {/if} + {/if} + + + {#if isStaffing || isColead} +

{isColead ? 'My Volunteers' : 'Volunteers'}

+
+
+
Total
+
{volTotal}
+
+
+
Checked in
+
{volCheckedIn}
+
+
+
Leads
+
{volLeads}
{/if} -

+ + {#if isStaffing || isColead} +

{isColead ? 'My Shifts' : 'Shift Coverage'}

+
+
+
Total shifts
+
{shiftTotal}
+
+
+
With volunteers
+
{shiftsFilled}
+
+
+
Fill rate
+
{shiftFillPct}%
+
+
+ {/if} + + + {#if isTicketing} + + {:else if isStaffing || isColead} + + {/if} + +

Welcome, {session?.user?.username} · {session?.user?.role}

+ + diff --git a/frontend/src/pages/Departments.svelte b/frontend/src/pages/Departments.svelte index b50fde4..b7fd627 100644 --- a/frontend/src/pages/Departments.svelte +++ b/frontend/src/pages/Departments.svelte @@ -127,7 +127,7 @@ {#if ($allDepts ?? []).length === 0}
No departments yet -

Add departments to organize your volunteer teams.

+

Create departments to organize shifts and volunteer teams. Coleads are assigned to specific departments.

{:else}
diff --git a/frontend/src/pages/ScheduleBoard.svelte b/frontend/src/pages/ScheduleBoard.svelte index 5d9d265..2d0b555 100644 --- a/frontend/src/pages/ScheduleBoard.svelte +++ b/frontend/src/pages/ScheduleBoard.svelte @@ -315,8 +315,8 @@ {#if ($allShifts ?? []).length === 0 && !showAdd}
- No shifts yet -

Add shifts to schedule your volunteers.

+ No shifts scheduled yet +

Create departments first, then add shifts here. Volunteers can self-select shifts via the kiosk.

{:else} {#each board as { dept, days }} diff --git a/frontend/src/pages/Users.svelte b/frontend/src/pages/Users.svelte index 237617b..c683fb9 100644 --- a/frontend/src/pages/Users.svelte +++ b/frontend/src/pages/Users.svelte @@ -117,7 +117,7 @@ } function roleLabel(r) { - return { admin: 'Admin', coordinator: 'Coordinator', ticketing: 'Ticketing', gate: 'Gate', volunteer_lead: 'Vol. Lead' }[r] || r + return { admin: 'Admin', ticketing: 'Ticketing', staffing: 'Staffing', colead: 'Colead', gatekeeper: 'Gatekeeper' }[r] || r } @@ -129,6 +129,15 @@
+

+ Roles: + admin — full access · + ticketing — participants, tickets, import · + staffing — volunteers, shifts, departments · + colead — manage assigned departments only · + gatekeeper — check-in only +

+ {#if loadError}
{loadError}
{/if} @@ -187,7 +196,8 @@
Loading…
{:else if users.length === 0}
- No users yet + No additional users +

The admin account was created at setup. Add users above to delegate access.

{:else}
diff --git a/frontend/src/pages/Volunteers.svelte b/frontend/src/pages/Volunteers.svelte index 39bc98b..e5df71a 100644 --- a/frontend/src/pages/Volunteers.svelte +++ b/frontend/src/pages/Volunteers.svelte @@ -20,6 +20,14 @@ const role = $derived(session?.user?.role ?? '') const canManage = $derived(['admin', 'ticketing', 'staffing', 'colead'].includes(role)) + const myDeptIDs = $derived(session?.user?.department_ids ?? []) + + // Auto-filter coleads to their department on mount + $effect(() => { + if (role === 'colead' && myDeptIDs.length > 0 && !filterDept) { + filterDept = String(myDeptIDs[0]) + } + }) const allVolunteers = liveQuery(() => db.volunteers.filter(v => !v.deleted_at).toArray() @@ -177,7 +185,7 @@ {#if ($allVolunteers ?? []).length === 0}
No volunteers yet -

Add volunteers manually.

+

Add volunteers manually above, or enable public signup in Settings.

{:else}