web


git clone https://radroots.dev/git/web.git
Log | Files | Refs | Submodules | README | LICENSE

commit 13d415fcafa2bf21749669235fa1146720368297
parent fc1d9f780a4ebd147a8e713e43204f53f0c0e29a
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Wed, 11 Sep 2024 11:46:26 +0000

Edit `/models/trade-product` routes, update model routes, edit nostr routes,  add `trade_product` utils, add/edit lib components and types, add/edit css

Diffstat:
Mpackage.json | 15++++++++-------
Msrc/app.css | 40+++++++++++++++++++---------------------
Msrc/lib/components/button-submit.svelte | 3++-
Asrc/lib/components/layout-trellis-line.svelte | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/components/layout-view.svelte | 6++++--
Msrc/lib/components/map_control_full.svelte | 2+-
Msrc/lib/components/map_full_envelope.svelte | 2+-
Asrc/lib/css/form.css | 23+++++++++++++++++++++++
Msrc/lib/stores.ts | 8+++++---
Msrc/lib/types.ts | 1-
Asrc/lib/utils/trade_product.ts | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/routes/(app)/+layout.svelte | 4++--
Msrc/routes/(app)/+page.svelte | 9+++++----
Msrc/routes/(app)/models/location-gcs/+page.svelte | 173++++++++++++++++++++++++-------------------------------------------------------
Dsrc/routes/(app)/models/trade-offer/+page.svelte | 137-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/trade-offer/add/+page.svelte | 66------------------------------------------------------------------
Msrc/routes/(app)/models/trade-product/+page.svelte | 4++--
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 444++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/routes/(app)/nostr/+page.svelte | 4+---
Asrc/routes/(app)/test/+page.svelte | 17+++++++++++++++++
Msrc/routes/+layout.svelte | 18++++++++++--------
Mtailwind.config.ts | 1+
22 files changed, 548 insertions(+), 601 deletions(-)

