import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { officerSession } from '../../lib/api' import type { FieldVisit, VisitState } from '../../lib/api' import { ActionChip, Empty, Field, GhostBtn, KpiTile, Modal, PrimaryBtn, StatusPill, inputCls, } from '../../components/ui/bits' import { todayIso } from '../../lib/demo/seed' import { logOutcome, useVisits } from '../../lib/visitStore' import { useScope } from './officerScope' const FILTERS: { key: VisitState | 'ALL'; label: string }[] = [ { key: 'ALL', label: 'All' }, { key: 'PLANNED', label: 'Planned' }, { key: 'OVERDUE', label: 'Overdue' }, { key: 'DONE', label: 'Done' }, ] /** * The field officer playbook: ranked interventions with due dates. Logging an * outcome closes the loop the deck's process flow describes — the outcome and * any override feed the next retraining cycle. */ export function VisitsPage() { const navigate = useNavigate() const { block } = useScope() const officer = officerSession()?.fullName ?? 'Field Officer' const { visits } = useVisits(block, officer) const [filter, setFilter] = useState('ALL') const [logging, setLogging] = useState(null) const rows = visits .filter((v) => filter === 'ALL' || v.state === filter) .sort((a, b) => a.priority - b.priority || a.dueDate.localeCompare(b.dueDate)) const done = visits.filter((v) => v.state === 'DONE').length const overdue = visits.filter((v) => v.state === 'OVERDUE').length const planned = visits.filter((v) => v.state === 'PLANNED').length return (
{FILTERS.map((f) => ( setFilter(f.key)}> {f.label} ))} {rows.length} shown
{rows.map((v) => ( ))}
Priority Enterprise Recommended action Block Due State Outcome
{v.priority === 1 ? 'Act first' : 'Routine'} {v.enterpriseId} {v.action} {v.block} {v.dueDate} {v.outcome ?? '—'} {v.state !== 'DONE' && setLogging(v)}>Log outcome}
{!rows.length && }
{logging && setLogging(null)} />}
) } const PRESET_OUTCOMES = [ 'Stock and receivables verified against the ledger.', 'Instalment date moved to align with the earning season.', 'Working-capital top-up recommended to the branch.', 'Owner unavailable — revisit required.', 'No action needed; figures explained by a one-off event.', ] function LogOutcomeModal({ visit, onClose }: { visit: FieldVisit; onClose: () => void }) { const [preset, setPreset] = useState(PRESET_OUTCOMES[0]) const [note, setNote] = useState('') function save() { logOutcome(visit.visitId, note.trim() ? `${preset} ${note.trim()}` : preset) onClose() } return (
{visit.visitId} · {visit.action} · due {visit.dueDate}