diff --git a/lib/screens/my_reports_screen.dart b/lib/screens/my_reports_screen.dart new file mode 100644 index 0000000..2cdfc86 --- /dev/null +++ b/lib/screens/my_reports_screen.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import '../main.dart'; +import '../theme/app_colors.dart'; + +class MyReportsScreen extends StatefulWidget { + const MyReportsScreen({super.key}); + + @override + State createState() => _MyReportsScreenState(); +} + +class _MyReportsScreenState extends State { + bool _isLoading = true; + String? _error; + List> _reports = []; + + @override + void initState() { + super.initState(); + _loadReports(); + } + + Future _loadReports() async { + final user = supabase.auth.currentUser; + if (user == null) return; + + setState(() { + _isLoading = true; + _error = null; + }); + + try { + final rows = await supabase + .from('car_reports') + .select('id, hw_id, reason, note, status, created_at, global_cars(name, series)') + .eq('reporter_user_id', user.id) + .order('created_at', ascending: false); + + if (!mounted) return; + setState(() { + _reports = List>.from(rows); + _isLoading = false; + }); + } catch (e) { + if (!mounted) return; + setState(() { + _error = e.toString(); + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('My Reports'), + ), + body: RefreshIndicator( + onRefresh: _loadReports, + child: _buildBody(), + ), + ); + } + + Widget _buildBody() { + if (_isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + if (_error != null) { + return ListView( + physics: const AlwaysScrollableScrollPhysics(), + children: [ + const SizedBox(height: 120), + Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + children: [ + const Icon(Icons.error_outline, color: AppColors.error, size: 42), + const SizedBox(height: 10), + Text( + _error!, + textAlign: TextAlign.center, + style: const TextStyle(color: AppColors.textSecondary), + ), + const SizedBox(height: 12), + ElevatedButton( + onPressed: _loadReports, + child: const Text('Retry'), + ), + ], + ), + ), + ), + ], + ); + } + + if (_reports.isEmpty) { + return ListView( + physics: const AlwaysScrollableScrollPhysics(), + children: const [ + SizedBox(height: 130), + Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Column( + children: [ + Icon(Icons.flag_outlined, size: 48, color: AppColors.textHint), + SizedBox(height: 12), + Text( + 'No reports yet', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + ), + SizedBox(height: 6), + Text( + 'When you report a catalog issue, it will appear here.', + textAlign: TextAlign.center, + style: TextStyle(color: AppColors.textSecondary), + ), + ], + ), + ), + ), + ], + ); + } + + return ListView.separated( + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.fromLTRB(12, 12, 12, 24), + itemBuilder: (_, index) { + final report = _reports[index]; + final global = report['global_cars'] as Map?; + final status = (report['status'] as String? ?? 'open').toLowerCase(); + final createdAt = DateTime.tryParse(report['created_at'] as String? ?? ''); + final note = report['note'] as String?; + + return Card( + child: Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + report['hw_id'] as String? ?? 'Unknown ID', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + letterSpacing: 0.4, + ), + ), + ), + _StatusChip(status: status), + ], + ), + if ((global?['name'] as String?) != null) ...[ + const SizedBox(height: 6), + Text( + global?['name'] as String, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: AppColors.textSecondary, + ), + ), + ], + const SizedBox(height: 8), + Text( + 'Reason: ${report['reason'] as String? ?? 'Unknown'}', + style: const TextStyle(color: AppColors.textSecondary), + ), + if (note != null && note.isNotEmpty) ...[ + const SizedBox(height: 4), + Text( + note, + style: const TextStyle(color: AppColors.textHint), + ), + ], + if (createdAt != null) ...[ + const SizedBox(height: 10), + Text( + _formatDateTime(createdAt), + style: const TextStyle(fontSize: 12, color: AppColors.textHint), + ), + ], + ], + ), + ), + ); + }, + separatorBuilder: (context, index) => const SizedBox(height: 8), + itemCount: _reports.length, + ); + } + + String _formatDateTime(DateTime date) { + final d = date.toLocal(); + final mm = d.month.toString().padLeft(2, '0'); + final dd = d.day.toString().padLeft(2, '0'); + final hh = d.hour.toString().padLeft(2, '0'); + final min = d.minute.toString().padLeft(2, '0'); + return '$dd.$mm.${d.year} $hh:$min'; + } +} + +class _StatusChip extends StatelessWidget { + final String status; + + const _StatusChip({required this.status}); + + @override + Widget build(BuildContext context) { + final normalized = status.toLowerCase(); + final (label, color) = switch (normalized) { + 'reviewed' => ('Reviewed', Colors.blueGrey), + 'resolved' => ('Resolved', AppColors.success), + 'dismissed' => ('Dismissed', AppColors.error), + _ => ('Open', AppColors.orange), + }; + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(999), + ), + child: Text( + label, + style: TextStyle( + color: color, + fontWeight: FontWeight.w600, + fontSize: 12, + ), + ), + ); + } +} diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index a128d8e..538c1a7 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../main.dart'; import '../theme/app_colors.dart'; import 'about_screen.dart'; +import 'my_reports_screen.dart'; /// Profile / settings tab. class ProfileScreen extends StatelessWidget { @@ -93,6 +94,12 @@ class ProfileScreen extends StatelessWidget { title: 'Change Password', onTap: () => _changePassword(context), ), + _SettingsTile( + icon: Icons.flag_outlined, + title: 'My Reports', + subtitle: 'Track report status', + onTap: () => _showMyReports(context), + ), const SizedBox(height: 24), Text( 'App', @@ -202,6 +209,12 @@ class ProfileScreen extends StatelessWidget { MaterialPageRoute(builder: (_) => const AboutScreen()), ); } + + void _showMyReports(BuildContext context) { + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const MyReportsScreen()), + ); + } } // ── Settings tile widget ──────────────────────────────────────────────