commit f175505f39709bddeef7ae3621be09507ab8884d
parent f74182ac529be89789cfe0c850209cb6a39dcda9
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Mon, 9 Dec 2024 06:36:03 +0000
Add `/search` page basis with search service. Add/edit components, conf.
Diffstat:
4 files changed, 339 insertions(+), 0 deletions(-)
diff --git a/src/lib/components/search_result_container.svelte b/src/lib/components/search_result_container.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+ import type { CallbackPromise } from "@radroots/svelte-lib";
+
+ export let basis: {
+ callback: CallbackPromise;
+ };
+</script>
+
+<button
+ class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`}
+ on:click={async () => {
+ await basis.callback();
+ }}
+>
+ <slot />
+</button>
diff --git a/src/lib/components/search_result_display.svelte b/src/lib/components/search_result_display.svelte
@@ -0,0 +1,204 @@
+<script lang="ts">
+ import type { ClientSearchResult } from "$lib/util/client-search";
+ import { ascii, Glyph, ls, route } from "@radroots/svelte-lib";
+ import SearchResultContainer from "./search_result_container.svelte";
+
+ export let basis: ClientSearchResult;
+</script>
+
+{#if `location_gcs` in basis && basis.location_gcs.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {
+ if (basis.location_gcs.kind === `farm_land`)
+ await route(`/farm/land/view`, [
+ [`id`, basis.location_gcs.id],
+ ]);
+ },
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+ weight: `bold`,
+ key: `compass`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$ls(`common.location`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+
+ {#if basis.location_gcs.kind === `farm_land`}
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$ls(`common.farm_land`)}`}
+ </p>
+ </div>
+ {/if}
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
+ >
+ {#if basis.location_gcs.label}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`}
+ >
+ {`${basis.location_gcs.label}`}
+ </p>
+ {:else}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`}
+ >
+ {`${basis.location_gcs.gc_name}, ${basis.location_gcs.gc_admin1_id}`}
+ </p>
+ {/if}
+ </div>
+ </SearchResultContainer>
+{:else if `nostr_profile` in basis && basis.nostr_profile.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {
+ await route(`/settings/profile`);
+ },
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+ weight: `bold`,
+ key: `user`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$ls(`common.profile`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {#if basis.result_k === `name`}
+ {`${$ls(`common.name`)}`}
+ {:else}
+ {`@todo`}
+ {/if}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
+ >
+ {#if basis.nostr_profile.name}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
+ >
+ {#if basis.result_k === `name`}
+ {`${basis.nostr_profile.name}`}
+ {:else if basis.result_v}
+ {`${basis.result_v}`}
+ {:else}
+ {`@todo`}
+ {/if}
+ </p>
+ {/if}
+ </div>
+ </SearchResultContainer>
+{:else if `nostr_relay` in basis && basis.nostr_relay.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {},
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+ weight: `bold`,
+ key: `user`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$ls(`common.relay`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$ls(`common.url`)}`}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`}
+ >
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
+ >
+ {`${basis.nostr_relay.url}`}
+ </p>
+ </div>
+ </SearchResultContainer>
+{/if}
diff --git a/src/lib/conf.ts b/src/lib/conf.ts
@@ -43,6 +43,9 @@ export const cfg = {
entry_focus: 2000,
load_notify: 3000,
},
+ debounce: {
+ search: 200
+ },
cmd: {
layout_route: `*-route`
},
diff --git a/src/routes/(app)/search/+page.svelte b/src/routes/(app)/search/+page.svelte
@@ -0,0 +1,116 @@
+<script lang="ts">
+ import { db } from "$lib/client";
+ import SearchResultDisplay from "$lib/components/search_result_display.svelte";
+ import { cfg } from "$lib/conf";
+ import type {
+ LocationGcs,
+ NostrProfile,
+ NostrRelay,
+ TradeProduct,
+ } from "@radroots/models";
+ import {
+ catch_err,
+ debounce_input,
+ fmt_id,
+ Glyph,
+ InputElement,
+ LayoutView,
+ ls,
+ NavToolbar,
+ PageHeader,
+ } from "@radroots/svelte-lib";
+ import { SearchService, type SearchServiceResult } from "@radroots/utils";
+ import { onMount } from "svelte";
+
+ type LoadData = {
+ location_gcs: LocationGcs[];
+ nostr_profile: NostrProfile[];
+ nostr_relay: NostrRelay[];
+ trade_product: TradeProduct[];
+ };
+ let ld: LoadData | undefined = undefined;
+
+ let client_search: SearchService | undefined = undefined;
+ let search_val = ``;
+ let search_results: SearchServiceResult[] = [];
+
+ onMount(async () => {
+ try {
+ await init_page();
+ } catch (e) {
+ } finally {
+ }
+ });
+
+ const init_page = async (): Promise<void> => {
+ try {
+ ld = await load_data();
+ if (ld) client_search = new SearchService(ld);
+ } catch (e) {
+ await catch_err(e, `init_page`);
+ }
+ };
+
+ const load_data = async (): Promise<LoadData | undefined> => {
+ try {
+ const location_gcs = await db.location_gcs_get({ list: [`all`] });
+ const nostr_profile = await db.nostr_profile_get({
+ list: [`all`],
+ });
+ const nostr_relay = await db.nostr_relay_get({ list: [`all`] });
+ const trade_product = await db.trade_product_get({
+ list: [`all`],
+ });
+ return {
+ location_gcs:
+ `results` in location_gcs ? location_gcs.results : [],
+ nostr_profile:
+ `results` in nostr_profile ? nostr_profile.results : [],
+ nostr_relay:
+ `results` in nostr_relay ? nostr_relay.results : [],
+ trade_product:
+ `results` in trade_product ? trade_product.results : [],
+ } satisfies LoadData;
+ } catch (e) {
+ await catch_err(e, `load_page`);
+ }
+ };
+
+ const handle_search_input = debounce_input((val: string) => {
+ if (client_search) search_results = client_search.search(val);
+ }, cfg.debounce.search);
+</script>
+
+<LayoutView>
+ <NavToolbar />
+ <PageHeader basis={{ label: `${$ls(`common.search`)}` }}></PageHeader>
+ <div class={`flex flex-col w-full px-4 gap-6 justify-center items-center`}>
+ <div
+ class={`relative flex flex-row w-full justify-center items-center bg-layer-1-surface rounded-2xl`}
+ >
+ <Glyph
+ basis={{
+ classes: `absolute left-4 text-layer-0-glyph-shade`,
+ dim: `sm`,
+ weight: `bold`,
+ key: `magnifying-glass`,
+ }}
+ />
+ <InputElement
+ bind:value={search_val}
+ basis={{
+ id: fmt_id(`search`),
+ sync: true,
+ classes: `pl-12 text-layer-0-glyph`,
+ placeholder: `Enter search query`,
+ callback: async ({ value }) => handle_search_input(value),
+ }}
+ />
+ </div>
+ <div class={`flex flex-col w-full gap-4 justify-center items-center`}>
+ {#each search_results as li (li.id)}
+ <SearchResultDisplay basis={li} />
+ {/each}
+ </div>
+ </div>
+</LayoutView>