"use client"; import { useState } from "react"; import { useTranslations } from "next-intl"; import { UserPlus, X } from "lucide-react"; import { toast } from "sonner"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Badge } from "@/components/ui/badge"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; type Role = "viewer" | "editor"; interface Member { id: string; userId: string; role: Role; createdAt: string; user: { name: string; username: string | null; avatarUrl: string | null; }; } interface Props { weekStart: string; } export function ShareMealPlanButton({ weekStart }: Props) { const t = useTranslations("mealPlan"); const ts = useTranslations("shareDialog"); const tCommon = useTranslations("common"); const [open, setOpen] = useState(false); const [email, setEmail] = useState(""); const [role, setRole] = useState("viewer"); const [members, setMembers] = useState([]); const [loading, setLoading] = useState(false); const [inviting, setInviting] = useState(false); async function fetchMembers() { setLoading(true); try { const res = await fetch(`/api/v1/meal-plans/${weekStart}/members`); if (!res.ok) throw new Error("Failed to load members"); const data = await res.json() as Member[]; setMembers(data); } catch { toast.error(ts("loadMembersFailed")); } finally { setLoading(false); } } function handleOpenChange(next: boolean) { setOpen(next); if (next) { void fetchMembers(); } else { setEmail(""); setRole("viewer"); } } async function handleInvite() { if (!email.trim()) { toast.error(ts("enterEmail")); return; } setInviting(true); try { const res = await fetch(`/api/v1/meal-plans/${weekStart}/members`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: email.trim(), role }), }); if (res.status === 409) { toast.error(ts("alreadyMember")); return; } if (res.status === 404) { toast.error(ts("userNotFound")); return; } if (!res.ok) { toast.error(ts("inviteFailed")); return; } toast.success(ts("invitationSent")); setEmail(""); await fetchMembers(); } catch { toast.error(ts("inviteFailed")); } finally { setInviting(false); } } async function handleRemove(memberId: string) { try { const res = await fetch( `/api/v1/meal-plans/${weekStart}/members?memberId=${memberId}`, { method: "DELETE" }, ); if (!res.ok) { toast.error(ts("removeMemberFailed")); return; } setMembers((prev) => prev.filter((m) => m.id !== memberId)); toast.success(ts("memberRemoved")); } catch { toast.error(ts("removeMemberFailed")); } } return ( <> handleOpenChange(true)} aria-label={tCommon("share")}> } /> {tCommon("share")} {t("shareTitle")} {t("shareDescription")}
setEmail(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") void handleInvite(); }} className="flex-1" />
{loading && (

{ts("loadingMembers")}

)} {!loading && members.length === 0 && (

{ts("noMembers")}

)} {members.map((m) => (
{m.user.name} {m.user.username && ( @{m.user.username} )}
{ts(m.role)}
))}
); }