Files
badoHair_fe/contexts/AdminContext.tsx
2026-05-21 22:24:22 +00:00

148 lines
4.5 KiB
TypeScript

"use client";
import React, { useContext, useState, useEffect, startTransition, ReactNode, createContext } from "react";
import { useAuth } from "@/contexts/AuthContext";
import * as productsApi from "@/lib/api/products";
import * as bookingsApi from "@/lib/api/bookings";
import { Product } from "@/data/products";
import { ApiError } from "@/lib/api";
// Adapter: maps BookingApi to the Reservation shape used by admin pages
export interface Reservation {
id: string;
clientName: string;
email: string;
phone: string;
service: string;
date: string;
time: string;
status: "pending" | "confirmed" | "cancelled";
createdAt: string;
}
function toReservation(b: bookingsApi.BookingApi): Reservation {
return {
id: b.id,
clientName: b.client_name ?? "—",
email: b.client_email ?? "—",
phone: b.client_phone ?? "—",
service: b.service_note ?? "—",
date: b.slot_date,
time: b.slot_start,
status: b.status === "completed" || b.status === "no_show" ? "confirmed" : b.status as Reservation["status"],
createdAt: b.created_at.slice(0, 10),
};
}
interface AdminContextType {
isAdmin: boolean;
products: Product[];
productsLoading: boolean;
refreshProducts: () => Promise<void>;
addProduct: (payload: productsApi.ProductPayload) => Promise<Product>;
updateProduct: (id: string, payload: Partial<productsApi.ProductPayload>) => Promise<void>;
deleteProduct: (id: string) => Promise<void>;
reservations: Reservation[];
reservationsLoading: boolean;
refreshReservations: () => Promise<void>;
updateReservationStatus: (id: string, status: "confirmed" | "cancelled") => Promise<void>;
deleteReservation: (id: string) => Promise<void>;
}
const AdminContext = createContext<AdminContextType | undefined>(undefined);
export const AdminProvider = ({ children }: { children: ReactNode }) => {
const { isAdmin } = useAuth();
const [products, setProducts] = useState<Product[]>([]);
const [productsLoading, setProductsLoading] = useState(false);
const [reservations, setReservations] = useState<Reservation[]>([]);
const [reservationsLoading, setReservationsLoading] = useState(false);
const refreshProducts = async () => {
if (!isAdmin) return;
setProductsLoading(true);
try {
const res = await productsApi.adminListProducts();
setProducts(res.data);
} finally {
setProductsLoading(false);
}
};
const refreshReservations = async () => {
if (!isAdmin) return;
setReservationsLoading(true);
try {
const res = await bookingsApi.adminListBookings();
setReservations(res.data.map(toReservation));
} finally {
setReservationsLoading(false);
}
};
useEffect(() => {
if (isAdmin) {
startTransition(() => {
refreshProducts();
refreshReservations();
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAdmin]);
const addProduct = async (payload: productsApi.ProductPayload): Promise<Product> => {
const newProduct = await productsApi.adminCreateProduct(payload);
setProducts((prev) => [newProduct, ...prev]);
return newProduct;
};
const updateProduct = async (id: string, payload: Partial<productsApi.ProductPayload>) => {
const updated = await productsApi.adminUpdateProduct(id, payload);
setProducts((prev) => prev.map((p) => (p.id === id ? updated : p)));
};
const deleteProduct = async (id: string) => {
await productsApi.adminDeleteProduct(id);
setProducts((prev) => prev.filter((p) => p.id !== id));
};
const updateReservationStatus = async (id: string, status: "confirmed" | "cancelled") => {
await bookingsApi.adminUpdateBookingStatus(id, status);
setReservations((prev) =>
prev.map((r) => (r.id === id ? { ...r, status } : r))
);
};
const deleteReservation = async (id: string) => {
await bookingsApi.adminDeleteBooking(id);
setReservations((prev) => prev.filter((r) => r.id !== id));
};
return (
<AdminContext.Provider
value={{
isAdmin,
products,
productsLoading,
refreshProducts,
addProduct,
updateProduct,
deleteProduct,
reservations,
reservationsLoading,
refreshReservations,
updateReservationStatus,
deleteReservation,
}}
>
{children}
</AdminContext.Provider>
);
};
export const useAdmin = () => {
const ctx = useContext(AdminContext);
if (!ctx) throw new Error("useAdmin must be used within AdminProvider");
return ctx;
};