buildDayRows can emit both an 'assignee' row and a 'helper' row for the
same taskId when a programmer previously held a task (accumulated clock
time) and later helped on it after reassignment. ValueKey(row.taskId)
collided in that case, crashing Flutter's widget tree with a red screen.
Fixed by keying on taskId:kind, making each row unique.
Caught during targeted QA on the new loading-state / dialog flows.
Co-Authored-By: claude-flow <ruv@ruv.net>
Day sheet dialogs (disapprove, justify): moved the async controller call
inside each dialog so loading state is owned there — button shows a
spinner and is disabled while the RPC is in flight; errors display inline
instead of via a snackbar after dialog close.
Detail screen (approve): added _approvingSheet bool; Approve button shows
CircularProgressIndicator while in flight. On success shows an AlertDialog
"Sheet Approved" with a check icon instead of silently updating state.
Approvals tab (bulk approve): upgraded post-approve feedback from a
snackbar to a success AlertDialog showing the count of approved sheets
and any skipped ones.
Day sheets tab (submit): added confirmation AlertDialog before the
existing submit flow so programmers don't accidentally submit mid-day.
Co-Authored-By: claude-flow <ruv@ruv.net>
Tab switching: _buildTabs was recreating TabController on every build
because `_tabs == tabs` compares List by reference identity, not content.
programmerTasksTabsFor() always returns a new list, so the guard was
always false, resetting the controller to initialIndex 0 on every tap.
Fixed by using listEquals from flutter/foundation.
Help given: computeProgrammerStats accumulated helpGivenThisMonth only
from approvedSnapshot rows, which only exist after admin approval.
Programmers who helped others but had pending sheets saw 0m indefinitely.
Fixed by adding helpGivenThisMonthProvider (FutureProvider.family) that
queries programmer_task_work_logs directly for the current Manila month,
and passing it as liveHelpThisMonthSec to computeProgrammerStats.
Co-Authored-By: claude-flow <ruv@ruv.net>
approvedSnapshot is only populated at approval time; pending and disapproved
sheets had null snapshot so the card always displayed 0m · 0 tasks.
Now totalSecs/taskCount are passed as nullable params from the parent; the
card renders — when no snapshot is available yet.
Also accept camelCase 'daySheets' URL param in parseProgrammerTasksTab
alongside the existing hyphenated 'day-sheets' spelling.
Co-Authored-By: claude-flow <ruv@ruv.net>
_buildTabs creates a new TabController (new ticker) before disposing the
old one, so two tickers briefly coexist — SingleTickerProviderStateMixin
throws. TickerProviderStateMixin supports multiple concurrent tickers.
Co-Authored-By: claude-flow <ruv@ruv.net>
Move reviewer tracking logic inside the non-empty rows guard in both
approved-only and include-unapproved modes. This ensures zero-row
approved sheets don't pollute the reviewer set, fixing the approverName
calculation to only consider reviewers from included ReportDays.
Add test: approved sheet with zero rows and different reviewer correctly
excluded from approver tracking.
All 9 tests passing.
Co-Authored-By: claude-flow <ruv@ruv.net>
Implement AccomplishmentReportData and buildAccomplishmentReportData() for
generating programmer accomplishment reports. Supports both approved-only and
include-unapproved modes, with proper row filtering, day aggregation, metrics
calculation (totalSeconds, tasksCompleted, helpSeconds, secondsByCategory),
and approver tracking.
All 8 tests passing.
Co-Authored-By: claude-flow <ruv@ruv.net>
- Wire daySheetsForUserProvider(userId) into ProgrammerDashboard so
computeProgrammerStats/computeBadges receive real sheet data instead of
the placeholder const <ProgrammerDaySheet>[].
- Monitor tab: watch allDaySheetsProvider and build sheetStatusByUser map
for today; pass it to buildMonitorEntries; add "Sheets pending" chip
(pending + disapproved today) to the summary row.
- ProgrammerDrillDownScreen: add _RecentDaySheetsPanel below the dashboard
showing the last 10 sheets (date + status pill) with tap-to-daySheetDetail.
- DaySheetDetailScreen (T17 gaps): replace const [] liveRows with
programmerDayDataProvider(sheet.workDate) built rows for non-approved
sheets; fix _onApprove timer check to filter only the sheet programmer's
own tasks (QN-1).
- Add test/programmer_drill_down_test.dart (3 tests, all passing).
Co-Authored-By: claude-flow <ruv@ruv.net>
Pass profileNames Map<String, String> from MonitorTab down to
ProgrammerMonitorCard and HelpPanel; use profileNames[id] ?? id
at all help-chip and help-edge row render sites so full names
display instead of raw UUIDs.
Co-Authored-By: claude-flow <ruv@ruv.net>
Changed buildMonitorEntries signature to accept Map<String, LeaveOfAbsence> and Map<String, PassSlip> instead of List types, matching the spec requirement that callers pass pre-computed maps from activeLeaveByUser() and activePassSlipByUser().
- Updated function signature for both parameters
- Removed internal calls to activeLeaveByUser/activePassSlipByUser
- Updated state-determination to use direct map lookups
- Updated all test cases to pass computed maps
- All 19 tests passing
- No new analyzer issues
Co-Authored-By: claude-flow <ruv@ruv.net>
Implements MonitorState enum, HelpEdge class, and buildMonitorEntries/buildHelpEdges functions for the monitor view model. Features include:
- MonitorState enum with values: working, paused, idle, onLeave, passSlip
- HelpEdge class for tracking help between programmers
- buildHelpEdges() to extract helped/helpedBy relationships from work logs
- ProgrammerMonitorEntry with current task tracking and duration calculations
- buildMonitorEntries() with proper state determination, task counting, and sorting
All tests passing. Sort order: working -> paused -> idle -> passSlip -> onLeave, then alphabetical by name.
Co-Authored-By: claude-flow <ruv@ruv.net>
Add StatTiles and BadgesStrip widgets wired into ProgrammerDashboard.
StatTiles shows 2 tiles (focus + completed) when showSheetStats is false,
and 5 tiles (+ streak, first-pass approval, help given) when the viewing
user's role is programmer. BadgesStrip renders earned/locked chips in a
horizontal Wrap with tap-to-dialog detail.
Dashboard feeds todayFocus via buildDayRows/totalSeconds, leaveDays via
approvedLeaveDays, and sheets: const [] until Task 20 wires real data.
Co-Authored-By: claude-flow <ruv@ruv.net>
NowWorkingCard was watching clockTickerProvider but received pre-computed
Duration values from ProgrammerDashboard, which never rebuilt on ticks.
Move ref.watch(clockTickerProvider) to ProgrammerDashboard.build() so
the parent rebuilds every 30 s and passes fresh durations to the card.
Remove the now-unused clockTickerProvider watch (and its import) from
NowWorkingCard.
Co-Authored-By: claude-flow <ruv@ruv.net>
Replace the Mine tab flat list with ProgrammerDashboard — a NowWorkingCard
hero (running task with Pause/Complete, or idle with nextUp Start), a
TODO stub for Task 8 stat tiles/badges, and collapsible TaskSections
(In progress, Paused, Not started always-visible; Completed/Cancelled
start collapsed with Show-all at 10+). groupTasksForDashboard excludes
other assignees and sorts completed/cancelled by terminal date desc.
Co-Authored-By: claude-flow <ruv@ruv.net>
Extract _TaskCard from the list screen into ProgrammerTaskCard, a
shared ConsumerWidget that reads programmerRunStatesProvider so the
paused-task pill and Start/Resume/Complete buttons are consistent
across any surface that embeds it.
Move the _complete body verbatim to completeProgrammerTaskWithConfirm
in programmer_task_actions.dart; the detail screen's _ActionBar now
calls the shared function.
Co-Authored-By: claude-flow <ruv@ruv.net>
Separate feature for the `programmer` role to record dev work by category,
distinct from the IT help-desk `tasks` feature. Modeled on the IT Service
Requests pattern (Brick offline-first model -> Riverpod stream/controller ->
list/detail screens); seed of a future Project Management workflow.
- programmer_tasks + programmer_task_activity_logs (category enum, RLS,
realtime) via supabase migration
- single assignee, self-logged or lead-assigned; assign/reassign logged
- pause-on-switch: starting a task while another runs prompts to pause the
running one, keeping one active task per user for accurate time attribution
- worked-duration math shared with help-desk tasks via a generic core in
task_duration.dart (existing callers unchanged)
- list distinguishes running vs paused with a per-task Resume shortcut
- accessible dialog labels; route /programmer-tasks + nav gated to admin/programmer
- unit/widget tests: controller lifecycle, duration math, pause-switch dialog
Co-Authored-By: claude-flow <ruv@ruv.net>