627645056b
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>
64 lines
2.2 KiB
Dart
64 lines
2.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../models/programmer_task.model.dart';
|
|
import '../../providers/programmer_tasks_provider.dart';
|
|
|
|
/// Starts (or resumes) [task] for the current user, enforcing the single-active
|
|
/// task discipline: if the user already has a *running* task, they are prompted
|
|
/// to pause it first. This is the headline "proper time management" behavior and
|
|
/// is shared by the list and detail screens.
|
|
///
|
|
/// Returns true if the task was started/resumed, false if the user cancelled.
|
|
Future<bool> startProgrammerTaskWithPausePrompt(
|
|
BuildContext context,
|
|
WidgetRef ref,
|
|
ProgrammerTask task,
|
|
) async {
|
|
final controller = ref.read(programmerTasksControllerProvider);
|
|
final messenger = ScaffoldMessenger.of(context);
|
|
|
|
final running = await controller.findRunningTaskForCurrentUser();
|
|
if (running != null && running.id != task.id) {
|
|
if (!context.mounted) return false;
|
|
final confirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
semanticLabel: 'Pause current task',
|
|
title: const Text('Pause current task?'),
|
|
content: Text(
|
|
"You're currently working on \"${running.title}\". "
|
|
'Pause it and start "${task.title}"?',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
child: const Text('Cancel'),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
child: const Text('Pause & Start'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (confirmed != true) return false;
|
|
await controller.pauseTask(taskId: running.id);
|
|
}
|
|
|
|
try {
|
|
if (task.status == ProgrammerTaskStatus.queued) {
|
|
await controller.startTask(taskId: task.id);
|
|
} else if (task.status == ProgrammerTaskStatus.inProgress) {
|
|
await controller.resumeTask(taskId: task.id);
|
|
}
|
|
messenger.showSnackBar(
|
|
SnackBar(content: Text('Started "${task.title}"')),
|
|
);
|
|
return true;
|
|
} catch (e) {
|
|
messenger.showSnackBar(SnackBar(content: Text('Could not start task: $e')));
|
|
return false;
|
|
}
|
|
}
|