import { useCallback, useEffect, useState } from 'react'; import { api, type FollowUp, type PendingNda, type Staff, type Today as TodayData, type Todo, } from '../api.js'; import { DEAL_LABELS, Dialog, Panel, StatusBadge, formatDay } from '../components.js'; import { TodoRow } from '../workflow.js'; const INPUT = 'w-full rounded border border-gray-300 px-2 py-1 text-sm'; /** Groups anything with a person attached, for the team tab. */ function groupBy(rows: T[], name: (row: T) => string): [string, T[]][] { const groups = new Map(); for (const row of rows) { const key = name(row); groups.set(key, [...(groups.get(key) ?? []), row]); } return [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0])); } export default function Today({ staff, onOpenBuyer, onOpenBusiness, onChanged, }: { staff: Staff; onOpenBuyer: (id: string) => void; onOpenBusiness: (id: string) => void; /** Lets the nav badge follow along after every action. */ onChanged: () => void; }) { const [team, setTeam] = useState(false); const [data, setData] = useState(null); const [error, setError] = useState(null); const [followUp, setFollowUp] = useState(null); const [ending, setEnding] = useState(null); const load = useCallback( () => api .today(team ? undefined : staff.id) .then((res) => { setData(res); setError(null); }) .catch((err: Error) => setError(err.message)), [team, staff.id], ); useEffect(() => { void load(); }, [load]); async function refresh() { await load(); onChanged(); } const empty = data && data.todos.length === 0 && data.follow_ups.length === 0 && data.pending_ndas.length === 0; return (
{[ { label: 'My day', value: false }, { label: 'Team', value: true }, ].map((tab) => ( ))}
{data && ( {data.counts.overdue_todos > 0 && ( {data.counts.overdue_todos} overdue )} {data.counts.overdue_todos > 0 && ' · '} {data.counts.total} item{data.counts.total === 1 ? '' : 's'} )}
{error &&

{error}

} {empty && (
Nothing due. Enjoy your coffee.
)} {data && data.todos.length > 0 && ( {team ? ( groupBy(data.todos, (todo) => todo.assigned_to.name).map(([name, todos]) => (

{name}

)) ) : ( )}
)} {data && data.follow_ups.length > 0 && ( {team ? ( groupBy(data.follow_ups, (row) => row.created_by_name ?? 'unassigned').map( ([name, rows]) => (

{name}

{rows.map((row) => ( ))}
), ) ) : ( data.follow_ups.map((row) => ( )) )}
)} {data && data.pending_ndas.length > 0 && ( {data.pending_ndas.map((row) => ( ))} )} {followUp && ( setFollowUp(null)} onDone={() => { setFollowUp(null); void refresh(); }} /> )} {ending && ( setEnding(null)} onDone={() => { setEnding(null); void refresh(); }} /> )}
); } function TodoList({ todos, onChanged, open, }: { todos: (Todo & { overdue?: boolean })[]; onChanged: () => void; open: { onOpenBuyer: (id: string) => void; onOpenBusiness: (id: string) => void }; }) { return (
    {todos.map((todo) => ( { // Deals and rounds live on the buyer page, so that wins when both exist. if (context.buyer_id) open.onOpenBuyer(context.buyer_id); else if (context.business_id) open.onOpenBusiness(context.business_id); }} /> ))}
); } function FollowUpRow({ row, onOpenBuyer, onOpenBusiness, onFollowUp, onEnd, }: { row: FollowUp; onOpenBuyer: (id: string) => void; onOpenBusiness: (id: string) => void; onFollowUp: (row: FollowUp) => void; onEnd: (row: FollowUp) => void; }) { return (
{' '} about{' '} , info sent {formatDay(row.follow_up_at)}
); } function PendingNdaRow({ row, team, onOpenBuyer, }: { row: PendingNda; team: boolean; onOpenBuyer: (id: string) => void; }) { return (
, sent {formatDay(row.sent_at)} {team && {row.created_by_name ?? '—'}}
); } /** "Follow-up sent" always writes a note; the choice is whether to keep waiting. */ function FollowUpDialog({ row, onClose, onDone, }: { row: FollowUp; onClose: () => void; onDone: () => void; }) { const [comment, setComment] = useState(''); const [rearm, setRearm] = useState(true); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); async function confirm() { setBusy(true); try { await api.followUpSent(row.id, comment, rearm); onDone(); } catch (err) { setError((err as Error).message); setBusy(false); } } return (

{row.buyer_name} about {row.business_name}