/// Renders the Schedule Components widget for the FocusFlow Flutter UI. library; import 'package:flutter/material.dart'; import 'package:scheduler_core/scheduler_core.dart'; import '../theme/scheduler_visual_tokens.dart'; class CompactPanel extends StatelessWidget { const CompactPanel({required this.items, super.key}); final List items; @override Widget build(BuildContext context) { if (items.isEmpty) { return const SizedBox.shrink(); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Compact', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), for (final item in items) TimelineRow(item: item), const SizedBox(height: 20), ], ); } } class TimelineRow extends StatelessWidget { const TimelineRow({required this.item, this.onDone, super.key}); final TodayTimelineItem item; final Future Function()? onDone; @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final projectColor = SchedulerVisualTokens.projectColor( item.projectColorToken, ); return Card( margin: const EdgeInsets.only(bottom: 8), color: SchedulerVisualTokens.backgroundColor( item.item.backgroundToken, scheme, ), shape: RoundedRectangleBorder( side: BorderSide(color: projectColor, width: 2), borderRadius: BorderRadius.circular(8), ), child: ListTile( leading: Icon( SchedulerVisualTokens.taskIcon(item.taskType), color: projectColor, ), title: Text(item.item.displayTitle), subtitle: Text(timeText(item.item)), trailing: Wrap( spacing: 6, children: [ Icon( SchedulerVisualTokens.rewardIcon(item.item.rewardIconToken), size: 18, ), Icon( SchedulerVisualTokens.difficultyIcon( item.item.difficultyIconToken, ), size: 18, ), if (onDone != null) IconButton( tooltip: 'Done', onPressed: () { onDone!(); }, icon: const Icon(Icons.check_circle_outline), ), ], ), ), ); } } class BacklogRow extends StatefulWidget { const BacklogRow({required this.item, this.onSchedule, super.key}); final BacklogItemReadModel item; final Future Function(int durationMinutes)? onSchedule; @override State createState() => _BacklogRowState(); } class _BacklogRowState extends State { final durationController = TextEditingController(); @override void dispose() { durationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: StalenessMarker(marker: widget.item.stalenessMarker), title: Text(widget.item.task.title), subtitle: Text( 'Priority ${widget.item.task.priority?.name ?? 'unset'}', ), iconColor: SchedulerVisualTokens.stalenessColor( widget.item.stalenessMarker, scheme, ), trailing: widget.onSchedule == null ? null : SizedBox( width: 168, child: Row( children: [ Expanded( child: TextField( key: ValueKey('duration-${widget.item.task.id}'), controller: durationController, decoration: const InputDecoration(labelText: 'Min'), keyboardType: TextInputType.number, ), ), IconButton( tooltip: 'Schedule', onPressed: () { final duration = int.tryParse( durationController.text.trim(), ); if (duration == null) { return; } widget.onSchedule!(duration); }, icon: const Icon(Icons.event_available), ), ], ), ), ), ); } } class StalenessMarker extends StatelessWidget { const StalenessMarker({required this.marker, super.key}); final BacklogStalenessMarker marker; @override Widget build(BuildContext context) { return Icon( SchedulerVisualTokens.stalenessIcon(marker), semanticLabel: 'Staleness ${marker.name}', ); } } class NoticeBanner extends StatelessWidget { const NoticeBanner({required this.message, super.key}); final String message; @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).colorScheme.secondaryContainer, borderRadius: BorderRadius.circular(8), ), child: Text(message), ); } } String timeText(TimelineItem item) { final start = item.start; final end = item.end; if (start == null || end == null) { return '${item.durationMinutes ?? 0} min'; } return '${_clockText(start)}-${_clockText(end)}'; } String _clockText(DateTime value) { final hour = value.hour.toString().padLeft(2, '0'); final minute = value.minute.toString().padLeft(2, '0'); return '$hour:$minute'; }