diff --git a/lib/features/home/presentation/daily_plan_task_row.dart b/lib/features/home/presentation/daily_plan_task_row.dart new file mode 100644 index 0000000..fb15dad --- /dev/null +++ b/lib/features/home/presentation/daily_plan_task_row.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; +import 'package:household_keeper/features/tasks/domain/relative_date.dart'; + +/// Warm coral/terracotta color for overdue indicators. +const _overdueColor = Color(0xFFE07A5F); + +/// A task row for the daily plan screen. +/// +/// Shows task name, a tappable room name tag (navigates to room task list), +/// relative due date (coral if overdue), and an optional checkbox. +/// +/// Per user decisions: +/// - NO onTap or onLongPress on the row itself +/// - Only the checkbox and room name tag are interactive +/// - Checkbox is hidden for tomorrow (read-only preview) tasks +class DailyPlanTaskRow extends StatelessWidget { + const DailyPlanTaskRow({ + super.key, + required this.taskWithRoom, + required this.showCheckbox, + this.onCompleted, + }); + + final TaskWithRoom taskWithRoom; + final bool showCheckbox; + final VoidCallback? onCompleted; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final task = taskWithRoom.task; + + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final dueDate = DateTime( + task.nextDueDate.year, + task.nextDueDate.month, + task.nextDueDate.day, + ); + final isOverdue = dueDate.isBefore(today); + final relativeDateText = formatRelativeDate(task.nextDueDate, now); + + return ListTile( + leading: showCheckbox + ? Checkbox( + value: false, + onChanged: (_) => onCompleted?.call(), + ) + : null, + title: Text( + task.name, + style: theme.textTheme.titleMedium, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Row( + children: [ + GestureDetector( + onTap: () => context.go('/rooms/${taskWithRoom.roomId}'), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: theme.colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + taskWithRoom.roomName, + style: theme.textTheme.labelSmall?.copyWith( + color: theme.colorScheme.onSecondaryContainer, + ), + ), + ), + ), + const SizedBox(width: 8), + Flexible( + child: Text( + relativeDateText, + style: theme.textTheme.bodySmall?.copyWith( + color: isOverdue + ? _overdueColor + : theme.colorScheme.onSurfaceVariant, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/home/presentation/progress_card.dart b/lib/features/home/presentation/progress_card.dart new file mode 100644 index 0000000..349f908 --- /dev/null +++ b/lib/features/home/presentation/progress_card.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import 'package:household_keeper/l10n/app_localizations.dart'; + +/// A progress banner card showing "X von Y erledigt" with a linear +/// progress bar. Displayed at the top of the daily plan screen. +class ProgressCard extends StatelessWidget { + const ProgressCard({ + super.key, + required this.completed, + required this.total, + }); + + final int completed; + final int total; + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + + return Card( + margin: const EdgeInsets.all(16), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + l10n.dailyPlanProgress(completed, total), + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: total > 0 ? completed / total : 0.0, + minHeight: 8, + backgroundColor: colorScheme.surfaceContainerHighest, + color: colorScheme.primary, + ), + ), + ], + ), + ), + ); + } +}