Add functionality to display gift reservations per guest
Implemented fetching and grouping of guests' gift reservations for events. Added a popover UI to display reservation details, including guest names and quantities, with a loading state for pending data. This enhances the gift dashboard's interactivity and usability.
This commit is contained in:
@@ -44,7 +44,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { GiftPriority, GiftStatus } from "@/client/types.gen";
|
import { GiftPriority, GiftPurchase, GiftStatus } from "@/client/types.gen";
|
||||||
import { CategoryModal } from "@/components/gifts/category-modal";
|
import { CategoryModal } from "@/components/gifts/category-modal";
|
||||||
import { GiftModal } from "@/components/gifts/gift-modal";
|
import { GiftModal } from "@/components/gifts/gift-modal";
|
||||||
import {
|
import {
|
||||||
@@ -52,6 +52,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import { useGuests } from "@/context/guest-context";
|
||||||
|
|
||||||
export default function GiftRegistryPage() {
|
export default function GiftRegistryPage() {
|
||||||
const { slug } = useParams<{ slug: string }>();
|
const { slug } = useParams<{ slug: string }>();
|
||||||
@@ -68,8 +69,10 @@ export default function GiftRegistryPage() {
|
|||||||
currentEventId,
|
currentEventId,
|
||||||
setCurrentEventId,
|
setCurrentEventId,
|
||||||
deleteItem,
|
deleteItem,
|
||||||
|
fetchGuestsGiftPurchases,
|
||||||
} = useGifts();
|
} = useGifts();
|
||||||
|
|
||||||
|
const { guests } = useGuests();
|
||||||
// State for modals
|
// State for modals
|
||||||
const [isAddGiftModalOpen, setIsAddGiftModalOpen] = useState(false);
|
const [isAddGiftModalOpen, setIsAddGiftModalOpen] = useState(false);
|
||||||
const [isEditGiftModalOpen, setIsEditGiftModalOpen] = useState(false);
|
const [isEditGiftModalOpen, setIsEditGiftModalOpen] = useState(false);
|
||||||
@@ -84,6 +87,39 @@ export default function GiftRegistryPage() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [categoryFilter, setCategoryFilter] = useState<string>("all");
|
const [categoryFilter, setCategoryFilter] = useState<string>("all");
|
||||||
const [statusFilter, setStatusFilter] = useState<string>("all");
|
const [statusFilter, setStatusFilter] = useState<string>("all");
|
||||||
|
const [reservations, setReservations] = useState<
|
||||||
|
Record<string, GiftPurchase[]>
|
||||||
|
>({});
|
||||||
|
const [loadingReservations, setLoadingReservations] = useState<boolean>(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadReservations = async () => {
|
||||||
|
if (currentEventId) {
|
||||||
|
setLoadingReservations(true);
|
||||||
|
try {
|
||||||
|
const data = await fetchGuestsGiftPurchases(currentEventId);
|
||||||
|
if (data) {
|
||||||
|
const groupedReservations = data.reduce(
|
||||||
|
(acc, purchase) => {
|
||||||
|
const giftId = purchase.gift_id;
|
||||||
|
if (!acc[giftId]) acc[giftId] = [];
|
||||||
|
acc[giftId].push(purchase);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, GiftPurchase[]>,
|
||||||
|
);
|
||||||
|
setReservations(groupedReservations);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Unable to fetch reservations:", err);
|
||||||
|
} finally {
|
||||||
|
setLoadingReservations(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadReservations();
|
||||||
|
}, [currentEventId, fetchGuestsGiftPurchases]);
|
||||||
|
|
||||||
// Filter items based on search query and filters
|
// Filter items based on search query and filters
|
||||||
const filteredItems = items
|
const filteredItems = items
|
||||||
@@ -436,20 +472,44 @@ export default function GiftRegistryPage() {
|
|||||||
{getStatusBadge(item.status)}
|
{getStatusBadge(item.status)}
|
||||||
</div>
|
</div>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
{/*<PopoverContent className="text-sm">*/}
|
<PopoverContent className="text-sm w-[220px]">
|
||||||
{/* {item.reservations &&*/}
|
{loadingReservations ? (
|
||||||
{/* item.reservations.length > 0 ? (*/}
|
<div className="flex items-center justify-center p-2">
|
||||||
{/* <ul className="list-disc">*/}
|
<Loader2
|
||||||
{/* {item.reservations.map((res) => (*/}
|
className="animate-spin"
|
||||||
{/* <li key={res.guest_id}>*/}
|
size={16}
|
||||||
{/* {res.guest_name}: {res.quantity}*/}
|
/>
|
||||||
{/* </li>*/}
|
<span className="ml-2">
|
||||||
{/* ))}*/}
|
Loading reservations...
|
||||||
{/* </ul>*/}
|
</span>
|
||||||
{/* ) : (*/}
|
</div>
|
||||||
{/* <p>No reservations available.</p>*/}
|
) : reservations[item.id] &&
|
||||||
{/* )}*/}
|
reservations[item.id].length > 0 ? (
|
||||||
{/*</PopoverContent>*/}
|
<ul className="list-disc pl-4 py-2">
|
||||||
|
{reservations[item.id].map(
|
||||||
|
(purchase, index) => {
|
||||||
|
const guest = guests?.find(
|
||||||
|
(g) => g.id === purchase.guest_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={`${purchase.id}_${index}`}>
|
||||||
|
<strong>
|
||||||
|
{guest?.full_name ||
|
||||||
|
purchase.guest_id}
|
||||||
|
</strong>
|
||||||
|
: {purchase.quantity}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p className="p-2 text-gray-500">
|
||||||
|
No reservations available.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
) : (
|
) : (
|
||||||
getStatusBadge(item.status)
|
getStatusBadge(item.status)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
readGiftPurchase,
|
readGiftPurchase,
|
||||||
readGiftPurchasesByGift,
|
readGiftPurchasesByGift,
|
||||||
readGiftPurchasesByGuest,
|
readGiftPurchasesByGuest,
|
||||||
|
readGuestsGiftReservations,
|
||||||
} from "@/client/sdk.gen";
|
} from "@/client/sdk.gen";
|
||||||
import {
|
import {
|
||||||
GiftCategory,
|
GiftCategory,
|
||||||
@@ -48,6 +49,10 @@ interface GiftContextState {
|
|||||||
refetchCategories: (eventId: string) => Promise<any>;
|
refetchCategories: (eventId: string) => Promise<any>;
|
||||||
|
|
||||||
fetchCategoryById: (id: string, eventId?: string) => void;
|
fetchCategoryById: (id: string, eventId?: string) => void;
|
||||||
|
fetchGuestsGiftPurchases: (
|
||||||
|
eventId: string,
|
||||||
|
) => Promise<GiftPurchase[] | undefined>;
|
||||||
|
|
||||||
createCategory: (
|
createCategory: (
|
||||||
data: GiftCategoryCreate,
|
data: GiftCategoryCreate,
|
||||||
) => Promise<GiftCategory | undefined>;
|
) => Promise<GiftCategory | undefined>;
|
||||||
@@ -147,6 +152,8 @@ const defaultGiftContextState: GiftContextState = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchCategoryById: () => {},
|
fetchCategoryById: () => {},
|
||||||
|
fetchGuestsGiftPurchases: async () => undefined,
|
||||||
|
|
||||||
createCategory: async () => {
|
createCategory: async () => {
|
||||||
throw new Error("GiftContext not initialized");
|
throw new Error("GiftContext not initialized");
|
||||||
},
|
},
|
||||||
@@ -310,6 +317,20 @@ export const GiftProvider: React.FC<GiftProviderProps> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchGuestsGiftPurchases = async (
|
||||||
|
eventId: string,
|
||||||
|
): Promise<GiftPurchase[] | undefined> => {
|
||||||
|
try {
|
||||||
|
const result = await readGuestsGiftReservations({
|
||||||
|
query: { event_id: eventId },
|
||||||
|
});
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching guests' gift purchases:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Create Category Mutation
|
// Create Category Mutation
|
||||||
const createCategoryMutation = useMutation({
|
const createCategoryMutation = useMutation({
|
||||||
mutationFn: (data: GiftCategoryCreate) =>
|
mutationFn: (data: GiftCategoryCreate) =>
|
||||||
@@ -771,7 +792,7 @@ export const GiftProvider: React.FC<GiftProviderProps> = ({ children }) => {
|
|||||||
isLoadingCategories,
|
isLoadingCategories,
|
||||||
isLoadingCategory,
|
isLoadingCategory,
|
||||||
refetchCategories,
|
refetchCategories,
|
||||||
|
fetchGuestsGiftPurchases,
|
||||||
fetchCategoryById,
|
fetchCategoryById,
|
||||||
createCategory: createCategoryMutation.mutateAsync,
|
createCategory: createCategoryMutation.mutateAsync,
|
||||||
updateCategory: (id, data, eventId) =>
|
updateCategory: (id, data, eventId) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user