feat(programmer): Mine dashboard hero + task sections
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>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../models/profile.model.dart';
|
||||
import '../../../models/programmer_task.model.dart';
|
||||
import '../../../providers/profile_provider.dart';
|
||||
import '../../../providers/programmer_day_data_provider.dart';
|
||||
import '../../../providers/programmer_tasks_provider.dart';
|
||||
import '../../../utils/programmer_daily_time.dart';
|
||||
import 'now_working_card.dart';
|
||||
import 'task_sections.dart';
|
||||
|
||||
/// The programmer's own work dashboard — hero card, stat tiles (Task 8), and
|
||||
/// task sections. Drop it into the Mine tab with `userId: me`.
|
||||
class ProgrammerDashboard extends ConsumerWidget {
|
||||
const ProgrammerDashboard({
|
||||
super.key,
|
||||
required this.userId,
|
||||
this.readOnly = false,
|
||||
});
|
||||
|
||||
final String userId;
|
||||
final bool readOnly;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final tasksAsync = ref.watch(programmerTasksProvider);
|
||||
final runStates = ref.watch(programmerRunStatesProvider);
|
||||
final todayKey = ref.watch(todayKeyProvider);
|
||||
final dayDataAsync = ref.watch(programmerDayDataProvider(todayKey));
|
||||
final profilesAsync = ref.watch(profilesProvider);
|
||||
|
||||
final tasks = tasksAsync.valueOrNull ?? const [];
|
||||
final profileById = <String, Profile>{
|
||||
for (final p in profilesAsync.valueOrNull ?? <Profile>[]) p.id: p,
|
||||
};
|
||||
|
||||
final groups = groupTasksForDashboard(tasks, runStates, userId);
|
||||
final runningTask =
|
||||
groups.running.isNotEmpty ? groups.running.first : null;
|
||||
final nextUp =
|
||||
groups.notStarted.isNotEmpty ? groups.notStarted.first : null;
|
||||
|
||||
final todayOnTask = _computeTodayDuration(
|
||||
runningTask: runningTask,
|
||||
dayDataAsync: dayDataAsync,
|
||||
userId: userId,
|
||||
todayKey: todayKey,
|
||||
);
|
||||
final totalOnTask = _computeTotalDuration(
|
||||
runningTask: runningTask,
|
||||
dayDataAsync: dayDataAsync,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
return ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
children: [
|
||||
NowWorkingCard(
|
||||
running: runningTask,
|
||||
nextUp: nextUp,
|
||||
todayOnTask: todayOnTask,
|
||||
totalOnTask: totalOnTask,
|
||||
readOnly: readOnly,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// TODO: Task 8 inserts StatTiles + BadgesStrip here
|
||||
TaskSections(
|
||||
groups: groups,
|
||||
profileById: profileById,
|
||||
readOnly: readOnly,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Duration _computeTodayDuration({
|
||||
required ProgrammerTask? runningTask,
|
||||
required AsyncValue<dynamic> dayDataAsync,
|
||||
required String userId,
|
||||
required DateTime todayKey,
|
||||
}) {
|
||||
if (runningTask == null) return Duration.zero;
|
||||
final dayData = dayDataAsync.valueOrNull;
|
||||
if (dayData == null) return Duration.zero;
|
||||
final logs = dayData.logsByTask[runningTask.id] ?? const [];
|
||||
final dailyByAssignee = dailySecondsByAssignee(
|
||||
task: runningTask,
|
||||
logs: logs,
|
||||
now: DateTime.now().toUtc(),
|
||||
);
|
||||
final secs = dailyByAssignee[userId]?[todayKey] ?? 0;
|
||||
return Duration(seconds: secs);
|
||||
}
|
||||
|
||||
Duration _computeTotalDuration({
|
||||
required ProgrammerTask? runningTask,
|
||||
required AsyncValue<dynamic> dayDataAsync,
|
||||
required String userId,
|
||||
}) {
|
||||
if (runningTask == null) return Duration.zero;
|
||||
final dayData = dayDataAsync.valueOrNull;
|
||||
if (dayData == null) return Duration.zero;
|
||||
final logs = dayData.logsByTask[runningTask.id] ?? const [];
|
||||
final dailyByAssignee = dailySecondsByAssignee(
|
||||
task: runningTask,
|
||||
logs: logs,
|
||||
now: DateTime.now().toUtc(),
|
||||
);
|
||||
final totalSecs =
|
||||
(dailyByAssignee[userId] ?? const {}).values.fold<int>(0, (s, v) => s + v);
|
||||
return Duration(seconds: totalSecs);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user