'use client'; /* EngagementProfileBreakdown — phase 08 (D-14..D-17). * Purpose: Single Card with three labeled subsections — Time / Communication / Meetings. * Rows use py-2 (UI-SPEC override of D-16's py-1.5). After-hours and Zoom rows are * conditional based on value presence (D-15, D-17). */ import { Card, CardContent } from '@/components/ui/card'; export interface EngagementProfileBreakdownProps { // Time subsection hoursWorked: number; billableHours: number; daysWorked: number; // Communication subsection teamsMessages: number; // chat + private summed by caller emailsSent: number; afterHoursMessagesPct: number; afterHoursMeetingsPct: number; // Meetings subsection meetingsAttended: number; meetingsOrganized: number; meetingDurationSeconds: number; // Optional: only render Zoom row if non-null (D-17 presence rule) zoomCalls: number | null; } const subsectionLabel = "text-sm font-semibold text-muted-foreground mb-2"; const metricRow = "flex justify-between text-sm py-2"; function MetricRow({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } export function EngagementProfileBreakdown(props: EngagementProfileBreakdownProps) { const utilizationPct = props.hoursWorked > 0 ? Math.round((props.billableHours / props.hoursWorked) * 100) : null; const meetingHours = props.meetingDurationSeconds / 3600; const showAfterHours = props.afterHoursMessagesPct > 0 || props.afterHoursMeetingsPct > 0; const showZoom = props.zoomCalls !== null && props.zoomCalls !== undefined; return ( {/* Time */}

Time

{utilizationPct !== null && ( )}
{/* Communication */}

Communication

{showAfterHours && (
After-hours · {props.afterHoursMessagesPct}% messages, {props.afterHoursMeetingsPct}% meetings
)}
{/* Meetings */}

Meetings

{showZoom && ( )}
); }