import React, { useEffect, useState } from 'react'; import { api, AdminVolunteer, CreateVolunteerInput, OPERATIONAL_ROLES } from '../api'; const EMPTY_FORM: CreateVolunteerInput = { name: '', email: '', role: 'volunteer', is_trainee: false, phone: '', operational_roles: '', }; export default function Volunteers() { const [volunteers, setVolunteers] = useState([]); const [error, setError] = useState(''); const [showCreate, setShowCreate] = useState(false); const [form, setForm] = useState(EMPTY_FORM); const [creating, setCreating] = useState(false); const [inviteLink, setInviteLink] = useState<{ name: string; token: string } | null>(null); const [expandedId, setExpandedId] = useState(null); const [editNotes, setEditNotes] = useState<{ id: number; notes: string } | null>(null); useEffect(() => { load(); }, []); function load() { api.listVolunteers() .then(vs => setVolunteers(vs as AdminVolunteer[])) .catch(() => setError('Could not load volunteers.')); } async function handleCreate(e: React.FormEvent) { e.preventDefault(); setCreating(true); try { const av = await api.createVolunteer(form); setVolunteers(prev => [...prev, av]); setShowCreate(false); setForm(EMPTY_FORM); if (av.invite_token) { setInviteLink({ name: av.name, token: av.invite_token }); } } catch (err: any) { setError(err.message); } finally { setCreating(false); } } async function handleToggleActive(v: AdminVolunteer) { try { const updated = await api.updateVolunteer(v.id, { active: !v.active }); setVolunteers(prev => prev.map(vol => vol.id === v.id ? { ...vol, ...updated } : vol)); } catch (err: any) { setError(err.message); } } async function handlePromoteTrainee(v: AdminVolunteer) { try { const updated = await api.updateVolunteer(v.id, { is_trainee: false }); setVolunteers(prev => prev.map(vol => vol.id === v.id ? { ...vol, ...updated } : vol)); } catch (err: any) { setError(err.message); } } async function handleSaveNotes(id: number, notes: string) { try { await api.updateVolunteer(id, { admin_notes: notes }); setVolunteers(prev => prev.map(v => v.id === id ? { ...v, admin_notes: notes } : v)); setEditNotes(null); } catch (err: any) { setError(err.message); } } async function handleResendInvite(v: AdminVolunteer) { try { const { invite_token } = await api.resendInvite(v.id); setInviteLink({ name: v.name, token: invite_token }); } catch (err: any) { setError(err.message); } } function toggleOpsRole(role: string) { const current = form.operational_roles ? form.operational_roles.split(',').filter(Boolean) : []; const next = current.includes(role) ? current.filter(r => r !== role) : [...current, role]; setForm(f => ({ ...f, operational_roles: next.join(',') })); } const inviteUrl = inviteLink ? `${window.location.origin}/activate?token=${inviteLink.token}` : ''; return (

Volunteers

{error &&

{error}

} {inviteLink && (
Invite link for {inviteLink.name}:
{inviteUrl}
)} {showCreate && (

New Volunteer

Operational roles {OPERATIONAL_ROLES.map(r => ( ))}
)} {volunteers.length === 0 ? (

No volunteers found.

) : ( {volunteers.map(v => ( {expandedId === v.id && (
Name Email Role Op. Roles Shifts Status Actions
{v.active ? v.name : `${v.name} (inactive)`} {v.is_trainee && Trainee} {v.email} {v.role} {v.operational_roles || '—'} {v.completed_shifts} {v.active ? 'Active' : 'Inactive'} {v.is_trainee && ( )} {v.invite_token && ( )}
{editNotes?.id === v.id ? (