"use client"; import { useState, useCallback } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { MemberList } from "./member-list"; import { TransferOwnershipModal } from "./transfer-ownership-modal"; import { GROUP_NAME_MAX_LENGTH } from "@/lib/constants"; interface GroupData { group: { id: string; name: string; invite_code: string; created_by: string; created_at: string; }; role: "admin" | "member"; } export function SettingsPanel({ groupId }: { groupId: string }) { const queryClient = useQueryClient(); const [newName, setNewName] = useState(""); const [copied, setCopied] = useState(false); const [transferTarget, setTransferTarget] = useState<{ userId: string; displayName: string; } | null>(null); const { data, isLoading, error } = useQuery({ queryKey: ["groups", groupId], queryFn: async () => { const res = await fetch(`/api/groups/${groupId}`); if (!res.ok) throw new Error("Failed to fetch group"); return res.json(); }, staleTime: 30_000, }); const rename = useMutation({ mutationFn: async (name: string) => { const res = await fetch(`/api/groups/${groupId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); if (!res.ok) { const body = await res.json(); throw new Error(body.error || "Failed to rename"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["groups", groupId] }); setNewName(""); }, }); const regenerateCode = useMutation({ mutationFn: async () => { const res = await fetch(`/api/groups/${groupId}/invite`, { method: "POST", }); if (!res.ok) { const body = await res.json(); throw new Error(body.error || "Failed to regenerate code"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["groups", groupId] }); }, }); const deleteGroup = useMutation({ mutationFn: async () => { const res = await fetch(`/api/groups/${groupId}`, { method: "DELETE" }); if (!res.ok) { const body = await res.json(); throw new Error(body.error || "Failed to delete group"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["groups"] }); }, }); const leaveGroup = useMutation({ mutationFn: async () => { const res = await fetch(`/api/groups/${groupId}/leave`, { method: "POST", }); if (!res.ok) { const body = await res.json(); throw new Error(body.error || "Failed to leave group"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["groups"] }); }, }); const inviteCode = data?.group.invite_code; const handleCopyCode = useCallback(async () => { if (!inviteCode) return; try { await navigator.clipboard.writeText(inviteCode); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { // Fallback: select text for manual copy } }, [inviteCode]); const handleTransferRequest = useCallback((userId: string, displayName: string) => { setTransferTarget({ userId, displayName }); }, []); if (isLoading) return

Loading...

; if (error) return

Failed to load group

; if (!data) return null; const isAdmin = data.role === "admin"; return (
{/* Invite Code */}

Invite Code

{data.group.invite_code} {isAdmin && ( )}
{regenerateCode.error && (

{regenerateCode.error.message}

)}
{/* Rename (admin only) */} {isAdmin && (

Rename Group

{ e.preventDefault(); const trimmed = newName.trim(); if (trimmed) rename.mutate(trimmed); }} className="flex gap-2" > setNewName(e.target.value)} maxLength={GROUP_NAME_MAX_LENGTH} placeholder={data.group.name} className="flex-1 rounded-md border border-gray-300 bg-transparent px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700" />
{rename.error &&

{rename.error.message}

}
)} {/* Members */} {/* Leave / Delete */}
{isAdmin ? (
{deleteGroup.error && (

{deleteGroup.error.message}

)}
) : (
{leaveGroup.error &&

{leaveGroup.error.message}

}
)}
{/* Transfer modal */} {transferTarget && ( setTransferTarget(null)} /> )}
); }