diff --git a/frontend/src/context/rsvp-context.tsx b/frontend/src/context/rsvp-context.tsx new file mode 100644 index 0000000..4ec7f9b --- /dev/null +++ b/frontend/src/context/rsvp-context.tsx @@ -0,0 +1,188 @@ +"use client"; + +import React, { createContext, ReactNode, useContext } from "react"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + getRsvps, + getRsvp, + createRsvp, + updateRsvp, + deleteRsvp, + updateRsvpStatus, +} from "@/client/sdk.gen"; +import { + RsvpSchema, + RsvpSchemaCreate, + RsvpSchemaUpdate, + RsvpStatus, +} from "@/client/types.gen"; + +// RSVP context state +interface RSVPContextState { + rsvps: RsvpSchema[] | undefined; + rsvp: RsvpSchema | undefined; + isLoadingRsvps: boolean; + isLoadingRsvp: boolean; + refetchRsvps: () => Promise; + + fetchRsvpById: (id: string) => void; + createRsvp: (data: RsvpSchemaCreate) => Promise; + updateRsvp: ( + id: string, + data: RsvpSchemaUpdate, + ) => Promise; + updateRsvpStatus: ( + id: string, + status: RsvpStatus, + additionalData?: Partial, + ) => Promise; + deleteRsvp: (id: string) => Promise; + + currentRsvpId: string | null; + setCurrentRsvpId: (id: string | null) => void; + + error: Error | null; +} + +// Default context state +const defaultRSVPContextState: RSVPContextState = { + rsvps: undefined, + rsvp: undefined, + isLoadingRsvps: false, + isLoadingRsvp: false, + refetchRsvps: async () => { + throw new Error("RSVPContext not initialized"); + }, + + fetchRsvpById: () => {}, + createRsvp: async () => { + throw new Error("RSVPContext not initialized"); + }, + updateRsvp: async () => { + throw new Error("RSVPContext not initialized"); + }, + updateRsvpStatus: async () => { + throw new Error("RSVPContext not initialized"); + }, + deleteRsvp: async () => { + throw new Error("RSVPContext not initialized"); + }, + + currentRsvpId: null, + setCurrentRsvpId: () => {}, + + error: null, +}; + +// Create context +const RSVPContext = createContext(defaultRSVPContextState); + +// Hook to use context +export const useRSVPs = () => { + const context = useContext(RSVPContext); + if (!context) { + throw new Error("useRSVPs must be used within an RSVPProvider"); + } + return context; +}; + +// RSVP Provider Props +interface RSVPProviderProps { + children: ReactNode; +} + +// RSVP Provider Component +export const RSVPProvider: React.FC = ({ children }) => { + const queryClient = useQueryClient(); + const [currentRsvpId, setCurrentRsvpId] = React.useState(null); + + // Fetch all RSVPs + const { + data: rsvps, + isLoading: isLoadingRsvps, + error, + refetch: refetchRsvps, + } = useQuery({ + queryKey: ["rsvps"], + queryFn: () => getRsvps().then((res) => res.data), + refetchInterval: 60000, + refetchOnWindowFocus: "always", + }); + + // Fetch specific RSVP + const { data: rsvp, isLoading: isLoadingRsvp } = useQuery({ + queryKey: ["rsvp", currentRsvpId], + queryFn: () => + getRsvp({ path: { rsvp_id: currentRsvpId ?? "" } }).then( + (res) => res.data, + ), + enabled: !!currentRsvpId, + }); + + const fetchRsvpById = (id: string) => { + setCurrentRsvpId(id); + }; + + // Create RSVP Mutation + const createMutation = useMutation({ + mutationFn: (data: RsvpSchemaCreate) => + createRsvp({ body: data }).then((res) => res.data), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ["rsvps"] }), + }); + + // Update RSVP Mutation + const updateMutation = useMutation({ + mutationFn: ({ id, data }: { id: string; data: RsvpSchemaUpdate }) => + updateRsvp({ path: { rsvp_id: id }, body: data }).then((res) => res.data), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ["rsvps"] }), + }); + + // Update RSVP Status Mutation specifically + const updateStatusMutation = useMutation({ + mutationFn: ({ + id, + status, + additionalData = {}, + }: { + id: string; + status: RsvpStatus; + additionalData?: Partial; + }) => + updateRsvpStatus({ + path: { rsvp_id: id }, + body: { status, ...additionalData }, + }).then((res) => res.data), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ["rsvps"] }), + }); + + // Delete RSVP Mutation + const deleteMutation = useMutation({ + mutationFn: (id: string) => + deleteRsvp({ path: { rsvp_id: id } }).then((res) => res.data), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ["rsvps"] }), + }); + + const contextValue: RSVPContextState = { + rsvps, + rsvp, + isLoadingRsvps, + isLoadingRsvp, + refetchRsvps, + + fetchRsvpById, + createRsvp: createMutation.mutateAsync, + updateRsvp: (id, data) => updateMutation.mutateAsync({ id, data }), + updateRsvpStatus: (id, status, additionalData) => + updateStatusMutation.mutateAsync({ id, status, additionalData }), + deleteRsvp: deleteMutation.mutateAsync, + + currentRsvpId, + setCurrentRsvpId, + + error: error as Error | null, + }; + + return ( + {children} + ); +}; diff --git a/frontend/src/providers/data.tsx b/frontend/src/providers/data.tsx index 65339c0..07a445c 100644 --- a/frontend/src/providers/data.tsx +++ b/frontend/src/providers/data.tsx @@ -1,11 +1,14 @@ import React from "react"; import { EventsProvider } from "@/context/event-context"; import { EventThemesProvider } from "@/context/event-theme-context"; +import { RSVPProvider } from "@/context/rsvp-context"; export function DataProviders({ children }: { children: React.ReactNode }) { return ( - {children} + + {children} + ); }