docs(02-02): complete RankingDashboard plan — SUMMARY, STATE, ROADMAP updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 (Phase 4 can be parallelize
|
|||||||
|
|
||||||
| Phase | Plans Complete | Status | Completed |
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. AI Ranking Backend | 4/4 | Complete | 2026-02-27 |
|
| 1. AI Ranking Backend | 4/4 | Complete | 2026-02-27 |
|
||||||
| 2. Ranking Dashboard UI | 2/3 | In Progress| |
|
| 2. Ranking Dashboard UI | 2/3 | In Progress| |
|
||||||
| 3. Advancement + Email | 0/TBD | Not started | - |
|
| 3. Advancement + Email | 0/TBD | Not started | - |
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: in_progress
|
status: in_progress
|
||||||
last_updated: "2026-02-27T00:25:00.000Z"
|
last_updated: "2026-02-27T08:48:11Z"
|
||||||
progress:
|
progress:
|
||||||
total_phases: 4
|
total_phases: 4
|
||||||
completed_phases: 1
|
completed_phases: 1
|
||||||
total_plans: 10
|
total_plans: 10
|
||||||
completed_plans: 5
|
completed_plans: 6
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -23,11 +23,11 @@ See: .planning/PROJECT.md (updated 2026-02-26)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 2 of 4 (Ranking Dashboard UI)
|
Phase: 2 of 4 (Ranking Dashboard UI)
|
||||||
Plan: 1 of 3 in current phase (Phase 2 Plan 1 complete)
|
Plan: 2 of 3 in current phase (Phase 2 Plan 2 complete)
|
||||||
Status: In progress
|
Status: In progress
|
||||||
Last activity: 2026-02-27 — Plan 02-01 complete: saveReorder mutation + Ranking tab entry point + RankingDashboard stub
|
Last activity: 2026-02-27 — Plan 02-02 complete: Full RankingDashboard with drag-and-drop, AI vs override badges, Sheet detail panel
|
||||||
|
|
||||||
Progress: [████░░░░░░] ~50%
|
Progress: [█████░░░░░] ~60%
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
@@ -44,11 +44,12 @@ Progress: [████░░░░░░] ~50%
|
|||||||
| 02-ranking-dashboard-ui | 1 | ~5 min | ~5 min |
|
| 02-ranking-dashboard-ui | 1 | ~5 min | ~5 min |
|
||||||
|
|
||||||
**Recent Trend:**
|
**Recent Trend:**
|
||||||
- Last 5 plans: 01-01 (~3 min), 01-02 (~3 min), 01-03 (~4 min), 01-04 (~8 min), 02-01 (~5 min)
|
- Last 5 plans: 01-02 (~3 min), 01-03 (~4 min), 01-04 (~8 min), 02-01 (~5 min), 02-02 (~8 min)
|
||||||
- Trend: Fast (service-layer + UI wiring tasks)
|
- Trend: Fast (service-layer + UI implementation tasks)
|
||||||
|
|
||||||
*Updated after each plan completion*
|
*Updated after each plan completion*
|
||||||
| Phase 02-ranking-dashboard-ui P01 | 5 | 2 tasks | 3 files |
|
| Phase 02-ranking-dashboard-ui P01 | 5 | 2 tasks | 3 files |
|
||||||
|
| Phase 02-ranking-dashboard-ui P02 | 8 | 1 task | 1 file |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -73,6 +74,9 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 01-04]: retroactiveScan uses RETROACTIVE triggerType to distinguish from MANUAL/AUTO/QUICK — prevents duplicate re-runs on subsequent scans
|
- [Phase 01-04]: retroactiveScan uses RETROACTIVE triggerType to distinguish from MANUAL/AUTO/QUICK — prevents duplicate re-runs on subsequent scans
|
||||||
- [02-01]: ReorderEvent type defined locally in ranking.ts (not exported) — only consumed by saveReorder procedure
|
- [02-01]: ReorderEvent type defined locally in ranking.ts (not exported) — only consumed by saveReorder procedure
|
||||||
- [02-01]: saveReorder is append-only: full ordered list stored per event, latest entry per category = current admin order, gives full audit trail
|
- [02-01]: saveReorder is append-only: full ordered list stored per event, latest entry per category = current admin order, gives full audit trail
|
||||||
|
- [02-02]: Double cast (as unknown as RankedProjectEntry[]) required for Prisma JsonValue — direct cast rejected by TypeScript strict mode
|
||||||
|
- [02-02]: getFullDetail returns { project, assignments, stats } shape — title accessed via .project.title not root level
|
||||||
|
- [02-02]: saveReorder has no onSuccess invalidation — avoids re-fetch that would reset localOrder and cause snap-back
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -87,5 +91,5 @@ None yet.
|
|||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-02-27
|
Last session: 2026-02-27
|
||||||
Stopped at: Completed 02-01-PLAN.md (saveReorder mutation + Ranking tab entry point + RankingDashboard stub)
|
Stopped at: Completed 02-02-PLAN.md (Full RankingDashboard with drag-and-drop, AI vs override badges, Sheet detail panel)
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
123
.planning/phases/02-ranking-dashboard-ui/02-02-SUMMARY.md
Normal file
123
.planning/phases/02-ranking-dashboard-ui/02-02-SUMMARY.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
phase: 02-ranking-dashboard-ui
|
||||||
|
plan: 02
|
||||||
|
subsystem: ui
|
||||||
|
tags: [react, dnd-kit, trpc, ranking, drag-and-drop, sheet-panel]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 02-01
|
||||||
|
provides: RankingDashboard stub, saveReorder mutation, Ranking tab entry point
|
||||||
|
provides:
|
||||||
|
- Full RankingDashboard component with drag-and-drop reorder, AI vs override visual states, and Sheet-based juror evaluation detail panel
|
||||||
|
affects: [02-03-advance-projects]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "useRef init guard: initialized.current prevents localOrder re-init from server data on every re-render — eliminates snap-back"
|
||||||
|
- "Fire-and-forget mutation inside setLocalOrder callback: setLocalOrder runs synchronously first, mutation fires async, no onSuccess invalidation"
|
||||||
|
- "Double cast via unknown: Prisma JsonValue cast to RankedProjectEntry[] requires (json ?? []) as unknown as RankedProjectEntry[]"
|
||||||
|
- "getFullDetail response shape: { project, assignments, stats } — title accessed as projectDetail.project.title"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: []
|
||||||
|
modified:
|
||||||
|
- src/components/admin/round/ranking-dashboard.tsx
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Double cast (as unknown as RankedProjectEntry[]) required for Prisma JsonValue — direct cast rejected by TypeScript strict mode"
|
||||||
|
- "getFullDetail returns { project, assignments, stats } shape, not flat — project title accessed via .project.title"
|
||||||
|
- "saveReorder mutation has no onSuccess invalidation — avoids triggering re-fetch that would reset localOrder"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "SortableProjectRow sub-component defined above export in same file (no separate file needed for inline sub-components)"
|
||||||
|
- "Per-category drag context: separate DndContext per category prevents cross-category drag"
|
||||||
|
|
||||||
|
requirements-completed: [DASH-01, DASH-02, DASH-03, DASH-04]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 8min
|
||||||
|
completed: 2026-02-27
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 2 Plan 02: Full RankingDashboard Component Summary
|
||||||
|
|
||||||
|
**Full RankingDashboard with per-category drag-and-drop (dnd-kit), AI vs override rank badges, snap-back-proof localOrder state, and lazy-loaded Sheet detail panel showing per-juror evaluation breakdown**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~8 min
|
||||||
|
- **Started:** 2026-02-27T08:40:00Z
|
||||||
|
- **Completed:** 2026-02-27T08:48:11Z
|
||||||
|
- **Tasks:** 1
|
||||||
|
- **Files modified:** 1
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Replaced RankingDashboard stub with full 486-line implementation
|
||||||
|
- DASH-01: Ranked project list per category (STARTUP / BUSINESS_CONCEPT) with composite score, pass rate, and evaluator count displayed per row
|
||||||
|
- DASH-02: Drag-and-drop reorder via GripVertical handle using dnd-kit (DndContext + SortableContext + useSortable), fire-and-forget saveReorder mutation
|
||||||
|
- DASH-03: localOrder stored in useState with useRef guard (`initialized.current`) — init fires once on first snapshot load, never re-initialized from server data; no snap-back
|
||||||
|
- DASH-04: Sheet panel opens on row click, lazy-loads `trpc.project.getFullDetail` (enabled only when selectedProjectId is set), displays stats summary and per-juror evaluation list filtered to SUBMITTED assignments for the current round
|
||||||
|
- AI-order rows display dark-blue rank badge (#N); admin-reordered rows display amber `#N (override)` badge
|
||||||
|
- "Run Ranking" button in header card calls `triggerAutoRank`, resets `initialized.current` to allow re-init on new snapshot
|
||||||
|
- Empty categories show a placeholder message instead of an empty drag zone
|
||||||
|
- TypeScript strict mode: 0 errors; build: PASSED
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
| Task | Name | Commit | Files |
|
||||||
|
|------|------|--------|-------|
|
||||||
|
| 1 | Build full RankingDashboard component | 6512e4e | src/components/admin/round/ranking-dashboard.tsx |
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `src/components/admin/round/ranking-dashboard.tsx` — Full component replacing stub (486 lines → includes SortableProjectRow sub-component + RankingDashboard main export)
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- **Double cast via unknown**: `(json ?? []) as unknown as RankedProjectEntry[]` — TypeScript strict mode rejects direct cast from Prisma `JsonValue`; intermediate `unknown` is required. Matches pattern from Phase 01-03.
|
||||||
|
- **getFullDetail response shape**: The procedure returns `{ project, assignments, stats }` (not flat) — `projectDetail.project.title`, not `projectDetail.title`.
|
||||||
|
- **No onSuccess invalidation in saveReorder**: Calling `utils.ranking.getSnapshot.invalidate()` in `onSuccess` would trigger a re-fetch that resets `localOrder` to server data, causing snap-back. Mutation only shows toast on error.
|
||||||
|
- **Per-category DndContext**: Separate `DndContext` per category prevents accidental cross-category drags.
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written. All type errors encountered were auto-fixed inline (Rule 1 — double cast pattern, Rule 1 — response shape).
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 1 - Bug] Prisma JsonValue cast requires double cast via unknown**
|
||||||
|
- **Found during:** Task 1 (typecheck)
|
||||||
|
- **Issue:** `(snapshot.startupRankingJson ?? []) as RankedProjectEntry[]` — TypeScript strict mode rejects because `JsonValue` and `RankedProjectEntry[]` don't sufficiently overlap
|
||||||
|
- **Fix:** Changed to `as unknown as RankedProjectEntry[]` (identical pattern used in Phase 01-03)
|
||||||
|
- **Files modified:** ranking-dashboard.tsx
|
||||||
|
- **Commit:** 6512e4e (same task commit)
|
||||||
|
|
||||||
|
**2. [Rule 1 - Bug] getFullDetail response shape — title not on root**
|
||||||
|
- **Found during:** Task 1 (typecheck)
|
||||||
|
- **Issue:** `projectDetail?.title` — getFullDetail returns `{ project, assignments, stats }`, not a flat object
|
||||||
|
- **Fix:** Changed to `projectDetail?.project.title`
|
||||||
|
- **Files modified:** ranking-dashboard.tsx
|
||||||
|
- **Commit:** 6512e4e (same task commit)
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None beyond the two auto-fixed type errors above.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- RankingDashboard fully functional — admin can view ranked projects, drag to reorder, see juror-level evaluation details in Sheet panel
|
||||||
|
- Plan 03 can now add the "Advance Projects" action button to the dashboard header
|
||||||
|
- saveReorder mutation is append-only audit log — Plan 03 can read latest reorder per category to determine final advance order
|
||||||
|
- Build and typecheck both pass with 0 errors
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 02-ranking-dashboard-ui*
|
||||||
|
*Completed: 2026-02-27*
|
||||||
Reference in New Issue
Block a user