From 97a9e1588fc172612d667da03f47012a4d545bde Mon Sep 17 00:00:00 2001 From: Felipe Cardoso Date: Sun, 16 Mar 2025 17:37:08 +0100 Subject: [PATCH] Add category-modal.tsx to gifts page --- .../dashboard/events/[slug]/gifts/page.tsx | 220 +++++++++++++++++- .../src/components/gifts/category-modal.tsx | 205 ++++++++++++++++ 2 files changed, 422 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/gifts/category-modal.tsx diff --git a/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx b/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx index 4206472..537f159 100644 --- a/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx +++ b/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx @@ -1,3 +1,5 @@ +// Updated page.tsx with gift categories and summary sections + "use client"; import React, { useEffect, useState } from "react"; @@ -13,6 +15,7 @@ import { Edit, Trash, MoreHorizontal, + Settings, } from "lucide-react"; import { useEvents } from "@/context/event-context"; import { useGifts } from "@/context/gift-context"; @@ -43,6 +46,7 @@ import { SelectValue, } from "@/components/ui/select"; import { GiftStatus, GiftPriority } from "@/client/types.gen"; +import { CategoryModal } from "@/components/gifts/category-modal"; export default function GiftRegistryPage() { const { slug } = useParams<{ slug: string }>(); @@ -59,6 +63,16 @@ export default function GiftRegistryPage() { setCurrentEventId, } = useGifts(); + // State for modals + const [isAddGiftModalOpen, setIsAddGiftModalOpen] = useState(false); + const [isEditGiftModalOpen, setIsEditGiftModalOpen] = useState(false); + const [isAddCategoryModalOpen, setIsAddCategoryModalOpen] = useState(false); + const [isEditCategoryModalOpen, setIsEditCategoryModalOpen] = useState(false); + const [selectedGiftId, setSelectedGiftId] = useState(null); + const [selectedCategoryId, setSelectedCategoryId] = useState( + null, + ); + // State for filtering and searching const [searchQuery, setSearchQuery] = useState(""); const [categoryFilter, setCategoryFilter] = useState("all"); @@ -168,6 +182,25 @@ export default function GiftRegistryPage() { .reduce((acc, item) => acc + (item.quantity_requested || 1), 0) || 0, }; + // Functions to handle modal actions + const handleAddGift = () => { + setIsAddGiftModalOpen(true); + }; + + const handleEditGift = (giftId: string) => { + setSelectedGiftId(giftId); + setIsEditGiftModalOpen(true); + }; + + const handleAddCategory = () => { + setIsAddCategoryModalOpen(true); + }; + + const handleEditCategory = (categoryId: string) => { + setSelectedCategoryId(categoryId); + setIsEditCategoryModalOpen(true); + }; + // Load event and gift data useEffect(() => { fetchEventBySlug(slug); @@ -227,18 +260,47 @@ export default function GiftRegistryPage() { {/* Header */}
-

EVENTSPACE

+ {/*

EVENTSPACE

*/}
Admin • {event.title}
+ {/* Breadcrumb Navigation */} +
+ + Dashboard + + + + Events + + + + {event.title} + + + Gift Registry +
+ {/* Gift Registry Content */}

GIFT REGISTRY MANAGEMENT

-
@@ -343,7 +405,9 @@ export default function GiftRegistryPage() { - + handleEditGift(item.id)} + > Edit @@ -387,7 +451,157 @@ export default function GiftRegistryPage() {
+ + {/* Categories Section */} +
+
+

CATEGORIES

+ +
+ +
+ {categories && categories.length > 0 ? ( + categories.map((category) => { + const categoryItems = + items?.filter((item) => item.category_id === category.id) || + []; + + return ( + + +
+ + {category.name} + + + + + + + handleEditCategory(category.id)} + > + Edit + + + Delete + + + +
+
+ +
+ {categoryItems.length} item + {categoryItems.length !== 1 && "s"} +
+ +
+
+ ); + }) + ) : ( + + +

+ No categories found. Create your first category! +

+ +
+
+ )} +
+
+ + {/* Summary Section */} +
+

SUMMARY

