agendamento
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Appointment, Barber, BarberShop, CartItem, Order, Product, Service, User } from '../types';
|
||||
import { Appointment, AppointmentStatus, Barber, BarberShop, CartItem, Order, Product, Service, User } from '../types';
|
||||
import { mockUsers } from '../data/mock';
|
||||
import { storage } from '../lib/storage';
|
||||
import { supabase } from '../lib/supabase';
|
||||
@@ -29,9 +29,9 @@ type AppContextValue = State & {
|
||||
addToCart: (item: CartItem) => void;
|
||||
removeFromCart: (refId: string) => void;
|
||||
clearCart: () => void;
|
||||
createAppointment: (input: Omit<Appointment, 'id' | 'status' | 'total'>) => Appointment | null;
|
||||
createAppointment: (input: Omit<Appointment, 'id' | 'status' | 'total'>) => Promise<Appointment | null>;
|
||||
placeOrder: (customerId: string, shopId?: string) => Order | null;
|
||||
updateAppointmentStatus: (id: string, status: Appointment['status']) => void;
|
||||
updateAppointmentStatus: (id: string, status: Appointment['status']) => Promise<void>;
|
||||
updateOrderStatus: (id: string, status: Order['status']) => void;
|
||||
addService: (shopId: string, service: Omit<Service, 'id'>) => Promise<void>;
|
||||
updateService: (shopId: string, service: Service) => Promise<void>;
|
||||
@@ -96,6 +96,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const { data: servicesData } = await supabase.from('services').select('*');
|
||||
const { data: barbersData } = await supabase.from('barbers').select('*');
|
||||
const { data: productsData } = await supabase.from('products').select('*');
|
||||
const { data: appointmentsData } = await supabase.from('appointments').select('*');
|
||||
|
||||
const fetchedShops: BarberShop[] = shopsData.map((shop) => ({
|
||||
id: shop.id,
|
||||
@@ -130,6 +131,17 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
})),
|
||||
}));
|
||||
|
||||
const formattedAppointments: Appointment[] = (appointmentsData ?? []).map((a) => ({
|
||||
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 AppointmentStatus,
|
||||
total: a.total,
|
||||
}));
|
||||
|
||||
setState((s) => {
|
||||
// A BD é agora a única fonte de verdade.
|
||||
// Como o CRUD já insere na BD antes do refresh, a sobreposição com 'localVersion' foi
|
||||
@@ -153,7 +165,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
return true;
|
||||
});
|
||||
|
||||
return { ...s, shops: dedupedShops, shopsReady: true };
|
||||
return { ...s, shops: dedupedShops, appointments: formattedAppointments, shopsReady: true };
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('refreshShops error:', err);
|
||||
@@ -348,7 +360,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const clearCart = () => setState((s) => ({ ...s, cart: [] }));
|
||||
|
||||
const createAppointment: AppContextValue['createAppointment'] = (input) => {
|
||||
const createAppointment: AppContextValue['createAppointment'] = async (input) => {
|
||||
const shop = state.shops.find((s) => s.id === input.shopId);
|
||||
if (!shop) return null;
|
||||
const svc = shop.services.find((s) => s.id === input.serviceId);
|
||||
@@ -359,9 +371,35 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
);
|
||||
if (exists) return null;
|
||||
|
||||
const appointment: Appointment = { ...input, id: nanoid(), status: 'pendente', total: svc.price };
|
||||
setState((s) => ({ ...s, appointments: [...s.appointments, appointment] }));
|
||||
return appointment;
|
||||
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 AppointmentStatus,
|
||||
total: newRow.total
|
||||
};
|
||||
};
|
||||
|
||||
const placeOrder: AppContextValue['placeOrder'] = (customerId, onlyShopId) => {
|
||||
@@ -392,8 +430,13 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
return newOrders[0] ?? null;
|
||||
};
|
||||
|
||||
const updateAppointmentStatus: AppContextValue['updateAppointmentStatus'] = (id, status) => {
|
||||
setState((s) => ({ ...s, appointments: s.appointments.map((a) => (a.id === id ? { ...a, status } : a)) }));
|
||||
const updateAppointmentStatus: AppContextValue['updateAppointmentStatus'] = async (id, status) => {
|
||||
const { error } = await supabase.from('appointments').update({ status }).eq('id', id);
|
||||
if (error) {
|
||||
console.error("Erro ao atualizar status da marcação:", error);
|
||||
return;
|
||||
}
|
||||
await refreshShops();
|
||||
};
|
||||
|
||||
const updateOrderStatus: AppContextValue['updateOrderStatus'] = (id, status) => {
|
||||
|
||||
@@ -6,22 +6,27 @@ const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYm
|
||||
const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
|
||||
async function check() {
|
||||
console.log("Testing barber insert...");
|
||||
console.log("Listing some tables...");
|
||||
|
||||
const shopId = 'e1e871f8-b88f-43c3-88de-e54b7b836ece'; // From previous output
|
||||
// Auth with a test user to test RLS
|
||||
await supabase.auth.signInWithPassword({
|
||||
email: 'test_shop_1773154696588@test.com',
|
||||
password: 'Password123!'
|
||||
});
|
||||
|
||||
// First try standard insert
|
||||
const { data: b1, error: e1 } = await supabase.from('barbers').insert([
|
||||
{ shop_id: shopId, name: 'Test Barber Basic' }
|
||||
]).select();
|
||||
|
||||
console.log("Insert without specialties:", e1 || "SUCCESS", b1);
|
||||
|
||||
const { data: b2, error: e2 } = await supabase.from('barbers').insert([
|
||||
{ shop_id: shopId, name: 'Test Barber Complex', specialties: ['corte'] }
|
||||
]).select();
|
||||
|
||||
console.log("Insert with specialties:", e2 || "SUCCESS", b2);
|
||||
const tablesToCheck = ['appointments', 'events', 'bookings', 'orders'];
|
||||
for (const t of tablesToCheck) {
|
||||
const { error } = await supabase.from(t).select('id').limit(1);
|
||||
if (error) {
|
||||
console.log(`Table ${t} error:`, error.message);
|
||||
} else {
|
||||
console.log(`Table ${t} EXISTS!`);
|
||||
const { data } = await supabase.from(t).select('*').limit(1);
|
||||
if (data && data.length > 0) {
|
||||
console.log(`${t} columns:`, Object.keys(data[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function Booking() {
|
||||
/**
|
||||
* Dispara a ação de guardar a nova marcação na base de dados Supabase via Context API.
|
||||
*/
|
||||
const submit = () => {
|
||||
const submit = async () => {
|
||||
if (!user) {
|
||||
// Bloqueia ações de clientes anónimos exigindo Sessão iniciada
|
||||
navigate('/login');
|
||||
@@ -94,12 +94,12 @@ export default function Booking() {
|
||||
if (!canSubmit) return;
|
||||
|
||||
// O método 'createAppointment' fará internamente um pedido `supabase.from('appointments').insert(...)`
|
||||
const appt = createAppointment({ shopId: shop.id, serviceId, barberId, customerId: user.id, date: `${date} ${slot}` });
|
||||
const appt = await createAppointment({ shopId: shop.id, serviceId, barberId, customerId: user.id, date: `${date} ${slot}` });
|
||||
|
||||
if (appt) {
|
||||
navigate('/perfil');
|
||||
} else {
|
||||
alert('Horário indisponível');
|
||||
alert('Horário indisponível ou ocorreu um erro a comunicar com o servidor.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user