"use client"; import { useState, useEffect, useRef } from "react"; import Image from "next/image"; import { useTranslations, useLocale } from "next-intl"; import { Star, Camera, X, ChefHat } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { getPublicUrl } from "@/lib/storage"; import { uploadToPresignedPost } from "@/lib/upload-client"; import { cn } from "@/lib/utils"; type Review = { id: string; score: number; reviewText: string | null; photoKey: string | null; createdAt: string; user: { id: string; name: string; username: string | null; avatarUrl: string | null }; }; function Stars({ value, hovered, onHover, onPick, }: { value: number; hovered: number; onHover: (v: number) => void; onPick: (v: number) => void; }) { const display = hovered || value; return (
{[1, 2, 3, 4, 5].map((i) => ( ))}
); } export function CookedItReview({ recipeId, initialScore = 0, initialText = "", initialPhotoKey = null, }: { recipeId: string; initialScore?: number; initialText?: string; initialPhotoKey?: string | null; }) { const t = useTranslations("social"); const locale = useLocale(); const [score, setScore] = useState(initialScore); const [hovered, setHovered] = useState(0); const [text, setText] = useState(initialText); const [photoKey, setPhotoKey] = useState(initialPhotoKey); const [preview, setPreview] = useState(null); const [uploading, setUploading] = useState(false); const [saving, setSaving] = useState(false); const [reviews, setReviews] = useState([]); const [loadingReviews, setLoadingReviews] = useState(true); const inputRef = useRef(null); useEffect(() => { fetch(`/api/v1/recipes/${recipeId}/reviews`) .then((res) => (res.ok ? res.json() : null)) .then((data: { data: Review[] } | null) => setReviews(data?.data ?? [])) .finally(() => setLoadingReviews(false)); }, [recipeId]); async function handlePhoto(file: File) { setUploading(true); try { const res = await fetch("/api/v1/upload/presign", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ recipeId, contentType: file.type, purpose: "review", fileSize: file.size }), }); if (!res.ok) { toast.error(t("reviewPhotoFailed")); return; } const { url, fields, key } = (await res.json()) as { url: string; fields: Record; key: string }; const uploaded = await uploadToPresignedPost(url, fields, file); if (!uploaded) { toast.error(t("reviewPhotoFailed")); return; } setPhotoKey(key); setPreview(URL.createObjectURL(file)); } finally { setUploading(false); } } async function submit() { if (score < 1) return; setSaving(true); try { const res = await fetch(`/api/v1/recipes/${recipeId}/rate`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ score, reviewText: text.trim() || undefined, photoKey: photoKey ?? undefined }), }); if (!res.ok) { const err = (await res.json()) as { error?: string }; toast.error(err.error ?? t("ratingFailed")); return; } toast.success(t("ratingSaved")); const listRes = await fetch(`/api/v1/recipes/${recipeId}/reviews`); if (listRes.ok) { const data = (await listRes.json()) as { data: Review[] }; setReviews(data.data); } } finally { setSaving(false); } } return (
{t("cookedItPrompt")}