agendamento

This commit is contained in:
2026-03-11 10:06:41 +00:00
parent 1ad78609e2
commit 35c0eb4844
4 changed files with 143 additions and 28 deletions

View File

@@ -13,12 +13,14 @@ type State = {
user?: User;
shops: BarberShop[];
cart: CartItem[];
appointments: Appointment[];
};
type AppContextValue = State & {
logout: () => void;
addToCart: (item: CartItem) => void;
clearCart: () => void;
createAppointment: (input: Omit<Appointment, 'id' | 'status' | 'total'>) => Promise<Appointment | null>;
addService: (shopId: string, service: Omit<Service, 'id'>) => Promise<void>;
updateService: (shopId: string, service: Service) => Promise<void>;
deleteService: (shopId: string, serviceId: string) => Promise<void>;
@@ -29,6 +31,7 @@ const AppContext = createContext<AppContextValue | undefined>(undefined);
export const AppProvider = ({ children }: { children: React.ReactNode }) => {
const [shops, setShops] = useState<BarberShop[]>([]);
const [appointments, setAppointments] = useState<Appointment[]>([]);
const [cart, setCart] = useState<CartItem[]>([]);
const [user, setUser] = useState<User | undefined>(undefined);
const [loading, setLoading] = useState(true);
@@ -114,6 +117,31 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
return;
}
// Query 5: Obtém a listagem global de Marcações (tabela 'appointments')
const { data: appointmentsData, error: appointmentsError } = await supabase
.from('appointments')
.select('*');
if (appointmentsError) {
console.error("Erro ao buscar appointments:", appointmentsError);
return;
}
if (appointmentsData) {
setAppointments(
appointmentsData.map((a: any) => ({
id: a.id,
shopId: a.shop_id,
serviceId: a.service_id,
barberId: a.barber_id,
customerId: a.customer_id,
date: a.date,
status: a.status as Appointment['status'],
total: a.total,
}))
);
}
// Associar serviços, barbeiros e produtos às respetivas shops, simulando um INNER JOIN nativo do SQL
const shopsWithServices = shopsData.map((shop) => ({
...shop,
@@ -222,21 +250,60 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
await refreshShops();
};
const createAppointment: AppContextValue['createAppointment'] = async (input) => {
const shop = shops.find((s) => s.id === input.shopId);
if (!shop) return null;
const svc = shop.services.find((s) => s.id === input.serviceId);
if (!svc) return null;
const { data: newRow, error } = await supabase.from('appointments').insert([
{
shop_id: input.shopId,
service_id: input.serviceId,
barber_id: input.barberId,
customer_id: input.customerId,
date: input.date,
status: 'pendente',
total: svc.price,
}
]).select().single();
if (error || !newRow) {
console.error("Erro ao criar marcação na BD:", error);
return null;
}
await refreshShops();
return {
id: newRow.id,
shopId: newRow.shop_id,
serviceId: newRow.service_id,
barberId: newRow.barber_id,
customerId: newRow.customer_id,
date: newRow.date,
status: newRow.status as Appointment['status'],
total: newRow.total,
};
};
// Empacotamento em objeto estabilizado memoizado face renderizações espúrias (React Context Pattern)
const value: AppContextValue = useMemo(
() => ({
user,
shops,
cart,
appointments,
logout,
addToCart,
clearCart,
createAppointment,
addService,
updateService,
deleteService,
refreshShops,
}),
[user, shops, cart]
[user, shops, cart, appointments]
);
// Loading Shield evita quebra generalizada se o app renderizar sem BD disponível