# PMFlow Dev Plan — User / Skill / Workload Modules **Stack:** FastAPI + React + shadcn/ui + Tailwind | **Timeline:** ~11 days, 1 dev --- ## Phase 1 — Core Foundation (Day 1–3) ### Day 1 — DB Migration + User CRUD **DB** ```sql ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT TRUE; CREATE TABLE user_projects ( id BIGSERIAL PRIMARY KEY, tenant_id BIGINT NOT NULL DEFAULT 1, user_id BIGINT NOT NULL, project_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenant_id) REFERENCES tenants(id), FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (project_id) REFERENCES projects(id), UNIQUE(tenant_id, user_id, project_id) ); ``` **Backend** - [x] Update ORM: `User.is_active`, new `UserProject` model - [x] `GET /api/users` — paginated list with filters (role/status/project/workload/skill) - [x] `POST /api/users` — create user (bcrypt password, auto-init `user_workloads` row with 40h/week) - [x] `PUT /api/users/{id}` — update user - [x] `DELETE /api/users/{id}` — block if user has incomplete tasks - [x] `POST /api/users/{id}/reset-password` - [x] `PATCH /api/users/{id}/status` — disable: clear `tasks.assignee_id`, zero out workload - [x] `POST /api/users/import` — bulk import via Excel (max 50 rows, return per-row result) - [x] Transaction: delete user → cascade delete `user_skill_bindings` + `user_workloads` - [x] Log all mutations (actor, time, diff) **Frontend** - [x] Routes: `/users`, `/skills`, `/workloads`, `/workloads/:id` - [x] `/users` page: top bar (title + New/Import/Bulk-delete buttons + search) - [x] Right filter panel (280px, collapsible): role / status / project / workload-state / skill multi-select - [x] Table: avatar+name / email / role badge / linked projects / top-3 skill tags / workload% / status / actions - [x] New/Edit user modal (form validation, edit pre-fills data) - [x] Reset password modal - [x] Bulk import modal (preview first 10 rows, highlight errors, show import result summary) - [x] Workload% color: green <70%, yellow 70–100%, red >100% --- ### Day 2 — Skill & Workload APIs **Backend — Skills** - [x] Seed system skill tags on startup (Python, FastAPI, LangChain, React, Prompt Eng, etc.) - [x] `GET /api/skill-tags` — list with category filter - [x] `POST /api/skill-tags` — admin only - [x] `PUT /api/skill-tags/{id}` — system tags: category only; custom: name + category - [x] `DELETE /api/skill-tags/{id}` — block if `is_system=true`; cascade delete `user_skill_bindings` - [x] `GET /api/users/{id}/skills` — user skill bindings - [x] `POST /api/users/{id}/skills` — bind skill (default: beginner, scores 0.0) - [x] `PUT /api/users/{id}/skills/{skill_id}` — update proficiency / scores - [x] `DELETE /api/users/{id}/skills/{skill_id}` — unbind (block system-auto-bound skills) **Backend — Workload** - [x] Workload calc util: `ratio = assigned_hours / ((max_weekly_hours / 5) * remaining_workdays)` - [x] `GET /api/workloads` — team list with project/date filters - [x] `GET /api/workloads/{user_id}` — personal detail - [x] `PUT /api/workloads/{user_id}` — update max weekly hours (triggers ratio recalc) - [x] `GET /api/workloads/{user_id}/tasks` — assigned task list - [x] `GET /api/workloads/{user_id}/trend` — daily ratio for last 7/30 days - [x] Cron job at 00:00 daily: recalc ratio for all users based on updated remaining workdays - [x] Auto-trigger ratio recalc on: task assign / estimate change / task complete / task delete --- ### Day 3 — Skill & Workload Frontend **Frontend — Skill Tags (`/skills`)** - [x] Table: name / category badge / linked-user count / actions - [x] System tags: show "System" label, disable delete, disable name edit - [x] New/Edit skill tag modal - [x] Category badge colors: dev=blue / algorithm=purple / product=orange / pm=green / qa=gray **Frontend — User Skill Detail (`/users/:id/skills`)** - [x] Top bar: avatar / name / role / linked projects / "Batch Edit" + "Refresh Scores" buttons - [x] Left 60%: skills grouped by category (collapsible), each row: name / proficiency dropdown / quality score / efficiency score / last used (read-only) - [x] "Remove" button per row (disabled for system-bound skills) - [x] "Add Skill" button → modal to select skill tags - [x] Right 40%: recharts `RadarChart` — weighted score per skill, filter by category, hover shows detail - [x] "Export Chart" button (save as image) **Frontend — Team Workload (`/workloads`)** - [x] Filter bar: project select + date range (this week / this month / custom) - [x] 3 stat cards: avg team ratio / headcount by state (idle/normal/overloaded) / AI alert summary - [x] Table: member info / projects / assigned hours / max weekly hours / ratio% / remaining hours / actions - [x] Overloaded rows: red highlight + warning icon **Frontend — Personal Workload (`/workloads/:id`)** - [x] Header: ratio% / remaining hours / "Adjust Hours" button - [x] recharts `LineChart`: daily ratio trend (7d/30d toggle), 70% + 100% reference lines, overloaded segments in red - [x] Task list table: title / project / priority / est. hours / due date / status / view link - [x] Task list filters: status / priority / due date --- ## Phase 2 — Sync Logic + AI (Day 4–8) ### Day 4–5 — Cross-module Sync **Backend** - [x] User create → init empty `user_skill_bindings`, init `user_workloads` - [x] User delete → transaction: delete bindings + workload record - [x] User disable → zero `current_assigned_hours` + `workload_ratio`, clear task assignments - [x] Skill score update → recalc top-3 skills for user, sync to user list response - [x] Workload ratio change → sync to user list `workload_ratio` field - [x] Skill proficiency level → pass to task estimation: expert → lower est. hours → lower ratio **Frontend** - [x] User list table: workload% and top-3 skill tags update without page reload (TanStack Query invalidation) - [x] Skill detail page: manual score edit shows warning "AI will overwrite on next update" - [x] Workload team table: ratio column updates in real-time after task changes --- ### Day 6–7 — SSTC Kanban Integration **Backend** - [x] Task assign endpoint: filter candidates to current-project users only, exclude disabled users - [x] `GET /api/tasks/recommend-assignees?task_id=` — rank users by skill match + proficiency + ratio (<70% preferred) - [x] Task status → `done`: trigger async job to update `current_assigned_hours` (subtract est. hours) and recalc ratio - [x] `GET /api/workloads/summary` — team health card data for kanban top bar **Frontend** - [x] SSTC kanban top bar: "Team Workload Health" card (avg ratio + overload count), click → `/workloads` - [x] Task breakdown panel: recommended assignees list shows name / role / top skills / ratio badge - [x] Assignee dropdown: disabled users grayed out and unselectable - [x] Task status change → trigger workload refetch (TanStack Query) --- ### Day 8 — AI Features **Backend** - [x] New LangChain prompt: skill score update — input: task quality_score + (est_hours / actual_hours), output: quality/efficiency score delta per skill tag - [x] Trigger on `tasks.status = done`: async call above prompt, apply weighted update (higher priority task = higher weight) - [x] New LangChain prompt: workload optimization — input: all members' skills + ratios, output: suggested task reassignments - [x] `POST /api/workloads/ai-optimize` — run optimization, return preview plan (no auto-apply) - [x] `POST /api/workloads/ai-optimize/apply` — apply approved plan (batch update `tasks.assignee_id`) - [x] Overload alert: ratio >100% → send notification (Feishu/email); if unresolved 24h → auto-generate transfer suggestion **Frontend** - [x] "AI Optimize Assignments" button → show optimization preview modal (old vs new assignee per task) - [x] Confirm → apply; Cancel → discard - [x] Overload alert banner on team workload page when any member >100% --- ## Phase 3 — Polish (Day 9–11) ### Day 9 — Filters / Export / Bulk Actions - [x] Verify all filter combinations work correctly across all 3 pages - [x] Export workload report (Excel/PDF) - [x] Export skill radar chart (PNG) - [x] Bulk delete users (with incomplete-task guard) - [x] Bulk assign project / role to selected users ### Day 10 — Permissions | Role | Users | Skills | Workload | |------|-------|--------|----------| | admin | Full | Full | Full | | manager | Own project users (no delete) | Bind/edit scores for own project | View + optimize own project | | member | View all + edit own profile | View own + edit own proficiency | View own + edit own weekly hours | - [x] Backend: permission middleware on all mutation endpoints - [x] Frontend: route guards + button-level permission checks ### Day 11 — Final QA - [x] Dark mode audit across all new pages - [x] Empty state / loading state / error state for all tables and cards - [x] Toast notifications for all CRUD operations - [x] End-to-end flow test: create user → bind skills → assign task → complete task → verify score + workload update - [x] Fix bugs, merge --- ## Reference **Workload ratio formula:** ``` ratio = current_assigned_hours / ((max_weekly_hours / 5) * remaining_workdays_this_week) ``` **Ratio recalc triggers:** task assign · est hours change · task complete · task delete · weekly hours update · daily cron **API response format:** ```json { "code": 200, "message": "ok", "data": {} } ```