"use client"; import { useState, useRef } from "react"; import { ChevronRight, Maximize2, Minimize2, X as XIcon, UserMinus } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { useCreateProject } from "@multica/core/projects/mutations"; import { PROJECT_STATUS_CONFIG, PROJECT_STATUS_ORDER, PROJECT_PRIORITY_CONFIG, PROJECT_PRIORITY_ORDER, } from "@multica/core/projects/config"; import { useWorkspaceId } from "@multica/core/hooks"; import { useCurrentWorkspace, useWorkspacePaths } from "@multica/core/paths"; import { memberListOptions, agentListOptions } from "@multica/core/workspace/queries"; import { useActorName } from "@multica/core/workspace/hooks"; import type { ProjectStatus, ProjectPriority } from "@multica/core/types"; import { cn } from "@multica/ui/lib/utils"; import { toast } from "sonner"; import { Dialog, DialogContent, DialogTitle } from "@multica/ui/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@multica/ui/components/ui/dropdown-menu"; import { Popover, PopoverTrigger, PopoverContent } from "@multica/ui/components/ui/popover"; import { Tooltip, TooltipTrigger, TooltipContent } from "@multica/ui/components/ui/tooltip"; import { Button } from "@multica/ui/components/ui/button"; import { EmojiPicker } from "@multica/ui/components/common/emoji-picker"; import { ContentEditor, type ContentEditorRef, TitleEditor } from "../editor"; import { PriorityIcon } from "../issues/components/priority-icon"; import { ActorAvatar } from "../common/actor-avatar"; import { useNavigation } from "../navigation"; function PillButton({ children, className, ...props }: React.ButtonHTMLAttributes) { return ( ); } export function CreateProjectModal({ onClose }: { onClose: () => void }) { const router = useNavigation(); const workspace = useCurrentWorkspace(); const workspaceName = workspace?.name; const wsPaths = useWorkspacePaths(); const wsId = useWorkspaceId(); const { data: members = [] } = useQuery(memberListOptions(wsId)); const { data: agents = [] } = useQuery(agentListOptions(wsId)); const { getActorName } = useActorName(); const [title, setTitle] = useState(""); const descEditorRef = useRef(null); const [status, setStatus] = useState("planned"); const [priority, setPriority] = useState("none"); const [leadType, setLeadType] = useState<"member" | "agent" | undefined>(); const [leadId, setLeadId] = useState(); const [icon, setIcon] = useState(); const [iconPickerOpen, setIconPickerOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [isExpanded, setIsExpanded] = useState(false); const [leadOpen, setLeadOpen] = useState(false); const [leadFilter, setLeadFilter] = useState(""); const leadQuery = leadFilter.toLowerCase(); const filteredMembers = members.filter((m) => m.name.toLowerCase().includes(leadQuery)); const filteredAgents = agents.filter( (a) => !a.archived_at && a.name.toLowerCase().includes(leadQuery), ); const leadLabel = leadType && leadId ? getActorName(leadType, leadId) : "Lead"; const createProject = useCreateProject(); const handleSubmit = async () => { if (!title.trim() || submitting) return; setSubmitting(true); try { const project = await createProject.mutateAsync({ title: title.trim(), description: descEditorRef.current?.getMarkdown()?.trim() || undefined, icon, status, priority, lead_type: leadType, lead_id: leadId, }); onClose(); toast.success("Project created"); router.push(wsPaths.projectDetail(project.id)); } catch { toast.error("Failed to create project"); } finally { setSubmitting(false); } }; return ( { if (!v) onClose(); }}> New Project
{workspaceName} New project
setIsExpanded(!isExpanded)} className="rounded-sm p-1.5 opacity-70 hover:opacity-100 hover:bg-accent/60 transition-all cursor-pointer" > {isExpanded ? : } } /> {isExpanded ? "Collapse" : "Expand"} } /> Close
{icon || "📁"} } /> { setIcon(emoji); setIconPickerOpen(false); }} /> setTitle(v)} onSubmit={handleSubmit} />
{PROJECT_STATUS_CONFIG[status].label} } /> {PROJECT_STATUS_ORDER.map((s) => ( setStatus(s)}> {PROJECT_STATUS_CONFIG[s].label} ))} {PROJECT_PRIORITY_CONFIG[priority].label} } /> {PROJECT_PRIORITY_ORDER.map((pr) => ( setPriority(pr)}> {PROJECT_PRIORITY_CONFIG[pr].label} ))} { setLeadOpen(v); if (!v) setLeadFilter(""); }} > {leadType && leadId ? ( <> {leadLabel} ) : ( Lead )} } />
setLeadFilter(e.target.value)} placeholder="Assign lead..." className="w-full bg-transparent text-sm placeholder:text-muted-foreground outline-none" />
{filteredMembers.length > 0 && ( <>
Members
{filteredMembers.map((m) => ( ))} )} {filteredAgents.length > 0 && ( <>
Agents
{filteredAgents.map((a) => ( ))} )} {filteredMembers.length === 0 && filteredAgents.length === 0 && leadFilter && (
No results
)}
); }