"use client";
import { useState, useEffect, useRef } from "react";
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 { 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" }),
});
if (!res.ok) {
toast.error(t("reviewPhotoFailed"));
return;
}
const { url, key } = (await res.json()) as { url: string; key: string };
await fetch(url, { method: "PUT", body: file, headers: { "Content-Type": file.type } });
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 (
{!loadingReviews && reviews.length > 0 && (
{t("reviewsTitle", { count: reviews.length })}
{reviews.map((r) => (
{r.user.name.slice(0, 2).toUpperCase()}
{r.user.name}
{[1, 2, 3, 4, 5].map((i) => (
))}
{new Date(r.createdAt).toLocaleDateString(locale)}
{r.reviewText &&
{r.reviewText}
}
{r.photoKey && (
})
)}
))}
)}
);
}