/* ActiveEngineers — today's engineers grouped by working vs PTO. * * Top section: engineers with billable/work time entries today. Each row's * ticket count is a button that opens a dialog listing the specific tickets * they logged time against (and hours per ticket). * * Bottom section: collapsible list of engineers whose only time today is on a * PTO/Vacation allocation code. Empty when nobody is out. */ 'use client'; import { useState } from 'react'; import { Activity, ChevronDown, Palmtree } from 'lucide-react'; import { EmptyState } from '@/components/ui/empty-state'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@/components/ui/dialog'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { Badge } from '@/components/ui/badge'; import { cn } from '@/lib/utils'; interface Ticket { id: string; ticketNumber: string | null; title: string | null; description: string | null; statusLabel: string | null; hours: number; } interface Engineer { resourceId: string; name: string; hours: number; ticketsTouched: number; tickets: Ticket[]; isPto: boolean; ptoNote: string | null; } interface ActiveEngineersProps { working: Engineer[]; pto: Engineer[]; } export function ActiveEngineers({ working, pto }: ActiveEngineersProps) { const [ticketsFor, setTicketsFor] = useState(null); const [ptoOpen, setPtoOpen] = useState(false); if (working.length === 0 && pto.length === 0) { return ( ); } const max = working.reduce((m, e) => Math.max(m, e.hours), 0) || 1; const totalHours = working.reduce((s, e) => s + e.hours, 0); const totalTickets = working.reduce((s, e) => s + e.ticketsTouched, 0); return (
{working.length === 0 ? (
No working time logged yet today.
) : (
{working.map((e) => { const pct = (e.hours / max) * 100; return (
{e.name}
{e.hours.toFixed(1)}h
); })}
Total today {totalHours.toFixed(1)}h{' '} across {totalTickets}{' '} ticket{totalTickets === 1 ? '' : 's'}
)} {pto.length > 0 && ( {pto.length} on PTO / Vacation
    {pto.map((e) => (
  • {e.name} {e.ptoNote ? ( {e.ptoNote} ) : null}
  • ))}
)} !open && setTicketsFor(null)}> {ticketsFor?.name} · today {ticketsFor ? `${ticketsFor.hours.toFixed(1)}h across ${ticketsFor.ticketsTouched} ticket${ ticketsFor.ticketsTouched === 1 ? '' : 's' }` : ''}
    {ticketsFor?.tickets.map((t) => ( ))}
); } /* Single ticket row inside the per-engineer dialog. Hovering anywhere on the * row reveals a popover with the ticket's current status and description. */ function TicketRow({ ticket: t }: { ticket: Ticket }) { const [open, setOpen] = useState(false); const hasHoverDetail = !!(t.description || t.statusLabel); const row = (
  • setOpen(true) : undefined} onMouseLeave={hasHoverDetail ? () => setOpen(false) : undefined} >
    {t.ticketNumber ?? `#${t.id}`} {t.title ? (
    {t.title}
    ) : null}
    {t.hours.toFixed(2)}h
  • ); if (!hasHoverDetail) return row; return ( {row} e.preventDefault()} >
    {t.statusLabel ? (
    Status {t.statusLabel}
    ) : null} {t.description ? (
    Description

    {t.description}

    ) : (
    No description on this ticket.
    )}
    ); }