import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../core/formatters.dart'; import '../core/theme.dart'; import '../models/availability.dart'; import '../state/availability_controller.dart'; import '../widgets/common.dart'; import '../widgets/week_strip.dart'; /// Auto-Match only offers shifts inside the blocks switched on here. class AvailabilityScreen extends ConsumerStatefulWidget { const AvailabilityScreen({super.key}); @override ConsumerState createState() => _AvailabilityScreenState(); } class _AvailabilityScreenState extends ConsumerState { DateTime _selected = DateTime.now(); List get _week { final now = DateTime.now(); return [for (var i = 0; i < 7; i++) now.add(Duration(days: i))]; } @override Widget build(BuildContext context) { final controller = ref.watch(availabilityProvider.notifier); ref.watch(availabilityProvider); final blocks = controller.blocksFor(_selected); final hours = blocks.fold(0, (sum, b) => sum + b.hours); final marked = { for (var i = 0; i < _week.length; i++) if (controller.blocksFor(_week[i]).isNotEmpty) i, }; return Scaffold( body: ListView( padding: EdgeInsets.zero, children: [ BrandHeader( padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ HeaderIconButton( icon: LucideIcons.arrowLeft, onPressed: () => Navigator.of(context).pop(), ), const SizedBox(width: 12), const Expanded( child: Text( 'Availability', style: TextStyle( fontSize: 25, fontWeight: FontWeight.w700, letterSpacing: -0.9, color: Colors.white, ), ), ), ], ), const SizedBox(height: 18), Wrap( spacing: 8, runSpacing: 8, children: [ _Preset( label: 'All week', onTap: () => controller.applyPreset(_week, test: (_) => true), ), _Preset( label: 'Weekdays', onTap: () => controller.applyPreset( _week, test: (d) => d.weekday <= DateTime.friday, ), ), _Preset( label: 'Weekends', onTap: () => controller.applyPreset( _week, test: (d) => d.weekday >= DateTime.saturday, ), ), _Preset( label: 'Clear', accent: true, onTap: () => controller.clearWeek(_week), ), ], ), ], ), ), Padding( padding: const EdgeInsets.fromLTRB(20, 18, 20, 28), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ WeekStrip( days: _week, selected: _selected, onSelect: (d) => setState(() => _selected = d), marked: marked, ), const SizedBox(height: 16), KrowCard( padding: const EdgeInsets.fromLTRB(16, 14, 16, 16), child: Column( children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( Fmt.dayShort(_selected), style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w700, letterSpacing: -0.5, ), ), const SizedBox(height: 2), Text( blocks.isEmpty ? 'Marked as a day off' : 'Available ยท $hours hrs', style: TextStyle( fontSize: 12.5, fontWeight: FontWeight.w500, color: blocks.isEmpty ? KrowPalette.mute : KrowPalette.blue, ), ), ], ), ), Switch( value: blocks.isNotEmpty, onChanged: (v) => controller.setDayAvailable(_selected, v), ), ], ), const SizedBox(height: 14), for (final block in DayBlock.values) Padding( padding: const EdgeInsets.only(bottom: 9), child: _BlockRow( block: block, on: blocks.contains(block), onTap: () => controller.toggleBlock(_selected, block), ), ), ], ), ), const SizedBox(height: 14), InfoBanner( blocks.isEmpty ? 'With every block off, Auto-Match will not send you ' 'anything on this day.' : 'Saved. Auto-Match will only offer you shifts inside ' 'these blocks.', icon: blocks.isEmpty ? null : LucideIcons.check, ), ], ), ), ], ), ); } } class _Preset extends StatelessWidget { const _Preset({ required this.label, required this.onTap, this.accent = false, }); final String label; final VoidCallback onTap; final bool accent; @override Widget build(BuildContext context) { // Built by hand rather than as a Chip: the app's ChipTheme paints a white // fill, which would hide white-on-blue header text. return Material( color: Colors.white.withValues(alpha: accent ? 0.06 : 0.16), borderRadius: BorderRadius.circular(9), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(9), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 9), child: Text( label, style: TextStyle( fontSize: 12.5, fontWeight: FontWeight.w600, color: accent ? KrowPalette.yellow : Colors.white, ), ), ), ), ); } } class _BlockRow extends StatelessWidget { const _BlockRow({required this.block, required this.on, required this.onTap}); final DayBlock block; final bool on; final VoidCallback onTap; IconData get _icon => switch (block) { DayBlock.morning => LucideIcons.sunrise, DayBlock.afternoon => LucideIcons.sun, DayBlock.evening => LucideIcons.moon, }; @override Widget build(BuildContext context) { const duration = Duration(milliseconds: 260); const curve = Curves.easeOutCubic; return GestureDetector( onTap: onTap, // Every part of the row animates together, so switching a block on is a // single motion rather than four things changing at once. child: AnimatedContainer( duration: duration, curve: curve, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: on ? KrowPalette.blueTint : const Color(0xFFE5F0E8), borderRadius: BorderRadius.circular(12), border: Border.all( color: on ? KrowPalette.blue.withValues(alpha: 0.3) : Colors.transparent, ), ), child: Row( children: [ AnimatedContainer( duration: duration, curve: curve, width: 38, height: 38, decoration: BoxDecoration( color: on ? KrowPalette.blue : Colors.white, borderRadius: BorderRadius.circular(11), border: Border.all( color: on ? KrowPalette.blue : KrowPalette.line, ), ), child: Icon( _icon, size: 18, color: on ? Colors.white : const Color(0xFF949FA5), ), ), const SizedBox(width: 13), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AnimatedDefaultTextStyle( duration: duration, curve: curve, style: TextStyle( fontSize: 14.5, fontWeight: FontWeight.w600, color: on ? KrowPalette.ink : KrowPalette.mute, ), child: Text(block.label), ), AnimatedDefaultTextStyle( duration: duration, curve: curve, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, color: on ? KrowPalette.blue : const Color(0xFF949FA5), ), child: Text(block.window), ), ], ), ), AnimatedContainer( duration: duration, curve: curve, width: 24, height: 24, decoration: BoxDecoration( color: on ? KrowPalette.blue : Colors.transparent, borderRadius: BorderRadius.circular(7), border: Border.all( color: on ? KrowPalette.blue : const Color(0xFFC4D4C9), width: 1.5, ), ), child: AnimatedScale( scale: on ? 1 : 0, duration: duration, curve: Curves.easeOutBack, child: const Icon( LucideIcons.check, size: 14, color: Colors.white, ), ), ), ], ), ), ); } }