import React, { useState, useEffect } from 'react'; import { View, Text, ScrollView, StyleSheet, Pressable, Alert, RefreshControl, } from 'react-native'; import { useAuth } from '../../context/AuthContext'; import { supabase, isSupabaseConfigured } from '../../services/supabase'; import LocalDataService from '../../services/localDataService'; import { COLORS, SIZES, FONTS, SHADOWS } from '../../constants/theme'; import { Booking, Barber, Service, User } from '../../types'; const AdminDashboardScreen: React.FC = () => { const { user } = useAuth(); const [stats, setStats] = useState({ totalBookings: 0, totalRevenue: 0, activeBarbers: 0, totalCustomers: 0, }); const [recentBookings, setRecentBookings] = useState([]); const [barbers, setBarbers] = useState([]); const [services, setServices] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); useEffect(() => { if (user?.role === 'admin') { loadDashboardData(); } }, [user]); const loadDashboardData = async () => { try { if (isSupabaseConfigured) { // Use Supabase if configured const [bookingsRes, barbersRes, servicesRes, customersRes] = await Promise.all([ supabase .from('bookings') .select(` *, service:services(name, price), customer:users(name), barber:barbers( user:users(name) ) `) .order('created_at', { ascending: false }) .limit(10), supabase .from('barbers') .select(` *, user:users(name) `) .eq('is_active', true), supabase .from('services') .select('*') .eq('is_active', true), supabase .from('users') .select('*') .eq('role', 'customer') ]); if (bookingsRes.data) { const bookings = bookingsRes.data as Booking[]; setRecentBookings(bookings.slice(0, 5)); // Calculate stats const completedBookings = bookings.filter(b => b.status === 'completed'); const totalRevenue = completedBookings.reduce((sum, b) => { return sum + (b.service as any)?.price || 0; }, 0); setStats({ totalBookings: bookings.length, totalRevenue, activeBarbers: barbersRes.data?.length || 0, totalCustomers: customersRes.data?.length || 0, }); } if (barbersRes.data) setBarbers(barbersRes.data); if (servicesRes.data) setServices(servicesRes.data); } else { // Use local database const [bookings, statsData, barbersData, servicesData] = await Promise.all([ LocalDataService.getBookings(), LocalDataService.getStats(), LocalDataService.getBarbers(), LocalDataService.getServices(), ]); if (bookings) { setRecentBookings(bookings.slice(0, 5)); } if (statsData) { setStats(statsData); } if (barbersData) { setBarbers(barbersData); } if (servicesData) { setServices(servicesData); } } } catch (error) { console.error('Erro ao carregar dados do painel:', error); Alert.alert('Erro', 'Falha ao carregar dados do painel'); } finally { setLoading(false); setRefreshing(false); } }; const onRefresh = () => { setRefreshing(true); loadDashboardData(); }; const handleUpdateBookingStatus = async (bookingId: string, status: string) => { try { if (isSupabaseConfigured) { const { error } = await supabase .from('bookings') .update({ status }) .eq('id', bookingId); if (error) throw error; } else { const result = await LocalDataService.updateBookingStatus(bookingId, status as Booking['status']); if (!result) throw new Error('Falha ao actualizar marcação'); } Alert.alert('Sucesso', `Marcação ${status} com sucesso`); loadDashboardData(); } catch (error: any) { Alert.alert('Erro', error.message || 'Falha ao actualizar marcação'); } }; const StatCard = ({ title, value, subtitle }: { title: string; value: string | number; subtitle?: string }) => ( {title} {value} {subtitle && {subtitle}} ); const BookingItem = ({ booking }: { booking: Booking }) => ( {booking.service?.name} {booking.status} Cliente: {booking.customer?.name} Barbeiro: {booking.barber?.user?.name} {new Date(booking.booking_date).toLocaleDateString('pt-PT')} às {booking.booking_time} €{booking.service?.price} {booking.status === 'pending' && ( [ styles.actionButton, styles.confirmButton, pressed && styles.actionButtonPressed ]} onPress={() => handleUpdateBookingStatus(booking.id, 'confirmed')} > Confirmar [ styles.actionButton, styles.cancelButton, pressed && styles.actionButtonPressed ]} onPress={() => handleUpdateBookingStatus(booking.id, 'cancelled')} > Cancelar )} ); if (user?.role !== 'admin') { return ( Acesso Negado Requer acesso de administrador ); } if (loading) { return ( A carregar painel... ); } return ( } > Painel de Administração {/* Stats Cards */} {/* Recent Bookings */} Marcações Recentes {recentBookings.length === 0 ? ( Sem marcações recentes ) : ( recentBookings.map((booking) => ( )) )} {/* Quick Actions */} Acções Rápidas [styles.quickActionButton, pressed && styles.quickActionButtonPressed]}> Gerir Serviços [styles.quickActionButton, pressed && styles.quickActionButtonPressed]}> Gerir Barbeiros [styles.quickActionButton, pressed && styles.quickActionButtonPressed]}> Ver Relatórios [styles.quickActionButton, pressed && styles.quickActionButtonPressed]}> Definições ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: COLORS.background, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, loadingText: { ...FONTS.body, color: COLORS.textSecondary, }, accessDenied: { flex: 1, justifyContent: 'center', alignItems: 'center', }, accessDeniedText: { ...FONTS.h2, color: COLORS.error, marginBottom: SIZES.base, }, accessDeniedSubtext: { ...FONTS.body, color: COLORS.textSecondary, }, title: { ...FONTS.h1, color: COLORS.text, textAlign: 'center', padding: SIZES.padding, borderBottomWidth: 1, borderBottomColor: COLORS.border, }, statsContainer: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', padding: SIZES.margin, }, statCard: { width: '48%', backgroundColor: COLORS.surface, borderRadius: SIZES.radius, padding: SIZES.padding, marginBottom: SIZES.margin, alignItems: 'center', ...SHADOWS.medium, }, statTitle: { ...FONTS.caption, color: COLORS.textSecondary, marginBottom: SIZES.base / 2, }, statValue: { ...FONTS.h2, color: COLORS.primary, fontWeight: 'bold', }, statSubtitle: { ...FONTS.caption, color: COLORS.textSecondary, marginTop: SIZES.base / 2, }, section: { margin: SIZES.margin, }, sectionTitle: { ...FONTS.h2, color: COLORS.text, marginBottom: SIZES.margin, }, emptyText: { ...FONTS.body, color: COLORS.textSecondary, textAlign: 'center', paddingVertical: SIZES.padding, }, bookingItem: { backgroundColor: COLORS.surface, borderRadius: SIZES.radius, padding: SIZES.padding, marginBottom: SIZES.margin, ...SHADOWS.light, }, bookingHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: SIZES.margin, }, bookingService: { ...FONTS.h3, color: COLORS.text, flex: 1, }, statusBadge: { paddingHorizontal: SIZES.base, paddingVertical: SIZES.base / 2, borderRadius: SIZES.base / 2, }, statusText: { ...FONTS.caption, color: COLORS.background, fontWeight: 'bold', }, bookingCustomer: { ...FONTS.body, color: COLORS.textSecondary, marginBottom: SIZES.base / 2, }, bookingBarber: { ...FONTS.body, color: COLORS.textSecondary, marginBottom: SIZES.base / 2, }, bookingDate: { ...FONTS.caption, color: COLORS.textSecondary, marginBottom: SIZES.base / 2, }, bookingPrice: { ...FONTS.h3, color: COLORS.primary, fontWeight: 'bold', marginBottom: SIZES.margin, }, bookingActions: { flexDirection: 'row', justifyContent: 'space-between', }, actionButton: { borderRadius: SIZES.base, paddingVertical: SIZES.base, paddingHorizontal: SIZES.padding, flex: 1, marginHorizontal: SIZES.base / 2, alignItems: 'center', cursor: 'pointer', }, actionButtonPressed: { opacity: 0.8, }, confirmButton: { backgroundColor: COLORS.success, }, cancelButton: { backgroundColor: COLORS.error, }, actionButtonText: { ...FONTS.caption, color: COLORS.background, fontWeight: 'bold', }, actionsGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', }, quickActionButton: { width: '48%', backgroundColor: COLORS.surface, borderRadius: SIZES.radius, padding: SIZES.padding, marginBottom: SIZES.margin, alignItems: 'center', ...SHADOWS.light, cursor: 'pointer', }, quickActionButtonPressed: { opacity: 0.9, }, quickActionText: { ...FONTS.body, color: COLORS.text, textAlign: 'center', }, }); export default AdminDashboardScreen;