import React, { useEffect, useMemo, useRef, useState } from "react"; import xor from "lodash/xor"; import { observer } from "mobx-react"; import { Search, X } from "lucide-react"; import { Combobox } from "@headlessui/react"; // plane ui import { useTranslation } from "@plane/i18n"; import { Button, Checkbox, EModalPosition, EModalWidth, ModalCore } from "@plane/ui"; // components import { Logo } from "@/components/common"; import { SimpleEmptyState } from "@/components/empty-state"; // helpers import { cn } from "@/helpers/common.helper"; // hooks import { useProject } from "@/hooks/store"; import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path"; type Props = { isOpen: boolean; onClose: () => void; selectedProjectIds: string[]; projectIds: string[]; onSubmit: (projectIds: string[]) => Promise; }; export const ProjectMultiSelectModal: React.FC = observer((props) => { const { isOpen, onClose, selectedProjectIds: selectedProjectIdsProp, projectIds, onSubmit } = props; // states const [searchTerm, setSearchTerm] = useState(""); const [selectedProjectIds, setSelectedProjectIds] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); // refs const moveButtonRef = useRef(null); // plane hooks const { t } = useTranslation(); // store hooks const { getProjectById } = useProject(); // derived values const projectDetailsMap = useMemo( () => new Map(projectIds.map((id) => [id, getProjectById(id)])), [projectIds, getProjectById] ); const areSelectedProjectsChanged = xor(selectedProjectIds, selectedProjectIdsProp).length > 0; const filteredProjectIds = projectIds.filter((id) => { const project = projectDetailsMap.get(id); const projectQuery = `${project?.identifier} ${project?.name}`.toLowerCase(); return projectQuery.includes(searchTerm.toLowerCase()); }); const filteredProjectResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/project", }); useEffect(() => { if (isOpen) setSelectedProjectIds(selectedProjectIdsProp); }, [isOpen, selectedProjectIdsProp]); const handleClose = () => { onClose(); setTimeout(() => { setSearchTerm(""); setSelectedProjectIds([]); }, 300); }; const handleSubmit = async () => { setIsSubmitting(true); await onSubmit(selectedProjectIds); setIsSubmitting(false); handleClose(); }; const handleSelectedProjectChange = (val: string[]) => { setSelectedProjectIds(val); setSearchTerm(""); moveButtonRef.current?.focus(); }; if (!isOpen) return null; return (
{selectedProjectIds.length > 0 && (
{selectedProjectIds.map((projectId) => { const projectDetails = projectDetailsMap.get(projectId); if (!projectDetails) return null; return (
{ handleSelectedProjectChange(selectedProjectIds.filter((id) => id !== projectDetails.id)); }} >

{projectDetails.identifier}

); })}
)} {filteredProjectIds.length === 0 ? (
) : (
    0, })} > {filteredProjectIds.map((projectId) => { const projectDetails = projectDetailsMap.get(projectId); if (!projectDetails) return null; const isProjectSelected = selectedProjectIds.includes(projectDetails.id); return ( cn( "flex items-center justify-between gap-2 truncate w-full cursor-pointer select-none rounded-md p-2 text-custom-text-200 transition-colors", { "bg-custom-background-80": active, "text-custom-text-100": isProjectSelected, } ) } >
    {projectDetails.identifier}

    {projectDetails.name}

    ); })}
)}
); });