"use client"; /** * InviteDialog — compact members + invite dialog (open from page-level * surfaces, e.g. a page's share popover). * * Uses hooks/use-members.ts for the invite call (the real route * POST /workspaces/:id/members/invites with UPPERCASE wire roles — the core * `workspaceApi.invite` path `/workspaces/:id/invites` 404s, flagged to the * core agent) and `useWorkspaceMembers` for the member list. There is no * endpoint to list pending invitations, so the dialog surfaces the members * list only. */ import { useState } from "react"; import { Loader2, Mail, UserPlus } from "lucide-react"; import { toast } from "@/stores/ui-store"; import { useWorkspaceMembers } from "@/hooks/use-workspace"; import { useInviteMember, ASSIGNABLE_ROLES, roleLabel } from "@/hooks/use-members"; import { UserAvatar } from "@/components/database/cell-renderer"; import { Dialog } from "@/components/dialogs/dialog"; export interface InviteDialogProps { open: boolean; workspaceId: string; workspaceName?: string; onClose: () => void; } export function InviteDialog({ open, workspaceId, workspaceName, onClose }: InviteDialogProps) { const members = useWorkspaceMembers(workspaceId); const invite = useInviteMember({ workspaceId }); const [email, setEmail] = useState(""); const [role, setRole] = useState("MEMBER"); const submit = () => { if (!email.trim()) return; invite.mutate( { email: email.trim(), role: role as (typeof ASSIGNABLE_ROLES)[number] }, { onSuccess: () => { toast("success", "Invitation sent", email.trim()); setEmail(""); }, onError: (e) => toast("error", "Invite failed", (e as Error).message) } ); }; return (
{/* invite form */}
setEmail(e.target.value)} onKeyDown={(e) => e.key === "Enter" && submit()} />
{/* members */}

Members ({members.data?.length ?? 0})

{members.isLoading &&

Loading…

}
    {(members.data ?? []).map((m) => (
  • {m.user.name}

    {m.user.email}

    {roleLabel(m.role)}
  • ))}
); }