import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../labels.dart'; import '../models/models.dart'; import '../services/auth_controller.dart'; import '../ui/feedback.dart'; class AdminScreen extends StatelessWidget { const AdminScreen({super.key}); @override Widget build(BuildContext context) { return DefaultTabController( length: 2, child: Column( children: [ const TabBar( tabs: [ Tab(text: 'Dealers'), Tab(text: 'Sales reps'), ], ), const Expanded( child: TabBarView( children: [ _DealersTab(), _RepsTab(), ], ), ), ], ), ); } } class _DealersTab extends StatefulWidget { const _DealersTab(); @override State<_DealersTab> createState() => _DealersTabState(); } class _DealersTabState extends State<_DealersTab> { final _name = TextEditingController(); final _code = TextEditingController(); final _address = TextEditingController(); final _phone = TextEditingController(); String? _district; int _districtField = 0; List _dealers = []; bool _loading = true; bool _saving = false; @override void initState() { super.initState(); _load(); } @override void dispose() { _name.dispose(); _code.dispose(); _address.dispose(); _phone.dispose(); super.dispose(); } Future _load() async { try { final dealers = await context.read().api.adminDealers(); if (!mounted) return; setState(() { _dealers = dealers; _loading = false; }); } catch (error) { if (!mounted) return; setState(() => _loading = false); showError(context, error); } } Future _delete(Dealer dealer) async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete dealer'), content: Text( 'Delete ${dealer.name}? Its login, stock, and plans will be removed.', ), actions: [ TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')), FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Delete')), ], ), ); if (confirmed != true || !mounted) return; try { await context.read().api.deleteDealer(dealer.id); await _load(); } catch (error) { if (mounted) showError(context, error); } } Future _save() async { setState(() => _saving = true); try { await context.read().api.createDealer({ 'name': _name.text.trim(), 'code': _code.text.trim(), 'district': _district ?? '', 'address': _address.text.trim(), 'phone': _phone.text.trim(), }); _name.clear(); _code.clear(); _address.clear(); _phone.clear(); _district = null; _districtField++; await _load(); } catch (error) { if (mounted) showError(context, error); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { if (_loading) return const Center(child: CircularProgressIndicator()); return ListView( padding: const EdgeInsets.all(16), children: [ Text('New dealer', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 12), TextField(controller: _name, decoration: const InputDecoration(labelText: 'Name')), const SizedBox(height: 12), TextField(controller: _code, decoration: const InputDecoration(labelText: 'Code')), const SizedBox(height: 12), DropdownButtonFormField( key: ValueKey(_districtField), initialValue: _district, isExpanded: true, decoration: const InputDecoration(labelText: 'District'), items: [ for (final district in sriLankaDistricts) DropdownMenuItem(value: district, child: Text(district)), ], onChanged: (value) => setState(() => _district = value), ), const SizedBox(height: 12), TextField(controller: _address, decoration: const InputDecoration(labelText: 'Address')), const SizedBox(height: 12), TextField( controller: _phone, keyboardType: TextInputType.phone, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: const InputDecoration( labelText: 'Contact phone', hintText: '0715127342', helperText: 'This number becomes the dealer login', ), ), const SizedBox(height: 12), FilledButton( onPressed: _saving ? null : _save, child: Text(_saving ? 'Saving' : 'Create dealer'), ), const SizedBox(height: 20), for (final dealer in _dealers) ListTile( contentPadding: EdgeInsets.zero, title: Text(dealer.name), subtitle: Text('${dealer.code}${dealer.district == null ? '' : ' ยท ${dealer.district}'}'), trailing: IconButton( tooltip: 'Delete', onPressed: () => _delete(dealer), icon: const Icon(Icons.delete_outline), ), ), ], ); } } class _RepsTab extends StatefulWidget { const _RepsTab(); @override State<_RepsTab> createState() => _RepsTabState(); } class _RepsTabState extends State<_RepsTab> { final _name = TextEditingController(); final _phone = TextEditingController(); final _password = TextEditingController(); List _dealers = []; List _reps = []; final Set _selected = {}; bool _loading = true; bool _saving = false; @override void initState() { super.initState(); _load(); } @override void dispose() { _name.dispose(); _phone.dispose(); _password.dispose(); super.dispose(); } Future _load() async { final api = context.read().api; try { final results = await Future.wait([api.adminDealers(), api.salesReps()]); if (!mounted) return; setState(() { _dealers = results[0] as List; _reps = results[1] as List; _loading = false; }); } catch (error) { if (!mounted) return; setState(() => _loading = false); showError(context, error); } } Future _save() async { setState(() => _saving = true); try { await context.read().api.createSalesRep({ 'name': _name.text.trim(), 'phone': _phone.text.trim(), 'password': _password.text, 'dealerIds': _selected.toList(), }); _name.clear(); _phone.clear(); _password.clear(); _selected.clear(); await _load(); } catch (error) { if (mounted) showError(context, error); } finally { if (mounted) setState(() => _saving = false); } } Future _changeAssignment(AppUser rep, {required bool assign}) async { final choices = _dealers.where((dealer) { final has = rep.assignedDealerIds.contains(dealer.id); return assign ? !has : has; }).toList(); if (choices.isEmpty) { showError(context, assign ? 'Every dealer is already assigned' : 'No dealers to remove'); return; } String? dealerId = choices.first.id; final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(assign ? 'Assign dealer' : 'Remove dealer'), content: DropdownButtonFormField( initialValue: dealerId, items: [ for (final dealer in choices) DropdownMenuItem(value: dealer.id, child: Text(dealer.name)), ], onChanged: (value) => dealerId = value, ), actions: [ TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')), FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Save')), ], ), ); if (confirmed != true || dealerId == null || !mounted) return; try { await context.read().api.assignDealer( salesRepId: rep.id, dealerId: dealerId!, assigned: assign, ); await _load(); } catch (error) { if (mounted) showError(context, error); } } String _dealerNames(AppUser rep) { if (rep.assignedDealerIds.isEmpty) return 'No dealers assigned'; return rep.assignedDealerIds .map((id) { for (final dealer in _dealers) { if (dealer.id == id) return dealer.name; } return id; }) .join(', '); } @override Widget build(BuildContext context) { if (_loading) return const Center(child: CircularProgressIndicator()); return ListView( padding: const EdgeInsets.all(16), children: [ Text('New sales rep', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 12), TextField(controller: _name, decoration: const InputDecoration(labelText: 'Name')), const SizedBox(height: 12), TextField( controller: _phone, keyboardType: TextInputType.phone, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: const InputDecoration(labelText: 'Phone', hintText: '0715127342'), ), const SizedBox(height: 12), TextField( controller: _password, obscureText: true, decoration: const InputDecoration(labelText: 'Password'), ), const SizedBox(height: 8), for (final dealer in _dealers) CheckboxListTile( contentPadding: EdgeInsets.zero, title: Text(dealer.name), value: _selected.contains(dealer.id), onChanged: (checked) { setState(() { if (checked == true) { _selected.add(dealer.id); } else { _selected.remove(dealer.id); } }); }, ), FilledButton( onPressed: _saving ? null : _save, child: Text(_saving ? 'Saving' : 'Create sales rep'), ), const SizedBox(height: 20), for (final rep in _reps) Card( child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(rep.name, style: Theme.of(context).textTheme.titleMedium), Text(rep.phone), const SizedBox(height: 4), Text(_dealerNames(rep)), Row( children: [ TextButton( onPressed: () => _changeAssignment(rep, assign: true), child: const Text('Assign'), ), TextButton( onPressed: () => _changeAssignment(rep, assign: false), child: const Text('Remove'), ), ], ), ], ), ), ), ], ); } }