// SPDX-FileCopyrightText: 2026 FocusFlow contributors // SPDX-License-Identifier: AGPL-3.0-only /// Renders the Backlog Pane widget for the FocusFlow Flutter UI. library; 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'; /// Read-driven pane that renders backlog content and command controls. class BacklogPane extends StatelessWidget { /// Creates a backlog pane from a read [controller]. const BacklogPane({ required this.controller, this.commandController, super.key, }); /// Controller that loads backlog data. final UiReadController controller; /// Optional command controller for mutating backlog items. final SchedulerCommandController? commandController; /// Builds the widget subtree for this component. /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data. @override Widget build(BuildContext context) { return ReadStateView( 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), ); } } /// Populated backlog view for a loaded backlog query result. class BacklogContent extends StatelessWidget { /// Creates backlog content for [result]. const BacklogContent({ required this.result, this.commandController, super.key, }); /// Loaded backlog result to display. final BacklogQueryResult result; /// Optional command controller for capture and scheduling actions. final SchedulerCommandController? commandController; /// Builds the widget subtree for this component. /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data. @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, ); }, ), ], ); } } /// Small form for quickly capturing a task into the backlog. class QuickCaptureForm extends StatefulWidget { /// Creates a quick capture form. const QuickCaptureForm({required this.commandController, super.key}); /// Command controller used to submit captured task titles. final SchedulerCommandController commandController; /// Creates the mutable state object used by this stateful widget. /// Flutter calls this once for each mounted widget instance before lifecycle callbacks and builds begin. @override State createState() => _QuickCaptureFormState(); } /// Private implementation type for `_QuickCaptureFormState` in this library. /// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly. class _QuickCaptureFormState extends State { /// Stores the `titleController` value for this object. /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior. final titleController = TextEditingController(); /// Releases resources owned by this object before it leaves the tree. /// Controllers, listeners, and other disposable collaborators should be cleaned up here to avoid stale callbacks. @override void dispose() { titleController.dispose(); super.dispose(); } /// Builds the widget subtree for this component. /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data. @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'), ), ], ); } } /// Displays the latest command state for backlog interactions. class CommandStatusBanner extends StatelessWidget { /// Creates a command status banner. const CommandStatusBanner({required this.controller, super.key}); /// Command controller whose state should be rendered. final SchedulerCommandController controller; /// Builds the widget subtree for this component. /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data. @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'}', ), }; }, ); } }