"use client" import { useState, useMemo } from "react" import { cn } from "@lib/utils" import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts" import { DEFAULT_PROJECT_ID } from "@repo/lib/constants" import { ChevronsLeftRight, Plus, Trash2, XIcon, Loader2, Globe, Layers, } from "lucide-react" import type { ContainerTagListType } from "@repo/lib/types" import { AddSpaceModal } from "./add-space-modal" import { SelectSpacesModal } from "./select-spaces-modal" import { useProjectMutations } from "@/hooks/use-project-mutations" import { useContainerTags } from "@/hooks/use-container-tags" import { motion } from "motion/react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@ui/components/dropdown-menu" import { Dialog, DialogContent, DialogTitle, DialogDescription, } from "@repo/ui/components/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@repo/ui/components/select" import { Button } from "@repo/ui/components/button" import { analytics } from "@/lib/analytics" export interface SpaceSelectorProps { selectedProjects: string[] onValueChange: (containerTags: string[]) => void variant?: "default" | "insideOut" showChevron?: boolean triggerClassName?: string contentClassName?: string showNewSpace?: boolean enableDelete?: boolean compact?: boolean } const triggerVariants = { default: "px-3 py-2 rounded-md hover:bg-white/5", insideOut: "px-3 py-2 rounded-full bg-[#0D121A] shadow-inside-out", } export function SpaceSelector({ selectedProjects, onValueChange, variant = "default", showChevron = false, triggerClassName, contentClassName, showNewSpace = true, enableDelete = false, compact = false, }: SpaceSelectorProps) { const [isOpen, setIsOpen] = useState(false) const [showCreateDialog, setShowCreateDialog] = useState(false) const [showSelectSpacesModal, setShowSelectSpacesModal] = useState(false) const [deleteDialog, setDeleteDialog] = useState<{ open: boolean project: { id: string; name: string; containerTag: string } | null action: "move" | "delete" targetProjectId: string }>({ open: false, project: null, action: "move", targetProjectId: "", }) const { deleteProjectMutation } = useProjectMutations() const { allProjects, novaProjects, isLoading } = useContainerTags() const isNovaSpaces = selectedProjects.length === 0 const displayInfo = useMemo(() => { if (isNovaSpaces) { return { name: "Nova Spaces", emoji: null, isMultiple: false } } if (selectedProjects.length === 1) { const containerTag = selectedProjects[0] if (containerTag === DEFAULT_PROJECT_ID) { return { name: "My Space", emoji: "📁", isMultiple: false } } const found = allProjects.find( (p: ContainerTagListType) => p.containerTag === containerTag, ) return { name: found?.name || containerTag, emoji: found?.emoji || "📁", isMultiple: false, } } return { name: `${selectedProjects.length} spaces`, emoji: null, isMultiple: true, } }, [allProjects, selectedProjects, isNovaSpaces]) const handleSelectNovaSpaces = () => { analytics.spaceSwitched({ space_id: "nova_spaces" }) onValueChange([]) // Empty array = "Nova Spaces" (all nova) setIsOpen(false) } const handleSelectSingleSpace = (containerTag: string) => { analytics.spaceSwitched({ space_id: containerTag }) onValueChange([containerTag]) setIsOpen(false) } const handleOpenSelectSpaces = () => { setIsOpen(false) setShowSelectSpacesModal(true) } const handleSelectSpacesApply = (selected: string[]) => { if (selected.length > 0) { analytics.spaceSwitched({ space_id: selected.length === 1 ? (selected[0] ?? "unknown") : "multiple", }) } onValueChange(selected) setShowSelectSpacesModal(false) } const handleNewSpace = () => { setIsOpen(false) setShowCreateDialog(true) } const handleDeleteClick = ( e: React.MouseEvent, project: { id: string; name: string; containerTag: string }, ) => { e.stopPropagation() e.preventDefault() setDeleteDialog({ open: true, project, action: "move", targetProjectId: "", }) } const handleDeleteConfirm = () => { if (!deleteDialog.project) return deleteProjectMutation.mutate( { projectId: deleteDialog.project.id, action: deleteDialog.action, targetProjectId: deleteDialog.action === "move" ? deleteDialog.targetProjectId : undefined, }, { onSuccess: () => { setDeleteDialog({ open: false, project: null, action: "move", targetProjectId: "", }) setIsOpen(false) }, }, ) } const handleDeleteCancel = () => { setDeleteDialog({ open: false, project: null, action: "move", targetProjectId: "", }) } const availableTargetProjects = useMemo(() => { const filtered = novaProjects.filter( (p: ContainerTagListType) => p.id !== deleteDialog.project?.id && p.containerTag !== deleteDialog.project?.containerTag, ) const defaultProject = novaProjects.find( (p: ContainerTagListType) => p.containerTag === DEFAULT_PROJECT_ID, ) const isDefaultProjectBeingDeleted = deleteDialog.project?.containerTag === DEFAULT_PROJECT_ID if (defaultProject && !isDefaultProjectBeingDeleted) { const defaultProjectIncluded = filtered.some( (p: ContainerTagListType) => p.containerTag === DEFAULT_PROJECT_ID, ) if (!defaultProjectIncluded) { return [defaultProject, ...filtered] } } return filtered }, [novaProjects, deleteDialog.project]) return ( <> {isNovaSpaces ? ( ) : displayInfo.isMultiple ? ( ) : ( {displayInfo.emoji} )} {!compact && ( {isLoading ? "..." : displayInfo.name} )} {showChevron && ( )} Nova Spaces My Spaces handleSelectSingleSpace(DEFAULT_PROJECT_ID)} className={cn( "flex items-center gap-2 px-3 py-2.5 rounded-md cursor-pointer text-white text-sm font-medium", selectedProjects.length === 1 && selectedProjects[0] === DEFAULT_PROJECT_ID ? "bg-[#293952]/40" : "opacity-60 hover:opacity-100 hover:bg-[#293952]/40", )} > 📁 My Space {novaProjects .filter( (p: ContainerTagListType) => p.containerTag !== DEFAULT_PROJECT_ID, ) .map((project: ContainerTagListType) => ( handleSelectSingleSpace(project.containerTag) } className={cn( "flex items-center gap-2 px-3 py-2.5 rounded-md cursor-pointer text-white text-sm font-medium group", selectedProjects.length === 1 && selectedProjects[0] === project.containerTag ? "bg-[#293952]/40" : "opacity-60 hover:opacity-100 hover:bg-[#293952]/40", )} > {project.emoji || "📁"} {project.name ?? project.containerTag} {enableDelete && ( handleDeleteClick(e, { id: project.id, name: project.name, containerTag: project.containerTag, }) } className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded-full hover:bg-red-500/20" > )} ))} Select Spaces {showNewSpace && ( New Space )} setShowCreateDialog(false)} /> setShowSelectSpacesModal(false)} selectedProjects={selectedProjects} onApply={handleSelectSpacesApply} projects={allProjects} /> { if (!open) { setDeleteDialog({ open: false, project: null, action: "move", targetProjectId: "", }) } }} > Delete space What would you like to do with the documents and memories in{" "} "{deleteDialog.project?.name}" ? Close setDeleteDialog((prev) => ({ ...prev, action: "move" })) } className={cn( "flex items-center gap-3 p-3 rounded-[12px] cursor-pointer transition-colors w-full text-left", deleteDialog.action === "move" ? "bg-[#14161A] border border-[rgba(82,89,102,0.3)]" : "bg-[#14161A]/50 border border-transparent hover:border-[rgba(82,89,102,0.2)]", )} style={{ boxShadow: deleteDialog.action === "move" ? "0px 1px 2px 0px rgba(0,43,87,0.1), inset 0px 0px 0px 1px rgba(43,49,67,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08)" : "none", }} > {deleteDialog.action === "move" && ( )} Move to another space {deleteDialog.action === "move" && ( setDeleteDialog((prev) => ({ ...prev, targetProjectId: val, })) } > {availableTargetProjects.map( (p: ContainerTagListType) => ( {p.emoji || "📁"} {p.containerTag === DEFAULT_PROJECT_ID ? "My Space" : p.name} ), )} )} setDeleteDialog((prev) => ({ ...prev, action: "delete" })) } className={cn( "flex items-center gap-3 p-3 rounded-[12px] cursor-pointer transition-colors w-full text-left", deleteDialog.action === "delete" ? "bg-[#14161A] border border-[rgba(220,38,38,0.3)]" : "bg-[#14161A]/50 border border-transparent hover:border-[rgba(82,89,102,0.2)]", )} style={{ boxShadow: deleteDialog.action === "delete" ? "0px 1px 2px 0px rgba(87,0,0,0.1), inset 0px 0px 0px 1px rgba(67,43,43,0.08), inset 0px 1px 1px 0px rgba(0,0,0,0.08)" : "none", }} > {deleteDialog.action === "delete" && ( )} Delete everything permanently {deleteDialog.action === "delete" && ( All documents and memories will be permanently deleted. )} > ) }