feat: search

This commit is contained in:
Philipinho
2024-01-30 00:14:21 +01:00
parent e0e5f7c43d
commit a0ec2f30ca
22 changed files with 509 additions and 161 deletions

View File

@ -0,0 +1,11 @@
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import { searchPage } from '@/features/search/services/search-service';
import { IPageSearch } from '@/features/search/types/search.types';
export function usePageSearchQuery(query: string): UseQueryResult<IPageSearch[], Error> {
return useQuery({
queryKey: ['page-history', query],
queryFn: () => searchPage(query),
enabled: !!query,
});
}

View File

@ -1,43 +1,59 @@
import { rem } from '@mantine/core';
import { Spotlight, SpotlightActionData } from '@mantine/spotlight';
import { IconHome, IconDashboard, IconSettings, IconSearch } from '@tabler/icons-react';
import { Group, Center, Text } from '@mantine/core';
import { Spotlight } from '@mantine/spotlight';
import { IconFileDescription, IconHome, IconSearch, IconSettings } from '@tabler/icons-react';
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDebouncedValue } from '@mantine/hooks';
import { usePageSearchQuery } from '@/features/search/queries/search-query';
const actions: SpotlightActionData[] = [
{
id: 'home',
label: 'Home',
description: 'Get to home page',
onClick: () => console.log('Home'),
leftSection: <IconHome style={{ width: rem(24), height: rem(24) }} stroke={1.5} />,
},
{
id: 'dashboard',
label: 'Dashboard',
description: 'Get full information about current system status',
onClick: () => console.log('Dashboard'),
leftSection: <IconDashboard style={{ width: rem(24), height: rem(24) }} stroke={1.5} />,
},
{
id: 'settings',
label: 'Settings',
description: 'Account settings and workspace management',
onClick: () => console.log('Settings'),
leftSection: <IconSettings style={{ width: rem(24), height: rem(24) }} stroke={1.5} />,
},
];
export function SearchSpotlight() {
const navigate = useNavigate();
const [query, setQuery] = useState('');
const [debouncedSearchQuery] = useDebouncedValue(query, 300);
const { data: searchResults, isLoading, error } = usePageSearchQuery(debouncedSearchQuery)
const items = (searchResults && searchResults.length > 0 ? searchResults : [])
.map((item) => (
<Spotlight.Action key={item.title} onClick={() => navigate(`/p/${item.id}`)}>
<Group wrap="nowrap" w="100%">
<Center>
{item?.icon ? (
<span style={{ fontSize: "20px" }}>{ item.icon }</span>
) : (
<IconFileDescription size={20} />
)}
</Center>
<div style={{ flex: 1 }}>
<Text>{item.title}</Text>
{item?.highlight && (
<Text opacity={0.6} size="xs" dangerouslySetInnerHTML={{ __html: item.highlight }}/>
)}
</div>
</Group>
</Spotlight.Action>
));
return (
<>
<Spotlight
actions={actions}
nothingFound="Nothing found..."
highlightQuery
searchProps={{
leftSection: <IconSearch style={{ width: rem(20), height: rem(20) }} stroke={1.5} />,
placeholder: 'Search...',
}}
/>
<Spotlight.Root query={query}
onQueryChange={setQuery}
scrollable
overlayProps={{
backgroundOpacity: 0.55,
}}>
<Spotlight.Search placeholder="Search..."
leftSection={
<IconSearch size={20} stroke={1.5} />
} />
<Spotlight.ActionsList>
{items.length > 0 ? items : <Spotlight.Empty>No results found...</Spotlight.Empty>}
</Spotlight.ActionsList>
</Spotlight.Root>
</>
);
}

View File

@ -0,0 +1,7 @@
import api from '@/lib/api-client';
import { IPageSearch } from '@/features/search/types/search.types';
export async function searchPage(query: string): Promise<IPageSearch[]> {
const req = await api.post<IPageSearch[]>('/search', { query });
return req.data as any;
}

View File

@ -0,0 +1,12 @@
export interface IPageSearch {
id: string;
title: string;
icon: string;
parentPageId: string;
creatorId: string;
createdAt: Date;
updatedAt: Date;
rank: string;
highlight: string;
}