wip: search page
This commit is contained in:
@ -7,6 +7,7 @@ import {
|
||||
Login,
|
||||
Settings,
|
||||
Entity,
|
||||
SearchPage,
|
||||
} from "./pages";
|
||||
import { SearchImageContextProvider } from "@contexts/SearchImageContext";
|
||||
import { WithNotifications } from "@contexts/Notifications";
|
||||
@ -29,6 +30,7 @@ export const App = () => {
|
||||
<Route path="/" component={WithNotifications}>
|
||||
<Route path="/" component={WithTopbarAndDock}>
|
||||
<Route path="/" component={FrontPage} />
|
||||
<Route path="/search" component={SearchPage} />
|
||||
<Route path="/image/:imageId" component={ImagePage} />
|
||||
<Route path="/entity/:entityId" component={Entity} />
|
||||
<Route path="/gallery/:entity" component={Gallery} />
|
||||
|
@ -8,7 +8,7 @@ export const Dock: Component = () => {
|
||||
<A href="/" class="w-full flex justify-center items-center">
|
||||
<IconHome />
|
||||
</A>
|
||||
<A href="/" class="w-full flex justify-center items-center">
|
||||
<A href="/search" class="w-full flex justify-center items-center">
|
||||
<IconSearch />
|
||||
</A>
|
||||
<A href="/" class="w-full flex justify-center items-center">
|
||||
|
@ -11,15 +11,10 @@ import {
|
||||
CategoryUnion,
|
||||
getUserImages,
|
||||
JustTheImageWhatAreTheseNames,
|
||||
UserImage,
|
||||
} from "../network";
|
||||
import { groupPropertiesWithImage } from "../utils/groupPropertiesWithImage";
|
||||
|
||||
export type ImageWithRawData = Awaited<
|
||||
ReturnType<typeof getUserImages>
|
||||
>["ImageProperties"][number] & {
|
||||
rawData: string[];
|
||||
};
|
||||
|
||||
type TaggedCategory<T extends CategoryUnion["type"]> = Extract<
|
||||
CategoryUnion,
|
||||
{ type: T }
|
||||
@ -30,7 +25,7 @@ type CategoriesSpecificData = {
|
||||
};
|
||||
|
||||
export type SearchImageStore = {
|
||||
images: Accessor<ImageWithRawData[]>;
|
||||
images: Accessor<UserImage[]>;
|
||||
|
||||
userImages: Accessor<JustTheImageWhatAreTheseNames>;
|
||||
|
||||
@ -44,51 +39,17 @@ export type SearchImageStore = {
|
||||
onRefetchImages: () => void;
|
||||
};
|
||||
|
||||
// How wonderfully functional
|
||||
const getAllValues = (object: object): Array<string> => {
|
||||
const loop = (acc: Array<string>, next: object): Array<string> => {
|
||||
for (const [key, _value] of Object.entries(next)) {
|
||||
if (key === "ID") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: unknown = _value;
|
||||
switch (typeof value) {
|
||||
case "object":
|
||||
if (value != null) {
|
||||
acc.push(...loop(acc, value));
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
case "number":
|
||||
case "boolean":
|
||||
acc.push(value.toString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
};
|
||||
|
||||
return loop([], object);
|
||||
};
|
||||
|
||||
const SearchImageContext = createContext<SearchImageStore>();
|
||||
export const SearchImageContextProvider: Component<ParentProps> = (props) => {
|
||||
const [data, { refetch }] = createResource(getUserImages);
|
||||
|
||||
const imageData = createMemo<ImageWithRawData[]>(() => {
|
||||
const imageData = createMemo(() => {
|
||||
const d = data();
|
||||
if (d == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return d.ImageProperties.map((d) => ({
|
||||
...d,
|
||||
rawData: getAllValues(d),
|
||||
}));
|
||||
return d.ImageProperties;
|
||||
});
|
||||
|
||||
const processingImages = () => data()?.ProcessingImages ?? [];
|
||||
|
@ -4,3 +4,4 @@ export * from "./image";
|
||||
export * from "./settings";
|
||||
export * from "./login";
|
||||
export * from "./entity";
|
||||
export * from "./search";
|
||||
|
48
frontend/src/pages/search/index.tsx
Normal file
48
frontend/src/pages/search/index.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { Search } from "@kobalte/core/search";
|
||||
import { IconSearch } from "@tabler/icons-solidjs";
|
||||
import { useSearch } from "./search";
|
||||
import { UserImage } from "@network/index";
|
||||
|
||||
export const SearchPage: Component = () => {
|
||||
const fuse = useSearch();
|
||||
|
||||
const [searchItems, setSearchItems] = createSignal<UserImage[]>([]);
|
||||
|
||||
return (
|
||||
<Search
|
||||
options={searchItems()}
|
||||
onInputChange={(e) => {
|
||||
const items = fuse().search(e);
|
||||
setSearchItems(items.map((i) => i.item.image));
|
||||
}}
|
||||
itemComponent={(props) => (
|
||||
<Search.Item item={props.item}>
|
||||
<Search.ItemLabel>
|
||||
{JSON.stringify(props.item.rawValue)}
|
||||
</Search.ItemLabel>
|
||||
</Search.Item>
|
||||
)}
|
||||
>
|
||||
<Search.Label />
|
||||
<Search.Control class="flex">
|
||||
<Search.Indicator class="bg-neutral-200 p-4 rounded-l-xl">
|
||||
<Search.Icon>
|
||||
<IconSearch />
|
||||
</Search.Icon>
|
||||
</Search.Indicator>
|
||||
<Search.Input
|
||||
class="w-full p-4 font-bold text-xl rounded-r-xl"
|
||||
placeholder="Woking Station..."
|
||||
/>
|
||||
</Search.Control>
|
||||
<Search.Portal>
|
||||
<Search.Content class="w-full rounded-xl bg-white p-4">
|
||||
<Search.Arrow />
|
||||
<Search.Listbox />
|
||||
<Search.NoResult>No result found</Search.NoResult>
|
||||
</Search.Content>
|
||||
</Search.Portal>
|
||||
</Search>
|
||||
);
|
||||
};
|
47
frontend/src/pages/search/search.ts
Normal file
47
frontend/src/pages/search/search.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { UserImage } from "@network/index";
|
||||
import { createMemo } from "solid-js";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
const getSearchTerms = (image: UserImage): Array<string> => {
|
||||
switch (image.type) {
|
||||
case "location":
|
||||
return [
|
||||
image.data.Name,
|
||||
image.data.Description,
|
||||
image.data.Address,
|
||||
image.data.CreatedAt,
|
||||
].filter((i) => i != null);
|
||||
case "event":
|
||||
return [
|
||||
image.data.Name,
|
||||
image.data.Description,
|
||||
image.data.CreatedAt,
|
||||
].filter((i) => i != null);
|
||||
case "contact":
|
||||
return [
|
||||
image.data.Name,
|
||||
image.data.Description,
|
||||
image.data.CreatedAt,
|
||||
image.data.Email,
|
||||
image.data.PhoneNumber,
|
||||
].filter((i) => i != null);
|
||||
case "note":
|
||||
return [
|
||||
image.data.Name,
|
||||
image.data.Description,
|
||||
image.data.CreatedAt,
|
||||
image.data.Content,
|
||||
].filter((i) => i != null);
|
||||
}
|
||||
};
|
||||
|
||||
export const useSearch = () => {
|
||||
const { images } = useSearchImageContext();
|
||||
|
||||
const searchTerms = createMemo(() =>
|
||||
images().map((i) => ({ image: i, searchTerms: getSearchTerms(i) })),
|
||||
);
|
||||
|
||||
return () => new Fuse(searchTerms(), { keys: ["searchTerms"] });
|
||||
};
|
Reference in New Issue
Block a user