"use client"; import * as React from "react"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { useRouter } from "next/navigation"; import { CheckSquare, ClipboardList, FileStack, FileText, LayoutGrid, Presentation, X, } from "lucide-react"; import { TemplatePicker, type PickerTemplate } from "@/components/templates"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { api } from "@/lib/trpc"; import { useWorkspaceStore } from "@/lib/stores/workspace-store"; import { cn } from "@/lib/utils"; const CREATE_TYPES = ["task", "document", "space", "whiteboard", "form"] as const; type CreateObjectType = (typeof CREATE_TYPES)[number]; const TYPE_OPTIONS: { value: CreateObjectType; label: string; icon: React.ComponentType<{ className?: string }>; }[] = [ { value: "task", label: "Task", icon: CheckSquare }, { value: "document", label: "Document", icon: FileText }, { value: "space", label: "Space", icon: LayoutGrid }, { value: "whiteboard", label: "Whiteboard", icon: Presentation }, { value: "form", label: "Form", icon: ClipboardList }, ]; const TASK_STATUSES = [ { value: "open", label: "Open" }, { value: "in_progress", label: "In Progress" }, { value: "done", label: "Done" }, ] as const; const selectFieldClass = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"; function normalizeDefaultType(raw?: string): CreateObjectType { if (!raw) return "task"; const v = raw.toLowerCase().trim().replace(/\s+/g, "_"); if (v === "doc") return "document"; if (v === "white_board") return "whiteboard"; if (v === "list") return "task"; if (v === "form") return "form"; if ((CREATE_TYPES as readonly string[]).includes(v)) { return v as CreateObjectType; } return "task"; } export interface CreateObjectDialogProps { open: boolean; onOpenChange: (open: boolean) => void; defaultType?: string; defaultParentId?: string; /** Workspace UUID or slug. Falls back to current workspace from the store. */ workspaceHandle?: string; } export function CreateObjectDialog({ open, onOpenChange, defaultType, defaultParentId, workspaceHandle: workspaceHandleProp, }: CreateObjectDialogProps) { const router = useRouter(); const storeWorkspace = useWorkspaceStore((s) => s.currentWorkspace); const storeHandle = storeWorkspace?.slug ?? storeWorkspace?.id; const resolvedWorkspace = workspaceHandleProp ?? storeHandle ?? undefined; const utils = api.useUtils(); const titleInputRef = React.useRef(null); const selectedTemplateRef = React.useRef(null); const [objectType, setObjectType] = React.useState(() => normalizeDefaultType(defaultType), ); const [title, setTitle] = React.useState(""); const [titleError, setTitleError] = React.useState(false); const [parentId, setParentId] = React.useState(""); const [taskStatus, setTaskStatus] = React.useState< (typeof TASK_STATUSES)[number]["value"] >("open"); const [selectedTemplate, setSelectedTemplate] = React.useState( null, ); const [showTemplatePicker, setShowTemplatePicker] = React.useState(false); React.useEffect(() => { selectedTemplateRef.current = selectedTemplate; }, [selectedTemplate]); const showParentPicker = objectType === "task" || objectType === "document"; const spacesQuery = api.objects.list.useQuery( { workspace: resolvedWorkspace!, type: "space", limit: 500, }, { enabled: Boolean(open && resolvedWorkspace && showParentPicker) }, ); const spaces = spacesQuery.data?.objects ?? []; React.useEffect(() => { if (!open) return; setObjectType(normalizeDefaultType(defaultType)); setTitle(""); setTitleError(false); setParentId(defaultParentId ?? ""); setTaskStatus("open"); setSelectedTemplate(null); setShowTemplatePicker(false); }, [open, defaultType, defaultParentId]); React.useEffect(() => { if (!open) return; const t = window.setTimeout(() => titleInputRef.current?.focus(), 0); return () => window.clearTimeout(t); }, [open]); const applyTemplateMutation = api.templates.applyTemplate.useMutation(); const createMutation = api.objects.create.useMutation({ onSuccess: async (newObj) => { const t = selectedTemplateRef.current; if (t && newObj?.id && resolvedWorkspace) { try { await applyTemplateMutation.mutateAsync({ workspace: resolvedWorkspace, templateId: t.id, objectId: newObj.id, }); } catch { // Template application failure shouldn't block creation } } await utils.objects.list.invalidate(); await utils.objects.getTree.invalidate(); onOpenChange(false); }, }); const createFormMutation = api.forms.create.useMutation({ onSuccess: (data) => { utils.objects.getTree.invalidate(); const newId = (data as { id?: string }).id; if (newId && resolvedWorkspace) { router.push(`/${resolvedWorkspace}/forms/${newId}/edit`); } onOpenChange(false); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const trimmed = title.trim(); if (!trimmed) { setTitleError(true); return; } if (!resolvedWorkspace) { return; } setTitleError(false); if (objectType === "form") { createFormMutation.mutate({ workspace: resolvedWorkspace, title: trimmed, }); return; } const parentForCreate = objectType === "task" || objectType === "document" ? parentId || null : null; createMutation.mutate({ workspace: resolvedWorkspace, type: objectType, title: trimmed, parentId: parentForCreate, ...(objectType === "task" ? { status: taskStatus } : {}), }); }; const isSubmitting = createMutation.isPending || createFormMutation.isPending || applyTemplateMutation.isPending; return ( ev.preventDefault()} >
Create object Choose a type, enter a title, and optional parent space or task status.
{!resolvedWorkspace ? (

Select a workspace to create objects.

) : null}
Type
{TYPE_OPTIONS.map(({ value, label, icon: Icon }) => { const selected = objectType === value; return ( ); })}
{(objectType === "task" || objectType === "document") && (
Template {selectedTemplate ? ( ) : null}
{selectedTemplate ? (
{selectedTemplate.name}
) : ( )}
)}
{ setTitle(ev.target.value); if (titleError) setTitleError(false); }} placeholder="Name" autoComplete="off" aria-invalid={titleError} /> {titleError ? (

Title is required

) : null}
{showParentPicker ? (
) : null} {objectType === "task" ? (
) : null}
{showTemplatePicker && resolvedWorkspace && ( { setSelectedTemplate(template); setShowTemplatePicker(false); }} /> )}
); }