feat: Introduce waitlist functionality and in-app notifications with supporting types and dependencies.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* lidando com Auth, Consultas (Shops/Services) e CRUD (Criar/Ler/Atualizar/Apagar).
|
||||
*/
|
||||
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { Appointment, Barber, BarberShop, CartItem, Order, Product, Service, User } from '../types';
|
||||
import { Appointment, Barber, BarberShop, CartItem, Order, Product, Service, User, WaitlistEntry, AppNotification } from '../types';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import { nanoid } from 'nanoid';
|
||||
import * as Notifications from 'expo-notifications';
|
||||
@@ -18,6 +18,8 @@ type State = {
|
||||
cart: CartItem[];
|
||||
appointments: Appointment[];
|
||||
orders: Order[];
|
||||
waitlists: WaitlistEntry[];
|
||||
notifications: AppNotification[];
|
||||
};
|
||||
|
||||
type AppContextValue = State & {
|
||||
@@ -40,6 +42,8 @@ type AppContextValue = State & {
|
||||
addBarber: (shopId: string, barber: Omit<Barber, 'id'>) => Promise<void>;
|
||||
updateBarber: (shopId: string, barber: Barber) => Promise<void>;
|
||||
deleteBarber: (shopId: string, barberId: string) => Promise<void>;
|
||||
joinWaitlist: (shopId: string, serviceId: string, barberId: string, date: string) => Promise<boolean>;
|
||||
markNotificationRead: (id: string) => Promise<void>;
|
||||
refreshShops: () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -50,6 +54,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [appointments, setAppointments] = useState<Appointment[]>([]);
|
||||
const [orders, setOrders] = useState<Order[]>([]);
|
||||
const [cart, setCart] = useState<CartItem[]>([]);
|
||||
const [waitlists, setWaitlists] = useState<WaitlistEntry[]>([]);
|
||||
const [notifications, setNotifications] = useState<AppNotification[]>([]);
|
||||
const [user, setUser] = useState<User | undefined>(undefined);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@@ -92,6 +98,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const { data: productsData } = await supabase.from('products').select('*');
|
||||
const { data: appointmentsData } = await supabase.from('appointments').select('*');
|
||||
const { data: ordersData } = await supabase.from('orders').select('*');
|
||||
const { data: waitlistsData } = await supabase.from('waitlist').select('*');
|
||||
const { data: notificationsData } = await supabase.from('notifications').select('*');
|
||||
|
||||
if (shopsData) {
|
||||
const merged: BarberShop[] = shopsData.map((shop: any) => ({
|
||||
@@ -142,6 +150,33 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
if (waitlistsData) {
|
||||
setWaitlists(
|
||||
waitlistsData.map((w: any) => ({
|
||||
id: w.id,
|
||||
shopId: w.shop_id,
|
||||
serviceId: w.service_id,
|
||||
barberId: w.barber_id,
|
||||
customerId: w.customer_id,
|
||||
date: w.date,
|
||||
status: w.status as WaitlistEntry['status'],
|
||||
createdAt: w.created_at,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
if (notificationsData) {
|
||||
setNotifications(
|
||||
notificationsData.map((n: any) => ({
|
||||
id: n.id,
|
||||
userId: n.user_id,
|
||||
message: n.message,
|
||||
read: n.read,
|
||||
createdAt: n.created_at,
|
||||
}))
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error refreshing shops:', err);
|
||||
}
|
||||
@@ -293,7 +328,26 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
};
|
||||
|
||||
const updateAppointmentStatus = async (id: string, status: Appointment['status']) => {
|
||||
const apt = appointments.find((a) => a.id === id);
|
||||
await supabase.from('appointments').update({ status }).eq('id', id);
|
||||
|
||||
if (status === 'cancelado' && apt) {
|
||||
const waitlistDate = apt.date.split(' ')[0]; // Extract YYYY-MM-DD
|
||||
const waitingUsers = waitlists.filter(w => w.barberId === apt.barberId && w.date === waitlistDate && w.status === 'pending');
|
||||
|
||||
if (waitingUsers.length > 0) {
|
||||
const notificationsToInsert = waitingUsers.map(w => ({
|
||||
user_id: w.customerId,
|
||||
message: `Surgiu uma vaga no horário que pretendia a ${waitlistDate} às ${apt.date.split(' ')[1]}! Corra para fazer a reserva.`
|
||||
}));
|
||||
|
||||
await supabase.from('notifications').insert(notificationsToInsert);
|
||||
|
||||
const waitlistIds = waitingUsers.map(w => w.id);
|
||||
await supabase.from('waitlist').update({ status: 'notified' }).in('id', waitlistIds);
|
||||
}
|
||||
}
|
||||
|
||||
await refreshShops();
|
||||
};
|
||||
|
||||
@@ -302,6 +356,30 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
await refreshShops();
|
||||
};
|
||||
|
||||
const joinWaitlist = async (shopId: string, serviceId: string, barberId: string, date: string) => {
|
||||
if (!user) return false;
|
||||
const { error } = await supabase.from('waitlist').insert([{
|
||||
shop_id: shopId,
|
||||
service_id: serviceId,
|
||||
barber_id: barberId,
|
||||
customer_id: user.id,
|
||||
date,
|
||||
status: 'pending'
|
||||
}]);
|
||||
if (error) {
|
||||
console.error('Erro ao entrar na lista de espera:', error);
|
||||
return false;
|
||||
}
|
||||
await refreshShops();
|
||||
return true;
|
||||
};
|
||||
|
||||
const markNotificationRead = async (id: string) => {
|
||||
const { error } = await supabase.from('notifications').update({ read: true }).eq('id', id);
|
||||
if (error) console.error("Erro ao marcar notificação:", error);
|
||||
else await refreshShops();
|
||||
};
|
||||
|
||||
const value: AppContextValue = useMemo(
|
||||
() => ({
|
||||
user,
|
||||
@@ -309,6 +387,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
cart,
|
||||
appointments,
|
||||
orders,
|
||||
waitlists,
|
||||
notifications,
|
||||
login,
|
||||
logout,
|
||||
register,
|
||||
@@ -328,9 +408,11 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
addBarber,
|
||||
updateBarber,
|
||||
deleteBarber,
|
||||
joinWaitlist,
|
||||
markNotificationRead,
|
||||
refreshShops,
|
||||
}),
|
||||
[user, shops, cart, appointments, orders]
|
||||
[user, shops, cart, appointments, orders, waitlists, notifications]
|
||||
);
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
Reference in New Issue
Block a user