import { useState, useCallback } from "react"; import { TextInput, ScrollArea, Loader } from "@mantine/core"; import { useDebouncedValue } from "@mantine/hooks"; import { IconSearch, IconFile } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; import { useGetSpacesQuery } from "@/features/space/queries/space-query"; import { useSearchSuggestionsQuery } from "@/features/search/queries/search-query"; import { ISpace } from "@/features/space/types/space.types"; import { IPage } from "@/features/page/types/page.types"; import { DestinationSelection } from "./destination-picker.types"; import { SpaceRow } from "./space-row"; import classes from "./destination-picker.module.css"; type DestinationPickerProps = { onSelectionChange: (selection: DestinationSelection | null) => void; excludePageId?: string; pageLimit?: number; }; export function DestinationPicker({ onSelectionChange, excludePageId, pageLimit = 15, }: DestinationPickerProps) { const { t } = useTranslation(); const [searchQuery, setSearchQuery] = useState(""); const [selection, setSelection] = useState(null); const [debouncedQuery] = useDebouncedValue(searchQuery, 300); const { data: spacesData, isLoading: spacesLoading } = useGetSpacesQuery({ limit: 100, }); const searchEnabled = debouncedQuery && debouncedQuery.length >= 2; const { data: searchData, isLoading: searchLoading } = useSearchSuggestionsQuery({ query: searchEnabled ? debouncedQuery : "", includePages: true, limit: 20, }); const isSearching = !!searchEnabled; const selectedId = selection?.type === "space" ? selection.spaceId : selection?.pageId ?? null; const updateSelection = useCallback( (next: DestinationSelection | null) => { setSelection(next); onSelectionChange(next); }, [onSelectionChange], ); const handleSearchResultClick = (page: Partial) => { if (!page.space || !page.id) return; updateSelection({ type: "page", spaceId: page.space.id, pageId: page.id, page, space: page.space, }); setSearchQuery(""); }; const handleSelectSpace = useCallback( (space: ISpace) => { updateSelection({ type: "space", spaceId: space.id, space }); }, [updateSelection], ); const handleSelectPage = useCallback( (page: Partial, space: ISpace) => { if (!page.id) return; updateSelection({ type: "page", spaceId: page.spaceId ?? space.id, pageId: page.id, page, space, }); }, [updateSelection], ); return ( <> } placeholder={t("Search pages and spaces...")} variant="filled" value={searchQuery} onChange={(e) => setSearchQuery(e.currentTarget.value)} className={classes.searchInput} /> {isSearching ? ( searchLoading ? (
) : searchData?.pages && searchData.pages.length > 0 ? ( searchData.pages.map( (page) => page && (
handleSearchResultClick(page)} >
{page.icon ? ( page.icon ) : ( )}
{page.title || t("Untitled")}
{page.space && (
{page.space.name}
)}
), ) ) : (
{t("No results found")}
) ) : spacesLoading ? (
) : ( spacesData?.items?.map((space) => ( )) )}
{selection && (
{selection.type === "space" ? selection.space.name : `${selection.space.name} / ${selection.page.title || t("Untitled")}`}
)} ); }