import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:go_router/go_router.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:intl/intl.dart'; import '/core/auth_provider.dart'; import '/models/profile.dart'; import '/models/child.dart'; import '/models/daily_access_approval.dart'; class HomeDashboard extends ConsumerWidget { const HomeDashboard({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final profileAsync = ref.watch(currentProfileProvider); return profileAsync.when( data: (profile) { if (profile == null) { return const Scaffold( body: Center(child: Text('Perfil não encontrado')), ); } switch (profile.role) { case 'principal': case 'admin': return _AdminDashboard(profile: profile); case 'teacher': return _TeacherDashboard(profile: profile); case 'parent': return _ParentDashboard(profile: profile); default: return const Scaffold( body: Center(child: Text('Role desconhecido'))); } }, loading: () => const Scaffold(body: Center(child: CircularProgressIndicator())), error: (e, _) => Scaffold(body: Center(child: Text('Erro: $e'))), ); } } // ─────────────── ADMIN DASHBOARD ─────────────── class _AdminDashboard extends ConsumerWidget { final Profile profile; const _AdminDashboard({required this.profile}); @override Widget build(BuildContext context, WidgetRef ref) { final supabase = Supabase.instance.client; return Scaffold( backgroundColor: const Color(0xFF0D1117), appBar: AppBar( backgroundColor: const Color(0xFF161B22), title: Row( children: [ Image.asset('assets/logo.png', height: 36, errorBuilder: (_, __, ___) => const Icon(Icons.child_care, color: Color(0xFF4FC3F7))), const SizedBox(width: 10), const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Sementes do Futuro', style: TextStyle( color: Color(0xFF4FC3F7), fontSize: 14, fontWeight: FontWeight.bold)), Text('Dashboard Admin', style: TextStyle(color: Color(0xFF888888), fontSize: 11)), ], ), ], ), actions: [ IconButton( icon: const Icon(Icons.notifications_outlined, color: Color(0xFF4FC3F7)), onPressed: () => context.go('/announcements'), ), IconButton( icon: const Icon(Icons.settings_outlined, color: Color(0xFF4FC3F7)), onPressed: () => context.go('/settings'), ), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Olá, ${profile.fullName.split(' ').first}! 👋', style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text(DateFormat('EEEE, d MMMM yyyy', 'pt_PT').format(DateTime.now()), style: const TextStyle(color: Color(0xFF888888), fontSize: 13)), const SizedBox(height: 24), // Quick actions Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _QuickAction( icon: Icons.add_circle, label: 'Diário', onTap: () => context.go('/new-diary')), _QuickAction( icon: Icons.check_circle, label: 'Presença', onTap: () => context.go('/attendance')), _QuickAction( icon: Icons.attach_money, label: 'Pagamentos', onTap: () => context.go('/payments')), _QuickAction( icon: Icons.campaign, label: 'Avisos', onTap: () => context.go('/announcements')), ], ), const SizedBox(height: 24), // Cards de estatísticas const Text('Visão Geral', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 12), GridView.count( crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 12, mainAxisSpacing: 12, childAspectRatio: 1.5, children: const [ _StatCard(title: 'Crianças Hoje', value: '–', icon: Icons.child_care, color: Color(0xFF4FC3F7)), _StatCard(title: 'Presença', value: '–%', icon: Icons.check_circle, color: Color(0xFFA5D6A7)), _StatCard(title: 'Pendentes', value: '–', icon: Icons.payment, color: Color(0xFFFFCC02)), _StatCard(title: 'Avisos', value: '–', icon: Icons.campaign, color: Color(0xFFFF7043)), ], ), const SizedBox(height: 24), // Aprovações pendentes const Text('Pedidos de Acesso Hoje', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 12), StreamBuilder>>( stream: supabase .from('daily_access_approvals') .stream(primaryKey: ['id']).eq('status', 'pending'), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center( child: CircularProgressIndicator( color: Color(0xFF4FC3F7))); } final approvals = snapshot.data!; if (approvals.isEmpty) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(12), ), child: const Row( children: [ Icon(Icons.check_circle, color: Color(0xFFA5D6A7)), SizedBox(width: 8), Text('Nenhum pedido pendente', style: TextStyle(color: Color(0xFF888888))), ], ), ); } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: approvals.length, itemBuilder: (context, index) { final approval = DailyAccessApproval.fromMap(approvals[index]); return _ApprovalCard( approval: approval, onApprove: () => _approve(supabase, approval.id), onReject: () => _reject(supabase, approval.id), ); }, ); }, ), const SizedBox(height: 80), ], ), ), bottomNavigationBar: _AdminBottomNav(), floatingActionButton: FloatingActionButton.extended( backgroundColor: const Color(0xFF4FC3F7), icon: const Icon(Icons.person_add, color: Colors.white), label: const Text('Nova Criança', style: TextStyle(color: Colors.white)), onPressed: () => context.go('/child/new'), ), ); } Future _approve(SupabaseClient supabase, String id) async { await supabase.from('daily_access_approvals').update({ 'status': 'approved', 'approved_at': DateTime.now().toIso8601String(), 'approved_by': supabase.auth.currentUser!.id, }).eq('id', id); } Future _reject(SupabaseClient supabase, String id) async { await supabase .from('daily_access_approvals') .update({'status': 'rejected'}).eq('id', id); } } // ─────────────── TEACHER DASHBOARD ─────────────── class _TeacherDashboard extends ConsumerWidget { final Profile profile; const _TeacherDashboard({required this.profile}); @override Widget build(BuildContext context, WidgetRef ref) { final supabase = Supabase.instance.client; return Scaffold( backgroundColor: const Color(0xFF0D1117), appBar: AppBar( backgroundColor: const Color(0xFF161B22), title: const Text('Minha Turma', style: TextStyle(color: Color(0xFF4FC3F7))), actions: [ IconButton( icon: const Icon(Icons.chat_outlined, color: Color(0xFF4FC3F7)), onPressed: () => context.go('/chat'), ), IconButton( icon: const Icon(Icons.person_outline, color: Color(0xFF4FC3F7)), onPressed: () => context.go('/profile'), ), ], ), body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(16), child: Text( 'Olá, ${profile.fullName.split(' ').first}! 👋', style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold), ), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text('Crianças da tua turma hoje:', style: TextStyle(color: Color(0xFF888888), fontSize: 14)), ), const SizedBox(height: 12), Expanded( child: StreamBuilder>>( stream: supabase .from('children') .stream(primaryKey: ['id']).eq('teacher_id', profile.id), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center( child: CircularProgressIndicator(color: Color(0xFF4FC3F7))); } final children = snapshot.data!.map(Child.fromMap).toList(); return ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: children.length, itemBuilder: (context, index) { final child = children[index]; return _ChildCard( child: child, onTap: () => context.go('/child/${child.id}'), ); }, ); }, ), ), ], ), floatingActionButton: FloatingActionButton.extended( backgroundColor: const Color(0xFF4FC3F7), icon: const Icon(Icons.add, color: Colors.white), label: const Text('Novo Diário', style: TextStyle(color: Colors.white)), onPressed: () => context.go('/new-diary'), ), bottomNavigationBar: _TeacherBottomNav(), ); } } // ─────────────── PARENT DASHBOARD ─────────────── class _ParentDashboard extends ConsumerWidget { final Profile profile; const _ParentDashboard({required this.profile}); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( backgroundColor: const Color(0xFF0D1117), appBar: AppBar( backgroundColor: const Color(0xFF161B22), title: Image.asset('assets/logo.png', height: 36, errorBuilder: (_, __, ___) => const Icon(Icons.child_care, color: Color(0xFF4FC3F7))), centerTitle: true, actions: [ IconButton( icon: const Icon(Icons.notifications_outlined, color: Color(0xFF4FC3F7)), onPressed: () => context.go('/announcements'), ), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(16), ), child: Row( children: [ CircleAvatar( radius: 30, backgroundColor: const Color(0xFF4FC3F7), child: Text( profile.fullName.isNotEmpty ? profile.fullName[0].toUpperCase() : 'P', style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), ), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Bem-vindo(a)!', style: TextStyle( color: Color(0xFF888888), fontSize: 12)), Text(profile.fullName, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), ], ), ], ), ), const SizedBox(height: 24), const Text('Ações Rápidas', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 12), Row( children: [ Expanded( child: _ActionButton( icon: Icons.book_outlined, label: 'Ver Diário', onTap: () => context.go('/children'), ), ), const SizedBox(width: 12), Expanded( child: _ActionButton( icon: Icons.restaurant_menu, label: 'Cardápio', onTap: () => context.go('/menu'), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _ActionButton( icon: Icons.medication, label: 'Medicação', onTap: () => context.go('/medication'), ), ), const SizedBox(width: 12), Expanded( child: _ActionButton( icon: Icons.chat_outlined, label: 'Falar c/ Educadora', onTap: () => context.go('/chat'), ), ), ], ), ], ), ), bottomNavigationBar: _ParentBottomNav(), ); } } // ─────────────── WIDGETS AUXILIARES ─────────────── class _QuickAction extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; const _QuickAction( {required this.icon, required this.label, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Column( children: [ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF333366)), ), child: Icon(icon, color: const Color(0xFF4FC3F7), size: 26), ), const SizedBox(height: 6), Text(label, style: const TextStyle(color: Color(0xFF888888), fontSize: 12)), ], ), ); } } class _StatCard extends StatelessWidget { final String title; final String value; final IconData icon; final Color color; const _StatCard( {required this.title, required this.value, required this.icon, required this.color}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(16), border: Border.all(color: color.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.min, children: [ Icon(icon, color: color, size: 22), const SizedBox(height: 4), FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(value, style: TextStyle( color: color, fontSize: 20, fontWeight: FontWeight.bold)), ), Text(title, style: const TextStyle( color: Color(0xFF888888), fontSize: 10), overflow: TextOverflow.ellipsis), ], ), ); } } class _ApprovalCard extends StatelessWidget { final DailyAccessApproval approval; final VoidCallback onApprove; final VoidCallback onReject; const _ApprovalCard( {required this.approval, required this.onApprove, required this.onReject}); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFFFCC02).withOpacity(0.3)), ), child: Row( children: [ const Icon(Icons.person_outline, color: Color(0xFF4FC3F7)), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Funcionário: ${approval.userId.substring(0, 8)}...', style: const TextStyle(color: Colors.white, fontSize: 13)), Text( 'IP: ${approval.ipAddress ?? 'N/A'} • ${DateFormat('HH:mm').format(approval.approvalDate)}', style: const TextStyle( color: Color(0xFF888888), fontSize: 11)), ], ), ), IconButton( icon: const Icon(Icons.check_circle, color: Color(0xFFA5D6A7)), onPressed: onApprove, tooltip: 'Aprovar', ), IconButton( icon: const Icon(Icons.cancel, color: Colors.red), onPressed: onReject, tooltip: 'Rejeitar', ), ], ), ); } } class _ChildCard extends StatelessWidget { final Child child; final VoidCallback onTap; const _ChildCard({required this.child, required this.onTap}); String get _moodEmoji { switch (child.mood) { case 'happy': return '😊'; case 'sad': return '😟'; case 'sick': return '🤒'; case 'excited': return '😃'; default: return '😐'; } } @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF333366)), ), child: Row( children: [ CircleAvatar( radius: 26, backgroundImage: child.photoUrl != null ? NetworkImage(child.photoUrl!) : null, backgroundColor: const Color(0xFF4FC3F7).withOpacity(0.2), child: child.photoUrl == null ? const Icon(Icons.child_care, color: Color(0xFF4FC3F7), size: 28) : null, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(child.fullName, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15)), Text('${child.age} anos', style: const TextStyle( color: Color(0xFF888888), fontSize: 12)), ], ), ), Text(_moodEmoji, style: const TextStyle(fontSize: 28)), const SizedBox(width: 8), const Icon(Icons.chevron_right, color: Color(0xFF888888)), ], ), ), ); } } class _ActionButton extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; const _ActionButton( {required this.icon, required this.label, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12), decoration: BoxDecoration( color: const Color(0xFF161B22), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF333366)), ), child: Column( children: [ Icon(icon, color: const Color(0xFF4FC3F7), size: 28), const SizedBox(height: 8), Text(label, style: const TextStyle(color: Colors.white, fontSize: 13), textAlign: TextAlign.center), ], ), ), ); } } // Bottom Navs class _AdminBottomNav extends StatelessWidget { @override Widget build(BuildContext context) { return BottomNavigationBar( backgroundColor: const Color(0xFF161B22), selectedItemColor: const Color(0xFF4FC3F7), unselectedItemColor: const Color(0xFF888888), type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Início'), BottomNavigationBarItem(icon: Icon(Icons.child_care), label: 'Crianças'), BottomNavigationBarItem(icon: Icon(Icons.check_box), label: 'Presença'), BottomNavigationBarItem(icon: Icon(Icons.people), label: 'Utilizadores'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Perfil'), ], onTap: (i) { final routes = ['/home', '/children', '/attendance', '/users', '/profile']; context.go(routes[i]); }, ); } } class _TeacherBottomNav extends StatelessWidget { @override Widget build(BuildContext context) { return BottomNavigationBar( backgroundColor: const Color(0xFF161B22), selectedItemColor: const Color(0xFF4FC3F7), unselectedItemColor: const Color(0xFF888888), type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Início'), BottomNavigationBarItem(icon: Icon(Icons.child_care), label: 'Crianças'), BottomNavigationBarItem(icon: Icon(Icons.book), label: 'Diários'), BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Chat'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Perfil'), ], onTap: (i) { final routes = ['/home', '/children', '/new-diary', '/chat', '/profile']; context.go(routes[i]); }, ); } } class _ParentBottomNav extends StatelessWidget { @override Widget build(BuildContext context) { return BottomNavigationBar( backgroundColor: const Color(0xFF161B22), selectedItemColor: const Color(0xFF4FC3F7), unselectedItemColor: const Color(0xFF888888), type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Início'), BottomNavigationBarItem(icon: Icon(Icons.child_care), label: 'Filhos'), BottomNavigationBarItem(icon: Icon(Icons.restaurant_menu), label: 'Cardápio'), BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Chat'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Perfil'), ], onTap: (i) { final routes = ['/home', '/children', '/menu', '/chat', '/profile']; context.go(routes[i]); }, ); } }