From 58ac33990f9dbd1d72e354ba31904b9aa09eae1f Mon Sep 17 00:00:00 2001 From: Felipe Cardoso Date: Sat, 15 Mar 2025 19:44:20 +0100 Subject: [PATCH] Add RSVP context and provider for managing RSVP state Introduce a context and provider for handling RSVP data, including fetching, updating, creating, and deleting RSVPs. The implementation utilizes React Query for data management and exposes methods for managing RSVP operations throughout the application. --- frontend/src/context/rsvp-context.tsx | 188 ++++++++++++++++++++++++++ frontend/src/providers/data.tsx | 5 +- 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 frontend/src/context/rsvp-context.tsx 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} + ); }