"use client"; import { useEffect, useRef, useState } from "react"; import { usePathname, useRouter } from "next/navigation"; import { useGroupsContext } from "@/hooks/groups-context"; import { useNotificationsContext } from "@/hooks/notifications-context"; type GroupDropdownProps = { onInviteCode: (code: string) => void; }; export default function GroupDropdown({ onInviteCode }: GroupDropdownProps) { const { groups, activeGroupId, loading, error, createGroup, joinGroup, setActiveGroup } = useGroupsContext(); const { notify } = useNotificationsContext(); const router = useRouter(); const pathname = usePathname(); const [open, setOpen] = useState(false); const [manageOpen, setManageOpen] = useState(false); const [activeTab, setActiveTab] = useState<"create" | "join">("create"); const [name, setName] = useState(""); const [inviteCode, setInviteCode] = useState(""); const [localError, setLocalError] = useState(""); const dropdownRef = useRef(null); const activeGroup = groups.find(group => group.id === activeGroupId) || null; const groupLabel = loading ? "Loading" : activeGroup ? activeGroup.name : "Select group"; async function handleCreate() { if (!name.trim()) { setLocalError("Group name is required"); return; } setLocalError(""); const group = await createGroup({ name }); if (group?.inviteCode) onInviteCode(group.inviteCode); if (group) { setName(""); notify({ title: "Group created", message: group.name, tone: "success" }); setManageOpen(false); setOpen(false); router.push("/groups/settings"); } } async function handleJoin() { if (!inviteCode.trim()) { setLocalError("Invite code is required"); return; } setLocalError(""); const raw = inviteCode.trim(); const inviteTokenMatch = raw.match(/\/invite\/([a-zA-Z0-9]+)/); if (inviteTokenMatch?.[1]) { setInviteCode(""); setManageOpen(false); setOpen(false); router.push(`/invite/${inviteTokenMatch[1]}`); return; } const group = await joinGroup({ inviteCode: raw }); if (group) { setInviteCode(""); notify({ title: "Joined group", message: group.name, tone: "success" }); setManageOpen(false); setOpen(false); } } async function handleSelect(groupId: number) { const ok = await setActiveGroup(groupId); if (ok) { setOpen(false); const group = groups.find(item => item.id === groupId); if (group) notify({ title: "Active group", message: group.name }); if (pathname.startsWith("/groups/")) router.push("/groups/settings"); } } function handleQuickEntries() { setOpen(false); router.push("/"); } function roleIcon(role: string) { if (role === "GROUP_OWNER") return "👑"; if (role === "GROUP_ADMIN") return "🛡️"; return "👤"; } useEffect(() => { function handleClickOutside(event: MouseEvent) { if (!open || !dropdownRef.current) return; if (!dropdownRef.current.contains(event.target as Node)) setOpen(false); } document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [open]); useEffect(() => { if (!manageOpen) return; function handleKeyDown(event: KeyboardEvent) { if (event.key === "Escape") setManageOpen(false); if (event.key === "Enter" && !event.shiftKey) { if (activeTab === "create") handleCreate(); else handleJoin(); } } window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [manageOpen, activeTab, handleCreate, handleJoin]); return (
{open ? (
{loading ? (
{[0, 1, 2].map(row => (
))}
) : groups.length ? ( groups.map(group => (
handleSelect(group.id)} onKeyDown={event => { if (event.key === "Enter" || event.key === " ") handleSelect(group.id); }} > {group.name} {roleIcon(group.role)}
)) ) : (
No groups yet
)}
{localError || error ? (
{localError || error}
) : null}
) : null} {manageOpen ? (
setManageOpen(false)}>
event.stopPropagation()} onKeyDown={event => { if (event.key === "Escape") setManageOpen(false); if (event.key === "Enter" && !event.shiftKey) { if (activeTab === "create") handleCreate(); else handleJoin(); } }} role="dialog" tabIndex={-1} >
Manage groups
{([ { key: "create", label: "Create" }, { key: "join", label: "Join" } ] as const).map(tab => ( ))}
{activeTab === "create" ? (
setName(e.target.value)} disabled={loading} />
) : (
setInviteCode(e.target.value)} disabled={loading} />
)} {localError || error ? (
{localError || error}
) : null}
) : null}
); }