import { useCallback, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { ListFilter, Plus, Search, X } from "lucide-react"; import { TModuleFilters } from "@plane/types"; // hooks import { Breadcrumbs, Button, Tooltip, DiceIcon } from "@plane/ui"; import { BreadcrumbLink } from "@/components/common"; import { FiltersDropdown } from "@/components/issues"; import { ModuleFiltersSelection, ModuleOrderByDropdown } from "@/components/modules"; import { ProjectLogo } from "@/components/project"; import { MODULE_VIEW_LAYOUTS } from "@/constants/module"; import { EUserProjectRoles } from "@/constants/project"; import { cn } from "@/helpers/common.helper"; import { useApplication, useEventTracker, useMember, useModuleFilter, useProject, useUser } from "@/hooks/store"; import useOutsideClickDetector from "@/hooks/use-outside-click-detector"; // components // constants // hooks import { usePlatformOS } from "@/hooks/use-platform-os"; // ui // helpers // types export const ModulesListHeader: React.FC = observer(() => { // states const [isSearchOpen, setIsSearchOpen] = useState(false); // refs const inputRef = useRef(null); // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; // store hooks const { commandPalette: commandPaletteStore } = useApplication(); const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); const { currentProjectDetails } = useProject(); const { isMobile } = usePlatformOS(); const { workspace: { workspaceMemberIds }, } = useMember(); const { currentProjectDisplayFilters: displayFilters, currentProjectFilters: filters, searchQuery, updateDisplayFilters, updateFilters, updateSearchQuery, } = useModuleFilter(); // outside click detector hook useOutsideClickDetector(inputRef, () => { if (isSearchOpen && searchQuery.trim() === "") setIsSearchOpen(false); }); const handleFilters = useCallback( (key: keyof TModuleFilters, value: string | string[]) => { if (!projectId) return; const newValues = filters?.[key] ?? []; if (Array.isArray(value)) value.forEach((val) => { if (!newValues.includes(val)) newValues.push(val); }); else { if (filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); else newValues.push(value); } updateFilters(projectId.toString(), { [key]: newValues }); }, [filters, projectId, updateFilters] ); const handleInputKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Escape") { if (searchQuery && searchQuery.trim() !== "") updateSearchQuery(""); else { setIsSearchOpen(false); inputRef.current?.blur(); } } }; // auth const canUserCreateModule = currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole); return (
) } /> } /> } />} />
{!isSearchOpen && ( )}
updateSearchQuery(e.target.value)} onKeyDown={handleInputKeyDown} /> {isSearchOpen && ( )}
{MODULE_VIEW_LAYOUTS.map((layout) => ( ))}
{ if (!projectId || val === displayFilters?.order_by) return; updateDisplayFilters(projectId.toString(), { order_by: val, }); }} /> } title="Filters" placement="bottom-end"> { if (!projectId) return; updateDisplayFilters(projectId.toString(), val); }} handleFiltersUpdate={handleFilters} memberIds={workspaceMemberIds ?? undefined} /> {canUserCreateModule && ( )}
); });