"use client"; import { useState, useEffect } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Play, Copy, Sparkles, Send, ThumbsUp, ThumbsDown } from "lucide-react"; import { RepurposeModal } from "./repurpose-modal"; import { VariationModal } from "./variation-modal"; import { ThumbsDownModal } from "./thumbs-down-modal"; interface Asset { id: string; type: string; platform?: string | null; format?: string | null; fileName: string; filePath: string; dimensions?: string | null; metadata?: string | null; status: string; createdAt: string; campaign?: { name: string }; parentAsset?: { id: string; fileName: string } | null; } interface AssetCardProps { asset: Asset; selected?: boolean; onSelect?: (id: string) => void; onPushToPostiz?: (assetIds: string[]) => void; onRefresh?: () => void; } export function AssetCard({ asset, selected, onSelect, onPushToPostiz, onRefresh, }: AssetCardProps) { const [repurposeOpen, setRepurposeOpen] = useState(false); const [variationOpen, setVariationOpen] = useState(false); const [thumbsDownOpen, setThumbsDownOpen] = useState(false); const [vote, setVote] = useState<"up" | "down" | null>(null); let metadata: Record = {}; if (asset.metadata) { try { metadata = JSON.parse(asset.metadata); } catch { // metadata may be truncated — ignore parse errors } } const isImage = asset.type === "image" || asset.format === "png" || asset.format === "jpg"; const isVideo = asset.type === "video" || asset.format === "mp4"; const fileSrc = `/api/files/${asset.filePath}`; const pathLower = asset.filePath.toLowerCase(); const source = pathLower.includes("/gemini/") ? "Gemini" : pathLower.includes("/posters/") ? "Canvas Design" : isVideo ? "Remotion" : isImage ? "Playwright" : null; const isVisual = isImage || isVideo; useEffect(() => { if (!isVisual) return; fetch(`/api/assets/${asset.id}/preference`) .then((r) => r.json()) .then((data) => setVote(data.vote ?? null)) .catch(() => {}); }, [asset.id, isVisual]); async function handleThumbsUp() { const newVote = vote === "up" ? null : "up"; setVote(newVote); // Optimistic try { const res = await fetch(`/api/assets/${asset.id}/preference`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ vote: "up" }), }); if (res.ok) { const data = await res.json(); setVote(data.vote); } } catch (err) { console.error("Preference API failed:", err); } } const sourceColors: Record = { Gemini: "text-purple-600 border-purple-200 bg-purple-50", "Canvas Design": "text-amber-600 border-amber-200 bg-amber-50", Remotion: "text-blue-600 border-blue-200 bg-blue-50", Playwright: "text-emerald-600 border-emerald-200 bg-emerald-50", }; return (
{/* Preview */}
onSelect?.(asset.id)} > {isImage && ( e.stopPropagation()} > {asset.fileName} )} {isVideo && ( <>
{/* Info */}
{source && ( {source} )} {asset.platform && ( {asset.platform} )} {asset.dimensions && ( {asset.dimensions} )}
{typeof metadata.caption === "string" && (

{metadata.caption}

)} {asset.parentAsset && (

Derived from: {asset.parentAsset.fileName}

)}
{asset.campaign && {asset.campaign.name}} {asset.campaign && asset.createdAt && ·} {asset.createdAt && ( {new Date(asset.createdAt).toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric", })} )}
{/* Style Preference Buttons */} {isVisual && (
)} {/* Actions */} {(isImage || isVideo) && (
{onPushToPostiz && ( )} {isImage && ( <> )}
)}
{/* Modals */} {repurposeOpen && ( { setRepurposeOpen(false); onRefresh?.(); }} /> )} {variationOpen && ( setVariationOpen(false)} /> )} {thumbsDownOpen && ( setThumbsDownOpen(false)} onSubmitted={() => setVote("down")} /> )}
); }