diff --git a/lib/screens/calendar/calendar_screen.dart b/lib/screens/calendar/calendar_screen.dart new file mode 100644 index 0000000..68751a5 --- /dev/null +++ b/lib/screens/calendar/calendar_screen.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:table_calendar/table_calendar.dart'; + +import '../../data/models/calendar_event.dart'; +import '../../providers/calendar_provider.dart'; +import 'event_form_sheet.dart'; + +class CalendarScreen extends ConsumerWidget { + const CalendarScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final calAsync = ref.watch(calendarProvider); + final notifier = ref.read(calendarProvider.notifier); + + return Scaffold( + appBar: AppBar(title: const Text('Calendar')), + body: calAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (e, _) => Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Could not load calendar.'), + const SizedBox(height: 12), + FilledButton.tonal( + onPressed: () => ref.invalidate(calendarProvider), + child: const Text('Retry'), + ), + ], + ), + ), + data: (cal) => Column( + children: [ + TableCalendar( + firstDay: DateTime(2020), + lastDay: DateTime(2030), + focusedDay: cal.focusedMonth, + selectedDayPredicate: (day) => isSameDay(day, cal.selectedDay), + eventLoader: (day) => + cal.eventsByDay[dateOnly(day)] ?? [], + calendarFormat: CalendarFormat.month, + headerStyle: const HeaderStyle( + formatButtonVisible: false, + titleCentered: true, + ), + calendarStyle: CalendarStyle( + selectedDecoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + shape: BoxShape.circle, + ), + todayDecoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .primary + .withValues(alpha: 0.3), + shape: BoxShape.circle, + ), + markerDecoration: BoxDecoration( + color: Theme.of(context).colorScheme.secondary, + shape: BoxShape.circle, + ), + ), + onDaySelected: (selected, _) => notifier.selectDay(selected), + onPageChanged: (focused) => notifier.loadMonth(focused), + ), + const Divider(height: 1), + Expanded( + child: _AgendaList( + events: + cal.eventsByDay[dateOnly(cal.selectedDay)] ?? [], + notifier: notifier, + ), + ), + ], + ), + ), + floatingActionButton: calAsync.hasValue + ? FloatingActionButton( + onPressed: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (_) => EventFormSheet( + event: null, + initialDate: calAsync.value!.selectedDay, + notifier: notifier, + ), + ), + child: const Icon(Icons.add), + ) + : null, + ); + } +} + +// ── Agenda list ─────────────────────────────────────────────────────────────── + +class _AgendaList extends StatelessWidget { + final List events; + final CalendarNotifier notifier; + + const _AgendaList({required this.events, required this.notifier}); + + @override + Widget build(BuildContext context) { + if (events.isEmpty) { + return const Center(child: Text('No events')); + } + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: events.length, + itemBuilder: (_, i) => + _EventTile(event: events[i], notifier: notifier), + ); + } +} + +// ── Event tile ──────────────────────────────────────────────────────────────── + +class _EventTile extends StatelessWidget { + final CalendarEvent event; + final CalendarNotifier notifier; + + const _EventTile({required this.event, required this.notifier}); + + Color _dotColor(BuildContext context) { + if (event.color.isEmpty) return Theme.of(context).colorScheme.primary; + try { + return Color(int.parse(event.color.replaceFirst('#', '0xFF'))); + } catch (_) { + return Theme.of(context).colorScheme.primary; + } + } + + String _timeLabel() { + if (event.allDay) return 'All day'; + final h = event.startDt.hour.toString().padLeft(2, '0'); + final m = event.startDt.minute.toString().padLeft(2, '0'); + return '$h:$m'; + } + + @override + Widget build(BuildContext context) { + return ListTile( + leading: CircleAvatar( + radius: 6, + backgroundColor: _dotColor(context), + ), + title: Text(event.title), + subtitle: Text(_timeLabel()), + onTap: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (_) => EventFormSheet( + event: event, + initialDate: null, + notifier: notifier, + ), + ), + ); + } +}