Some checks failed
CI / Lint, Typecheck & Test (push) Failing after 2m11s
- Allow require() in next.config.js (eslint-disable) for both apps - Replace all catch (e: any) with catch (e: unknown) and proper error handling - Remove no-explicit-any: add types (PreviewProduct, ProductImage, Id<addresses>, ProductDetailReview, error payloads) across admin and storefront - Admin: use next/image in ImageUploadSection and ProductImageCarousel; remove unused layout fonts and sidebar imports; fix products page useMemo deps - Storefront: use Link for /sign-in in header actions; fix useAddressMutations and product detail types; remove unused imports/vars and fix useMemo deps Made-with: Cursor
99 lines
2.9 KiB
TypeScript
99 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { useConvexAuth } from "convex/react";
|
|
import { useState, useCallback } from "react";
|
|
import { toast } from "@heroui/react";
|
|
import { useWishlist } from "@/lib/wishlist/useWishlist";
|
|
import { useWishlistMutations } from "@/lib/wishlist/useWishlistMutations";
|
|
import type { WishlistItem } from "@/lib/wishlist/types";
|
|
import { WishlistItemGrid } from "./WishlistItemGrid";
|
|
import { RemoveFromWishlistDialog } from "./RemoveFromWishlistDialog";
|
|
import { WishlistSkeleton } from "./state/WishlistSkeleton";
|
|
import { WishlistEmptyState } from "./state/WishlistEmptyState";
|
|
import { WishlistSignInPrompt } from "./state/WishlistSignInPrompt";
|
|
|
|
export function WishlistPageView() {
|
|
const { isAuthenticated, isLoading: authLoading } = useConvexAuth();
|
|
const { items, isLoading, isEmpty } = useWishlist();
|
|
const { removeItem, isRemoving, addToCart } = useWishlistMutations();
|
|
|
|
const [removeTarget, setRemoveTarget] = useState<WishlistItem | null>(null);
|
|
const [removingId, setRemovingId] = useState<string | null>(null);
|
|
const [addingToCartId, setAddingToCartId] = useState<string | null>(null);
|
|
|
|
const handleRemove = useCallback(
|
|
(id: string) => {
|
|
const target = items.find((i) => i._id === id);
|
|
setRemoveTarget(target ?? null);
|
|
},
|
|
[items],
|
|
);
|
|
|
|
const handleConfirmRemove = useCallback(async () => {
|
|
if (!removeTarget) return;
|
|
const name = removeTarget.product?.name ?? "Item";
|
|
setRemovingId(removeTarget._id);
|
|
try {
|
|
await removeItem(removeTarget._id);
|
|
toast.success(`${name} removed from wishlist`);
|
|
} catch {
|
|
toast.danger("Failed to remove item");
|
|
} finally {
|
|
setRemovingId(null);
|
|
setRemoveTarget(null);
|
|
}
|
|
}, [removeTarget, removeItem]);
|
|
|
|
const handleAddToCart = useCallback(
|
|
async (variantId: string) => {
|
|
const item = items.find(
|
|
(i) =>
|
|
i.variant?._id === variantId ||
|
|
i.product?.variants[0]?._id === variantId,
|
|
);
|
|
setAddingToCartId(item?._id ?? null);
|
|
try {
|
|
await addToCart(variantId, 1);
|
|
toast.success("Added to cart");
|
|
} catch {
|
|
toast.danger("This item is currently out of stock");
|
|
} finally {
|
|
setAddingToCartId(null);
|
|
}
|
|
},
|
|
[items, addToCart],
|
|
);
|
|
|
|
if (!authLoading && !isAuthenticated) {
|
|
return <WishlistSignInPrompt />;
|
|
}
|
|
|
|
if (authLoading || isLoading) {
|
|
return <WishlistSkeleton />;
|
|
}
|
|
|
|
if (isEmpty) {
|
|
return <WishlistEmptyState />;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<WishlistItemGrid
|
|
items={items}
|
|
onRemove={handleRemove}
|
|
onAddToCart={handleAddToCart}
|
|
removingId={removingId}
|
|
addingToCartId={addingToCartId}
|
|
/>
|
|
|
|
<RemoveFromWishlistDialog
|
|
isOpen={!!removeTarget}
|
|
onClose={() => setRemoveTarget(null)}
|
|
onConfirm={handleConfirmRemove}
|
|
isRemoving={isRemoving}
|
|
productName={removeTarget?.product?.name ?? "this item"}
|
|
/>
|
|
</>
|
|
);
|
|
}
|