"use client"; import * as React from "react"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { useRouter } from "next/navigation"; import { Loader2, X } from "lucide-react"; 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 SLUG_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/; function makeSlug(name: string): string { return ( name .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 60) || "" ); } export function CreateWorkspaceDialog({ open, onOpenChange, }: { open: boolean; onOpenChange: (next: boolean) => void; }) { const router = useRouter(); const utils = api.useUtils(); const setWorkspace = useWorkspaceStore((s) => s.setWorkspace); const [name, setName] = React.useState(""); const [slug, setSlug] = React.useState(""); const [slugTouched, setSlugTouched] = React.useState(false); const [error, setError] = React.useState(null); const createMut = api.workspaces.create.useMutation({ onSuccess: async (ws) => { await utils.workspaces.listForUser.invalidate(); setWorkspace({ id: ws.id, slug: ws.slug, name: ws.name }); onOpenChange(false); router.push(`/${ws.slug}`); }, onError: (e) => setError(e.message ?? "Failed to create workspace"), }); React.useEffect(() => { if (open) { setName(""); setSlug(""); setSlugTouched(false); setError(null); } }, [open]); const previewSlug = slugTouched ? slug : makeSlug(name); const slugInvalid = slugTouched && slug.length > 0 && !SLUG_RE.test(slug); const submit = (e: React.FormEvent) => { e.preventDefault(); if (createMut.isPending) return; setError(null); const trimmed = name.trim(); if (!trimmed) { setError("Workspace name is required"); return; } if (slugTouched && slug && !SLUG_RE.test(slug)) { setError("Slug must be lowercase letters, digits, or hyphens"); return; } createMut.mutate({ name: trimmed, ...(slugTouched && slug ? { slug } : {}), }); }; return (
Create workspace Workspaces isolate projects, tasks, and team members. You can rename or change the slug later.
setName(e.target.value)} autoFocus />
/ { setSlug(e.target.value); setSlugTouched(true); }} onFocus={() => { if (!slugTouched) { setSlug(previewSlug); setSlugTouched(true); } }} />

{slugInvalid ? "Use lowercase letters, digits, or hyphens (no leading/trailing dash)." : "Used in URLs (e.g. /acme/projects). Auto-generated from name."}

{error ? (

{error}

) : null}
); }