import { Link, useNavigate } from "@remix-run/react";
import { useEffect, useState, useRef, useMemo } from "react";
import { Skeleton } from "./ui/skeleton";
import { ProductImageCarousel } from "./product/ProductImageCarousel";
import { EmptyState, ErrorState } from "./UiProvider";
import { Brain, Eye, EyeOff, MoveRight, Pencil, X } from "lucide-react";
import { Dialog, DialogContent, DialogOverlay } from "./ui/dialog";
import { Button } from "./ui/button";
import { useDeleteInstagramPost } from "~/requestHandler/use-instagram-hooks";
import placeholderImage from "~/assets/icons/product.png";
import placeholderImageDark from "~/assets/icons/product-dark.png";
import { useRootData } from "~/hooks/use-root-data";
import discountRed from "~/assets/icons/product/discount-red.svg";
interface MondrianList {
id?: string;
title?: string;
images?: string[];
isDraft?: boolean;
isActive?: boolean;
discountPercent?: number;
discountPrice?: number;
basePrice?: number;
}
// Utility function to detect media type
export const isVideo = (url: string): boolean => {
const videoExtensions = [".mp4", ".webm", ".ogg", ".mov"];
return videoExtensions.some((ext) => url.toLowerCase().endsWith(ext));
};
// ProductList component to display product images
const MondrianProductList = ({
products,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
isError,
refetch,
emptyState,
isCommonList = false,
isDraft = false,
storeId,
storePage,
}: {
products: MondrianList[];
fetchNextPage: () => void;
hasNextPage: boolean | undefined;
isFetchingNextPage: boolean;
isLoading: boolean;
isError: boolean;
refetch: () => void;
emptyState: {
image?: React.ReactNode;
title?: string;
description?: string;
};
isCommonList?: boolean;
isDraft?: boolean;
storeId?: string;
storePage?: boolean;
}) => {
// Handle scroll for infinite loading with throttling
useEffect(() => {
let ticking = false;
const handleScroll = () => {
if (!ticking) {
window.requestAnimationFrame(() => {
const scrollPosition = window.innerHeight + window.scrollY;
const documentHeight = document.documentElement.scrollHeight;
if (
scrollPosition >= documentHeight - 300 &&
!isFetchingNextPage &&
hasNextPage
) {
fetchNextPage();
}
ticking = false;
});
ticking = true;
}
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);
// Memoize expensive filtering operations
const { oddProducts, evenProducts } = useMemo(() => {
const odd = products.filter((_, index) => index % 2 === 0);
const even = products.filter((_, index) => index % 2 !== 0);
return { oddProducts: odd, evenProducts: even };
}, [products]);
const oddHeight = useMemo(() => [220, 260, 320, 200, 280], []);
const evenHeight = useMemo(() => [280, 200, 260, 220, 320], []);
return (
{!isCommonList ? (
<>
{isLoading ? (
) : isError ? (
) : oddProducts.length === 0 && evenProducts.length === 0 ? (
) : (
<>
{/* Column 1 (Odd products - index 0, 2, ...) */}
{oddProducts.map((product, index: number) => {
const height = oddHeight[index % 5];
return (
);
})}
{/* Column 2 (Even products - index 1, 3, ...) */}
{evenProducts.map((product, index: number) => {
const height = evenHeight[index % 5];
return (
);
})}
{hasNextPage &&
}
>
)}
>
) : (
<>
{isLoading ? (
) : isError ? (
) : oddProducts.length === 0 && evenProducts.length === 0 ? (
) : (
<>
{products.map((product, index: number) => {
return (
);
})}
{hasNextPage &&
}
>
)}
>
)}
);
};
const ProductCard = ({
product,
height,
isDraft,
storeId,
storePage,
}: {
product: MondrianList;
height: number;
isDraft?: boolean;
storeId?: string;
storePage?: boolean;
}) => {
const [isPublishModalOpen, setIsPublishModalOpen] = useState(false);
const videoRef = useRef(null);
const { theme } = useRootData();
const handleVideoClick = () => {
if (!videoRef.current) return;
if (videoRef.current.paused) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
};
return (
<>
{isDraft ? (
<>
{product.images?.[0] &&
(isVideo(product.images[0]) ? (
) : (

{
e.currentTarget.src =
theme === "dark"
? placeholderImageDark
: placeholderImage;
e.currentTarget.style.objectFit = "contain";
e.currentTarget.style.backgroundColor =
theme === "dark" ? "#121212" : "#f2f0f0";
}}
/>
))}
{/* Draft mode overlay - clickable container */}
{product.isDraft && (
{
if (e.key === "Enter") {
setIsPublishModalOpen(true);
}
}}
tabIndex={0}
onClick={() => {
setIsPublishModalOpen(true);
}}
>
{/* Top-right corner for EyeOff icon */}
{/* Semi-transparent overlay for the whole area */}
)}
{/* Discount badge */}
{product.discountPercent && product.discountPercent !== 0 && (
)}
>
) : (
{/* Discount badge */}
{product.discountPercent && product.discountPercent !== 0 && (
)}
{/* Inactive product overlay */}
{product.isActive === false && (
{/* Top-right corner for EyeOff icon */}
{/* Semi-transparent overlay for the whole area */}
)}
{/* Overlay info - Airbnb style */}
{/* Title */}
{product.title}
{/* Price */}
{product.discountPrice && (
{`${product.discountPrice.toLocaleString()}`}
تومان
)}
{product.basePrice && product.discountPercent ? (
{product.basePrice.toLocaleString()}
) : null}
)}
>
);
};
const DiscountBadge = ({ discountPercent }: { discountPercent?: number }) => {
return (
{discountPercent}
);
};
const PublishModal = ({
open,
onOpenChange,
storeId,
postId,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
storeId?: string;
postId?: string;
}) => {
const navigate = useNavigate();
const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false);
const deletePostMutation = useDeleteInstagramPost();
const handleManualAddClick = (isAI: boolean) => {
// Navigate to manual add page
if (storeId && postId) {
navigate(`/store/add/manual/${storeId}?id=${postId}&ai=${isAI}`);
} else {
// Fallback to create store if no storeId available
navigate("/store/create");
}
onOpenChange(false);
};
const handleDeleteClick = () => {
onOpenChange(false); // Close the publish modal first
setIsDeleteConfirmOpen(true);
};
const handleConfirmDelete = async () => {
if (postId) {
try {
await deletePostMutation.mutateAsync(postId);
onOpenChange(false);
setIsDeleteConfirmOpen(false);
} catch (error) {
console.error("Failed to delete post:", error);
}
}
};
return (
);
};
export const CommonSkeleton = () => {
return (
{Array(9)
.fill(1)
.map((_, index: number) => {
return (
);
})}
);
};
const MondrianSkeleton = ({ isMini }: { isMini?: boolean }) => {
const oddHeight = isMini ? [200, 240] : [200, 240, 300, 180, 260];
const evenHeight = isMini ? [260, 180] : [260, 180, 240, 200, 300];
return (
{/* Column 1 (Odd products - index 0, 2, ...) */}
{oddHeight.map((height, index: number) => {
return (
);
})}
{/* Column 2 (Even products - index 1, 3, ...) */}
{evenHeight.map((height, index: number) => {
return (
);
})}
);
};
export default MondrianProductList;