Celebrations
Celebrations Reference Overview
Section titled “Celebrations Reference Overview”This section provides detailed reference documentation for working with the Celebrations module.
The Celebrations reference documentation is broken down into functional subsections: Celebrations
Celebrations features:
-
Module Overview — What the module does and key capabilities.
-
Endpoints — API functions used by the module.
-
Client Workflows — Client-side functions that orchestrate endpoint calls (example: creating a post).
Module Overview
Section titled “Module Overview”The module allows users to create celebration posts with a main image and attachments. Other users can react to posts and reply in a comment thread.
Admins/Super Admins can add, edit, and archive different celebration types.
The module also includes a Birthdays section to view upcoming birthdays and search for text inside main posts and replies.
Endpoints
Section titled “Endpoints”This section displays the endpoints being used.
Post Celebration
Section titled “Post Celebration”- The Post Celebrations endpoint creates a new main celebration.
export async function postCelebrations( payload: CelebrationPayload): Promise<any> { const response = await Api.post("/celebrations", payload); return response?.ok ? response.data?.data : null;}Get Celebration
Section titled “Get Celebration”- Fetches the list of celebrations and returns an array of celebration items.
export const getCelebrations = async (): Promise<any[] | null> => { const res: any = await Api.get("/celebrations"); if (!res?.ok) return null;
const body = res?.data ?? res; if (body?.success === false) return null;
return body?.data?.celebrations ?? [];};Get Celebration thread
Section titled “Get Celebration thread”This function fetches the full thread for a single celebration post — meaning:
-
The main celebration post (returned under thread.celebration)
-
The comment thread / replies for that post (usually under thread.replies)
export const getCelebrationThread = async ( celebrationId: number): Promise<{ celebration: any; replies: any[] } | null> => { const res: any = await Api.get(`/celebrations/${celebrationId}`); if (!res?.ok) return null;
const body = res?.data ?? res; if (body?.success === false) return null;
return body?.data ?? null;};Post Celebration Attachments
Section titled “Post Celebration Attachments”This function uploads an attachment file to an existing celebration post.
export async function postCelebrationAttachments( celebrationId: number, payload: any): Promise<any> { const response = await Api.post( `/celebrations/${celebrationId}/attachments`, payload ); return response?.ok ? response.data?.data ?? response.data ?? null : null;}Post Celebration Images
Section titled “Post Celebration Images”This function uploads an images to an existing celebration post
This function uploads an Image to an existing celebration post.export async function postCelebrationImages( celebrationId: number, payload: any): Promise<any> { const response = await Api.post( `/celebrations/${celebrationId}/images`, payload ); return response?.ok ? response.data?.data ?? response.data ?? null : null;}Get Celebration Images
Section titled “Get Celebration Images”this function should fetch the images that a user posted
export async function getCelebrationImages( celebrationId: number): Promise<any[] | null> { const response = await Api.get(`/celebrations/${celebrationId}/images`); return response?.ok ? response.data?.data ?? response.data ?? null : null;}Get Celebration Attachments
Section titled “Get Celebration Attachments”this function should fetch the attachments user posted
export async function getCelebrationAttachments( celebrationId: number): Promise<any[] | null> { const response = await Api.get( `/celebrations/${celebrationId}/attachments` ); return response?.ok ? response.data?.data ?? response.data ?? null : null;}Get Celebration Type
Section titled “Get Celebration Type”this function should fetch the celebration type.It can optionally ask the backend to include archived and/or inactive types.
export async function getCelebrationType(options?: { includeArchived?: boolean; includeInactive?: boolean;}): Promise<any[] | null> { const qs = new URLSearchParams();
if (options?.includeArchived) qs.set("include_archived", "1"); if (options?.includeInactive) qs.set("include_inactive", "1");
const query = qs.toString() ? `?${qs.toString()}` : "";
const res: any = await Api.get(`/celebrations/types${query}`); if (!res?.ok) return null;
const body = res?.data ?? res;
const types = body?.data?.types ?? body?.types ?? body?.data ?? [];
return Array.isArray(types) ? types : [];}Client Workflows
Section titled “Client Workflows”Functions
Section titled “Functions”handleSendPost function
- This handleSendPost function is the client-side Create Celebration Post workflow. It controls everything that happens when a user clicks Post—validation → post creation → media upload → feed refresh → UI reset—and it uses the postCelebrations endpoint to create the main post.
const handleSendPost = async () => { if (!canCreateTopLevel || isSubmitting) return;
if (!selectedTypeValue) { toast.error("Please select a celebration type."); return; }
const titleText = title.trim(); const bodyHtml = (body ?? "").trim(); const bodyPlain = bodyHtml .replace(/<[^>]+>/g, "") .replace(/\s+/g, " ") .trim();
if (!titleText && !bodyPlain && !attachmentFile && !imageFile) return;
const payload = { contentText: titleText || "Untitled", contentJson: { blocks: [{ type: "text", value: bodyHtml }] }, celebrationType: selectedTypeValue, };
setIsSubmitting(true); try { const created = await postCelebrations(payload);
const celebrationId = created?.celebrationId ?? created?.id ?? created?.data?.celebrationId ?? created?.data?.id ?? created?.celebration?.id ?? created?.data?.celebration?.id ?? null;
if (celebrationId && imageFile) { const imageForm = new FormData(); imageForm.append("file", imageFile);
const uploadedImages = await postCelebrationImages(celebrationId, imageForm);
if (!uploadedImages) toast.warning("Post created, but image failed to upload"); else toast.success("Image uploaded successfully"); }
if (celebrationId && attachmentFile) { const fileForm = new FormData(); fileForm.append("file", attachmentFile);
const uploadedFiles = await postCelebrationAttachments(celebrationId, fileForm);
if (!uploadedFiles) toast.warning("Post created, but attachment failed to upload"); else toast.success("Attachment uploaded successfully"); }
toast.success("Post created");
setTitle(""); setBody(""); setAttachmentFile(null); setImageFile(null); setShowEmojiPicker(false); setShowMentionPicker(false); setMentionQuery(""); setShowTypePicker(false);
setVisibleCount(BATCH_SIZE); await refetchCelebrations();
if (scrollContainerRef.current) scrollContainerRef.current.scrollTop = 0; } catch { toast.error("Failed to create post"); } finally { setIsSubmitting(false); }};