/// Renders the Task Selection Modal widget for the FocusFlow Flutter UI. library; import 'package:flutter/material.dart'; import '../models/today_screen_models.dart'; import '../theme/focus_flow_tokens.dart'; import 'timeline/difficulty_bars.dart'; import 'timeline/reward_icon.dart'; /// Modal shown when a selectable timeline task card is opened. class TaskSelectionModal extends StatelessWidget { /// Creates a task selection modal for [card]. const TaskSelectionModal({ required this.card, required this.onClose, this.onStatusPressed, super.key, }); /// Card model whose details and actions should be displayed. final TimelineCardModel card; /// Callback invoked when the modal should close. final VoidCallback onClose; /// Callback invoked when the status circle should toggle completion. final VoidCallback? onStatusPressed; @override Widget build(BuildContext context) { final accent = _accentFor(card.visualKind); return Material( color: Colors.transparent, child: Container( key: const ValueKey('task-selection-modal'), width: 548, padding: const EdgeInsets.fromLTRB(24, 22, 24, 20), decoration: BoxDecoration( color: FocusFlowTokens.elevatedPanel.withValues(alpha: 0.98), borderRadius: BorderRadius.circular(FocusFlowTokens.modalRadius), border: Border.all(color: accent.withValues(alpha: 0.86)), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ _ModalStatusButton( card: card, color: accent, onPressed: onStatusPressed, ), const SizedBox(width: 28), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( card.title, style: const TextStyle( color: FocusFlowTokens.textPrimary, fontSize: FocusFlowTokens.modalTitleSize, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 6), _HeaderMetadata(card: card), ], ), ), IconButton( key: const ValueKey('close-task-modal'), onPressed: onClose, style: IconButton.styleFrom( backgroundColor: FocusFlowTokens.glassPanelStrong, foregroundColor: FocusFlowTokens.textPrimary, ), icon: const Icon(Icons.close), ), ], ), const SizedBox(height: 26), Row( children: [ Expanded( child: _InfoTile( label: card.rewardLabel, child: RewardIcon(color: FocusFlowTokens.accentMagenta), ), ), const SizedBox(width: 28), Expanded( child: _InfoTile( label: card.effortLabel, child: DifficultyBars( difficulty: card.difficultyIconToken, color: FocusFlowTokens.restPurple, ), ), ), ], ), const SizedBox(height: 30), GridView.count( crossAxisCount: 2, crossAxisSpacing: 26, mainAxisSpacing: 16, childAspectRatio: 4.1, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: const [ _ActionButton(icon: Icons.check, label: 'Done'), _ActionButton(icon: Icons.arrow_forward, label: 'Push'), _ActionButton( icon: Icons.inventory_2_outlined, label: 'Move to Backlog', ), _ActionButton(icon: Icons.apps, label: 'Break up'), ], ), const SizedBox(height: 14), const Text( 'Options apply after confirmation.', style: TextStyle(color: FocusFlowTokens.textMuted, fontSize: 14), ), ], ), ), ); } } class _ModalStatusButton extends StatelessWidget { const _ModalStatusButton({ required this.card, required this.color, required this.onPressed, }); final TimelineCardModel card; final Color color; final VoidCallback? onPressed; @override Widget build(BuildContext context) { final ring = Container( key: const ValueKey('task-modal-status'), width: 44, height: 44, decoration: BoxDecoration( shape: BoxShape.circle, color: card.isCompleted ? color : Colors.transparent, border: Border.all(color: color, width: 4), ), child: card.isCompleted ? const Icon( Icons.check, color: FocusFlowTokens.appBackground, size: 26, ) : null, ); if (onPressed == null) { return ring; } return Tooltip( message: card.isCompleted ? 'Mark not done' : 'Mark complete', child: Semantics( button: true, label: card.isCompleted ? 'Mark ${card.title} not done' : 'Mark ${card.title} complete', child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: onPressed, child: ring, ), ), ), ); } } class _HeaderMetadata extends StatelessWidget { const _HeaderMetadata({required this.card}); final TimelineCardModel card; @override Widget build(BuildContext context) { const metadataStyle = TextStyle( color: FocusFlowTokens.textMuted, fontSize: 17, ); if (!card.isCompleted) { return Text('${card.typeLabel} - ${card.timeText}', style: metadataStyle); } final completedTime = card.completedTimeText; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( completedTime == null || completedTime.isEmpty ? 'Completed' : 'Completed - $completedTime', style: metadataStyle, ), const SizedBox(height: 3), Text( 'Planned: ${card.timeText}', style: metadataStyle.copyWith(fontSize: 15), ), ], ); } } class _InfoTile extends StatelessWidget { const _InfoTile({required this.child, required this.label}); final Widget child; final String label; @override Widget build(BuildContext context) { return Container( height: 58, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(FocusFlowTokens.smallButtonRadius), border: Border.all(color: FocusFlowTokens.subtleBorder), ), child: Row( children: [ child, const SizedBox(width: 18), Flexible( child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: FocusFlowTokens.textPrimary, fontSize: 17, fontWeight: FontWeight.w700, ), ), ), ], ), ); } } class _ActionButton extends StatelessWidget { const _ActionButton({required this.icon, required this.label}); final IconData icon; final String label; @override Widget build(BuildContext context) { return OutlinedButton.icon( onPressed: () {}, style: OutlinedButton.styleFrom( foregroundColor: FocusFlowTokens.textPrimary, side: const BorderSide(color: FocusFlowTokens.subtleBorder), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( FocusFlowTokens.smallButtonRadius, ), ), ), icon: Icon(icon), label: Text( label, style: const TextStyle( fontSize: FocusFlowTokens.modalButtonSize, fontWeight: FontWeight.w700, ), ), ); } } Color _accentFor(TaskVisualKind kind) { return switch (kind) { TaskVisualKind.flexible => FocusFlowTokens.flexibleGreen, TaskVisualKind.required => FocusFlowTokens.requiredPink, TaskVisualKind.appointment => FocusFlowTokens.appointmentBlue, TaskVisualKind.freeSlot => FocusFlowTokens.restPurple, TaskVisualKind.completedSurprise => FocusFlowTokens.completedGray, }; }