Add EventsContext for managing events state and operations

Introduce an EventsContext and EventsProvider to centralize event-related logic, including queries, mutations, and pagination. Wrap the application with the EventsProvider in the providers index to make the context accessible throughout the app.
This commit is contained in:
2025-03-11 06:19:06 +01:00
parent 114f0e7807
commit 80ff350053
2 changed files with 264 additions and 1 deletions

View File

@@ -0,0 +1,260 @@
"use client";
import React, {
createContext,
ReactNode,
useCallback,
useContext,
useState,
} from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
createEventMutation,
deleteEventMutation,
getEventBySlugOptions,
getEventOptions,
getPublicEventsOptions,
getUpcomingEventsOptions,
getUserEventsOptions,
updateEventMutation,
} from "@/client/@tanstack/react-query.gen";
import {
EventCreate,
EventResponse,
EventUpdate,
PaginatedResponseEventResponse,
} from "@/client/types.gen";
// Events context state interface
interface EventsContextState {
// Query access methods
userEvents: PaginatedResponseEventResponse | undefined;
upcomingEvents: PaginatedResponseEventResponse | undefined;
publicEvents: PaginatedResponseEventResponse | undefined;
isLoadingUserEvents: boolean;
isLoadingUpcomingEvents: boolean;
isLoadingPublicEvents: boolean;
// Single event methods
getEvent: (id: string) => Promise<EventResponse | undefined>;
getEventBySlug: (slug: string) => Promise<EventResponse | undefined>;
// Mutation methods
createEvent: (event: EventCreate) => Promise<EventResponse>;
updateEvent: (id: string, event: EventUpdate) => Promise<EventResponse>;
deleteEvent: (id: string) => Promise<void>;
// Pagination control
currentPage: number;
pageSize: number;
setCurrentPage: (page: number) => void;
setPageSize: (size: number) => void;
// Loading & error states
isCreating: boolean;
isUpdating: boolean;
isDeleting: boolean;
error: Error | null;
}
// Default context state
const defaultEventsState: EventsContextState = {
userEvents: undefined,
upcomingEvents: undefined,
publicEvents: undefined,
isLoadingUserEvents: false,
isLoadingUpcomingEvents: false,
isLoadingPublicEvents: false,
getEvent: async () => undefined,
getEventBySlug: async () => undefined,
createEvent: async () => {
throw new Error("EventsProvider not initialized");
},
updateEvent: async () => {
throw new Error("EventsProvider not initialized");
},
deleteEvent: async () => {
throw new Error("EventsProvider not initialized");
},
currentPage: 1,
pageSize: 10,
setCurrentPage: () => {},
setPageSize: () => {},
isCreating: false,
isUpdating: false,
isDeleting: false,
error: null,
};
// Create context
const EventsContext = createContext<EventsContextState>(defaultEventsState);
// Custom hook to use the events context
export const useEvents = () => {
const context = useContext(EventsContext);
if (!context) {
throw new Error("useEvents must be used within an EventsProvider");
}
return context;
};
// Events Provider Props
interface EventsProviderProps {
children: ReactNode;
}
// Events Provider Component
export const EventsProvider: React.FC<EventsProviderProps> = ({ children }) => {
const queryClient = useQueryClient();
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
// Query options with pagination
const paginationParams = {
query: {
skip: (currentPage - 1) * pageSize,
limit: pageSize,
},
};
// Get the user's events
const {
data: userEvents,
isLoading: isLoadingUserEvents,
error: userEventsError,
} = useQuery({
...getUserEventsOptions(paginationParams),
});
// Get upcoming events
const {
data: upcomingEvents,
isLoading: isLoadingUpcomingEvents,
error: upcomingEventsError,
} = useQuery({
...getUpcomingEventsOptions(paginationParams),
});
// Get public events
const {
data: publicEvents,
isLoading: isLoadingPublicEvents,
error: publicEventsError,
} = useQuery({
...getPublicEventsOptions(paginationParams),
});
// Get a single event by ID
const getEvent = useCallback(
async (id: string): Promise<EventResponse | undefined> => {
return await queryClient.fetchQuery({
...getEventOptions({ path: { event_id: id } }),
});
},
[queryClient],
);
// Get a single event by slug
const getEventBySlug = useCallback(
async (slug: string): Promise<EventResponse | undefined> => {
return await queryClient.fetchQuery({
...getEventBySlugOptions({ path: { slug } }),
});
},
[queryClient],
);
// Create event mutation
const createEventMutationHook = useMutation(createEventMutation());
const createEvent = useCallback(
async (event: EventCreate): Promise<EventResponse> => {
const result = await createEventMutationHook.mutateAsync({
body: event,
});
// Invalidate queries to refresh the lists
queryClient.invalidateQueries({ queryKey: ["getUserEvents"] });
return result;
},
[createEventMutationHook, queryClient],
);
// Update event mutation
const updateEventMutationHook = useMutation(updateEventMutation());
const updateEvent = useCallback(
async (id: string, event: EventUpdate): Promise<EventResponse> => {
const result = await updateEventMutationHook.mutateAsync({
path: { event_id: id },
body: event,
});
// Invalidate specific queries
queryClient.invalidateQueries({ queryKey: ["getEvent", id] });
queryClient.invalidateQueries({ queryKey: ["getUserEvents"] });
if (event.slug) {
queryClient.invalidateQueries({
queryKey: ["getEventBySlug", event.slug],
});
}
return result;
},
[updateEventMutationHook, queryClient],
);
// Delete event mutation
const deleteEventMutationHook = useMutation(deleteEventMutation());
const deleteEvent = useCallback(
async (id: string): Promise<void> => {
await deleteEventMutationHook.mutateAsync({
path: { event_id: id },
});
// Invalidate queries to refresh the lists
queryClient.invalidateQueries({ queryKey: ["getUserEvents"] });
},
[deleteEventMutationHook, queryClient],
);
// Combine all errors from queries for error state
const error =
userEventsError || upcomingEventsError || publicEventsError || null;
// Context value
const value: EventsContextState = {
userEvents,
upcomingEvents,
publicEvents,
isLoadingUserEvents,
isLoadingUpcomingEvents,
isLoadingPublicEvents,
getEvent,
getEventBySlug,
createEvent,
updateEvent,
deleteEvent,
currentPage,
pageSize,
setCurrentPage,
setPageSize,
isCreating: createEventMutationHook.isPending,
isUpdating: updateEventMutationHook.isPending,
isDeleting: deleteEventMutationHook.isPending,
error,
};
return (
<EventsContext.Provider value={value}>{children}</EventsContext.Provider>
);
};

View File

@@ -4,6 +4,7 @@ import React from "react";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { AuthProvider } from "@/context/auth-context";
import { EventsProvider } from "@/context/event-context";
import { ThemeProvider } from "next-themes";
// Create a client
@@ -26,7 +27,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
enableSystem
disableTransitionOnChange
>
<AuthProvider>{children}</AuthProvider>
<AuthProvider>
<EventsProvider>{children}</EventsProvider>
</AuthProvider>
</ThemeProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>