feat(08-02): activity breakdown + recent entries + meetings + page wiring (Task 2)

- New EngagementProfileBreakdown: Time/Communication/Meetings subsections, after-hours
  and Zoom conditional rows, py-2 per UI-SPEC override
- New EngagementRecentEntries: collapsible list up to 10, Billable badge, Set<string>
  expand state, empty-state copy
- New EngagementRecentMeetings: collapsible list up to 10, matched entries + attendees
  in expanded view, (no subject) fallback, Set<string> expand state
- Page updated: 3 new component imports + breakdown/entries/meetings mounted in order
- No dangerouslySetInnerHTML; D-01/D-22 guard rails untouched
This commit is contained in:
lorentz 2026-05-07 20:50:56 -04:00
parent df78ab8fa5
commit 0be0c1f7f8
4 changed files with 362 additions and 1 deletions

View file

@ -7,6 +7,9 @@ import { EngagementPeriodChips, type EngagementPeriod } from '@/components/mobil
import { EngagementProfileSkeleton } from '@/components/mobile/EngagementProfileSkeleton';
import { EngagementProfileHeader } from '@/components/mobile/EngagementProfileHeader';
import { EngagementProfileMetricGrid } from '@/components/mobile/EngagementProfileMetricGrid';
import { EngagementProfileBreakdown } from '@/components/mobile/EngagementProfileBreakdown';
import { EngagementRecentEntries } from '@/components/mobile/EngagementRecentEntries';
import { EngagementRecentMeetings } from '@/components/mobile/EngagementRecentMeetings';
interface ApiResponse {
user: {
@ -201,7 +204,24 @@ export default function MobileEngagementUserProfilePage({
daysWorked={daysWorkedForPeriod}
meetingsAttended={Number(meetingsAttended)}
/>
{/* Task 2 will mount EngagementProfileBreakdown + EngagementRecentEntries + EngagementRecentMeetings here. */}
<EngagementProfileBreakdown
hoursWorked={hoursForPeriod}
billableHours={billableForPeriod}
daysWorked={daysWorkedForPeriod}
teamsMessages={
Number(snapshotForPeriod?.teams_chat_messages ?? 0) +
Number(snapshotForPeriod?.teams_private_messages ?? 0)
}
emailsSent={Number(snapshotForPeriod?.emails_sent ?? 0)}
afterHoursMessagesPct={data.afterHours.messagesPct}
afterHoursMeetingsPct={data.afterHours.meetingsPct}
meetingsAttended={Number(snapshotForPeriod?.teams_meetings_attended ?? 0)}
meetingsOrganized={Number(snapshotForPeriod?.teams_meetings_organized ?? 0)}
meetingDurationSeconds={Number(snapshotForPeriod?.meeting_duration_seconds ?? 0)}
zoomCalls={data.zoom ? Number(data.zoom.calls[periodKey].total) : null}
/>
<EngagementRecentEntries entries={data.recentEntries} />
<EngagementRecentMeetings meetings={data.recentTeamsMeetings} />
</>
)}
</div>