refactor layout

* ui polishing
* frontend and backend fixes
This commit is contained in:
Philipinho
2024-05-31 21:51:44 +01:00
parent 046dd6d150
commit 06d854a7d2
95 changed files with 1548 additions and 821 deletions

View File

@ -5,17 +5,18 @@ import {
} from "@/features/search/services/search-service";
import {
IPageSearch,
IPageSearchParams,
ISuggestionResult,
SearchSuggestionParams,
} from "@/features/search/types/search.types";
export function usePageSearchQuery(
query: string,
params: IPageSearchParams,
): UseQueryResult<IPageSearch[], Error> {
return useQuery({
queryKey: ["page-search", query],
queryFn: () => searchPage(query),
enabled: !!query,
queryKey: ["page-search", params],
queryFn: () => searchPage(params),
enabled: !!params.query,
});
}

View File

@ -1,65 +1,92 @@
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';
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, useParams } from "react-router-dom";
import { useDebouncedValue } from "@mantine/hooks";
import { usePageSearchQuery } from "@/features/search/queries/search-query";
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts";
import { useRecentChangesQuery } from "@/features/page/queries/page-query.ts";
import { buildPageUrl } from "@/features/page/page.utils.ts";
export function SearchSpotlight() {
interface SearchSpotlightProps {
spaceId?: string;
}
export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
const navigate = useNavigate();
const [query, setQuery] = useState('');
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>
const {
data: searchResults,
isLoading,
error,
} = usePageSearchQuery({ query: debouncedSearchQuery, spaceId });
<div style={{ flex: 1 }}>
<Text>{item.title}</Text>
const pages = (
searchResults && searchResults.length > 0 ? searchResults : []
).map((page) => (
<Spotlight.Action
key={page.id}
onClick={() =>
navigate(buildPageUrl(page.space.slug, page.slugId, page.title))
}
>
<Group wrap="nowrap" w="100%">
<Center>
{page?.icon ? (
<span style={{ fontSize: "20px" }}>{page.icon}</span>
) : (
<IconFileDescription size={20} />
)}
</Center>
{item?.highlight && (
<Text opacity={0.6} size="xs" dangerouslySetInnerHTML={{ __html: item.highlight }}/>
)}
</div>
<div style={{ flex: 1 }}>
<Text>{page.title}</Text>
</Group>
</Spotlight.Action>
));
{page?.highlight && (
<Text
opacity={0.6}
size="xs"
dangerouslySetInnerHTML={{ __html: page.highlight }}
/>
)}
</div>
</Group>
</Spotlight.Action>
));
return (
<>
<Spotlight.Root query={query}
onQueryChange={setQuery}
scrollable
overlayProps={{
backgroundOpacity: 0.55,
}}>
<Spotlight.Search placeholder="Search..."
leftSection={
<IconSearch size={20} stroke={1.5} />
} />
<Spotlight.Root
query={query}
onQueryChange={setQuery}
scrollable
overlayProps={{
backgroundOpacity: 0.55,
}}
>
<Spotlight.Search
placeholder="Search..."
leftSection={<IconSearch size={20} stroke={1.5} />}
/>
<Spotlight.ActionsList>
{query.length === 0 && items.length === 0 && <Spotlight.Empty>Start typing to search...</Spotlight.Empty>}
{query.length === 0 && pages.length === 0 && (
<Spotlight.Empty>Start typing to search...</Spotlight.Empty>
)}
{query.length > 0 && items.length === 0 && <Spotlight.Empty>No results found...</Spotlight.Empty>}
{query.length > 0 && pages.length === 0 && (
<Spotlight.Empty>No results found...</Spotlight.Empty>
)}
{items.length > 0 && items}
{pages.length > 0 && pages}
</Spotlight.ActionsList>
</Spotlight.Root>
</>
);
}

View File

@ -1,12 +1,15 @@
import api from "@/lib/api-client";
import {
IPageSearch,
IPageSearchParams,
ISuggestionResult,
SearchSuggestionParams,
} from "@/features/search/types/search.types";
export async function searchPage(query: string): Promise<IPageSearch[]> {
const req = await api.post<IPageSearch[]>("/search", { query });
export async function searchPage(
params: IPageSearchParams,
): Promise<IPageSearch[]> {
const req = await api.post<IPageSearch[]>("/search", params);
return req.data;
}

View File

@ -1,16 +1,19 @@
import { IUser } from "@/features/user/types/user.types.ts";
import { IGroup } from "@/features/group/types/group.types.ts";
import { ISpace } from "@/features/space/types/space.types.ts";
export interface IPageSearch {
id: string;
title: string;
icon: string;
parentPageId: string;
slugId: string;
creatorId: string;
createdAt: Date;
updatedAt: Date;
rank: string;
highlight: string;
space: Partial<ISpace>;
}
export interface SearchSuggestionParams {
@ -23,3 +26,8 @@ export interface ISuggestionResult {
users?: Partial<IUser[]>;
groups?: Partial<IGroup[]>;
}
export interface IPageSearchParams {
query: string;
spaceId?: string;
}