diff --git a/package.json b/package.json @@ -5,23 +5,24 @@ "license": "GPLv3", "scripts": { "build": "just build && vite build", - "build:native": "npm run sql:wasm:rm && npm run build", - "build:web": "npm run sql:wasm:cp && npm run build", + "build:native": "npm run build", + "build:web": "npm run build", + "build:sql:wasm": "rsync -u node_modules/sql.js/dist/sql-wasm.wasm static/", "dev": "", - "dev:web": "npm run sql:wasm:cp && vite dev --debug hmr", - "dev:native": "npm run sql:wasm:rm && vite dev --debug hmr", + "dev:web": "vite dev --debug hmr", + "dev:native": "vite dev --debug hmr", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "sql:wasm:cp": "rsync -u node_modules/sql.js/dist/sql-wasm.wasm static/assets", - "sql:wasm:rm": "rm -rf static/assets/sql-wasm.wasm" + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" }, "devDependencies": { "@capacitor/cli": "^6.1.2", "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@types/better-sqlite3": "^7.6.11", "@types/node": "^20.12.7", + "@types/sql.js": "^1.4.9", "autoprefixer": "^10.4.19", "daisyui": "^4.10.0", "postcss": "^8.4.38", diff --git a/src/app.css b/src/app.css @@ -1,3 +1,4 @@ +@import "./lib/css/form.css"; @import "./lib/css/spinner.css"; @import "./lib/css/spinner-6.css"; @import "./lib/css/spinner-12.css"; @@ -13,22 +14,8 @@ @apply h-40 w-72; } - .form-select-e { - @apply border-0 focus:border-0 outline-0 focus:outline-0; - } - - .form-line-e { - @apply pl-4 justify-center items-center; - } - - .form-select-e, - .form-line-e, - .form-line { - @apply flex flex-row h-form_line w-full; - } - - .form-input { - @apply input flex flex-row w-full p-0 border-0 focus:border-0 outline-0 focus:outline-0 placeholder:font-[300] bg-transparent; + .nobg { + @apply bg-transparent; } .form-input-invalid { @@ -49,7 +36,7 @@ } .button-submit { - @apply h-[32px] min-w-[82px] w-fit rounded-xl active:bg-layer-1-surface_a; + @apply h-[34px] min-w-[82px] w-fit rounded-xl active:bg-layer-1-surface_a; } .button-simple { @@ -62,7 +49,6 @@ @apply flex flex-row justify-center items-center font-mono text-sm lowercase transition-all select-none cursor-none; } - .surface-1, .button-simple, .button-submit { @apply bg-layer-1-surface text-layer-2-glyph; @@ -103,15 +89,15 @@ } .active-ring-gray { - @apply active:ring-4 active:ring-slate-300/20; + @apply active:ring-4 active:ring-stone-200/60; } - .tap-active-op { + .active-op { @apply active:opacity-60; } .tap-rise { - @apply active:scale-[101%] group-active:scale-[101%] delay-[150ms] duration-[400ms] ease-in-out transition-all; + @apply active:scale-[102%] group-active:scale-[102%] delay-[150ms] duration-[400ms] ease-in-out transition-all; } .tap-in { @@ -135,4 +121,16 @@ button:focus { outline: none; } + + select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background: transparent; + background-image: none; + } + + select::-ms-expand { + display: none; + } } \ No newline at end of file diff --git a/src/lib/components/button-submit.svelte b/src/lib/components/button-submit.svelte @@ -5,6 +5,7 @@ export let basis: { callback: CallbackPromise; loading?: boolean; + label?: string; }; $: basis = basis; </script> @@ -18,6 +19,6 @@ {#if basis.loading} <Loading basis={{ dim: `xs` }} /> {:else} - {`${$t(`common.submit`, { default: `submit` })}`} + {basis.label || `${$t(`common.submit`, { default: `submit` })}`} {/if} </button> diff --git a/src/lib/components/layout-trellis-line.svelte b/src/lib/components/layout-trellis-line.svelte @@ -0,0 +1,42 @@ +<script lang="ts"> + import { + fmt_cl, + type ICb, + type IClOpt, + type ILabel, + } from "@radroots/svelte-lib"; + + export let basis: ILabel & { + notify?: IClOpt & ICb & ILabel; + }; + $: basis = basis; +</script> + +<div class={`flex flex-col w-full gap-1 justify-start items-start`}> + <div class={`flex flex-row w-full px-2 gap-2 justify-start items-center`}> + <p + class={`${fmt_cl(basis.label.classes)} font-sans font-[400] uppercase text-layer-2-glyph text-sm`} + > + {basis.label.value} + </p> + {#if basis.notify} + <div + class={`${fmt_cl(basis.notify.classes)} flex flex-row justify-start items-center fade-in transition-all`} + > + <button + class={`flex flex-row justify-center items-center`} + on:click={async () => { + await basis.notify?.callback(); + }} + > + <p + class={`${fmt_cl(basis.notify.label.classes)} font-sans font-[400] uppercase text-layer-2-glyph text-sm`} + > + {basis.notify.label.value} + </p> + </button> + </div> + {/if} + </div> + <slot /> +</div> diff --git a/src/lib/components/layout-view.svelte b/src/lib/components/layout-view.svelte @@ -2,9 +2,11 @@ import { app_tabs_blur, app_tabs_visible } from "$lib/stores"; import { type AppLayoutKey, + type IClOpt, app_layout, app_nav_blur, app_nav_visible, + fmt_cl, } from "@radroots/svelte-lib"; import { onDestroy, onMount } from "svelte"; @@ -13,7 +15,7 @@ lg: "pt-16", }; - export let basis: { fade?: boolean } | undefined = undefined; + export let basis: (IClOpt & { fade?: boolean }) | undefined = undefined; $: basis = basis; let el: HTMLElement | null; @@ -51,7 +53,7 @@ <div bind:this={el} - class={`absolute top-0 left-0 flex flex-col h-[100vh] w-full overflow-y-scroll scroll-hide ${classes_nav} ${classes_tabs} ${classes_fade}`} + class={`${fmt_cl(basis?.classes)} absolute top-0 left-0 flex flex-col h-[100vh] w-full overflow-y-scroll scroll-hide ${classes_nav} ${classes_tabs} ${classes_fade}`} > <slot /> </div> diff --git a/src/lib/components/map_control_full.svelte b/src/lib/components/map_control_full.svelte @@ -10,7 +10,7 @@ class={`z-10 absolute top-dim_map_offset_top_${$app_layout} left-6 flex flex-col w-full gap-8 justify-start items-start`} > <button - class={`surface-1 flex flex-row h-8 w-8 justify-center items-center rounded-2xl`} + class={`flex flex-row h-8 w-8 justify-center items-center rounded-2xl bg-layer-1-surface`} on:click={async () => { await goto(`/`); }} diff --git a/src/lib/components/map_full_envelope.svelte b/src/lib/components/map_full_envelope.svelte @@ -10,7 +10,7 @@ class={`z-10 absolute top-dim_map_offset_top_${$app_layout} left-6 flex flex-col w-full gap-8 justify-start items-start`} > <button - class={`surface-1 flex flex-row h-8 w-8 justify-center items-center rounded-2xl`} + class={`flex flex-row h-8 w-8 justify-center items-center rounded-2xl bg-layer-1-surface`} on:click={async () => { await goto(`/`); }} diff --git a/src/lib/css/form.css b/src/lib/css/form.css @@ -0,0 +1,22 @@ +@layer components { + .form-invalid-layer-1 { + @apply bg-red-300; + } + + .form-line-select { + @apply h-full w-full rounded-2xl; + } + + .form-line-wrap { + @apply flex flex-row h-form_line w-full rounded-2xl; + } + + .form-input { + @apply input w-full p-0 pl-2 placeholder:font-[300]; + } + + .form-select, + .form-input { + @apply flex flex-row justify-center items-center border-0 focus:border-0 outline-0 focus:outline-0 bg-transparent transition-all; + } +} +\ No newline at end of file diff --git a/src/lib/stores.ts b/src/lib/stores.ts @@ -3,7 +3,7 @@ import { type NumberTuple } from "@radroots/utils"; import { writable } from "svelte/store"; export const app_thc = writable<ColorMode>(`light`); -export const app_thm = writable<ThemeKey>(`os`); +export const app_th = writable<ThemeKey>(`earth`); export const app_config = writable<boolean>(false); export const app_render = writable<boolean>(false); @@ -17,7 +17,9 @@ export const app_nostr_key = writable<string>(``); export const app_pwa_polyfills = writable<boolean>(false); export const app_sqlite = writable<boolean>(false); -export const app_init_route = writable<string>(``); - export const map_full_center = writable<NumberTuple>([0, 0]); export const map_full_zoom = writable<number>(4); + +export const carousel_active = writable<boolean>(false); +export const carousel_index = writable<number>(0); +export const carousel_index_max = writable<number>(0); diff --git a/src/lib/types.ts b/src/lib/types.ts @@ -4,4 +4,3 @@ export type NavParamPrev = NavigationPreviousParam[]; export type NavParamTitle = { label: string; }; - diff --git a/src/lib/utils/trade_product.ts b/src/lib/utils/trade_product.ts @@ -0,0 +1,129 @@ +import { goto } from "$app/navigation"; +import { lc } from "$lib/client"; +import { parse_trade_product_form_keys, trade_product_form_fields, trade_product_form_vals, type LocationGcs, type TradeProductFormFields } from "@radroots/client"; +import { kv } from "@radroots/svelte-lib"; + +export const trade_product_submit_preview = async (kv_pref: string): Promise<{ + trade_product_fields: TradeProductFormFields; + location_gcs: LocationGcs +} | undefined> => { + try { + const location_gcs_id = await kv.get(`${kv_pref}-location_gcs_id`) + if (!location_gcs_id || typeof location_gcs_id !== `string`) { + await lc.dialog.alert(`The product location is missing.`); + return; + } + + const location_gcs_res = await lc.db.location_gcs_get({ + id: location_gcs_id, + }); + + if (typeof location_gcs_res === `string`) { + await lc.dialog.alert( + `There was an error finding the selected location`, + ); + await goto(`/`); + return; + } + + const trade_product_fields = { + ...trade_product_form_vals + } + + for (const [k, field] of Object.entries( + trade_product_form_fields, + )) { + const field_k = parse_trade_product_form_keys(k); + if (!field_k) continue; + const field_id = `${kv_pref}-${field_k}` + let field_val = ``; + field_val = await kv.get(field_id); + if (field_val) trade_product_fields[field_k] = field_val; + + //@todo add validation + } + + trade_product_fields.price_qty_unit = trade_product_fields.qty_unit; + + return { + trade_product_fields, + location_gcs: location_gcs_res[0] + }; + } catch (e) { + console.log(`(error) trade_product_submit_preview `, e); + } +}; + +export const trade_product_kv_init = async (kv_pref: string): Promise<void> => { + try { + for (const k of Object.keys( + trade_product_form_fields, + )) { + const field_k = parse_trade_product_form_keys(k); + if (!field_k) continue; + const field_id = `${kv_pref}-${field_k}` + await kv.delete(field_id); + } + + } catch (e) { + console.log(`(error) trade_product_kv_init `, e); + } +}; + +/* + const vals = trade_product_form_vals; + console.log(`vals 0!`, vals); + for (const [k, field] of Object.entries( + trade_product_form_fields, + )) { + const field_k = parse_trade_product_form_keys(k); + if (!field_k) continue; + const field_id = fmt_id(field_k); + let field_val = ``; + if (field_k === `price_qty_unit`) { + field_val = await $kv.get(`price_unit`); + } else { + field_val = await $kv.get(field_id); + } + + /* + + if (field_k === `key`) { + field_val = sel_trade_product_key; + } else + + if ( + (!field.optional && !field.validation.test(field_val)) || + (field.optional && + field_val && + !field.validation.test(field_val)) + ) { + loading = false; + await lc.dialog.alert( + `Invalid product ${field_k} value.`, + ); + return; + } + vals[field_k] = field_val; + } + + console.log(JSON.stringify(vals, null, 4), `vals`); + /* + const db_add = await lc.db.trade_product_add(vals); + if (typeof db_add !== `string` && !Array.isArray(db_add)) { + const { id: trade_product_id } = db_add; + const db_rel = await lc.db.set_trade_product_location({ + trade_product_id, + location_gcs_id: location_gcs_res[0].id, + }); + if (typeof db_rel === `string`) { + // @todo + } + await goto(`/models/trade-product`); + } else { + // @todo + await lc.dialog.alert( + `There was an error: ${db_add.toString()}`, + ); + } +*/ +\ No newline at end of file diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -25,14 +25,14 @@ { icon: `compass`, callback: async (tab_i) => { - await goto(`/models/trade-offer`); + await goto(`/models/trade-product/add`); }, }, { icon: `network`, callback: async (tab_i) => { app_tab_active.set(tab_i); - await goto("/nostr"); + await goto("/test"); }, }, { diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -38,7 +38,7 @@ }, { route: `/models/trade-product`, - label: `Post Goods`, + label: `Products`, key: `handbag-simple`, weight: `fill`, }, @@ -57,7 +57,7 @@ </p> </div> <button - class={`flex flex-row w-full h-[34px] px-4 gap-2 justify-start items-center surface-1 rounded-xl active:bg-layer-1-surface_a transition-all`} + class={`flex flex-row w-full h-[34px] px-4 gap-2 justify-start items-center rounded-xl bg-layer-1-surface active:bg-layer-1-surface_a transition-all`} on:click={async () => { tmp_show_envelope = !tmp_show_envelope; }} @@ -67,9 +67,10 @@ key: `magnifying-glass`, dim: `sm-`, weight: `bold`, + classes: `text-layer-2-glyph/90`, }} /> - <p class={`font-sans font-[500] text-layer-2-glyph/70`}> + <p class={`font-sans font-[500] text-layer-2-glyph/80`}> {`Search`} </p> </button> @@ -78,7 +79,7 @@ > {#each buttons as btn} <button - class={`surface-1 col-span-6 flex flex-col h-20 py-2 px-3 justify-between rounded-2xl font-[500] text-lg font-mono tap-active-op transition-all`} + class={`col-span-6 flex flex-col h-20 py-2 px-3 justify-between rounded-2xl bg-layer-1-surface text-layer-2-glyph font-[500] text-lg font-mono tap-rise active-ring-gray transition-all`} on:click={async () => { await goto(btn.route); }} diff --git a/src/routes/(app)/models/location-gcs/+page.svelte b/src/routes/(app)/models/location-gcs/+page.svelte @@ -2,12 +2,10 @@ import { lc } from "$lib/client"; import LayoutTrellis from "$lib/components/layout-trellis.svelte"; import LayoutView from "$lib/components/layout-view.svelte"; - import { _cf } from "$lib/conf"; - import { app_tabs_visible, app_thc } from "$lib/stores"; + import { app_tabs_visible } from "$lib/stores"; import { location_gcs_add } from "$lib/utils/models"; import { type LocationGcs } from "@radroots/client"; - import { Fill, fmt_geo_direction, Nav } from "@radroots/svelte-lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; + import { Nav, Trellis } from "@radroots/svelte-lib"; import { onMount } from "svelte"; let models_list: LocationGcs[] = []; @@ -41,123 +39,56 @@ <LayoutTrellis> {#if models_list.length} {#each models_list as li} - <button - class={`surface-1 flex flex-row w-full justify-between items-center rounded-xl overflow-hidden tap-active-op active-ring-blue`} - > - <div - class={`flex flex-row h-full flex-grow justify-start items-start`} - > - <div - class={`flex flex-col h-full w-8 justify-start items-center`} - > - <Fill /> - </div> - <div - class={`flex flex-col w-full gap-[6px] justify-start items-center`} - > - <div - class={`flex flex-row h-10 w-full pr-2 justify-between items-center border-b-line border-b-layer-1-surface-edge`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm`} - > - {`${li.label}`} - </p> - </div> - <div - class={`flex flex-col w-full pr-2 gap-1 justify-between items-center`} - > - <div - class={`flex flex-row w-full justify-between items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm max-w-[90px] truncate`} - > - {`lat: `} - </p> - <div - class={`flex flex-row gap-1 justify-end items-center`} - > - <div - class={`flex flex-row w-[86px] justify-end items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm max-w-[90px] truncate`} - > - {`${Math.abs(li.lat)}`} - </p> - </div> - <div - class={`flex flex-row w-6 justify-start items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm max-w-[90px] truncate`} - > - {fmt_geo_direction({ - lat: li.lat, - })} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm max-w-[90px] truncate`} - > - {`lng:`} - </p> - <div - class={`flex flex-row gap-1 justify-end items-center`} - > - <div - class={`flex flex-row w-[86px] justify-end items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm truncate`} - > - {`${Math.abs(li.lng)}`} - </p> - </div> - <div - class={`flex flex-row w-6 justify-start items-center`} - > - <p - class={`font-sans font-[300] text-layer-1-glyph text-sm truncate`} - > - {fmt_geo_direction({ - lng: li.lng, - })} - </p> - </div> - </div> - </div> - </div> - </div> - </div> - <MapLibre - center={[li.lng, li.lat]} - zoom={10} - class={`map-card`} - style={_cf.map.styles.base[$app_thc]} - interactive={false} - > - <Marker lngLat={[li.lng, li.lat]}> - <div class="flex flex-row p-1"> - <div - class={`flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} - > - <div - class={`flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} - > - <Fill /> - </div> - </div> - </div> - </Marker> - </MapLibre> - </button> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Your Locations`, + }, + list: [ + { + hide_active: true, + touch: { + label: { + left: [ + { + value: `Location:`, + classes: `capitalize`, + }, + ], + right: [ + { + value: li.label, + }, + ], + }, + callback: async () => {}, + }, + }, + { + hide_active: true, + touch: { + label: { + left: [ + { + value: `Coordinates:`, + classes: `capitalize`, + }, + ], + right: [ + { + value: `${li.lat.toFixed(3)} ${li.lng.toFixed(3)}`, + }, + ], + }, + callback: async () => {}, + }, + }, + ], + }, + }} + /> {/each} {:else if !loading_models} <div diff --git a/src/routes/(app)/models/trade-offer/+page.svelte b/src/routes/(app)/models/trade-offer/+page.svelte @@ -1,137 +0,0 @@ -<script lang="ts"> - import { goto } from "$app/navigation"; - import { lc } from "$lib/client"; - import LayoutTrellis from "$lib/components/layout-trellis.svelte"; - import LayoutView from "$lib/components/layout-view.svelte"; - import { app_tabs_visible } from "$lib/stores"; - import { type TradeOffer } from "@radroots/client"; - import { Nav, Trellis } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - let models_list: TradeOffer[] = []; - let loading_models = false; - - onMount(async () => { - try { - app_tabs_visible.set(false); - await fetch_models(); - } catch (e) { - } finally { - } - }); - - const fetch_models = async (): Promise<void> => { - try { - loading_models = true; - const res = await lc.db.trade_offer_get({ - list: [`all`], - }); - if (typeof res !== `string`) models_list = res; - } catch (e) { - console.log(`(error) fetch_models `, e); - } finally { - loading_models = false; - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - {#if models_list.length} - {#each models_list as li, li_i} - <Trellis - basis={{ - args: { - layer: 1, - title: - li_i === 0 - ? { - value: `Trade Offers`, - } - : undefined, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - value: `Price:`, - classes: `capitalize`, - }, - ], - right: [ - { - value: `${li.price_amt} ${li.price_currency}`, - classes: `capitalize`, - }, - ], - }, - callback: async () => {}, - }, - }, - { - hide_active: true, - touch: { - label: { - left: [ - { - value: `Quantity:`, - classes: `capitalize`, - }, - ], - right: [ - { - value: `${li.quantity_amt} ${li.quantity_unit}`, - classes: `capitalize`, - }, - ], - }, - callback: async () => {}, - }, - }, - ], - }, - }} - /> - {/each} - {:else if !loading_models} - <div - class={`flex flex-col w-full justify-center items-center px-4 gap-3`} - > - <p class={`font-sans font-[400] text-layer-2-glyph`}> - {`No items to display.`} - </p> - <a href={`/models/trade-offer/add`}> - <p - class={`font-sans font-[400] text-layer-2-glyph-hl text-sm`} - > - {`Click to add a new product`} - </p> - </a> - </div> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `Home`, - route: `/`, - }, - title: { - label: `Products`, - }, - option: models_list.length - ? { - label: { - value: `Add`, - classes: `tap-color`, - }, - callback: async () => { - await goto(`/models/trade-offer/add`); - }, - } - : undefined, - }} -/> diff --git a/src/routes/(app)/models/trade-offer/add/+page.svelte b/src/routes/(app)/models/trade-offer/add/+page.svelte @@ -1,66 +0,0 @@ -<script lang="ts"> - import LayoutTrellis from "$lib/components/layout-trellis.svelte"; - import LayoutView from "$lib/components/layout-view.svelte"; - import { trade_offer_form_fields } from "@radroots/client"; - import { fmt_id, InputForm, Nav } from "@radroots/svelte-lib"; -</script> - -<LayoutView> - <LayoutTrellis> - <div - class={`flex flex-col w-full px-4 gap-3 justify-center items-center`} - > - {#each Object.entries(trade_offer_form_fields) as [field_k, field], field_i} - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full px-2 justify-start items-center`} - > - <p - class={`font-sans font-[400] uppercase text-layer-2-glyph text-sm`} - > - {`Product - ${field_k}`} - </p> - </div> - <div - class={`form-line-e bg-layer-1-surface text-layer-1-glyph/70 rounded-xl`} - > - <InputForm - basis={{ - id: fmt_id(field_k), - layer: 1, - sync: true, - field: { - charset: field.charset, - validate: field.validation, - }, - }} - /> - </div> - </div> - {/each} - </div> - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `Offers`, - route: `/models/trade-offer`, - }, - title: { - label: `Add New`, - }, - option: { - glyph: { - key: `info`, - dim: `md`, - classes: `text-layer-1-glyph-hl tap-scale`, - }, - callback: async () => { - alert(`Todo!`); - }, - }, - }} -/> diff --git a/src/routes/(app)/models/trade-product/+page.svelte b/src/routes/(app)/models/trade-product/+page.svelte @@ -117,13 +117,13 @@ label: { left: [ { - value: `Varietal:`, + value: `Process:`, classes: `capitalize`, }, ], right: [ { - value: li.varietal, + value: li.process, }, ], }, diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,301 +1,303 @@ <script lang="ts"> - import { goto } from "$app/navigation"; - import { lc } from "$lib/client"; import ButtonSubmit from "$lib/components/button-submit.svelte"; + import LayoutTrellisLine from "$lib/components/layout-trellis-line.svelte"; import LayoutTrellis from "$lib/components/layout-trellis.svelte"; import LayoutView from "$lib/components/layout-view.svelte"; - import { location_gcs_add } from "$lib/utils/models"; import { - parse_trade_product_form_keys, - trade_product_form_fields, - trade_product_form_vals, - type LocationGcs, - } from "@radroots/client"; + trade_product_kv_init, + trade_product_submit_preview, + } from "$lib/utils/trade_product"; + import { mass_units, trade_product_form_fields } from "@radroots/client"; import { - fmt_capitalize, fmt_id, InputForm, + InputSelect, kv, - Loading, Nav, + t, } from "@radroots/svelte-lib"; - import { trade_keys } from "@radroots/utils"; - + import { + fmt_trade_quantity_val, + parse_trade_key, + trade_keys, + trade_quantities, + type TradeKey, + } from "@radroots/utils"; import { onMount } from "svelte"; - const texts_key = { - opt_lcs_add: `location_gcs-add-current`, - }; + const trade_key_default: TradeKey = `coffee`; - const texts = { - 1: `No locations saved`, - 2: `Add new location`, - }; + let loading_submit = false; - const texts_kv: Record<string, string> = { - key: `Kind`, - }; + let sel_key = ``; + let show_sel_key_other = false; - let loading = false; - let loading_location = false; + let sel_price_qty_amt = ``; + let sel_price_qty_unit = ``; - let ls_model_location_gcs: LocationGcs[] = []; - let sel_model_location_gcs_id = ``; - let sel_model_trade_good_key = ``; + let show_sel_price_qty_amt_other = false; - $: { - if (ls_model_location_gcs.length && !sel_model_location_gcs_id) - sel_model_location_gcs_id = ls_model_location_gcs[0].id; - } + $: sel_key_parsed = parse_trade_key(sel_key); + $: ls_trade_product_quantities = sel_key_parsed + ? trade_quantities[sel_key_parsed] + : trade_quantities[trade_key_default]; + + $: sel_price_qty_amt = sel_key_parsed + ? fmt_trade_quantity_val(trade_quantities[sel_key_parsed][0]) + : fmt_trade_quantity_val(trade_quantities[trade_key_default][0]); onMount(async () => { try { - sel_model_trade_good_key = trade_keys[0]; - await fetch_models_location_gcs(); + sel_key = trade_key_default; + /* + sel_key = trade_keys[0]; + sel_price_qty_amt = sel_key_parsed + ? fmt_trade_quantity_val(trade_quantities[sel_key_parsed][0]) + : ``; + */ } catch (e) { } finally { } }); - const fetch_models_location_gcs = async (): Promise<void> => { + onMount(async () => { try { - const res = await lc.db.location_gcs_get({ - list: [`all`], - }); - if (typeof res !== `string`) ls_model_location_gcs = res; } catch (e) { - console.log(`(error) fetch_models_location_gcs `, e); + } finally { } - }; + }); - const add_model_location_gcs = async (): Promise<void> => { + const toggle_show_key_other = async (visible: boolean): Promise<void> => { try { - loading_location = true; - await location_gcs_add(); - await fetch_models_location_gcs(); + show_sel_key_other = visible; + if (visible) { + await kv.set(fmt_id(`key`), ``); + } else { + sel_key = trade_keys[0]; + } } catch (e) { - console.log(`(error) add_model_location_gcs `, e); - } finally { - loading_location = false; + console.log(`(error) toggle_show_key_other `, e); } }; - const submit = async (): Promise<void> => { + const toggle_show_price_qty_amt_other = async ( + visible: boolean, + ): Promise<void> => { try { - loading = true; - - if (!sel_model_location_gcs_id) { - await lc.dialog.alert(`The product location is missing.`); - return; + show_sel_price_qty_amt_other = visible; + if (visible) { + sel_price_qty_unit = mass_units[0]; + await kv.set(fmt_id(`price_qty_amt`), ``); + } else { + sel_price_qty_amt = sel_key_parsed + ? fmt_trade_quantity_val( + trade_quantities[sel_key_parsed][0], + ) + : ``; } + } catch (e) { + console.log(`(error) toggle_show_price_qty_amt_other `, e); + } + }; - const db_location_gcs = await lc.db.location_gcs_get({ - id: sel_model_location_gcs_id, - }); - - if ( - typeof db_location_gcs === `string` || - db_location_gcs.length !== 1 - ) { - await lc.dialog.alert( - `There was an error finding the selected location`, - ); - await goto(`/`); - return; - } + const submit = async (): Promise<void> => { + try { + loading_submit = true; - const vals = trade_product_form_vals; - for (const [k, field] of Object.entries( - trade_product_form_fields, - )) { - const field_k = parse_trade_product_form_keys(k); - if (!field_k) continue; - const field_id = fmt_id(field_k); - const field_val = - field_k === `key` - ? sel_model_trade_good_key - : await $kv.get(field_id); + const res = await trade_product_submit_preview(fmt_id()); - if ( - (!field.optional && !field.validation.test(field_val)) || - (field.optional && - field_val && - !field.validation.test(field_val)) - ) { - loading = false; - await lc.dialog.alert( - `Invalid product ${texts_kv[field_k]?.toLowerCase() || field_k} value.`, - ); - return; - } - vals[field_k] = field_val; - } - const db_add = await lc.db.trade_product_add(vals); - if (typeof db_add !== `string` && !Array.isArray(db_add)) { - const { id: trade_product_id } = db_add; - const db_rel = await lc.db.set_trade_product_location({ - trade_product_id, - location_gcs_id: db_location_gcs[0].id, - }); - if (typeof db_rel === `string`) { - // @todo - } - await goto(`/models/trade-product`); - } else { - // @todo - await lc.dialog.alert( - `There was an error: ${db_add.toString()}`, - ); - } + console.log(JSON.stringify(res, null, 4), `res`); } catch (e) { console.log(`(error) submit `, e); } finally { - loading = false; + loading_submit = false; } }; </script> <LayoutView> <LayoutTrellis> - <div - class={`flex flex-col w-full px-4 gap-3 justify-center items-center`} + <LayoutTrellisLine + basis={{ + label: { + value: `Product`, + }, + notify: show_sel_key_other + ? { + classes: `w-full justify-end`, + label: { + classes: `text-xs font-[600]`, + value: `Show Options`, + }, + callback: async () => { + await toggle_show_key_other(false); + }, + } + : undefined, + }} > - {#each Object.entries(trade_product_form_fields).filter((i) => i[0] !== `key`) as [field_k, field], field_i} - {#if field_i === 0} - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full px-2 justify-start items-center`} - > - <p - class={`font-sans font-[400] uppercase text-layer-2-glyph text-sm`} - > - {`Product - Kind`} - </p> - </div> - <select - class={`form-select-e w-full bg-layer-1-surface rounded-xl text-layer-1-glyph/70`} - bind:value={sel_model_trade_good_key} - on:change={async ({ currentTarget: el }) => { - const val = el.value; - console.log(`val `, val); - }} - > - {#each trade_keys as li} - <option value={li}> - {fmt_capitalize(li)} - </option> - {/each} - </select> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full px-2 justify-start items-center`} - > - <p - class={`font-sans font-[400] uppercase text-layer-2-glyph text-sm`} - > - {`Product - Location`} - </p> - </div> - {#if loading_location} - <div - class={`form-line surface-1 flex flex-row justify-center items-center rounded-xl`} - > - <Loading basis={{ dim: `xs-` }} /> - </div> - {:else} - <select - class={`form-select-e w-full bg-layer-1-surface rounded-xl text-layer-1-glyph/70`} - bind:value={sel_model_location_gcs_id} - on:change={async ({ currentTarget: el }) => { - const val = el.value; - if (val === texts_key.opt_lcs_add) { - sel_model_location_gcs_id = ``; - await add_model_location_gcs(); - } - }} - > - {#if ls_model_location_gcs.length} - {#each ls_model_location_gcs as li} - <option value={li.id}> - {`${li.label}`} - </option> - {/each} - {:else} - <option disabled selected={true}> - {texts[1]} - </option> - {/if} - <option value={texts_key.opt_lcs_add}> - {texts[2]} - </option> - </select> - {/if} - </div> - {/if} + {#if show_sel_key_other} <div - class={`flex flex-col w-full gap-1 justify-start items-start`} + class={`relative flex flex-row w-full justify-center items-center`} > + <InputForm + basis={{ + id: fmt_id(`key`), + sync: true, + placeholder: `Enter the product name`, + field: { + charset: trade_product_form_fields.key.charset, + validate: + trade_product_form_fields.key.validation, + validate_keypress: true, + }, + }} + /> + </div> + {:else} + <InputSelect + bind:value={sel_key} + basis={{ + id: fmt_id(`key`), + sync: true, + options: [ + ...trade_keys.map((i) => ({ + value: i, + label: `${$t(`trade.product.key.${i}`, { default: i })}`, + })), + { + value: `other`, + label: `${$t(`common.other`)}`, + }, + ], + callback: async (val) => { + if (val === `other`) { + await toggle_show_key_other(true); + } + }, + }} + /> + {/if} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `Quantity`, + }, + notify: show_sel_price_qty_amt_other + ? { + classes: `w-full justify-end`, + label: { + classes: `text-xs font-[600]`, + value: `Show Options`, + }, + callback: async () => { + await toggle_show_price_qty_amt_other(false); + }, + } + : undefined, + }} + > + <div + class={`relative flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph/70 rounded-2xl transition-all`} + > + {#if show_sel_price_qty_amt_other} <div - class={`flex flex-row w-full px-2 justify-start items-center`} - > - <p - class={`font-sans font-[400] uppercase text-layer-2-glyph text-sm`} - > - {`Product - ${texts_kv[field_k] || field_k}`} - </p> - </div> - <div - class={`form-line-e bg-layer-1-surface text-layer-1-glyph/70 rounded-xl`} + class={`relative flex flex-row w-full justify-center items-center`} > <InputForm basis={{ - id: fmt_id(field_k), - layer: 1, + id: fmt_id(`price_qty_amt`), + layer: false, sync: true, + placeholder: `Enter number of ${$t(`trade.quantity.mass_unit.${sel_price_qty_unit}_ab`, { default: sel_price_qty_unit })} per order`, field: { - charset: field.charset, - validate: field.validation, + charset: + trade_product_form_fields.price_qty_amt + .charset, + validate: + trade_product_form_fields.price_qty_amt + .validation, + validate_keypress: true, }, }} /> + <div + class={`absolute top-0 right-0 flex flex-row h-full gap-2 justify-center items-center`} + > + <InputSelect + bind:value={sel_price_qty_unit} + basis={{ + id: fmt_id(`price_qty_unit`), + classes: `w-16 text-layer-1-glyph/70 font-[500]`, + layer: false, + sync: true, + options: mass_units.map((i) => ({ + value: i, + label: `${$t(`trade.quantity.mass_unit.${i}_ab`, { default: i })}`, + })), + }} + /> + </div> </div> - </div> - {/each} - <div class={`flex flex-row w-full pt-4 justify-end items-center`}> - <ButtonSubmit - basis={{ - callback: async () => { - await submit(); - }, - loading, - }} - /> + {:else} + <InputSelect + bind:value={sel_price_qty_amt} + basis={{ + id: fmt_id(`price_qty_tup`), + sync: true, + options: [ + ...ls_trade_product_quantities.map((i) => ({ + value: `${i.qty_amt}-${i.qty_unit}`, + label: `${i.qty_amt} ${$t(`trade.quantity.mass_unit.${i.qty_unit}_ab`, { default: i.qty_unit })}${i.label ? ` ${i.label}` : ``}`, + })), + { + value: `other`, + label: `${$t(`common.other`)}`, + }, + ], + callback: async (val) => { + if (val === `other`) { + await toggle_show_price_qty_amt_other(true); + } + }, + }} + /> + {/if} </div> + </LayoutTrellisLine> + <div class={`flex flex-row w-full pt-4 justify-end items-center`}> + <ButtonSubmit + basis={{ + loading: loading_submit, + label: `preview`, + callback: async () => { + await submit(); + }, + }} + /> </div> </LayoutTrellis> </LayoutView> <Nav basis={{ prev: { - label: `Products`, + label: `Back`, route: `/models/trade-product`, + callback: async () => { + await trade_product_kv_init(fmt_id()); + }, }, title: { - label: `Add New`, + label: `Add Product`, }, option: { - glyph: { - key: `info`, - dim: `md`, + label: { + value: `Preview`, classes: `text-layer-1-glyph-hl tap-scale`, }, callback: async () => { - alert(`Todo!`); + alert(`@todo!`); }, }, }} diff --git a/src/routes/(app)/nostr/+page.svelte b/src/routes/(app)/nostr/+page.svelte @@ -16,12 +16,12 @@ }, list: [ { - hide_active: true, touch: { label: { left: [ { value: `View Your Keys`, + classes: `font-[300]`, }, ], }, @@ -36,7 +36,6 @@ }, }, { - hide_active: true, touch: { label: { left: [ @@ -56,7 +55,6 @@ }, }, { - hide_active: true, touch: { label: { left: [ diff --git a/src/routes/(app)/test/+page.svelte b/src/routes/(app)/test/+page.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import LayoutView from "$lib/components/layout-view.svelte"; + import { Nav } from "@radroots/svelte-lib"; +</script> + +<LayoutView basis={{ classes: `px-4 gap-8` }}>test</LayoutView> +<Nav + basis={{ + prev: { + label: `Home`, + route: `/`, + }, + title: { + label: `Test`, + }, + }} +/> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -9,18 +9,18 @@ import { _cf } from "$lib/conf"; import { app_config, - app_init_route, app_nostr_key, app_pwa_polyfills, app_render, app_sqlite, + app_th, app_thc, - app_thm, app_win, } from "$lib/stores"; import { app_layout, CssStatic, + kv, ndk, ndk_setup_privkey, ndk_user, @@ -71,13 +71,13 @@ app_thc.subscribe((app_thc) => { const color_mode = parse_color_mode(app_thc); - theme_set(parse_theme_key($app_thm), color_mode); + theme_set(parse_theme_key($app_th), color_mode); lc.window.status_style(color_mode); }); - app_thm.subscribe((app_thm) => { + app_th.subscribe((app_th) => { const color_mode = parse_color_mode($app_thc); - theme_set(parse_theme_key(app_thm), color_mode); + theme_set(parse_theme_key(app_th), color_mode); lc.window.status_style(color_mode); }); @@ -147,9 +147,10 @@ console.log(`app_render `, app_render); if (!app_render) return; let init_route = `/`; - if ($app_init_route) { - init_route = $app_init_route; - app_init_route.set(`/`); + const app_init_route = await kv.get(`app-init-route`); + if (app_init_route) { + init_route = app_init_route; + await kv.delete(`app-init-route`); } console.log(`init_route `, init_route); await goto(init_route); @@ -174,3 +175,4 @@ <div class="hidden h-nav_base pt-h_nav_base pb-h_nav_base h-nav_lg pt-h_nav_lg pb-h_nav_lg h-tabs_base pt-h_tabs_base pb-h_tabs_base h-tabs_lg pt-h_tabs_lg pb-h_tabs_lg top-dim_map_offset_top_base top-dim_map_offset_top_lg" ></div> +<div class="hidden border-layer-1-surface-edge/40"></div> diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -102,6 +102,7 @@ const config: Config = { daisyui: { themes: [ themes.theme_earth_light, + themes.theme_earth_dark, themes.theme_os_dark, themes.theme_os_light, ],