import 'package:flutter/material.dart'; import '../api_service.dart'; import '../theme.dart'; class TeamScreen extends StatefulWidget { final ApiService api; final VoidCallback onAuthError; const TeamScreen({super.key, required this.api, required this.onAuthError}); @override State createState() => _TeamScreenState(); } class _TeamScreenState extends State { List _team = []; List _pending = []; bool _loading = true; bool _busy = false; @override void initState() { super.initState(); _load(); } Future _load() async { try { final t = await widget.api.team(); final p = await widget.api.teamLeave(); if (mounted) setState(() { _team = t; _pending = p; _loading = false; }); } on ApiException catch (e) { if (e.status == 401) return widget.onAuthError(); if (mounted) setState(() => _loading = false); } } Future _decide(String gid, bool approve) async { setState(() => _busy = true); try { approve ? await widget.api.approve(gid) : await widget.api.reject(gid); await _load(); } on ApiException catch (e) { if (e.status == 401) return widget.onAuthError(); if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message))); } finally { if (mounted) setState(() => _busy = false); } } Color _sc(String? s) => statusColor(s); @override Widget build(BuildContext context) { if (_loading) return const Center(child: CircularProgressIndicator(color: AppColors.emerald)); return RefreshIndicator( onRefresh: _load, color: AppColors.emerald, child: ListView( padding: const EdgeInsets.all(16), children: [ if (_pending.isNotEmpty) ...[ const Text('Pending approvals', style: TextStyle(fontWeight: FontWeight.w600, color: AppColors.navy)), const SizedBox(height: 8), ..._pending.map((r) => Card( margin: const EdgeInsets.only(bottom: 8), child: Padding( padding: const EdgeInsets.all(12), child: Row(children: [ Expanded( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(r.employeeName ?? '—', style: const TextStyle(fontWeight: FontWeight.w600, color: AppColors.navy)), Text('${r.leaveTypeName ?? 'Leave'} · ${r.days ?? ''}d · ${r.fromDate} → ${r.toDate}', style: const TextStyle(fontSize: 12, color: AppColors.muted)), ]), ), IconButton( onPressed: _busy ? null : () => _decide(r.gid, true), icon: const Icon(Icons.check_circle_outline, color: AppColors.emerald), ), IconButton( onPressed: _busy ? null : () => _decide(r.gid, false), icon: const Icon(Icons.cancel_outlined, color: AppColors.danger), ), ]), ), )), const SizedBox(height: 16), ], const Text('Team today', style: TextStyle(fontWeight: FontWeight.w600, color: AppColors.navy)), const SizedBox(height: 8), if (_team.isEmpty) const Text('No team members.', style: TextStyle(color: AppColors.muted)) else ..._team.map((m) => Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: CircleAvatar( backgroundColor: AppColors.emerald.withValues(alpha: 0.12), child: Text(_initials(m.employeeName), style: const TextStyle(color: AppColors.emerald, fontWeight: FontWeight.bold, fontSize: 13)), ), title: Text(m.employeeName, style: const TextStyle(fontWeight: FontWeight.w600)), subtitle: Text('In ${m.checkIn ?? '—'} · Out ${m.checkOut ?? '—'}${m.hours != null ? ' · ${m.hours}h' : ''}'), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration(color: _sc(m.status).withValues(alpha: 0.12), borderRadius: BorderRadius.circular(20)), child: Text((m.status).replaceAll('_', ' '), style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: _sc(m.status))), ), ), )), ], ), ); } String _initials(String name) { final p = name.trim().split(RegExp(r'\s+')).where((x) => x.isNotEmpty).toList(); return p.isEmpty ? '?' : p.take(2).map((x) => x[0].toUpperCase()).join(); } }