+
    +
  • + • {summaryStats.totalItems} total gift item + {summaryStats.totalItems !== 1 ? "s" : ""} across{" "} + {summaryStats.totalCategories} categor + {summaryStats.totalCategories !== 1 ? "ies" : "y"} +
  • +
  • + • {summaryStats.availableItems} gift + {summaryStats.availableItems !== 1 ? "s are" : " is"} available ( + {summaryStats.availableQuantity} item + {summaryStats.availableQuantity !== 1 ? "s" : ""}) +
  • +
  • + • {summaryStats.reservedItems} gift + {summaryStats.reservedItems !== 1 ? "s are" : " is"} reserved ( + {summaryStats.reservedQuantity} item + {summaryStats.reservedQuantity !== 1 ? "s" : ""}) +
  • +
  • + • {summaryStats.purchasedItems} gift + {summaryStats.purchasedItems !== 1 ? "s have" : " has"} been + purchased ({summaryStats.purchasedQuantity} item + {summaryStats.purchasedQuantity !== 1 ? "s" : ""}) +
  • +
  • + • {summaryStats.receivedItems} gift + {summaryStats.receivedItems !== 1 ? "s have" : " has"} been fully + received ({summaryStats.receivedQuantity} item + {summaryStats.receivedQuantity !== 1 ? "s" : ""}) +
  • +
+
+ + {/* Category Modals */} + {isAddCategoryModalOpen && ( + setIsAddCategoryModalOpen(false)} + eventId={event.id} + /> + )} + + {isEditCategoryModalOpen && selectedCategoryId && ( + { + setIsEditCategoryModalOpen(false); + setSelectedCategoryId(null); + }} + categoryId={selectedCategoryId} + eventId={event.id} + /> + )} + + {/* Modal placeholders - will be implemented later */} + {/* These would render the appropriate modals when isAddGiftModalOpen, etc. are true */} ); } diff --git a/frontend/src/components/gifts/category-modal.tsx b/frontend/src/components/gifts/category-modal.tsx new file mode 100644 index 0000000..ef70d43 --- /dev/null +++ b/frontend/src/components/gifts/category-modal.tsx @@ -0,0 +1,205 @@ +// components/gifts/category-modal.tsx + +import React, { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { useGifts } from "@/context/gift-context"; +import { + GiftCategory, + GiftCategoryCreate, + GiftCategoryUpdate, +} from "@/client/types.gen"; +import { Loader2 } from "lucide-react"; +import { useAuth } from "@/context/auth-context"; + +interface CategoryModalProps { + isOpen: boolean; + onClose: () => void; + categoryId?: string; + eventId: string; +} + +export function CategoryModal({ + isOpen, + onClose, + categoryId, + eventId, +}: CategoryModalProps) { + const { + category, + createCategory, + updateCategory, + fetchCategoryById, + isLoadingCategory, + } = useGifts(); + + const { user } = useAuth(); + const isEditMode = Boolean(categoryId); + + const { + register, + handleSubmit, + reset, + formState: { errors, isSubmitting }, + } = useForm(); + + // Load category data when editing + useEffect(() => { + if (isEditMode && categoryId) { + fetchCategoryById(categoryId, eventId); + } + }, [isEditMode, categoryId, eventId, fetchCategoryById]); + + // Reset form when modal opens or category changes + useEffect(() => { + if (isOpen) { + if (isEditMode && category) { + reset({ + name: category.name, + description: category.description, + icon: category.icon, + color: category.color, + }); + } else { + reset({ + name: "", + description: "", + icon: "", + color: "#4f46e5", // Default color + }); + } + } + }, [isOpen, category, isEditMode, reset]); + + const onSubmit = async (data: GiftCategoryCreate | GiftCategoryUpdate) => { + try { + if (isEditMode && categoryId) { + await updateCategory(categoryId, data as GiftCategoryUpdate, eventId); + } else { + const createData = { + ...data, + created_by: user?.id, // This should be replaced with the actual user ID + } as GiftCategoryCreate; + + await createCategory(createData); + } + onClose(); + } catch (error) { + console.error("Error saving category:", error); + } + }; + + return ( + + + + + {isEditMode ? "Edit Category" : "Add Category"} + + + + {isLoadingCategory && isEditMode ? ( +
+ +
+ ) : ( +
+
+
+ +
+ + {errors.name && ( +

+ {errors.name.message} +

+ )} +
+
+ +
+ +
+