import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:timetrack/features/projects/domain/projects_provider.dart'; import 'package:timetrack/features/settings/data/export_service.dart'; class ExportSheet extends ConsumerStatefulWidget { const ExportSheet({super.key}); static Future show(BuildContext context) { return showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (_) => const ExportSheet(), ); } @override ConsumerState createState() => _ExportSheetState(); } class _ExportSheetState extends ConsumerState { ExportFormat _format = ExportFormat.csv; _Range _range = _Range.thisWeek; int? _projectId; bool _exporting = false; @override Widget build(BuildContext context) { final projectsAsync = ref.watch(allProjectsProvider); return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + 16, top: 16, left: 16, right: 16, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Export data', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 16), // Format Text('Format', style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 8), Wrap( spacing: 8, children: ExportFormat.values.map((f) { final label = f.name.toUpperCase(); return ChoiceChip( label: Text(label), selected: _format == f, onSelected: (_) => setState(() => _format = f), ); }).toList(), ), const SizedBox(height: 12), // Date range Text('Date range', style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 8), Wrap( spacing: 8, children: _Range.values.map((r) { return ChoiceChip( label: Text(r.label), selected: _range == r, onSelected: (_) => setState(() => _range = r), ); }).toList(), ), const SizedBox(height: 12), // Project filter projectsAsync.when( data: (projects) => DropdownButtonFormField( initialValue: _projectId, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Project (optional)', ), items: [ const DropdownMenuItem(value: null, child: Text('All projects')), ...projects.map((p) => DropdownMenuItem( value: p.id, child: Text(p.name), )), ], onChanged: (id) => setState(() => _projectId = id), ), loading: () => const LinearProgressIndicator(), error: (e, _) => const SizedBox.shrink(), ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: _exporting ? null : _doExport, icon: _exporting ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.share), label: const Text('Share'), ), ), ], ), ); } Future _doExport() async { setState(() => _exporting = true); try { final (from, to) = _range.bounds; await ref.read(exportServiceProvider).export( format: _format, from: from, to: to, projectId: _projectId, ); if (mounted) Navigator.pop(context); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Export failed: $e')), ); } } finally { if (mounted) setState(() => _exporting = false); } } } enum _Range { today('Today'), thisWeek('This week'), thisMonth('This month'); const _Range(this.label); final String label; (DateTime, DateTime) get bounds { final now = DateTime.now(); switch (this) { case _Range.today: final start = DateTime(now.year, now.month, now.day); return (start, start.add(const Duration(days: 1))); case _Range.thisWeek: final monday = now.subtract(Duration(days: now.weekday - 1)); final start = DateTime(monday.year, monday.month, monday.day); return (start, start.add(const Duration(days: 7))); case _Range.thisMonth: final start = DateTime(now.year, now.month); return (start, DateTime(now.year, now.month + 1)); } } }