diff --git a/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx b/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx new file mode 100644 index 0000000..4021325 --- /dev/null +++ b/frontend/src/app/(main)/dashboard/events/[slug]/gifts/page.tsx @@ -0,0 +1,421 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { useParams } from "next/navigation"; +import Link from "next/link"; +import { + ChevronRight, + Loader2, + AlertTriangle, + Plus, + Search, + Filter, + Edit, + Trash, + MoreHorizontal, +} from "lucide-react"; +import { useEvents } from "@/context/event-context"; +import { useGifts } from "@/context/gift-context"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { GiftStatus, GiftPriority } from "@/client/types.gen"; + +export default function GiftRegistryPage() { + const { slug } = useParams<{ slug: string }>(); + const { event, fetchEventBySlug, isLoadingEvent, eventError } = useEvents(); + const { + categories, + items, + isLoadingCategories, + isLoadingItems, + error, + refetchCategories, + refetchItems, + currentEventId, + setCurrentEventId, + } = useGifts(); + + // State for filtering and searching + const [searchQuery, setSearchQuery] = useState(""); + const [categoryFilter, setCategoryFilter] = useState("all"); + const [statusFilter, setStatusFilter] = useState("all"); + + // Filter items based on search query and filters + const filteredItems = items + ? items.filter((item) => { + // Filter by search query + const matchesSearch = + searchQuery === "" || + item.name.toLowerCase().includes(searchQuery.toLowerCase()) || + item.description?.toLowerCase().includes(searchQuery.toLowerCase()) || + item.store_name?.toLowerCase().includes(searchQuery.toLowerCase()) || + item.brand?.toLowerCase().includes(searchQuery.toLowerCase()); + + // Filter by category + const matchesCategory = + categoryFilter === "all" || item.category_id === categoryFilter; + + // Filter by status + const matchesStatus = + statusFilter === "all" || item.status === statusFilter; + + return matchesSearch && matchesCategory && matchesStatus; + }) + : []; + + // Helper to get status badge with color + const getStatusBadge = (status: GiftStatus | null | undefined) => { + if (!status) return null; + + const statusStyles: Record = { + available: + "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300", + reserved: + "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300", + purchased: + "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300", + received: + "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300", + removed: "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300", + }; + + return ( +
+
+ + {status.toUpperCase()} + +
+ ); + }; + + // Helper to get priority badge with color + const getPriorityBadge = (priority: GiftPriority | null | undefined) => { + if (!priority) return null; + + const priorityStyles: Record = { + low: "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300", + medium: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300", + high: "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300", + must_have: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300", + }; + + const priorityLabels: Record = { + low: "LOW", + medium: "MEDIUM", + high: "HIGH", + must_have: "MUST-HAVE", + }; + + return ( + + {priorityLabels[priority]} + + ); + }; + + // Calculate summary statistics + const summaryStats = { + totalItems: items?.length || 0, + totalCategories: categories?.length || 0, + availableItems: + items?.filter((item) => item.status === GiftStatus.AVAILABLE).length || 0, + availableQuantity: + items + ?.filter((item) => item.status === GiftStatus.AVAILABLE) + .reduce((acc, item) => acc + (item.quantity_requested || 1), 0) || 0, + reservedItems: + items?.filter((item) => item.status === GiftStatus.RESERVED).length || 0, + reservedQuantity: + items + ?.filter((item) => item.status === GiftStatus.RESERVED) + .reduce((acc, item) => acc + (item.quantity_requested || 1), 0) || 0, + purchasedItems: + items?.filter((item) => item.status === GiftStatus.PURCHASED).length || 0, + purchasedQuantity: + items + ?.filter((item) => item.status === GiftStatus.PURCHASED) + .reduce((acc, item) => acc + (item.quantity_requested || 1), 0) || 0, + receivedItems: + items?.filter((item) => item.status === GiftStatus.RECEIVED).length || 0, + receivedQuantity: + items + ?.filter((item) => item.status === GiftStatus.RECEIVED) + .reduce((acc, item) => acc + (item.quantity_requested || 1), 0) || 0, + }; + + // Load event and gift data + useEffect(() => { + fetchEventBySlug(slug); + }, [slug, fetchEventBySlug]); + + useEffect(() => { + if (event?.id) { + setCurrentEventId(event.id); + refetchCategories(event.id); + refetchItems(undefined, event.id); + } + }, [event, setCurrentEventId, refetchCategories, refetchItems]); + + // Loading state + if (isLoadingEvent || isLoadingCategories || isLoadingItems) { + return ( +
+ + Loading gift registry... +
+ ); + } + + // Error state + if (eventError || error) { + return ( +
+ + + Error loading gift registry: {(eventError || error)?.message} + +
+ ); + } + + // Not found state + if (!event) { + return ( + + + Event Not Found + + +

+ The event you're looking for doesn't exist or may have been removed. +

+ +
+
+ ); + } + + return ( +
+ {/* Header */} +
+
+

EVENTSPACE

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

GIFT REGISTRY MANAGEMENT

+ +
+ + {/* Filters and Search */} +
+
+
+ Filter by: + +
+
+ +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ + {/* Gift Items Table */} +
+ + + + Category + Name + Qty + Price + Priority + Status + Actions + + + + {filteredItems.length === 0 ? ( + + + No gifts found. Add your first gift! + + + ) : ( + filteredItems.map((item) => { + const category = categories?.find( + (c) => c.id === item.category_id, + ); + return ( + + + + {category?.name || "Uncategorized"} + + + {item.name} + + {item.quantity_requested || 1} + {item.formatted_price || "-"} + {getPriorityBadge(item.priority)} + {getStatusBadge(item.status)} + + + + + + + + Edit + + + + Delete + + + + + + + + {item.status === GiftStatus.AVAILABLE && ( + <> + Store: {item.store_name || "N/A"} •{" "} + {item.purchase_url + ? `URL: ${item.purchase_url}` + : `Brand: ${item.brand || "N/A"}`} + + )} + {item.status === GiftStatus.RESERVED && ( + <>Reserved by: John Smith on April 12, 2025 + )} + {item.status === GiftStatus.PURCHASED && ( + <>Purchased by: Maria Lopez on April 10, 2025 + )} + {item.status === GiftStatus.RECEIVED && ( + <> + Received: Complete! • Brand: {item.brand || "N/A"} + + )} + + + + ); + }) + )} + +
+
+
+
+ ); +}