forked from eva/focus-flow
144 lines
4.2 KiB
Dart
144 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:scheduler_core/scheduler_core.dart';
|
|
|
|
import '../controllers/scheduler_command_controller.dart';
|
|
import '../controllers/scheduler_read_controller.dart';
|
|
import 'read_state_view.dart';
|
|
import 'schedule_components.dart';
|
|
|
|
class BacklogPane extends StatelessWidget {
|
|
const BacklogPane({
|
|
required this.controller,
|
|
this.commandController,
|
|
super.key,
|
|
});
|
|
|
|
final UiReadController<BacklogQueryResult> controller;
|
|
final SchedulerCommandController? commandController;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ReadStateView<BacklogQueryResult>(
|
|
controller: controller,
|
|
title: 'Backlog',
|
|
emptyTitle: 'Backlog is clear',
|
|
emptyMessage: 'Captured tasks will appear here when they need planning.',
|
|
dataBuilder: (context, result) =>
|
|
BacklogContent(result: result, commandController: commandController),
|
|
);
|
|
}
|
|
}
|
|
|
|
class BacklogContent extends StatelessWidget {
|
|
const BacklogContent({
|
|
required this.result,
|
|
this.commandController,
|
|
super.key,
|
|
});
|
|
|
|
final BacklogQueryResult result;
|
|
final SchedulerCommandController? commandController;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
Text('Backlog', style: Theme.of(context).textTheme.headlineMedium),
|
|
const SizedBox(height: 12),
|
|
if (commandController != null) ...[
|
|
QuickCaptureForm(commandController: commandController!),
|
|
const SizedBox(height: 12),
|
|
CommandStatusBanner(controller: commandController!),
|
|
const SizedBox(height: 12),
|
|
],
|
|
for (final item in result.items)
|
|
BacklogRow(
|
|
item: item,
|
|
onSchedule: commandController == null
|
|
? null
|
|
: (durationMinutes) {
|
|
return commandController!.scheduleBacklogItem(
|
|
taskId: item.task.id,
|
|
durationMinutes: durationMinutes,
|
|
expectedUpdatedAt: item.task.updatedAt,
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class QuickCaptureForm extends StatefulWidget {
|
|
const QuickCaptureForm({required this.commandController, super.key});
|
|
|
|
final SchedulerCommandController commandController;
|
|
|
|
@override
|
|
State<QuickCaptureForm> createState() => _QuickCaptureFormState();
|
|
}
|
|
|
|
class _QuickCaptureFormState extends State<QuickCaptureForm> {
|
|
final titleController = TextEditingController();
|
|
|
|
@override
|
|
void dispose() {
|
|
titleController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
key: const ValueKey('quick-capture-title'),
|
|
controller: titleController,
|
|
decoration: const InputDecoration(labelText: 'Quick capture'),
|
|
textInputAction: TextInputAction.done,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
FilledButton.icon(
|
|
onPressed: () async {
|
|
final title = titleController.text.trim();
|
|
if (title.isEmpty) {
|
|
return;
|
|
}
|
|
await widget.commandController.quickCaptureToBacklog(title);
|
|
titleController.clear();
|
|
},
|
|
icon: const Icon(Icons.add),
|
|
label: const Text('Capture'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class CommandStatusBanner extends StatelessWidget {
|
|
const CommandStatusBanner({required this.controller, super.key});
|
|
|
|
final SchedulerCommandController controller;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: controller,
|
|
builder: (context, _) {
|
|
return switch (controller.state) {
|
|
SchedulerCommandIdle() => const SizedBox.shrink(),
|
|
SchedulerCommandRunning(label: final label) => Text(label),
|
|
SchedulerCommandSuccess(message: final message) => Text(message),
|
|
SchedulerCommandFailure(failure: final failure, error: final error) =>
|
|
Text(
|
|
'Command issue: '
|
|
'${failure?.detailCode ?? failure?.code.name ?? error ?? 'unexpected'}',
|
|
),
|
|
};
|
|
},
|
|
);
|
|
}
|
|
}
|