Add design spec: programmer oversight (day-sheet approvals, monitor, report, dashboard)
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
# Programmer Oversight — Design Spec
|
||||
|
||||
**Date:** 2026-09-27 · **Branch:** `feat/programmer-tasks` · **Status:** Draft for review
|
||||
|
||||
Adds an oversight layer to the Programmer Tasks module: daily **Day Sheet** approvals with a
|
||||
remarks ↔ justification loop, a live **admin Monitor**, a PDF **Accomplishment Report**,
|
||||
and a gamified **Mine dashboard**.
|
||||
|
||||
## 1. Background
|
||||
|
||||
The Programmer Tasks module records programmers' dev work: tasks with a pause-aware timer,
|
||||
per-contributor work logs (assignee progress notes + helper-entered minutes), comments, and
|
||||
projects. Today:
|
||||
|
||||
- The admin cannot see who is working on what right now, or who is helping whom.
|
||||
- Nobody reviews or approves programmers' daily work.
|
||||
- There is no Accomplishment Report.
|
||||
- The "Mine" tab is a flat list of cards.
|
||||
|
||||
## 2. Goals
|
||||
|
||||
1. Admin sees each programmer's live state (working / paused / idle / away), current task,
|
||||
today's time, and help given/received.
|
||||
2. Each programmer's work is reviewed per day: approve, or disapprove with remarks; the
|
||||
programmer justifies (and may append corrections) until approved.
|
||||
3. Programmers generate their own Accomplishment Report (PDF, date-filtered); the admin
|
||||
generates one for any programmer.
|
||||
4. The Mine tab becomes a motivating dashboard with stats, a streak, badges, and tasks
|
||||
separated into In progress / Paused / Not started / Completed / Cancelled.
|
||||
|
||||
### Non-goals
|
||||
|
||||
- Live "help sessions" (help is derived from work logs after the fact).
|
||||
- XP/levels and team leaderboards.
|
||||
- Editing or deleting existing work-log entries (they stay append-only).
|
||||
- Migrating the existing DTR / task PDFs to the new shared PDF helpers.
|
||||
|
||||
## 3. Decisions
|
||||
|
||||
| Topic | Decision |
|
||||
|---|---|
|
||||
| Approval unit | **Day Sheet** — one per programmer per day; admin can **flag task rows** in it |
|
||||
| Submission | **Auto-submit at day end**, plus **manual early submit** |
|
||||
| Same-day approval | **Allowed**; later work that day reopens the sheet as "amended after approval" |
|
||||
| On disapprove | Programmer **justifies** and **may append corrective entries**; originals are immutable |
|
||||
| Late entries | Back-dated work logs allowed on **pending or disapproved** days, never on approved days; no backfilling days that have no sheet |
|
||||
| Helping | **Derived from work logs** (helper = work-log author ≠ task assignee) |
|
||||
| Report scope | **Toggle when generating**: approved days only (default) or include unapproved days (marked) |
|
||||
| Report layout | **By day**: Date · Task/Project · Accomplishment · Time, then summary and signatures |
|
||||
| Gamification | **Stat tiles + streak + badges** |
|
||||
| Notifications | **In-app + push** |
|
||||
| Monitor states | Working / Paused / Idle / **On leave** / **Pass slip** |
|
||||
| Tabs | Programmer: **Mine · All · Day Sheets** — Admin: **Monitor · Approvals · Mine · All** |
|
||||
| Core approach | Day-sheet table + append-only review thread + SECURITY DEFINER RPCs + pg_cron auto-submit + approval snapshot |
|
||||
|
||||
**"Admin" means `profiles.role = 'admin'` only.** The existing `isAdminProvider`
|
||||
(`lib/providers/profile_provider.dart:305`) also returns true for programmers, and the
|
||||
module's RLS treats both roles alike. All approval and monitor gating uses a new
|
||||
`isStrictAdminProvider` on the client and `role = 'admin'` in SQL.
|
||||
|
||||
## 4. Existing code this builds on
|
||||
|
||||
| Fact | Where |
|
||||
|---|---|
|
||||
| Task status is `queued \| in_progress \| completed \| cancelled`. **Paused** = `in_progress` whose latest lifecycle activity-log event is `paused`. **Not started** = `queued`. | `lib/models/programmer_task.model.dart:39-47` |
|
||||
| Work logs are append-only; the day is known only from `created_at`; assignee rows have `minutes = null`, helper rows have minutes. | `supabase/migrations/20260927140000_add_programmer_projects_worklogs_comments.sql:90-111` |
|
||||
| Duration core `computeEffectiveDuration` | `lib/utils/task_duration.dart:38-75` |
|
||||
| Ledger `computeTaskLedger` credits **all** clock time to the *current* assignee — the per-day split must not do this. | `lib/utils/programmer_task_ledger.dart:46-78` |
|
||||
| `ReportDateFilter` is hard-wired to `reportDateRangeProvider`; `ReportDateRange.end` is exclusive. | `lib/screens/reports/report_date_filter.dart`, `lib/providers/reports_provider.dart:13-51` |
|
||||
| Per-person PDF with letterhead + preview dialog (period label is off by one day at :211). | `lib/screens/attendance/dtr_pdf.dart` |
|
||||
| DTO + pure PDF builder pattern | `lib/screens/attendance/work_log_pdf.dart` |
|
||||
| Notification `type` is free text; render + tap switch; push via `send_fcm`. | `lib/screens/notifications/notifications_screen.dart:63-140`, `lib/services/notification_bridge.dart:97,152`, `lib/providers/notifications_provider.dart:376` |
|
||||
| Scheduled push: pg_cron → `scheduled_notifications` (`schedule_id` nullable) → edge function `notify_type` switch. | `supabase/functions/process_scheduled_notifications/index.ts` |
|
||||
| SECURITY DEFINER RPC that writes status + notifications atomically | `respond_shift_swap`, `supabase/migrations/20260322190000_pm_oncall_companion_swap.sql` |
|
||||
| Per-user "on leave / pass slip today" logic | `lib/screens/dashboard/dashboard_screen.dart:371-433` |
|
||||
|
||||
## 5. Day Sheets
|
||||
|
||||
### 5.1 What a Day Sheet contains
|
||||
|
||||
One **row per task** the programmer worked on that day:
|
||||
|
||||
- task title, category, project;
|
||||
- **time that day**:
|
||||
- *Assignee rows*: timer intervals (started/resumed → paused/completed/cancelled)
|
||||
clipped to Manila day boundaries, credited to **whoever was assignee at that moment**
|
||||
(walk `created` / `assigned` / `reassigned` events). A later reassignment never changes
|
||||
a past day. `adjustment` events count against the day they were created.
|
||||
- *Helper rows*: the helper's entered minutes, labelled "Helped <assignee>".
|
||||
- **notes**: that day's work-log entries for the task.
|
||||
|
||||
The admin flags **rows** when disapproving.
|
||||
|
||||
### 5.2 Schema
|
||||
|
||||
New migration `supabase/migrations/20260927150000_add_programmer_day_sheets.sql`.
|
||||
|
||||
**`programmer_day_sheets`**
|
||||
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| `id` | uuid pk | |
|
||||
| `programmer_id` | uuid → profiles | |
|
||||
| `work_date` | date | Manila calendar day |
|
||||
| `status` | text | check `draft \| pending \| disapproved \| approved`, default `draft` |
|
||||
| `submitted_at` | timestamptz | |
|
||||
| `auto_submitted` | bool | default false |
|
||||
| `reviewed_by` | uuid → profiles | last admin to approve/disapprove |
|
||||
| `reviewed_at` | timestamptz | |
|
||||
| `resubmissions` | int | default 0; incremented by justify |
|
||||
| `approved_snapshot` | jsonb | rows as approved (see 5.5) |
|
||||
| `created_at`, `updated_at` | timestamptz | |
|
||||
|
||||
Unique `(programmer_id, work_date)`.
|
||||
|
||||
**`programmer_day_sheet_events`** — append-only review thread
|
||||
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| `id` | uuid pk | |
|
||||
| `sheet_id` | uuid → sheets, on delete cascade | |
|
||||
| `actor_id` | uuid null | null = system |
|
||||
| `kind` | text | check `submitted \| auto_submitted \| disapproved \| justified \| approved \| amended` |
|
||||
| `body` | text | remarks / justification; check non-empty for `disapproved` and `justified` |
|
||||
| `flagged_task_ids` | uuid[] | default `'{}'` |
|
||||
| `created_at` | timestamptz | |
|
||||
|
||||
**`programmer_task_work_logs.work_date`** — `date not null default (now() at time zone
|
||||
'Asia/Manila')::date`. Existing rows are backfilled from `created_at`. A BEFORE INSERT
|
||||
guard trigger allows:
|
||||
|
||||
- `work_date` = today (Manila); or
|
||||
- a past `work_date` only if the author's sheet for that date is `pending` or
|
||||
`disapproved`.
|
||||
|
||||
Anything else raises an error.
|
||||
|
||||
**`notifications.day_sheet_id`** — `uuid null references programmer_day_sheets on delete
|
||||
cascade`.
|
||||
|
||||
**RLS:** sheets and events are readable by the owner and by `role = 'admin'`. There are no
|
||||
client INSERT/UPDATE/DELETE policies; every write goes through the RPCs or triggers below.
|
||||
Both tables are added to the `supabase_realtime` publication.
|
||||
|
||||
### 5.3 Status transitions
|
||||
|
||||
Each transition is a SECURITY DEFINER RPC with `set search_path = public`, explicit
|
||||
`auth.uid()`, role, and current-status checks. Each one atomically updates the sheet,
|
||||
inserts a thread event, and inserts notification rows, then returns the recipient user ids
|
||||
so the client can call `send_fcm`. A stale status raises `already_reviewed`.
|
||||
|
||||
| RPC | Who | From → To | Rule |
|
||||
|---|---|---|---|
|
||||
| `submit_day_sheet(p_date)` | owner | draft → pending | Any time, including early. Upserts the row. |
|
||||
| `disapprove_day_sheet(p_sheet, p_remarks, p_flagged_task_ids)` | admin | pending → disapproved | Remarks required. |
|
||||
| `justify_day_sheet(p_sheet, p_body)` | owner | disapproved → pending | Justification required; `resubmissions += 1`. |
|
||||
| `approve_day_sheets(p_items)` — `[{sheet_id, snapshot}]` | admin | pending or disapproved → approved | Same-day allowed. Bulk-capable. Stores the snapshot. |
|
||||
|
||||
```
|
||||
draft ──submit / auto──▶ pending ──approve──▶ approved
|
||||
│ ▲ │
|
||||
disapprove justify │ new work on the same day
|
||||
▼ │ │ (system "amended")
|
||||
disapproved ◀────────────┘ (back to pending)
|
||||
```
|
||||
|
||||
### 5.4 Automatic behaviour
|
||||
|
||||
- **Draft creation.** Triggers upsert a `draft` sheet when a work log is inserted (for the
|
||||
author, on `work_date`) and when a lifecycle activity-log event is inserted (for the
|
||||
task's assignee, on the event's Manila date). Only for `role = 'programmer'`.
|
||||
- **Amended after approval.** A work log or lifecycle event landing on an **approved
|
||||
sheet for today** sets it back to `pending` and adds a system `amended` event. This is not
|
||||
counted as a disapproval. The Approve button warns the admin when that programmer has a
|
||||
timer running.
|
||||
- **Nightly auto-submit** — `auto_submit_day_sheets()` via pg_cron at 00:05 Manila:
|
||||
1. For assignees of tasks still running, upsert yesterday's sheet (reopening it as
|
||||
`amended` if it was approved).
|
||||
2. Flip every `draft` sheet with `work_date < today` to `pending`
|
||||
(`auto_submitted = true`, `auto_submitted` event).
|
||||
3. If anything is pending, insert one `scheduled_notifications` row per admin with
|
||||
`notify_type = 'day_sheet_digest'`, scheduled for 08:00 Manila.
|
||||
|
||||
The cron registration uses the same `DO $$ … EXCEPTION` guard as existing cron
|
||||
migrations.
|
||||
|
||||
### 5.5 Approval snapshot
|
||||
|
||||
On approval the admin client sends the rows it displayed:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"total_seconds": 27900,
|
||||
"rows": [
|
||||
{
|
||||
"task_id": "…", "title": "…", "category": "Bug Fix", "project_name": "HIS",
|
||||
"kind": "assignee", "helped_name": null,
|
||||
"seconds": 11400, "notes": ["Fixed session timeout…"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Approved days are thereby fixed records: the report and stats read the snapshot, so later
|
||||
renames or reassignments do not change what was approved.
|
||||
|
||||
## 6. Approval UX
|
||||
|
||||
### 6.1 Tabs
|
||||
|
||||
`programmer_tasks_list_screen.dart` shows tabs by role:
|
||||
|
||||
- **Programmer:** Mine · All · Day Sheets
|
||||
- **Admin:** Monitor · Approvals · Mine · All
|
||||
|
||||
### 6.2 Day Sheets tab (programmer)
|
||||
|
||||
- **Today card** — today's rows (live), total time, Draft/Submitted chip, **Submit day**.
|
||||
- **Needs your attention** — disapproved sheets, pinned, warning style.
|
||||
- **History** — sheets by date with status filter chips (`AppStatusSummaryRow`).
|
||||
- App bar: **Accomplishment Report**.
|
||||
|
||||
### 6.3 Sheet detail — `/programmer-tasks/day-sheets/:id` (both roles)
|
||||
|
||||
- Header: date, programmer, status, total time, "Round N" when resubmitted.
|
||||
- Rows table: task · time · notes. Flagged rows are highlighted (`AppStatusColors`
|
||||
warning + flag icon).
|
||||
- Thread timeline, styled like `widgets/activity_timeline.dart`.
|
||||
- **Programmer, when disapproved:**
|
||||
- **Add correction** — the existing work-log dialog with `workDate` preset to that day,
|
||||
flagged tasks listed first.
|
||||
- Justification field (cannot be empty; modeled on the checkout-justification dialog,
|
||||
`attendance_screen.dart:1462-1510`).
|
||||
- **Resubmit**.
|
||||
- **Admin, when pending or disapproved:**
|
||||
- Row checkboxes for flagging.
|
||||
- **Disapprove** — dialog lists the flagged rows; remarks required.
|
||||
- **Approve** — warns if a timer is running. The admin client builds the snapshot.
|
||||
|
||||
### 6.4 Approvals tab (admin)
|
||||
|
||||
- Summary chips: Pending · Awaiting justification · Approved.
|
||||
- Programmer filter and the shared date filter.
|
||||
- Cards grouped by date: programmer, total time, task count, Early/Auto badge, Round N.
|
||||
- Multi-select → **Approve selected**. Disapproval stays per sheet because it needs remarks.
|
||||
|
||||
## 7. Admin Monitor
|
||||
|
||||
- **Summary row:** Working · Paused · Idle · On leave/Pass slip · Sheets pending.
|
||||
- **One card per programmer** (grid: 1 column on mobile, 2–3 on desktop):
|
||||
- State pill: **Working** (success) / **Paused** (warning) / **Idle** (neutral) /
|
||||
**On leave** or **Pass slip** (info; overrides the others).
|
||||
- Current task: title, category/project `TechChip`s, live "running 1h 12m today".
|
||||
- Today's total; in-progress / paused / not-started counts; today's sheet status.
|
||||
- Help chips: "Helped → Ben · task · 45m", "Helped by ← Ana · 30m".
|
||||
- **Drill-down** (tap a card): `ProgrammerDashboard(userId, readOnly: true)`, their recent
|
||||
Day Sheets, and **Generate Accomplishment Report** with the programmer preselected.
|
||||
- **Who's helping whom** panel: helper → assignee · task · minutes · time, with a day
|
||||
stepper (defaults to today). The working/paused state is always live.
|
||||
|
||||
**Data**
|
||||
|
||||
- New `programmerRunStatesProvider`: realtime stream of lifecycle activity logs for all
|
||||
`in_progress` task ids → latest lifecycle event per task → Working vs Paused. It also
|
||||
replaces the Mine tab's `myRunningProgrammerTaskIdProvider` inference, which currently
|
||||
marks every in-progress task as paused when offline.
|
||||
- Today's time: the per-day split (5.1).
|
||||
- Help: work logs with `work_date = day` whose author ≠ task assignee.
|
||||
- Leave / pass slip: extract the per-user "today" logic from
|
||||
`dashboard_screen.dart:371-433` into `lib/utils/staff_presence.dart`, used by both the
|
||||
dashboard and the Monitor.
|
||||
- Admin-only. Programmers keep the existing All tab and get no live view of teammates.
|
||||
|
||||
## 8. Mine dashboard
|
||||
|
||||
A single widget, `ProgrammerDashboard(userId, {readOnly})`, used for the Mine tab and for
|
||||
the Monitor drill-down.
|
||||
|
||||
1. **Now working hero** — the running task with a live timer (today + total) and
|
||||
Pause/Complete. If nothing is running: "Nothing running" and **Start** on the top
|
||||
not-started task (via `startProgrammerTaskWithPausePrompt`).
|
||||
2. **Stat tiles** (`AppMetricCard`):
|
||||
- **Today's focus** — ring against an 8h target (pattern:
|
||||
`reports/widgets/conversion_rate_card.dart:43-55`).
|
||||
- **Completed this week**, with change vs last week.
|
||||
- **Streak** — consecutive weekdays whose sheet is pending or approved. Weekends and
|
||||
approved-leave days are skipped (neither count nor break). A disapproved day breaks
|
||||
the streak until justified. Today counts once submitted. Shows best streak too.
|
||||
- **First-pass approval rate** (last 30 days) — approved sheets with no `disapproved`
|
||||
event ÷ reviewed sheets.
|
||||
- **Help given** this month.
|
||||
- Streak and approval rate are hidden for admins (admins have no sheets).
|
||||
3. **Badges** — computed from data each time (no table). Locked badges are greyed out with
|
||||
progress (e.g. 7/10).
|
||||
|
||||
| Badge | Unlocks at |
|
||||
|---|---|
|
||||
| First Finish | first completed task |
|
||||
| Finisher | 10 / 50 / 100 completed tasks |
|
||||
| Bug Squasher | 10 completed Bug Fix tasks |
|
||||
| Deep Work | 100 / 500 approved hours |
|
||||
| On a Roll | best streak 5 / 10 / 20 |
|
||||
| Team Player | 10h help given |
|
||||
| Clean Week | a Mon–Fri week with every day approved first time |
|
||||
|
||||
4. **Task sections** (`AppSectionHeader` + count): In progress · Paused · Not started ·
|
||||
Completed (collapsed; last 10 + Show all) · Cancelled (collapsed). The list's private
|
||||
`_TaskCard` is extracted to `widgets/programmer_task_card.dart`.
|
||||
|
||||
**Data:** hour-based stats and badges sum approved snapshots; "today" figures come from the
|
||||
live per-day split; counts come from `programmerTasksProvider` filtered to the user.
|
||||
Calculations live in pure functions (`computeProgrammerStats`, `computeBadges`).
|
||||
|
||||
## 9. Accomplishment Report (PDF)
|
||||
|
||||
**Entry points:** Day Sheets tab app bar (programmer, self); Approvals tab app bar and
|
||||
Monitor drill-down (admin).
|
||||
|
||||
**Generate dialog**
|
||||
|
||||
- Programmer picker — admin only; searchable single-select of `role = 'programmer'`
|
||||
from `profilesProvider`.
|
||||
- Date filter — `ReportDateFilter.controlled`, default "This Month".
|
||||
- Toggle — "Include unapproved days (marked)", off by default.
|
||||
- **Generate** → preview dialog with Print and Download.
|
||||
|
||||
**Date filter reuse:** add `ReportDateFilter.controlled({value, onChanged})` to
|
||||
`lib/screens/reports/report_date_filter.dart`. `_DateFilterDialog` returns the chosen
|
||||
`ReportDateRange` instead of writing the provider. The default constructor keeps binding
|
||||
`reportDateRangeProvider`, so the Reports screen is unchanged.
|
||||
|
||||
**Layout** (A4 MultiPage, Roboto, CRMC letterhead):
|
||||
|
||||
1. Info box — name, position, period (**inclusive** end date: `end − 1 day`).
|
||||
2. By-day table — Date | Task/Project | Accomplishment | Time.
|
||||
- Accomplishment = that day's work-log notes (Quill delta → plain text); "(no notes)"
|
||||
when a row has time but no notes.
|
||||
- Help rows read "Helped Ana — <task>".
|
||||
- When unapproved days are included, they carry a Pending/Disapproved tag.
|
||||
3. Footnote (approved-only mode) — "N days in the period are not included (x pending,
|
||||
y disapproved)".
|
||||
4. Summary — days reported, total time, tasks completed in the period, help given, time by
|
||||
category.
|
||||
5. Signatures — "Prepared by" the programmer; "Approved by" the reviewing admin's name if
|
||||
one admin approved every included day, otherwise a blank line.
|
||||
|
||||
**Data:** approved days come from `approved_snapshot`; unapproved days (when included) are
|
||||
computed live. `buildAccomplishmentReportData(...)` (pure) produces a DTO;
|
||||
`buildAccomplishmentReportPdf(dto)` only draws.
|
||||
|
||||
**Shared helpers (used by new code only):** `lib/utils/pdf/pdf_letterhead.dart` (fonts,
|
||||
logo, letterhead) and `lib/widgets/pdf_preview_dialog.dart` (pdfrx preview,
|
||||
`Printing.layoutPdf`, `Printing.sharePdf`, modeled on `_DtrPdfDialog`).
|
||||
|
||||
## 10. Notifications
|
||||
|
||||
| Type | Recipient | Trigger |
|
||||
|---|---|---|
|
||||
| `day_sheet_submitted` | all admins | programmer submits early |
|
||||
| `day_sheet_digest` | each admin, 08:00 | nightly auto-submit left sheets pending; opens Approvals |
|
||||
| `day_sheet_disapproved` | programmer | admin disapproves (includes remarks) |
|
||||
| `day_sheet_justified` | the reviewing admin | programmer justifies and resubmits |
|
||||
| `day_sheet_approved` | programmer | admin approves (single or bulk) |
|
||||
|
||||
RPCs insert the notification rows and return recipients; the client sends push through
|
||||
`NotificationsController.sendPush`. The digest goes through `scheduled_notifications` with
|
||||
a new case in `process_scheduled_notifications`. New render cases and tap routing go into
|
||||
`notifications_screen.dart` and `notification_bridge.dart`, and `day_sheet_id` is added to
|
||||
`notification_item.model.dart`.
|
||||
|
||||
## 11. Error handling
|
||||
|
||||
- **Stale status** (e.g. two admins act at once) → "This sheet was already reviewed" and
|
||||
the view refreshes.
|
||||
- **Empty remarks/justification** → blocked in the dialog and by the DB check.
|
||||
- **Disallowed back-dated entry** → the guard trigger's error is shown as a clear message.
|
||||
- **Push failure** → non-blocking; logged, as in existing flows.
|
||||
- **Offline** → Day Sheets are online-only (like work logs); actions are disabled with a
|
||||
banner.
|
||||
|
||||
## 12. Files
|
||||
|
||||
New (each under 500 lines):
|
||||
|
||||
- `supabase/migrations/20260927150000_add_programmer_day_sheets.sql`
|
||||
- `lib/models/programmer_day_sheet.model.dart`,
|
||||
`lib/models/programmer_day_sheet_event.model.dart` (plain online models)
|
||||
- `lib/providers/programmer_day_sheets_provider.dart` (streams, RPC controller, push)
|
||||
- `lib/providers/programmer_run_states_provider.dart`
|
||||
- `lib/utils/programmer_daily_time.dart` (per-day split with assignee attribution)
|
||||
- `lib/utils/programmer_stats.dart` (stats, streak, badges)
|
||||
- `lib/utils/staff_presence.dart`
|
||||
- `lib/utils/pdf/pdf_letterhead.dart`, `lib/widgets/pdf_preview_dialog.dart`
|
||||
- `lib/screens/programmer_tasks/dashboard/` — dashboard, hero, stat tiles, badges, sections
|
||||
- `lib/screens/programmer_tasks/monitor/` — monitor tab, programmer card, help panel
|
||||
- `lib/screens/programmer_tasks/day_sheets/` — Day Sheets tab, Approvals tab, detail
|
||||
screen, disapprove/justify dialogs
|
||||
- `lib/screens/programmer_tasks/report/` — generate dialog, report data, report PDF
|
||||
- `lib/screens/programmer_tasks/widgets/programmer_task_card.dart`
|
||||
|
||||
Modified:
|
||||
|
||||
- `lib/screens/programmer_tasks/programmer_tasks_list_screen.dart` — role tabs, card extract
|
||||
- `lib/models/programmer_task_work_log.model.dart`,
|
||||
`lib/providers/programmer_task_work_logs_provider.dart`,
|
||||
`lib/screens/programmer_tasks/widgets/work_log_section.dart` — `workDate`
|
||||
- `lib/screens/reports/report_date_filter.dart` — controlled constructor
|
||||
- `lib/providers/profile_provider.dart` — `isStrictAdminProvider`
|
||||
- `lib/routing/app_router.dart` — day-sheet detail route
|
||||
- `lib/screens/notifications/notifications_screen.dart`,
|
||||
`lib/services/notification_bridge.dart`, `lib/models/notification_item.model.dart`
|
||||
- `supabase/functions/process_scheduled_notifications/index.ts` — digest case
|
||||
- `lib/screens/dashboard/dashboard_screen.dart` — use `staff_presence.dart`
|
||||
|
||||
## 13. Delivery slices
|
||||
|
||||
Each slice ships independently on `feat/programmer-tasks`.
|
||||
|
||||
1. **Mine dashboard** — run-state provider, per-day split util, dashboard. No schema
|
||||
change; fixes the offline "everything paused" bug. Approval-based stats appear once
|
||||
slice 3 lands.
|
||||
2. **Admin Monitor** — plus the `staff_presence.dart` extraction.
|
||||
3. **Day Sheets** — migration, RPCs, cron, Day Sheets/Approvals tabs, detail screen,
|
||||
notifications. **The user applies the migration and redeploys
|
||||
`process_scheduled_notifications`** (the Supabase migration tool is blocked in this
|
||||
environment).
|
||||
4. **Accomplishment Report** — date-filter refactor, PDF helpers, report.
|
||||
|
||||
## 14. Testing and verification
|
||||
|
||||
- `flutter analyze lib` clean; `flutter test` passes apart from the three known,
|
||||
unrelated failures (theme_overhaul, profile_screen, user_management).
|
||||
- **Unit (pure Dart, TDD):** per-day split (midnight crossing, reassignment attribution,
|
||||
pauses, adjustments); stats and streak (weekends, leave, disapproved days); badges;
|
||||
report data (approved-only vs marked, snapshot vs live, footnote, inclusive period).
|
||||
- **Controller (mock-first, pattern of `test/programmer_tasks_controller_test.dart`):**
|
||||
RPC calls, push invocation, stale-status error mapping.
|
||||
- **Widget:** Disapprove dialog requires remarks; dashboard sections; tabs by role.
|
||||
- **SQL checklist** after the migration is applied (rolled-back transactions with
|
||||
`request.jwt.claims` set per role): every transition allowed/denied by role and status;
|
||||
back-dated work-log guard; amended-after-approval; `auto_submit_day_sheets()` called
|
||||
directly; `get_advisors` security check.
|
||||
- **End-to-end** via the QA harness (programmer, then admin; light and dark): log work →
|
||||
submit early → admin flags and disapproves → programmer adds a correction and justifies
|
||||
→ admin approves → both generate the PDF (approved-only and marked) → Monitor shows
|
||||
Working/Paused/On leave and help edges → notifications render and route.
|
||||
- `flutter build web` succeeds.
|
||||
Reference in New Issue
Block a user