Haystack V2: Removing entities completely
This commit is contained in:
@@ -2,11 +2,9 @@ import { Navigate, Route, Router } from "@solidjs/router";
|
||||
import { onAndroidMount } from "./mobile";
|
||||
import {
|
||||
FrontPage,
|
||||
Gallery,
|
||||
ImagePage,
|
||||
Login,
|
||||
Settings,
|
||||
Entity,
|
||||
SearchPage,
|
||||
AllImages,
|
||||
List,
|
||||
@@ -36,8 +34,6 @@ export const App = () => {
|
||||
<Route path="/all-images" component={AllImages} />
|
||||
<Route path="/image/:imageId" component={ImagePage} />
|
||||
<Route path="/list/:listId" component={List} />
|
||||
<Route path="/entity/:entityId" component={Entity} />
|
||||
<Route path="/gallery/:entity" component={Gallery} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { UserImage } from "../../network";
|
||||
import { Show, type Component } from "solid-js";
|
||||
import SolidjsMarkdown from "solidjs-markdown";
|
||||
|
||||
type Props = {
|
||||
item: UserImage;
|
||||
};
|
||||
|
||||
const NullableParagraph: Component<{
|
||||
item: string | null;
|
||||
itemTitle: string;
|
||||
}> = (props) => {
|
||||
return (
|
||||
<Show when={props.item}>
|
||||
{(item) => (
|
||||
<>
|
||||
<p class="font-semibold text-xl">{props.itemTitle}</p>
|
||||
<p class="text-md">{item()}</p>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const ConcreteItemModal: Component<Props> = (props) => {
|
||||
switch (props.item.type) {
|
||||
case "note":
|
||||
return (
|
||||
<SolidjsMarkdown>
|
||||
{props.item.data.Content.slice(
|
||||
"```markdown".length,
|
||||
props.item.data.Content.length - "```".length,
|
||||
)}
|
||||
</SolidjsMarkdown>
|
||||
);
|
||||
case "location":
|
||||
return (
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="font-semibold text-xl">Address</p>
|
||||
<p class="text-md">{props.item.data.Address}</p>
|
||||
</div>
|
||||
);
|
||||
case "event":
|
||||
return (
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="font-semibold text-xl">Event</p>
|
||||
<p class="text-md">{props.item.data.Name}</p>
|
||||
|
||||
<NullableParagraph
|
||||
itemTitle="Start Time"
|
||||
item={props.item.data.StartDateTime}
|
||||
/>
|
||||
<NullableParagraph
|
||||
itemTitle="End Time"
|
||||
item={props.item.data.EndDateTime}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "contact":
|
||||
return (
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="font-semibold text-xl">Contact</p>
|
||||
<p class="text-md">{props.item.data.Name}</p>
|
||||
|
||||
<NullableParagraph itemTitle="Email" item={props.item.data.Email} />
|
||||
|
||||
<NullableParagraph
|
||||
itemTitle="Phone Number"
|
||||
item={props.item.data.PhoneNumber}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const ItemModal: Component<Props> = (props) => {
|
||||
return (
|
||||
<div class="rounded-2xl p-4 bg-white border border-neutral-300 flex flex-col gap-2 mb-2">
|
||||
<ConcreteItemModal item={props.item} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import type { UserImage } from "../../network";
|
||||
import { SearchCardContact } from "./SearchCardContact";
|
||||
import { SearchCardEvent } from "./SearchCardEvent";
|
||||
import { SearchCardLocation } from "./SearchCardLocation";
|
||||
|
||||
const UnwrappedSearchCard = (props: { item: UserImage }) => {
|
||||
const { item } = props;
|
||||
|
||||
switch (item.type) {
|
||||
case "location":
|
||||
return <SearchCardLocation item={item} />;
|
||||
case "event":
|
||||
return <SearchCardEvent item={item} />;
|
||||
case "contact":
|
||||
return <SearchCardContact item={item} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const SearchCard = (props: { item: UserImage }) => {
|
||||
return (
|
||||
<A
|
||||
href={`/entity/${props.item.data.ID}`}
|
||||
class="w-full h-[144px] border relative border-neutral-200 cursor-pointer overflow-hidden rounded-xl"
|
||||
>
|
||||
<UnwrappedSearchCard item={props.item} />
|
||||
</A>
|
||||
);
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import { IconUser } from "@tabler/icons-solidjs";
|
||||
import type { UserImage } from "../../network";
|
||||
|
||||
type Props = {
|
||||
item: Extract<UserImage, { type: "contact" }>;
|
||||
};
|
||||
|
||||
export const SearchCardContact = ({ item }: Props) => {
|
||||
const { data } = item;
|
||||
|
||||
return (
|
||||
<div class="h-full inset-0 p-3 bg-orange-50">
|
||||
<div class="flex mb-1 items-center gap-1">
|
||||
<IconUser size={14} class="text-neutral-500" />
|
||||
<p class="text-xs text-neutral-500">Contact</p>
|
||||
</div>
|
||||
<p class="text-sm text-neutral-900 font-bold mb-1">
|
||||
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
|
||||
</p>
|
||||
<p class="text-xs text-neutral-700">Phone: {data.PhoneNumber}</p>
|
||||
<p class="text-xs text-neutral-700">Mail: {data.Email}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import { IconCalendar } from "@tabler/icons-solidjs";
|
||||
import type { UserImage } from "../../network";
|
||||
|
||||
type Props = {
|
||||
item: Extract<UserImage, { type: "event" }>;
|
||||
};
|
||||
|
||||
export const SearchCardEvent = ({ item }: Props) => {
|
||||
const { data } = item;
|
||||
|
||||
return (
|
||||
<div class="h-full inset-0 p-3 bg-purple-50">
|
||||
<div class="flex mb-1 items-center gap-1">
|
||||
<IconCalendar size={14} class="text-neutral-500" />
|
||||
<p class="text-xs text-neutral-500">Event</p>
|
||||
</div>
|
||||
<p class="text-sm text-neutral-900 font-bold mb-1">
|
||||
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
|
||||
</p>
|
||||
<p class="text-xs text-neutral-700">
|
||||
On{" "}
|
||||
{data.StartDateTime
|
||||
? new Date(data.StartDateTime).toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
})
|
||||
: "unknown date"}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
import { IconMapPin } from "@tabler/icons-solidjs";
|
||||
import type { UserImage } from "../../network";
|
||||
|
||||
type Props = {
|
||||
item: Extract<UserImage, { type: "location" }>;
|
||||
};
|
||||
|
||||
export const SearchCardLocation = ({ item }: Props) => {
|
||||
const { data } = item;
|
||||
|
||||
return (
|
||||
<div class="h-full inset-0 p-3 bg-red-50">
|
||||
<div class="flex mb-1 items-center gap-1">
|
||||
<IconMapPin size={14} class="text-neutral-500" />
|
||||
<p class="text-xs text-neutral-500">Location</p>
|
||||
</div>
|
||||
<p class="text-sm text-neutral-900 font-bold mb-1">
|
||||
{data.Name.length > 0 ? data.Name : "Unknown 🐞"}
|
||||
</p>
|
||||
<p class="text-xs text-neutral-700">Address: {data.Address}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -7,26 +7,9 @@ import {
|
||||
createResource,
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
import {
|
||||
CategoryUnion,
|
||||
getUserImages,
|
||||
JustTheImageWhatAreTheseNames,
|
||||
List,
|
||||
UserImage,
|
||||
} from "../network";
|
||||
import { groupPropertiesWithImage } from "../utils/groupPropertiesWithImage";
|
||||
|
||||
type TaggedCategory<T extends CategoryUnion["type"]> = Extract<
|
||||
CategoryUnion,
|
||||
{ type: T }
|
||||
>["data"];
|
||||
|
||||
type CategoriesSpecificData = {
|
||||
[K in CategoryUnion["type"]]: Array<TaggedCategory<K>>;
|
||||
};
|
||||
import { getUserImages, JustTheImageWhatAreTheseNames } from "../network";
|
||||
|
||||
export type SearchImageStore = {
|
||||
images: Accessor<UserImage[]>;
|
||||
imagesByDate: Accessor<
|
||||
Array<{ date: Date; images: JustTheImageWhatAreTheseNames }>
|
||||
>;
|
||||
@@ -35,13 +18,10 @@ export type SearchImageStore = {
|
||||
|
||||
userImages: Accessor<JustTheImageWhatAreTheseNames>;
|
||||
|
||||
imagesWithProperties: Accessor<ReturnType<typeof groupPropertiesWithImage>>;
|
||||
processingImages: Accessor<
|
||||
Awaited<ReturnType<typeof getUserImages>>["ProcessingImages"] | undefined
|
||||
>;
|
||||
|
||||
categories: Accessor<CategoriesSpecificData>;
|
||||
|
||||
onRefetchImages: () => void;
|
||||
};
|
||||
|
||||
@@ -49,15 +29,6 @@ const SearchImageContext = createContext<SearchImageStore>();
|
||||
export const SearchImageContextProvider: Component<ParentProps> = (props) => {
|
||||
const [data, { refetch }] = createResource(getUserImages);
|
||||
|
||||
const imageData = createMemo(() => {
|
||||
const d = data();
|
||||
if (d == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return d.ImageProperties;
|
||||
});
|
||||
|
||||
const sortedImages = createMemo<ReturnType<SearchImageStore["imagesByDate"]>>(
|
||||
() => {
|
||||
const d = data();
|
||||
@@ -89,42 +60,14 @@ export const SearchImageContextProvider: Component<ParentProps> = (props) => {
|
||||
|
||||
const processingImages = () => data()?.ProcessingImages ?? [];
|
||||
|
||||
const imagesWithProperties = createMemo<
|
||||
ReturnType<typeof groupPropertiesWithImage>
|
||||
>(() => {
|
||||
const d = data();
|
||||
if (d == null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return groupPropertiesWithImage(d);
|
||||
});
|
||||
|
||||
const categories = createMemo(() => {
|
||||
const c: ReturnType<SearchImageStore["categories"]> = {
|
||||
contact: [],
|
||||
event: [],
|
||||
location: [],
|
||||
};
|
||||
|
||||
for (const category of data()?.ImageProperties ?? []) {
|
||||
c[category.type].push(category.data as any);
|
||||
}
|
||||
|
||||
return c;
|
||||
});
|
||||
|
||||
return (
|
||||
<SearchImageContext.Provider
|
||||
value={{
|
||||
images: imageData,
|
||||
imagesByDate: sortedImages,
|
||||
lists: () => data()?.Lists ?? [],
|
||||
imagesWithProperties: imagesWithProperties,
|
||||
userImages: () => data()?.UserImages ?? [],
|
||||
processingImages,
|
||||
onRefetchImages: refetch,
|
||||
categories,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
string,
|
||||
union,
|
||||
uuid,
|
||||
variant,
|
||||
} from "valibot";
|
||||
|
||||
type BaseRequestParams = Partial<{
|
||||
@@ -85,62 +84,6 @@ export const sendImage = async (
|
||||
return parse(sendImageResponseValidator, res);
|
||||
};
|
||||
|
||||
const locationValidator = strictObject({
|
||||
ID: pipe(string(), uuid()),
|
||||
CreatedAt: pipe(string()),
|
||||
Name: string(),
|
||||
Address: nullable(string()),
|
||||
Description: nullable(string()),
|
||||
Images: array(pipe(string(), uuid())),
|
||||
});
|
||||
|
||||
const contactValidator = strictObject({
|
||||
ID: pipe(string(), uuid()),
|
||||
CreatedAt: pipe(string()),
|
||||
Name: string(),
|
||||
Description: nullable(string()),
|
||||
PhoneNumber: nullable(string()),
|
||||
Email: nullable(string()),
|
||||
Images: array(pipe(string(), uuid())),
|
||||
});
|
||||
|
||||
const eventValidator = strictObject({
|
||||
ID: pipe(string(), uuid()),
|
||||
CreatedAt: nullable(pipe(string())),
|
||||
Name: string(),
|
||||
StartDateTime: nullable(pipe(string())),
|
||||
EndDateTime: nullable(pipe(string())),
|
||||
Description: nullable(string()),
|
||||
LocationID: nullable(pipe(string(), uuid())),
|
||||
// Location: nullable(locationValidator),
|
||||
OrganizerID: nullable(pipe(string(), uuid())),
|
||||
// Organizer: nullable(contactValidator),
|
||||
Images: array(pipe(string(), uuid())),
|
||||
});
|
||||
|
||||
const locationDataType = strictObject({
|
||||
type: literal("location"),
|
||||
data: locationValidator,
|
||||
});
|
||||
|
||||
const eventDataType = strictObject({
|
||||
type: literal("event"),
|
||||
data: eventValidator,
|
||||
});
|
||||
|
||||
const contactDataType = strictObject({
|
||||
type: literal("contact"),
|
||||
data: contactValidator,
|
||||
});
|
||||
|
||||
const dataTypeValidator = variant("type", [
|
||||
locationDataType,
|
||||
eventDataType,
|
||||
contactDataType,
|
||||
]);
|
||||
|
||||
export type CategoryUnion = InferOutput<typeof dataTypeValidator>;
|
||||
|
||||
const imageMetaValidator = strictObject({
|
||||
ID: pipe(string(), uuid()),
|
||||
ImageName: string(),
|
||||
@@ -168,8 +111,6 @@ const userProcessingImageValidator = strictObject({
|
||||
]),
|
||||
});
|
||||
|
||||
export type UserImage = InferOutput<typeof dataTypeValidator>;
|
||||
|
||||
const listValidator = strictObject({
|
||||
ID: pipe(string(), uuid()),
|
||||
UserID: pipe(string(), uuid()),
|
||||
@@ -212,7 +153,6 @@ export type List = InferOutput<typeof listValidator>;
|
||||
|
||||
const imageRequestValidator = strictObject({
|
||||
UserImages: array(userImageValidator),
|
||||
ImageProperties: array(dataTypeValidator),
|
||||
ProcessingImages: array(userProcessingImageValidator),
|
||||
Lists: array(listValidator),
|
||||
});
|
||||
@@ -233,15 +173,6 @@ export const getUserImages = async (): Promise<
|
||||
return parse(imageRequestValidator, res);
|
||||
};
|
||||
|
||||
export const getImage = async (imageId: string): Promise<UserImage> => {
|
||||
const request = getBaseAuthorizedRequest({
|
||||
path: `image-properties/${imageId}`,
|
||||
});
|
||||
|
||||
const res = await fetch(request).then((res) => res.json());
|
||||
return parse(dataTypeValidator, res);
|
||||
};
|
||||
|
||||
export const postLogin = async (email: string): Promise<void> => {
|
||||
const request = getBaseRequest({
|
||||
path: "login",
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { ImageComponent } from "@components/image";
|
||||
import { ItemModal } from "@components/item-modal/ItemModal";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, For, Show } from "solid-js";
|
||||
|
||||
export const Entity: Component = () => {
|
||||
const params = useParams<{ entityId: string }>();
|
||||
|
||||
const { images } = useSearchImageContext();
|
||||
|
||||
const entity = () => images().find((i) => i.data.ID === params.entityId);
|
||||
|
||||
return (
|
||||
<Show when={entity()} fallback={<>Sorry, this entity could not be found</>}>
|
||||
{(e) => (
|
||||
<div>
|
||||
<ItemModal item={e()} />
|
||||
<div class="w-full grid grid-cols-4 auto-rows-[minmax(100px,1fr)] gap-4 bg-white p-4 rounded-xl border border-neutral-200">
|
||||
<For each={e().data.Images}>
|
||||
{(imageId) => <ImageComponent ID={imageId} />}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +1,8 @@
|
||||
import { Component, For } from "solid-js";
|
||||
import { A } from "@solidjs/router";
|
||||
import {
|
||||
SearchImageStore,
|
||||
useSearchImageContext,
|
||||
} from "@contexts/SearchImageContext";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import fastHashCode from "../../utils/hash";
|
||||
|
||||
// TODO: lots of stuff to do with Entities, this could be seperated into a centralized place.
|
||||
const CategoryColor: Record<
|
||||
keyof ReturnType<SearchImageStore["categories"]>,
|
||||
string
|
||||
> = {
|
||||
contact: "bg-orange-50",
|
||||
location: "bg-red-50",
|
||||
event: "bg-purple-50",
|
||||
};
|
||||
|
||||
const colors = [
|
||||
"bg-emerald-50",
|
||||
"bg-lime-50",
|
||||
@@ -31,30 +18,10 @@ const colors = [
|
||||
];
|
||||
|
||||
export const Categories: Component = () => {
|
||||
const { categories, lists } = useSearchImageContext();
|
||||
const { lists } = useSearchImageContext();
|
||||
|
||||
return (
|
||||
<div class="rounded-xl bg-white p-4 flex flex-col gap-2">
|
||||
<h2 class="text-xl font-bold">Entities</h2>
|
||||
<div class="w-full grid grid-cols-4 auto-rows-[minmax(100px,1fr)] gap-4">
|
||||
<For each={Object.entries(categories())}>
|
||||
{([category, group]) => (
|
||||
<A
|
||||
href={`/gallery/${category}`}
|
||||
class={
|
||||
"col-span-2 flex flex-col justify-center items-center rounded-lg p-4 border border-neutral-200 " +
|
||||
"capitalize " +
|
||||
CategoryColor[category as keyof typeof CategoryColor] +
|
||||
" " +
|
||||
(group.length === 0 ? "row-span-1 order-10" : "row-span-2")
|
||||
}
|
||||
>
|
||||
<p class="text-xl font-bold">{category}s</p>
|
||||
<p class="text-lg">{group.length}</p>
|
||||
</A>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<h2 class="text-xl font-bold">Generated Lists</h2>
|
||||
<div class="w-full grid grid-cols-3 auto-rows-[minmax(100px,1fr)] gap-4">
|
||||
<For each={lists()}>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Component, For, Show } from "solid-js";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { union, literal, safeParse, InferOutput, parse } from "valibot";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { SearchCard } from "@components/search-card/SearchCard";
|
||||
|
||||
const entityValidator = union([
|
||||
literal("event"),
|
||||
literal("note"),
|
||||
literal("location"),
|
||||
literal("contact"),
|
||||
]);
|
||||
|
||||
const EntityGallery: Component<{
|
||||
entity: InferOutput<typeof entityValidator>;
|
||||
}> = (props) => {
|
||||
// Just to be doubly sure.
|
||||
parse(entityValidator, props.entity);
|
||||
|
||||
// These names are being silly. Entity or Category?
|
||||
const { images } = useSearchImageContext();
|
||||
|
||||
const filteredCategories = () =>
|
||||
images().filter((i) => i.type === props.entity);
|
||||
|
||||
return (
|
||||
<div class="w-full flex flex-col gap-4 capitalize bg-white rounded-xl p-4">
|
||||
<h2 class="font-bold text-xl">
|
||||
{props.entity}s ({filteredCategories().length})
|
||||
</h2>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<For each={filteredCategories()}>
|
||||
{(category) => <SearchCard item={category} />}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Gallery: Component = () => {
|
||||
const params = useParams();
|
||||
const validated = safeParse(entityValidator, params.entity);
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={validated.success}
|
||||
fallback={<p>Sorry, this entity is not supported</p>}
|
||||
>
|
||||
<EntityGallery entity={validated.output as any} />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
@@ -1,25 +1,16 @@
|
||||
import { ImageComponent } from "@components/image";
|
||||
import { SearchCard } from "@components/search-card/SearchCard";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { UserImage } from "@network/index";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { createEffect, For, Show, type Component } from "solid-js";
|
||||
import { type Component } from "solid-js";
|
||||
import SolidjsMarkdown from "solidjs-markdown";
|
||||
|
||||
export const ImagePage: Component = () => {
|
||||
const { imageId } = useParams<{ imageId: string }>();
|
||||
|
||||
const { imagesWithProperties, userImages } = useSearchImageContext();
|
||||
const { userImages } = useSearchImageContext();
|
||||
|
||||
const image = () => userImages().find((i) => i.ImageID === imageId);
|
||||
|
||||
createEffect(() => {
|
||||
console.log(userImages());
|
||||
});
|
||||
|
||||
const imageProperties = (): UserImage[] | undefined =>
|
||||
Object.entries(imagesWithProperties()).find(([id]) => id === imageId)?.[1];
|
||||
|
||||
return (
|
||||
<main class="flex flex-col items-center gap-4">
|
||||
<div class="w-full bg-white rounded-xl p-4">
|
||||
@@ -29,15 +20,7 @@ export const ImagePage: Component = () => {
|
||||
<h2 class="font-bold text-xl">Description</h2>
|
||||
<SolidjsMarkdown>{image()?.Image.Description}</SolidjsMarkdown>
|
||||
</div>
|
||||
<div class="w-full grid grid-cols-3 gap-2 grid-flow-row-dense p-4 bg-white rounded-xl">
|
||||
<Show when={imageProperties()}>
|
||||
{(image) => (
|
||||
<For each={image()}>
|
||||
{(property) => <SearchCard item={property} />}
|
||||
</For>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<div class="w-full grid grid-cols-3 gap-2 grid-flow-row-dense p-4 bg-white rounded-xl"></div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export * from "./front";
|
||||
export * from "./gallery";
|
||||
export * from "./image";
|
||||
export * from "./settings";
|
||||
export * from "./login";
|
||||
export * from "./entity";
|
||||
export * from "./search";
|
||||
export * from "./all-images";
|
||||
export * from "./list";
|
||||
|
||||
@@ -2,13 +2,13 @@ import { Component, createSignal, For } from "solid-js";
|
||||
import { Search } from "@kobalte/core/search";
|
||||
import { IconSearch } from "@tabler/icons-solidjs";
|
||||
import { useSearch } from "./search";
|
||||
import { UserImage } from "@network/index";
|
||||
import { SearchCard } from "@components/search-card/SearchCard";
|
||||
import { JustTheImageWhatAreTheseNames } from "@network/index";
|
||||
|
||||
export const SearchPage: Component = () => {
|
||||
const fuse = useSearch();
|
||||
|
||||
const [searchItems, setSearchItems] = createSignal<UserImage[]>([]);
|
||||
const [searchItems, setSearchItems] =
|
||||
createSignal<JustTheImageWhatAreTheseNames>([]);
|
||||
|
||||
return (
|
||||
<Search
|
||||
@@ -36,7 +36,9 @@ export const SearchPage: Component = () => {
|
||||
<Search.Portal>
|
||||
<Search.Content class="container relative w-full rounded-xl bg-white p-4 grid grid-cols-3 gap-4">
|
||||
<Search.Arrow />
|
||||
<For each={searchItems()}>{(item) => <SearchCard item={item} />}</For>
|
||||
<For each={searchItems()}>
|
||||
{(item) => <div>{item.Image.Description}</div>}
|
||||
</For>
|
||||
<Search.NoResult>No result found</Search.NoResult>
|
||||
</Search.Content>
|
||||
</Search.Portal>
|
||||
|
||||
@@ -1,46 +1,12 @@
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { UserImage } from "@network/index";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
// This language is stupid. `keyof` only returns common keys but this somehow doesnt.
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||
|
||||
const weightedTerms: Record<
|
||||
KeysOfUnion<UserImage["data"]>,
|
||||
number | undefined
|
||||
> = {
|
||||
ID: undefined,
|
||||
LocationID: undefined,
|
||||
OrganizerID: undefined,
|
||||
Images: undefined,
|
||||
|
||||
Description: 10,
|
||||
|
||||
Name: 5,
|
||||
Address: 2,
|
||||
|
||||
PhoneNumber: 2,
|
||||
Email: 2,
|
||||
|
||||
CreatedAt: 1,
|
||||
StartDateTime: 1,
|
||||
EndDateTime: 1,
|
||||
};
|
||||
|
||||
export const useSearch = () => {
|
||||
const { images, userImages } = useSearchImageContext();
|
||||
|
||||
const imageDescriptions = () =>
|
||||
userImages().map((i) => ({ data: { Description: i.Image.Description } }));
|
||||
const { userImages } = useSearchImageContext();
|
||||
|
||||
return () =>
|
||||
new Fuse([...images(), ...imageDescriptions()], {
|
||||
new Fuse(userImages(), {
|
||||
shouldSort: true,
|
||||
keys: Object.entries(weightedTerms)
|
||||
.filter(([, w]) => w != null)
|
||||
.map(([name, weight]) => ({
|
||||
name: `data.${name}`,
|
||||
weight,
|
||||
})),
|
||||
keys: ["Image.Description"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { getUserImages } from "../network";
|
||||
|
||||
export const groupPropertiesWithImage = ({
|
||||
UserImages,
|
||||
ImageProperties,
|
||||
}: Awaited<ReturnType<typeof getUserImages>>) => {
|
||||
const imageToProperties: Record<string, typeof ImageProperties> = {};
|
||||
|
||||
for (const image of UserImages) {
|
||||
imageToProperties[image.ImageID] = ImageProperties.filter((i) =>
|
||||
i.data.Images.includes(image.ImageID),
|
||||
);
|
||||
}
|
||||
|
||||
return imageToProperties;
|
||||
};
|
||||
Reference in New Issue
Block a user