import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../state/app_state.dart'; import '../utils/colors.dart'; class KelolaKasirScreen extends StatefulWidget { final bool embedded; const KelolaKasirScreen({super.key, this.embedded = false}); @override State createState() => _KelolaKasirScreenState(); } class _KelolaKasirScreenState extends State { final TextEditingController _emailCtrl = TextEditingController(); final TextEditingController _passwordCtrl = TextEditingController(); final TextEditingController _nameCtrl = TextEditingController(); bool _loading = false; String? _error; @override void initState() { super.initState(); context.read().loadKasirAccounts(); } @override void dispose() { _emailCtrl.dispose(); _passwordCtrl.dispose(); _nameCtrl.dispose(); super.dispose(); } Future _addKasir() async { final email = _emailCtrl.text.trim(); final password = _passwordCtrl.text; final name = _nameCtrl.text.trim(); if (email.isEmpty || password.isEmpty) { setState(() => _error = 'Email dan password harus diisi'); return; } setState(() { _loading = true; _error = null; }); final state = context.read(); final err = await state.createKasir(email, password, name); if (mounted) { setState(() => _loading = false); if (err != null) { setState(() => _error = err); } else { _emailCtrl.clear(); _passwordCtrl.clear(); _nameCtrl.clear(); } } } @override Widget build(BuildContext context) { final body = _buildBody(context); if (widget.embedded) return body; return Scaffold( appBar: AppBar( title: const Text('Akun Kasir'), ), body: SingleChildScrollView( padding: const EdgeInsets.all(12), child: body, ), ); } Widget _buildBody(BuildContext context) { final state = context.watch(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.person_add_outlined, color: kAccent), const SizedBox(width: 8), Text( 'Tambah Akun Kasir', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: kText), ), ], ), const SizedBox(height: 14), TextField( controller: _nameCtrl, decoration: const InputDecoration( labelText: 'Nama Lengkap', prefixIcon: Icon(Icons.person_outline), ), ), const SizedBox(height: 10), TextField( controller: _emailCtrl, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.email_outlined), ), ), const SizedBox(height: 10), TextField( controller: _passwordCtrl, obscureText: true, decoration: const InputDecoration( labelText: 'Password', prefixIcon: Icon(Icons.lock_outlined), ), ), if (_error != null) ...[ const SizedBox(height: 10), Text(_error!, style: const TextStyle(color: kDanger, fontSize: 13)), ], const SizedBox(height: 14), SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: _loading ? null : _addKasir, icon: _loading ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFF1A1206)), ) : const Icon(Icons.add), label: Text(_loading ? 'Menyimpan...' : 'Tambah Kasir'), ), ), ], ), ), ), const SizedBox(height: 12), Text( 'Daftar Akun Kasir', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: kText), ), const SizedBox(height: 10), if (state.kasirAccounts.isEmpty) Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 40), child: Column( children: const [ Icon(Icons.people_outline, size: 48, color: kMuted), SizedBox(height: 8), Text('Belum ada akun kasir', style: TextStyle(color: kMuted)), ], ), ) ) else ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: state.kasirAccounts.length, separatorBuilder: (_, __) => const SizedBox(height: 8), itemBuilder: (context, i) { final kasir = state.kasirAccounts[i]; return Card( child: ListTile( leading: CircleAvatar( backgroundColor: kAccent.withValues(alpha: 0.15), child: Text( (kasir['full_name']?.toString().isNotEmpty == true ? kasir['full_name'].toString() : kasir['email']?.toString()) ?.isNotEmpty == true ? (kasir['full_name']?.toString().isNotEmpty == true ? kasir['full_name'].toString() : kasir['email'].toString())[0].toUpperCase() : '?', style: TextStyle(color: kAccent, fontWeight: FontWeight.w700), ), ), title: Text( (kasir['full_name']?.toString().isNotEmpty == true ? kasir['full_name'].toString() : kasir['email']?.toString().isNotEmpty == true ? kasir['email'].toString() : 'Tanpa Nama'), style: const TextStyle(fontWeight: FontWeight.w600), ), subtitle: Text( '${kasir['role'] == 'kasir' ? 'Kasir' : 'Admin'}${kasir['email']?.toString().isNotEmpty == true ? ' • ${kasir['email']}' : ''}', style: const TextStyle(fontSize: 12, color: kMuted), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit_outlined, size: 20), color: kAccent, onPressed: () { _showEditDialog(context, state, kasir); }, ), IconButton( icon: const Icon(Icons.delete_outline, size: 20), color: kDanger, onPressed: () async { final confirm = await showDialog( context: context, builder: (context) => AlertDialog( icon: Icon(Icons.person_remove_outlined, color: kDanger, size: 28), title: const Text('Hapus Akun'), content: Text( 'Yakin ingin menghapus akun ${kasir['full_name']?.toString().isNotEmpty == true ? kasir['full_name'] : (kasir['email']?.toString().isNotEmpty == true ? kasir['email'] : '')}?'), actions: [ SizedBox( width: double.infinity, child: OutlinedButton( onPressed: () => Navigator.pop(context, false), style: OutlinedButton.styleFrom( foregroundColor: kMuted, side: const BorderSide(color: kBorder), ), child: const Text('Batal'), ), ), const SizedBox(height: 8), SizedBox( width: double.infinity, child: FilledButton( onPressed: () => Navigator.pop(context, true), style: FilledButton.styleFrom( backgroundColor: kDanger, ), child: const Text('Hapus'), ), ), ], ), ); if (confirm == true && mounted) { await state.deleteKasir(kasir['id'].toString()); } }, ), ], ), ), ); }, ), ], ); } void _showEditDialog(BuildContext context, AppState state, Map kasir) { final emailCtrl = TextEditingController(text: kasir['email']?.toString() ?? ''); final nameCtrl = TextEditingController(text: kasir['full_name']?.toString() ?? ''); final passwordCtrl = TextEditingController(); bool isLoading = false; String? error; showDialog( context: context, builder: (dialogContext) { return StatefulBuilder( builder: (ctx, setDialog) { Future submit() async { final email = emailCtrl.text.trim(); final name = nameCtrl.text.trim(); final password = passwordCtrl.text; if (email.isEmpty) { setDialog(() => error = 'Email harus diisi'); return; } setDialog(() { isLoading = true; error = null; }); final err = await state.updateKasir( kasir['id'].toString(), email, name, password.isEmpty ? null : password, ); if (!dialogContext.mounted) return; setDialog(() => isLoading = false); if (err != null) { setDialog(() => error = err); } else { Navigator.pop(dialogContext); } } return AlertDialog( title: const Text('Edit Akun Kasir'), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: emailCtrl, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.email_outlined), ), ), const SizedBox(height: 10), TextField( controller: nameCtrl, decoration: const InputDecoration( labelText: 'Nama Lengkap', prefixIcon: Icon(Icons.person_outline), ), ), const SizedBox(height: 10), TextField( controller: passwordCtrl, obscureText: true, decoration: const InputDecoration( labelText: 'Password (kosongkan jika tidak diubah)', prefixIcon: Icon(Icons.lock_outlined), ), ), if (error != null) ...[ const SizedBox(height: 10), Text(error!, style: const TextStyle(color: kDanger, fontSize: 13)), ], ], ), actions: [ TextButton( onPressed: isLoading ? null : () => Navigator.pop(dialogContext), child: const Text('Batal'), ), FilledButton( onPressed: isLoading ? null : submit, style: FilledButton.styleFrom(backgroundColor: kAccent), child: isLoading ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFF1A1206)), ) : const Text('Simpan'), ), ], ); }, ); }, ); } }