docs(08-02): SUMMARY — mobile engagement profile page complete (SC#2 partial)
This commit is contained in:
parent
435051ddc8
commit
7fcb156cfc
1 changed files with 99 additions and 0 deletions
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
phase: 08-engagement-user-profile-new
|
||||
plan: "02"
|
||||
subsystem: mobile-engagement
|
||||
tags: [mobile, engagement, profile, page, components]
|
||||
dependency_graph:
|
||||
requires:
|
||||
- GET /api/mobile/engagement/user/[userId]/photo
|
||||
- GET /api/engagement/user/[userId]
|
||||
- components/mobile/EngagementPeriodChips.tsx
|
||||
- components/mobile/EngagementUserRow.tsx#getInitials
|
||||
provides:
|
||||
- GET /mobile/engagement/[userId] (real route, not modal)
|
||||
affects:
|
||||
- app/mobile/engagement/[userId]/page.tsx
|
||||
- components/mobile/EngagementProfileSkeleton.tsx
|
||||
- components/mobile/EngagementProfileHeader.tsx
|
||||
- components/mobile/EngagementProfileMetricGrid.tsx
|
||||
- components/mobile/EngagementProfileBreakdown.tsx
|
||||
- components/mobile/EngagementRecentEntries.tsx
|
||||
- components/mobile/EngagementRecentMeetings.tsx
|
||||
- app/mobile/engagement/page.tsx (sessionStorage scroll shim only)
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "App Router dynamic route `[userId]` with `params: Promise<{userId}>` unwrapped via React.use"
|
||||
- "Image error → initials fallback via local useState toggle"
|
||||
- "rAF-throttled scroll capture + retry-on-restore (D-04 fallback)"
|
||||
key_files:
|
||||
modified:
|
||||
- app/mobile/engagement/page.tsx
|
||||
- components/mobile/EngagementRecentEntries.tsx
|
||||
- components/mobile/EngagementRecentMeetings.tsx
|
||||
created:
|
||||
- app/mobile/engagement/[userId]/page.tsx
|
||||
- components/mobile/EngagementProfileSkeleton.tsx
|
||||
- components/mobile/EngagementProfileHeader.tsx
|
||||
- components/mobile/EngagementProfileMetricGrid.tsx
|
||||
- components/mobile/EngagementProfileBreakdown.tsx
|
||||
- components/mobile/EngagementRecentEntries.tsx
|
||||
- components/mobile/EngagementRecentMeetings.tsx
|
||||
decisions:
|
||||
- "Profile is a real route, not a modal — preserves device-back behavior (D-04 intent)"
|
||||
- "Photo fallback to initials is a render-time `<img>` onError → useState toggle (no double-fetch)"
|
||||
- "Coerce Postgres NUMERIC strings to Number() at render — pg returns numeric columns as strings; period totals are parseFloat'd in the API but recentEntries[]/matchedEntries[] hours_worked are passed through verbatim"
|
||||
- "Scroll restoration: SC#2 is partial — sessionStorage shim with rAF retry was added but did not reliably restore window scroll on this layout in user testing. User accepted as a known limitation; no follow-up plan filed."
|
||||
metrics:
|
||||
duration_minutes: 60
|
||||
completed_date: "2026-05-08"
|
||||
tasks_completed: 4
|
||||
files_modified: 3
|
||||
files_created: 7
|
||||
requirements_addressed: [ENG-06, ENG-07, ENG-08]
|
||||
---
|
||||
|
||||
# Phase 8 Plan 02: Mobile Engagement User Profile Summary
|
||||
|
||||
**One-liner:** Mobile engagement user profile at `/mobile/engagement/[userId]` — a real Next.js route (not a modal) composing six new components on top of the existing `/api/engagement/user/[userId]` endpoint plus the Plan 01 photo proxy.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1a | Page shell + Skeleton + period/fetch wiring | 3247c92 | app/mobile/engagement/[userId]/page.tsx, components/mobile/EngagementProfileSkeleton.tsx |
|
||||
| 1b | Identity header + 2×2 metric grid + page wiring | df78ab8 | components/mobile/EngagementProfileHeader.tsx, components/mobile/EngagementProfileMetricGrid.tsx, app/mobile/engagement/[userId]/page.tsx |
|
||||
| 2 | Activity breakdown + Recent entries + Recent meetings | 0be0c1f | components/mobile/EngagementProfileBreakdown.tsx, components/mobile/EngagementRecentEntries.tsx, components/mobile/EngagementRecentMeetings.tsx, app/mobile/engagement/[userId]/page.tsx |
|
||||
| 3 | Manual verification (scroll restoration) | n/a | (user-tested) |
|
||||
|
||||
### Post-test fixes
|
||||
|
||||
| Fix | Commit | Files | Reason |
|
||||
|-----|--------|-------|--------|
|
||||
| Coerce hours_worked to Number before .toFixed | 81079ad | components/mobile/EngagementRecentEntries.tsx, components/mobile/EngagementRecentMeetings.tsx | Postgres NUMERIC arrives as string via pg — runtime TypeError on render |
|
||||
| Scroll-restoration shim attempt | 6bdc937, then revised in subsequent commits | app/mobile/engagement/page.tsx | Mobile shell uses overflow-y-auto on `<main>` but the document scrolls in practice; sessionStorage save+restore added with rAF retry. SC#2 still partial — see Decisions. |
|
||||
|
||||
## What Was Built
|
||||
|
||||
**`/mobile/engagement/[userId]` page** — full profile surface composed of:
|
||||
|
||||
1. **H1** — display name from Graph user
|
||||
2. **Sticky period chips** (D7/D30/D90) — defaults to D30, refetches on change
|
||||
3. **Identity card** — Graph photo via the Plan 01 proxy with onError → `getInitials(displayName)` fallback; title, department, mailto link, last-active label (relative ≤7d via `date-fns`, absolute >7d via `formatInUserTimezone`)
|
||||
4. **2×2 metric grid** — Hours worked, Billable hours, Days worked, Meetings attended (period-scoped)
|
||||
5. **Activity breakdown card** — Time / Communication / Meetings subsections with conditional Zoom + after-hours rows
|
||||
6. **Recent time entries** — collapsed list of recent Autotask `time_entries` with billable badge + truncated note
|
||||
7. **Recent Teams meetings** — list with participant names, client attendees, inline matched time entries
|
||||
|
||||
Loading state renders `EngagementProfileSkeleton`. Error states: 404 → "User not found" with back link; 5xx/network → skeleton + Retry button (uses `retryNonce` to re-trigger the fetch effect).
|
||||
|
||||
Reuses `/api/engagement/user/[userId]` verbatim — no API modifications. The existing `EngagementUserRow.tsx` link to `/mobile/engagement/${graphUserId}` (already present from Phase 7) now reaches a real destination.
|
||||
|
||||
## Verification Results
|
||||
|
||||
**SC#1 — Page renders all sections:** ✓ Passed (user-confirmed)
|
||||
**SC#2 — Scroll restoration on back gesture:** ⚠ Partial — list scrolls back to top, not to the previous row position. The mobile shell's layout has `<main>` with `overflow-y-auto` but in practice the document scrolls (`window.scrollY` carries the value, `<main>.scrollTop` stays 0). A sessionStorage shim with rAF retry was added but did not consistently restore `window.scrollY` after the row list re-rendered. User accepted as a known limitation.
|
||||
|
||||
## Notable Deviations
|
||||
|
||||
None. Plan was followed; the only adjustments were the post-test fixes documented above.
|
||||
Loading…
Add table
Add a link
Reference in a new issue