feat(programmer): sheet-driven stats, monitor status, drill-down history
- 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>
This commit is contained in:
@@ -143,8 +143,16 @@ class _DaySheetDetailScreenState extends ConsumerState<DaySheetDetailScreen> {
|
||||
final today = ref.read(todayKeyProvider);
|
||||
final isToday = sheet.workDate == today;
|
||||
if (isToday) {
|
||||
// Only warn if THIS programmer has a running task (T20/QN-1 fix).
|
||||
final allTasks =
|
||||
ref.read(programmerTasksProvider).valueOrNull ?? const [];
|
||||
final programmerTaskIds = allTasks
|
||||
.where((t) => t.assigneeId == sheet.programmerId)
|
||||
.map((t) => t.id)
|
||||
.toSet();
|
||||
final runStates = ref.read(programmerRunStatesProvider);
|
||||
final hasRunning = runStates.entries.any((e) =>
|
||||
programmerTaskIds.contains(e.key) &&
|
||||
e.value.state == ProgrammerRunState.running);
|
||||
if (hasRunning) {
|
||||
final confirm = await showDialog<bool>(
|
||||
@@ -230,10 +238,26 @@ class _DaySheetDetailScreenState extends ConsumerState<DaySheetDetailScreen> {
|
||||
.where((t) => t.assigneeId == sheet.programmerId)
|
||||
.toList();
|
||||
|
||||
// For simplicity, build live rows from any tasks that appear in events
|
||||
// or we just use empty — the sheet already has snapshot rows if approved.
|
||||
// Live rows come from an RPC-built dataset; use snapshot rows if approved.
|
||||
final liveRows = _buildLiveRowsFrom(sheet, events, allTasks);
|
||||
// For approved sheets use the stored snapshot. For all other
|
||||
// statuses fetch live row data from the day-activity provider.
|
||||
final dayDataForSheet = ref
|
||||
.watch(programmerDayDataProvider(sheet.workDate))
|
||||
.valueOrNull;
|
||||
final liveRows = sheet.status == DaySheetStatus.approved &&
|
||||
sheet.approvedSnapshot != null
|
||||
? sheet.approvedSnapshot!.rows
|
||||
: dayDataForSheet != null
|
||||
? buildDayRows(
|
||||
userId: sheet.programmerId,
|
||||
day: sheet.workDate,
|
||||
tasks: allTasks,
|
||||
logsByTask: dayDataForSheet.logsByTask,
|
||||
workLogs: dayDataForSheet.workLogs,
|
||||
projectNames: const {},
|
||||
profileNames: profileNames,
|
||||
now: AppTime.now(),
|
||||
)
|
||||
: const <DaySheetRow>[];
|
||||
final rows = rowsForSheet(sheet, liveRows);
|
||||
|
||||
final totalSecs = rows.fold<int>(0, (s, r) => s + r.seconds);
|
||||
@@ -475,24 +499,3 @@ class _Header extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Live rows helper
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Attempt to surface live rows from the existing tasks list.
|
||||
/// For a fully approved sheet the snapshot rows will be used anyway,
|
||||
/// so this is only a best-effort fallback.
|
||||
List<DaySheetRow> _buildLiveRowsFrom(
|
||||
ProgrammerDaySheet sheet,
|
||||
List<ProgrammerDaySheetEvent> events,
|
||||
List<ProgrammerTask> allTasks,
|
||||
) {
|
||||
// If the sheet has an approved snapshot, the caller will use snapshot rows.
|
||||
if (sheet.approvedSnapshot != null &&
|
||||
sheet.status == DaySheetStatus.approved) {
|
||||
return sheet.approvedSnapshot!.rows;
|
||||
}
|
||||
// Otherwise we don't have the full row data client-side without a separate
|
||||
// fetch — return empty and let the screen show whatever the snapshot provides.
|
||||
return const [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user