web


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

commit a0dab68d304c89bf20aa82726c34b52cd5fbf134
parent 2d179cd8c530d8411d2f64fb36e20349d4612b3e
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sun,  6 Oct 2024 10:28:12 +0000

Edit `/models/trade-product/add` adding page views and input fields for updated `trade_product` model, edit root layout adding nostr sync, edit models nostr profile routes, edit models nostr relay routes, edit nostr routes, add nostr utils, edit trade product utils, edit app.css imports, add styles, update package.json dependencies

Diffstat:
Mpackage.json | 17++++++++---------
Msrc/app.css | 4++--
Msrc/lib/stores.ts | 4----
Msrc/lib/utils/index.ts | 3+--
Asrc/lib/utils/nostr.ts | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/utils/trade_product.ts | 7++++---
Msrc/routes/(app)/+layout.svelte | 47+++++++++++++++++++++++++++++++++++------------
Msrc/routes/(app)/+page.svelte | 6++----
Msrc/routes/(app)/models/nostr-profile/view/+page.svelte | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/routes/(app)/models/nostr-relay/+page.svelte | 4++--
Msrc/routes/(app)/models/nostr-relay/view/+page.svelte | 21++++++++++++++++++++-
Msrc/routes/(app)/models/trade-product/+page.svelte | 2+-
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 1422++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Dsrc/routes/(app)/models/trade-product/add/preview/+page.svelte | 469-------------------------------------------------------------------------------
Msrc/routes/(app)/nostr/notes/+page.svelte | 16++++++----------
Msrc/routes/(app)/nostr/notes/post/+page.svelte | 2+-
Msrc/routes/(app)/nostr/profile/+page.svelte | 2+-
Msrc/routes/(app)/test/+page.svelte | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/routes/(conf)/init/+page.svelte | 34++++++++++------------------------
Msrc/routes/+layout.svelte | 43++++++++++++++++++++++++++++---------------
Mtailwind.config.ts | 5++++-
21 files changed, 1434 insertions(+), 924 deletions(-)

diff --git a/package.json b/package.json @@ -6,14 +6,14 @@ "scripts": { "build:dev": "vite build --mode development", "build:prod": "vite build --mode production", - "prepare:native": "npm run gen && npm run sql:rm", - "prepare:web": "npm run gen && npm run sql:cp", + "prepare:native": "npm run gen", + "prepare:web": "npm run gen", "serve:dev": "vite dev --mode development --debug hmr", "serve:prod": "vite dev --mode production", "dev": "", "gen": "", - "sql:cp": "rsync -u node_modules/sql.js/dist/sql-wasm.wasm static/assets", - "sql:rm": "rm -rf static/assets/sql-wasm.wasm", + "sql:cp": "rsync -u node_modules/sql.js/dist/sql-wasm.wasm static", + "sql:rm": "rm -rf static/sql-wasm.wasm", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" @@ -23,9 +23,7 @@ "@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", @@ -51,7 +49,7 @@ "@capacitor/share": "^6.0.0", "@capacitor/splash-screen": "^6.0.0", "@capacitor/status-bar": "^6.0.0", - "@ionic/pwa-elements": "^3.3.0", + "@ionic/pwa-elements": "^3.2.2", "@nostr-dev-kit/ndk": "^2.10.0", "@nostr-dev-kit/ndk-cache-dexie": "^2.5.1", "@nostr-dev-kit/ndk-svelte": "^2.2.18", @@ -65,9 +63,10 @@ "@radroots/svelte-maplibre": "workspace:*", "@radroots/theme": "workspace:*", "@radroots/utils": "workspace:*", + "jeep-sqlite": "2.7.1", + "loader": "link:@ionic/pwa-elements/loader", "maplibre-gl": "^4.0.0", "pmtiles": "^3.0.3", - "protomaps-themes-base": "^3.1.0", - "sql.js": "^1.11.0" + "protomaps-themes-base": "^3.1.0" } } \ No newline at end of file diff --git a/src/app.css b/src/app.css @@ -1,3 +1,4 @@ +@import "/static/stylesheets/tailwindcss-app-carousel.css"; @import "/static/stylesheets/tailwindcss-app-form.css"; @import "/static/stylesheets/tailwindcss-app-styles.css"; @import "/static/stylesheets/tailwindcss-app.css"; @@ -19,6 +20,5 @@ .tap-scale { @apply active:scale-[97%] group-active:scale-[97%] delay-75 duration-700 ease-in-out transition-all; - } - + } } \ No newline at end of file diff --git a/src/lib/stores.ts b/src/lib/stores.ts @@ -12,7 +12,3 @@ export const app_sqlite = writable<boolean>(false); 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/utils/index.ts b/src/lib/utils/index.ts @@ -12,4 +12,4 @@ export const restart = async (route_to: true | NavigationRoute, notify_message?: } catch (e) { console.log(`(error) restart `, e); } -}; -\ No newline at end of file +}; diff --git a/src/lib/utils/nostr.ts b/src/lib/utils/nostr.ts @@ -0,0 +1,44 @@ +import { lc } from "$lib/client"; +import NDK, { NDKKind, NDKUser } from "@nostr-dev-kit/ndk"; +import { fmt_tags_basis_nip99, ndk_event } from "@radroots/utils"; + +export const nostr_sync_models_trade_product = async (opts: { + $ndk: NDK; + $ndk_user: NDKUser; +}): Promise<void> => { + try { + console.log(`(nostr_sync_models_trade_product) run`) + + const trade_products_all = await lc.db.trade_product_get({ + list: [`all`], + }); + if (typeof trade_products_all === `string`) return; + + for (const trade_product of trade_products_all) { + const tags_basis = await fmt_tags_basis_nip99({ + d_tag: trade_product.id, + title: trade_product.key, + summary: `${trade_product.key} ${trade_product.lot} ${trade_product.process}`, + }); + if (!tags_basis) continue; + + const { $ndk, $ndk_user } = opts; + const ev = await ndk_event({ + $ndk, + $ndk_user, + basis: { + kind: NDKKind.Classified, + content: `This is a rad roots posting of ${trade_product.key}`, + tags: [...tags_basis], + }, + }); + if (!ev) return; + await ev.publish(); + } + + console.log(`(nostr_sync_models_trade_product) done`) + + } catch (e) { + console.log(`(error) nostr_sync_models_trade_product `, e); + } +}; +\ No newline at end of file diff --git a/src/lib/utils/trade_product.ts b/src/lib/utils/trade_product.ts @@ -1,9 +1,9 @@ import { parse_trade_product_form_keys, trade_product_form_fields, trade_product_form_vals, type TradeProductFormFields } from "@radroots/models"; import { kv } from "@radroots/svelte-lib"; -export const trade_product_kv_vals = async (opts: { +export const validate_trade_product_vals = async (opts: { kv_pref: string; - no_validation?: string[]; + no_validation?: string[] | true; }): Promise<TradeProductFormFields | string> => { try { let no_validation = opts.no_validation || []; @@ -19,7 +19,8 @@ export const trade_product_kv_vals = async (opts: { const field_id = `${opts.kv_pref}-${field_k}`; const field_val = await kv.get(field_id); if (field_val) vals[field_k] = field_val; - if ( + if (no_validation === true) continue; + else if ( (!field.optional && !field.validation.test(field_val)) || (field.optional && field_val && diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -2,6 +2,7 @@ import { lc } from "$lib/client"; import { _conf } from "$lib/conf"; import { app_nostr_key } from "$lib/stores"; + import { nostr_sync_models_trade_product } from "$lib/utils/nostr"; import { type NostrRelayFormFields, parse_nostr_relay_form_keys, @@ -9,14 +10,17 @@ import { LayoutWindow, ndk, - ndk_init, ndk_user, nostr_ndk_configured, nostr_relays_connected, nostr_relays_poll_documents, nostr_relays_poll_documents_count, + nostr_sync_prevent, } from "@radroots/svelte-lib"; - import { parse_nostr_relay_information_document_fields } from "@radroots/utils"; + import { + ndk_init, + parse_nostr_relay_information_document_fields, + } from "@radroots/utils"; app_nostr_key.subscribe(async (_app_nostr_key) => { try { @@ -25,14 +29,14 @@ _conf.kv.nostr_key(_app_nostr_key), ); if (!secret_key) { - alert(`!secret_key`); //@todo + alert(`!secret_key - go to recovery (todo)`); //@todo return; } const nostr_relays = await lc.db.nostr_relay_get({ list: ["all"], }); if (typeof nostr_relays === `string`) { - alert(nostr_relays); //@todo + alert(`${nostr_relays} - go to recovery (todo)`); //@todo return; } for (const { url } of nostr_relays) $ndk.addExplicitRelay(url); @@ -58,6 +62,7 @@ if (!_nostr_ndk_configured) return; console.log(`(nostr_ndk_configured) success`); nostr_relays_poll_documents.set(true); + await sync_nostr(); } catch (e) { console.log(`(error) nostr_ndk_configured`, e); } @@ -74,29 +79,47 @@ }, ); - nostr_relays_connected.subscribe(async (_nostr_relays_connected) => { + const sync_nostr = async (): Promise<void> => { try { - if (!_nostr_relays_connected.length) return; - else if ($nostr_ndk_configured) { - console.log(`nostr sync...`); + console.log(`!!! SYNC NOSTR`); + if (!$nostr_ndk_configured) { + console.log(`!!! SYNC NOSTR ndk not configured`); + return; } + if ($nostr_sync_prevent) { + const confirm = await lc.dialog.confirm({ + message: `Sync to nostr network is disabled. Do you want to turn it on?`, + cancel_label: `No`, + ok_label: `Yes`, + }); + if (confirm === true) { + nostr_sync_prevent.set(false); + await sync_nostr(); + return; + } + return; + } + + await nostr_sync_models_trade_product({ $ndk, $ndk_user }); } catch (e) { - console.log(`(error) nostr_relays_connected `, e); + console.log(`(error) sync_nostr `, e); } - }); + }; const fetch_relay_documents = async (): Promise<void> => { try { if ( $nostr_relays_poll_documents_count >= _conf.nostr.relay_polling_count_max - ) + ) { + nostr_relays_poll_documents.set(false); return; + } nostr_relays_poll_documents_count.set( $nostr_relays_poll_documents_count + 1, ); const nostr_relays = await lc.db.nostr_relay_get({ - list: ["on_key", { public_key: $app_nostr_key }], + list: [`on_profile`, { public_key: $app_nostr_key }], }); if (typeof nostr_relays === `string`) throw new Error(); diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -167,9 +167,7 @@ }, { icon: `compass`, - callback: async (tab_i) => { - await route(`/models/trade-product/add`); - }, + callback: async (tab_i) => {}, }, { icon: `network`, @@ -180,7 +178,7 @@ { icon: `bell-simple`, callback: async (tab_i) => { - await route(`/models/nostr-relay`); + await route(`/test`); }, }, ], diff --git a/src/routes/(app)/models/nostr-profile/view/+page.svelte b/src/routes/(app)/models/nostr-profile/view/+page.svelte @@ -2,7 +2,7 @@ import { lc } from "$lib/client"; import { _conf } from "$lib/conf"; import { app_nostr_key } from "$lib/stores"; - import type { NostrProfile } from "@radroots/models"; + import type { NostrProfile, NostrRelay } from "@radroots/models"; import { app_notify, app_submit_route, @@ -12,16 +12,20 @@ LayoutView, Nav, nav_prev, + nostr_relays_connected, qp_nostr_pk, route, show_toast, t, Trellis, + type ITrellisKindTouch, } from "@radroots/svelte-lib"; import { onMount } from "svelte"; type LoadData = { nostr_profile: NostrProfile; + nostr_relays: NostrRelay[]; + nostr_relays_unconnected: NostrRelay[]; secret_key: string; }; let ld: LoadData | undefined = undefined; @@ -59,9 +63,25 @@ return; } + const nostr_relays = await lc.db.nostr_relay_get({ + list: [`on_profile`, { public_key: $qp_nostr_pk }], + sort: `oldest`, + }); + + const nostr_relays_unconnected = await lc.db.nostr_relay_get({ + list: [`off_profile`, { public_key: $qp_nostr_pk }], + sort: `oldest`, + }); + const data: LoadData = { nostr_profile, secret_key, + nostr_relays: + typeof nostr_relays !== `string` ? nostr_relays : [], + nostr_relays_unconnected: + typeof nostr_relays_unconnected !== `string` + ? nostr_relays_unconnected + : [], }; return data; } catch (e) { @@ -69,6 +89,71 @@ } }; + $: { + console.log(JSON.stringify(ld, null, 4), `ld`); + } + + let tr_nostr_relays: ITrellisKindTouch[] = []; + $: tr_nostr_relays = + ld?.nostr_profile && ld?.nostr_relays.length + ? ld.nostr_relays.map((nostr_relay) => ({ + layer: 1, + touch: { + label: { + left: [ + { + value: nostr_relay.url, + }, + ], + }, + callback: async () => { + if (!ld) return; + nav_prev.set([ + ...$nav_prev, + { + route: `/models/nostr-profile/view`, + label: `Profile`, + params: [ + [`nostr_pk`, ld.nostr_profile.public_key], + ], + }, + ]); + await route(`/models/nostr-relay/view`, [ + [`id`, nostr_relay.id], + ]); + }, + }, + offset: { + mod: { + glyph_circle: { + classes_wrap: $nostr_relays_connected.includes( + nostr_relay.id, + ) + ? `bg-layer-1-glyph-hl/60 group-active:opacity-40` + : `bg-yellow-600/90 group-active:opacity-40`, + glyph: { + classes: $nostr_relays_connected.includes( + nostr_relay.id, + ) + ? `text-white/80 group-active:opacity-60 fade-in` + : `text-yellow-100/80 group-active:opacity-60 fade-in`, + key: $nostr_relays_connected.includes( + nostr_relay.id, + ) + ? `check` + : `exclamation-mark`, + weight: `bold`, + dim: `xs-`, + }, + }, + }, + callback: async (ev) => { + ev.stopPropagation(); + }, + }, + })) + : []; + const toggle_hex_pk = (): void => { show_public_key_hex = !show_public_key_hex; }; @@ -291,7 +376,41 @@ args: { layer: 1, title: { - value: `${$t(`common.profile`)}`, + value: `${$t(`icu.connected_*`, { value: `${$t(`common.relays`)}` })}`, + }, + list: tr_nostr_relays.length + ? tr_nostr_relays + : [ + { + hide_active: vl_secret_key_unlock, + touch: { + label: { + left: [ + { + classes: ld.nostr_profile + .name + ? `` + : `text-layer-1-glyph-shade`, + value: + ld.nostr_profile + .name || + `${$t(`icu.no_*_published`, { value: `${$t(`common.relays`)}`.toLowerCase() })}`, + }, + ], + }, + }, + }, + ], + }, + }} + /> + + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `${$t(`common.profile_name`)}`, }, list: [ { @@ -352,35 +471,6 @@ }, }} /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$t(`icu.connected_*`, { value: `${$t(`common.relays`)}` })}`, - }, - list: [ - { - hide_active: vl_secret_key_unlock, - touch: { - label: { - left: [ - { - classes: ld.nostr_profile.name - ? `` - : `text-layer-1-glyph-shade`, - value: - ld.nostr_profile.name || - `${$t(`icu.no_*_published`, { value: `${$t(`common.relays`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> {/if} </LayoutTrellis> </LayoutView> diff --git a/src/routes/(app)/models/nostr-relay/+page.svelte b/src/routes/(app)/models/nostr-relay/+page.svelte @@ -36,12 +36,12 @@ const load_data = async (): Promise<LoadData | undefined> => { try { const nostr_relays = await lc.db.nostr_relay_get({ - list: [`on_key`, { public_key: $app_nostr_key }], + list: [`on_profile`, { public_key: $app_nostr_key }], sort: `oldest`, }); const nostr_relays_other = await lc.db.nostr_relay_get({ - list: [`off_key`, { public_key: $app_nostr_key }], + list: [`off_profile`, { public_key: $app_nostr_key }], sort: `oldest`, }); diff --git a/src/routes/(app)/models/nostr-relay/view/+page.svelte b/src/routes/(app)/models/nostr-relay/view/+page.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import { lc } from "$lib/client"; - import type { NostrRelay } from "@radroots/models"; + import type { NostrProfile, NostrRelay } from "@radroots/models"; import { app_blur, app_notify, @@ -17,6 +17,8 @@ type LoadData = { nostr_relay: NostrRelay; + nostr_profiles: NostrProfile[]; + nostr_profiles_unconnected: NostrProfile[]; }; let ld: LoadData | undefined = undefined; let show_edit = false; @@ -42,8 +44,22 @@ const nostr_relay = nostr_relays[0]; + const nostr_profiles = await lc.db.nostr_profile_get({ + list: [`on_relay`, { id: nostr_relay.id }], + }); + + const nostr_profiles_unconnected = await lc.db.nostr_profile_get({ + list: [`off_relay`, { id: nostr_relay.id }], + }); + const data: LoadData = { nostr_relay, + nostr_profiles: + typeof nostr_profiles !== `string` ? nostr_profiles : [], + nostr_profiles_unconnected: + typeof nostr_profiles_unconnected !== `string` + ? nostr_profiles_unconnected + : [], }; return data; } catch (e) { @@ -55,6 +71,9 @@ app_blur.set(show_edit); } + $: { + console.log(JSON.stringify(ld, null, 4), `ld`); + } const relay_connect = async (): Promise<void> => { try { } catch (e) { diff --git a/src/routes/(app)/models/trade-product/+page.svelte b/src/routes/(app)/models/trade-product/+page.svelte @@ -142,7 +142,7 @@ {/each} {:else if !loading_models} <div - class={`flex flex-col w-full justify-center items-center px-4 gap-3`} + class={`flex flex-col w-full justify-center items-center px-4 gap-2`} > <p class={`font-sans font-[400] text-layer-2-glyph`}> {`No items to display.`} diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,77 +1,163 @@ <!-- svelte-ignore a11y-no-noninteractive-tabindex --> - <script lang="ts"> - import { goto } from "$app/navigation"; import { lc } from "$lib/client"; import { location_gcs_add } from "$lib/utils/location_gcs"; import { trade_product_kv_init, - trade_product_kv_vals, + validate_trade_product_vals, } from "$lib/utils/trade_product"; import { mass_units, trade_product_form_fields, type LocationGcs, + type TradeProductFormFields, } from "@radroots/models"; import { + carousel_index, + carousel_index_max, + carousel_next, + carousel_prev, el_id, + Fill, fmt_id, + Glyph, InputForm, InputSelect, + int_step, kv, LayoutTrellis, LayoutTrellisLine, LayoutView, + locale, Nav, NotifyGlyph, route, t, + TextareaElement, + view_effect, } from "@radroots/svelte-lib"; import { fiat_currencies, fmt_trade_quantity_val, + parse_currency_price, parse_trade_key, parse_trade_mass_tuple, trade_keys, trade_quantities, + type CurrencyPrice, type TradeKey, } from "@radroots/utils"; import { onMount } from "svelte"; const trade_key_default: TradeKey = `coffee`; + type CarouselConf = { + label_prev?: string; + label_next: string; + }; + const carousel_conf = new Map<number, CarouselConf>([ + [ + 0, + { + label_next: `Preview`, + }, + ], + [ + 1, + { + label_next: `Listing`, + }, + ], + [ + 2, + { + label_next: `Location`, + }, + ], + [ + 3, + { + label_next: `Review`, + }, + ], + [ + 4, + { + label_next: `Post`, + }, + ], + ]); + let el_trellis_wrap_price: HTMLElement | null; - let loading_submit = false; - let loading_location = false; + type View = `form_1` | `load`; + let view: View = `load`; - let sel_key: string = trade_key_default; - let show_sel_key_other = false; + $: { + view_effect<View>(view); + } + + let show_sel_trade_product_key_other = false; + let sel_trade_product_key: string = trade_key_default; + $: sel_trade_product_key_parsed = parse_trade_key(sel_trade_product_key); + $: ls_trade_product_quantities = sel_trade_product_key_parsed + ? trade_quantities[sel_trade_product_key_parsed] + : trade_quantities[trade_key_default]; let sel_location_gcs_id = ``; - let ls_location_gcs: LocationGcs[] = []; + let ls_models_location_gcs: LocationGcs[] = []; - let sel_qty_tup = ``; - let sel_price_qty_unit = ``; - let show_sel_qty_tup_other = false; + let show_sel_trade_product_qty_tup_other = false; + let sel_trade_product_qty_tup = ``; - let sel_price_currency = ``; - let show_sel_price_currency = false; + let show_sel_trade_product_price_currency = false; + let sel_trade_product_price_currency = ``; + let sel_trade_product_price_qty_unit = ``; - let sel_qty_unit = ``; + let sel_trade_product_qty_unit = ``; - $: 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]; + let preview_trade_product: TradeProductFormFields | undefined = undefined; + let preview_trade_product_qty_avail = 0; + + let review_trade_product: TradeProductFormFields | undefined = undefined; + let review_location_gcs: LocationGcs | undefined = undefined; - $: sel_qty_tup = sel_key_parsed - ? fmt_trade_quantity_val(trade_quantities[sel_key_parsed][0]) + let loading_submit = false; + let loading_location_gcs = false; + + $: num_trade_product_qty_amt = preview_trade_product + ? Number(preview_trade_product.qty_amt) + : 0; + + $: num_trade_product_price_amt = preview_trade_product + ? Number(preview_trade_product.price_amt) + : 0; + + $: num_trade_product_price_total = + num_trade_product_price_amt * + num_trade_product_qty_amt * + preview_trade_product_qty_avail; + + let preview_trade_product_price_currency: CurrencyPrice | undefined = + undefined; + $: preview_trade_product_price_currency = + preview_trade_product && num_trade_product_price_amt > 0 + ? parse_currency_price( + $locale, + preview_trade_product.price_currency, + num_trade_product_price_amt, + ) + : undefined; + + $: sel_trade_product_qty_tup = sel_trade_product_key_parsed + ? fmt_trade_quantity_val( + trade_quantities[sel_trade_product_key_parsed][0], + ) : fmt_trade_quantity_val(trade_quantities[trade_key_default][0]); $: { - if (ls_location_gcs.length && !sel_location_gcs_id) { - sel_location_gcs_id = ls_location_gcs[0].id; + if (ls_models_location_gcs.length && !sel_location_gcs_id) { + sel_location_gcs_id = ls_models_location_gcs[0].id; } } @@ -89,15 +175,16 @@ } $: { - if (sel_qty_tup) { + if (sel_trade_product_qty_tup) { (async () => { try { - const tup = parse_trade_mass_tuple(sel_qty_tup); - if (tup) { - await kv.set(fmt_id(`qty_amt`), tup[0].toString()); - await kv.set(fmt_id(`qty_unit`), tup[1]); - await kv.set(fmt_id(`qty_label`), tup[2]); - + const mass_tup = parse_trade_mass_tuple( + sel_trade_product_qty_tup, + ); + if (mass_tup) { + await kv.set(fmt_id(`qty_amt`), mass_tup[0].toString()); + await kv.set(fmt_id(`qty_unit`), mass_tup[1]); + await kv.set(fmt_id(`qty_label`), mass_tup[2]); //@note await kv.set(fmt_id(`qty_avail`), `1`); } @@ -107,10 +194,13 @@ } $: { - if (sel_price_currency) { + if (sel_trade_product_price_currency) { (async () => { try { - await kv.set(fmt_id(`price_currency`), sel_price_currency); + await kv.set( + fmt_id(`price_currency`), + sel_trade_product_price_currency, + ); } catch (e) {} })(); } @@ -118,97 +208,227 @@ onMount(async () => { try { - sel_price_currency = "eur"; - sel_price_qty_unit = "kg"; - sel_qty_unit = "kg"; - await fetch_models_location_gcs(); - - const el_focus = el_id(await kv.get(`*-el-focus`)); - if (el_focus) { - await kv.delete(`*-el-focus`); - el_focus.focus(); - } + carousel_index.set(0); + carousel_index_max.set(Array.from(carousel_conf.keys()).length - 1); + + sel_trade_product_price_currency = "eur"; + sel_trade_product_price_qty_unit = "kg"; + sel_trade_product_qty_unit = "kg"; + await fetch_models_models_location_gcs(); } catch (e) { } finally { + view = `form_1`; + } + }); + + carousel_index.subscribe(async (_carousel_index) => { + if (_carousel_index === 1) { + const kv_qty_avail = await kv.get(fmt_id(`qty_avail`)); + if (isNaN(kv_qty_avail)) preview_trade_product_qty_avail = 1; + else preview_trade_product_qty_avail = Number(kv_qty_avail); } }); + const handle_carousel_prev = async (): Promise<void> => { + try { + switch ($carousel_index) { + case 1: + { + await carousel_prev(view); + } + break; + case 2: + { + await carousel_prev(view); + } + break; + case 3: + { + await carousel_prev(view); + } + break; + case 4: + { + await carousel_prev(view); + } + break; + } + } catch (e) { + console.log(`(error) handle_carousel_prev `, e); + } + }; + + const handle_carousel_next = async (): Promise<void> => { + try { + switch ($carousel_index) { + case 0: + { + const vals_init = await validate_trade_product_vals({ + kv_pref: fmt_id(), + no_validation: [ + `year`, + `price_qty_amt`, + `qty_avail`, + `summary`, + `title`, + ], + }); + if (typeof vals_init === `string`) { + await lc.dialog.alert( + `${$t(`trade.product.fields.${vals_init}.err_invalid`, { default: `${$t(`icu.invalid_*`, { value: vals_init })}` })}`, + ); + return; + } + + if (!vals_init.year) + await kv.set( + fmt_id(`year`), + new Date().getFullYear().toString(), + ); + if (!vals_init.price_qty_amt) + await kv.set(fmt_id(`price_qty_amt`), `1`); + if (!vals_init.qty_avail) + await kv.set(fmt_id(`qty_avail`), `1`); + + const vals = await validate_trade_product_vals({ + kv_pref: fmt_id(), + no_validation: [`summary`, `title`], + }); + if (typeof vals === `string`) { + await lc.dialog.alert(`The entry is incomplete`); + return; + } + preview_trade_product = vals; + await carousel_next(view); + } + break; + case 1: + { + await carousel_next(`form_1`); + } + break; + case 2: + { + const vals = await validate_trade_product_vals({ + kv_pref: fmt_id(), + }); + console.log(JSON.stringify(vals, null, 4), `vals`); + if (typeof vals === `string`) { + await lc.dialog.alert(`Enter the listing details`); + return; + } + console.log(JSON.stringify(vals, null, 4), `vals`); + await carousel_next(`form_1`); + } + break; + case 3: + { + const location_gcs_kv_id = await kv.get( + fmt_id(`location_gcs_id`), + ); + if (!location_gcs_kv_id) { + await lc.dialog.alert( + `The product location is missing`, + ); + return; + } + const location_gcs_get = await lc.db.location_gcs_get({ + id: location_gcs_kv_id, + }); + if (typeof location_gcs_get === `string`) { + //@todo + await lc.dialog.alert( + `The product location is missing`, + ); + preview_trade_product = undefined; + return; + } + review_location_gcs = location_gcs_get[0]; + + const vals = await validate_trade_product_vals({ + kv_pref: fmt_id(), + }); + if (typeof vals === `string`) { + await lc.dialog.alert(`The entry is incomplete`); + //@todo + return; + } + review_trade_product = vals; + + await carousel_next(`form_1`); + } + break; + case 4: + { + await submit(); + } + break; + } + } catch (e) { + console.log(`(error) handle_carousel_next `, e); + } + }; + const toggle_show_key_other = async (visible: boolean): Promise<void> => { try { - show_sel_key_other = visible; + show_sel_trade_product_key_other = visible; if (visible) { await kv.set(fmt_id(`key`), ``); } else { - sel_key = trade_keys[0]; + sel_trade_product_key = trade_keys[0]; } - } catch (e) {} + } catch (e) { + console.log(`(error) toggle_show_key_other `, e); + } }; const toggle_show_qty_amt_other = async ( visible: boolean, ): Promise<void> => { try { - show_sel_qty_tup_other = visible; + show_sel_trade_product_qty_tup_other = visible; if (visible) { - sel_price_qty_unit = mass_units[0]; + sel_trade_product_price_qty_unit = mass_units[0]; await kv.set(fmt_id(`qty_amt`), ``); } else { - sel_qty_tup = sel_key_parsed + sel_trade_product_qty_tup = sel_trade_product_key_parsed ? fmt_trade_quantity_val( - trade_quantities[sel_key_parsed][0], + trade_quantities[sel_trade_product_key_parsed][0], ) : ``; } - } catch (e) {} + } catch (e) { + console.log(`(error) toggle_show_qty_amt_other `, e); + } }; - const fetch_models_location_gcs = async (): Promise<void> => { + const fetch_models_models_location_gcs = async (): Promise<void> => { try { const res = await lc.db.location_gcs_get({ list: [`all`], }); - if (typeof res !== `string`) ls_location_gcs = res; + if (typeof res !== `string`) ls_models_location_gcs = res; } catch (e) { - console.log(`(error) fetch_models_location_gcs `, e); + console.log(`(error) fetch_models_models_location_gcs `, e); } }; const add_model_location_gcs = async (): Promise<void> => { try { - loading_location = true; + loading_location_gcs = true; await location_gcs_add(); - await fetch_models_location_gcs(); + await fetch_models_models_location_gcs(); } catch (e) { console.log(`(error) add_model_location_gcs `, e); } finally { - loading_location = false; + loading_location_gcs = false; } }; const submit = async (): Promise<void> => { try { loading_submit = true; - - const vals = await trade_product_kv_vals({ - kv_pref: fmt_id(), - no_validation: [`year`, `price_qty_amt`, `qty_avail`], - }); - if (typeof vals === `string`) { - await lc.dialog.alert( - `${$t(`trade.product.fields.${vals}.err_invalid`, { default: `Invalid ${vals}` })}`, //@todo - ); - return; - } - - if (!vals.year) - await kv.set( - fmt_id(`year`), - new Date().getFullYear().toString(), - ); - if (!vals.price_qty_amt) await kv.set(fmt_id(`price_qty_amt`), `1`); - if (!vals.qty_avail) await kv.set(fmt_id(`qty_avail`), `1`); - - await route(`/models/trade-product/add/preview`); + // await route(`/models/trade-product/add/preview`); } catch (e) { console.log(`(error) submit `, e); } finally { @@ -219,181 +439,170 @@ <LayoutView> <LayoutTrellis> - <LayoutTrellisLine - basis={{ - label: { - value: `Product`, - }, - notify: show_sel_key_other - ? { - classes: `w-full justify-end`, - label: { - value: `Show Options`, - }, - callback: async () => { - await toggle_show_key_other(false); - }, - } - : undefined, - }} + <div + data-view={`load`} + class={`flex flex-col h-full w-full justify-start items-start`} > - {#if show_sel_key_other} - <div - class={`relative flex flex-row w-full justify-center items-center`} + <Fill /> + </div> + <div + data-view={`form_1`} + data-carousel-container={`form_1`} + class={`carousel-container carousel-container-trellis hidden`} + > + <div + data-carousel-item={`form_1`} + class={`carousel-item carousel-item-trellis`} + > + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.product`)}`, + }, + notify: show_sel_trade_product_key_other + ? { + classes: `w-full justify-end`, + label: { + value: `Show Options`, + }, + callback: async () => { + await toggle_show_key_other(false); + }, + } + : undefined, + }} > - <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} + {#if show_sel_trade_product_key_other} + <div + class={`relative flex flex-row w-full justify-center items-center`} + > + <InputForm + basis={{ + id: fmt_id(`key`), + sync: true, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`common.product_name`)}`.toLowerCase() })}`, + + field: { + charset: + trade_product_form_fields.key + .charset, + validate: + trade_product_form_fields.key + .validation, + validate_keypress: true, + }, + }} + /> + </div> + {:else} + <InputSelect + bind:value={sel_trade_product_key} + basis={{ + id_wrap: fmt_id(`key_wrap`), + id: fmt_id(`key`), + classes: `font-mono-display`, + 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={{ - id_wrap: fmt_id(`key_wrap`), - id: fmt_id(`key`), - classes: `font-mono-display`, - 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); - } + label: { + value: `${$t(`common.price`)}`, }, }} - /> - {/if} - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `Location`, - }, - }} - > - <InputSelect - bind:value={sel_location_gcs_id} - basis={{ - id_wrap: fmt_id(`location_gcs_id_wrap`), - id: fmt_id(`location_gcs_id`), - classes: `font-mono-display`, - sync: true, - loading: loading_location, - options: ls_location_gcs.length - ? [ - ...ls_location_gcs.map((i) => ({ - value: i.id, - label: `${i.label}`, - })), - { - value: `add-new`, - label: `${$t(`common.add_current_location`)}`, - }, - { - value: `add-map`, - label: `${$t(`common.add_map_location`)}`, - }, - ] - : [ - { - value: ``, - disabled: true, - selected: true, - label: `${$t(`common.no_locations_saved`)}`, - }, - { - value: `add-new`, - label: `${$t(`common.add_current_location`)}`, - }, - { - value: `add-map`, - label: `${$t(`common.add_map_location`)}`, - }, - ], - callback: async (val) => { - if (val === `add-new`) { - sel_location_gcs_id = ``; - await add_model_location_gcs(); - } else if (val === `add-map`) { - sel_location_gcs_id = ``; - console.log(`add map location`); - await goto(`/map/choose-location`); - } - }, - }} - /> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `Quantity`, - }, - notify: show_sel_qty_tup_other - ? { - classes: `w-full justify-end`, - label: { - value: `Show Options`, - }, - callback: async () => { - await toggle_show_qty_amt_other(false); - }, - } - : undefined, - }} - > - <div - id={fmt_id(`qty_wrap`)} - tabindex={-1} - class={`relative form-line-active flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl transition-all`} - > - {#if show_sel_qty_tup_other} + > <div - class={`relative flex flex-row w-full justify-center items-center`} + bind:this={el_trellis_wrap_price} + id={fmt_id(`price_wrap`)} + tabindex={-1} + class={`relative form-line-active flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl transition-all`} > <InputForm basis={{ - id: fmt_id(`qty_amt`), + id: fmt_id(`price_amt`), layer: false, sync: true, - placeholder: `Enter number of ${$t(`measurement.mass.unit.${sel_price_qty_unit}_ab`, { default: sel_price_qty_unit })} per order`, + sync_init: false, + classes: `font-mono-display`, + placeholder: `Enter price`, field: { charset: - trade_product_form_fields.qty_amt + trade_product_form_fields.price_amt .charset, validate: - trade_product_form_fields.qty_amt + trade_product_form_fields.price_amt .validation, validate_keypress: true, }, + callback: async ({ val, pass }) => { + const lastchar = val[val.length - 1]; + const period_count = + val.split(".").length - 1; + if ( + (pass && + lastchar !== `.` && + period_count < 2) || + val.length < 1 + ) { + el_trellis_wrap_price?.classList.remove( + `bg-layer-1-surface-err`, + ); + show_sel_trade_product_price_currency = false; + } else { + el_trellis_wrap_price?.classList.add( + `bg-layer-1-surface-err`, + ); + show_sel_trade_product_price_currency = true; + } + }, }} /> <div - class={`absolute top-0 right-0 flex flex-row h-full gap-2 justify-center items-center`} + class={`flex flex-row gap-2 pr-4 justify-end items-center text-layer-1-glyph/70`} > <InputSelect - bind:value={sel_qty_unit} + bind:value={sel_trade_product_price_currency} basis={{ - id: fmt_id(`qty_unit`), - classes: `w-[3.5rem] text-layer-1-glyph font-[500]`, + id: fmt_id(`price_currency`), + classes: `w-fit font-mono-display font-[500] text-lg`, layer: false, + hide_arrows: true, + sync: true, + options: fiat_currencies.map((i) => ({ + value: i, + label: `${$t(`currency.${i}.symbol`, { default: i })}`, + })), + }} + /> + <p + class={`font-sans font-[500] text-[1.1rem] -translate-y-[1px] scale-y-[110%]`} + > + {`/`} + </p> + <InputSelect + bind:value={sel_trade_product_price_qty_unit} + basis={{ + id: fmt_id(`price_qty_unit`), + classes: `w-fit font-mono-display font-[600] text-[0.95rem] leading-[1rem]`, + layer: false, + hide_arrows: true, sync: true, options: mass_units.map((i) => ({ value: i, @@ -402,132 +611,669 @@ }} /> </div> + {#if show_sel_trade_product_price_currency} + <NotifyGlyph + basis={{ + glyph: `warning-circle`, + layer: 2, + classes: `translate-x-[32px]`, + }} + /> + {/if} </div> - {:else} - <InputSelect - bind:value={sel_qty_tup} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.quantity`)}`, + }, + notify: show_sel_trade_product_qty_tup_other + ? { + classes: `w-full justify-end`, + label: { + value: `Show Options`, + }, + callback: async () => { + await toggle_show_qty_amt_other(false); + }, + } + : undefined, + }} + > + <div + id={fmt_id(`qty_wrap`)} + tabindex={-1} + class={`relative form-line-active flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl transition-all`} + > + {#if show_sel_trade_product_qty_tup_other} + <div + class={`relative flex flex-row w-full justify-center items-center`} + > + <InputForm + basis={{ + id: fmt_id(`qty_amt`), + layer: false, + sync: true, + placeholder: `Enter number of ${$t(`measurement.mass.unit.${sel_trade_product_price_qty_unit}_ab`, { default: sel_trade_product_price_qty_unit })} per order`, + field: { + charset: + trade_product_form_fields + .qty_amt.charset, + validate: + trade_product_form_fields + .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_trade_product_qty_unit} + basis={{ + id: fmt_id(`qty_unit`), + classes: `w-[3.5rem] text-layer-1-glyph font-[500]`, + layer: false, + sync: true, + options: mass_units.map((i) => ({ + value: i, + label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, + })), + }} + /> + </div> + </div> + {:else} + <InputSelect + bind:value={sel_trade_product_qty_tup} + basis={{ + classes: `font-mono-display`, + options: [ + ...ls_trade_product_quantities.map( + (i) => ({ + value: fmt_trade_quantity_val( + i, + ), + label: `${i.mass} ${$t(`measurement.mass.unit.${i.mass_unit}_ab`, { default: i.mass_unit })} ${i.label}`, + }), + ), + { + value: `other`, + label: `${$t(`common.other`)}`, + }, + ], + callback: async (val) => { + if (val === `other`) { + await toggle_show_qty_amt_other( + true, + ); + } else { + await kv.set( + fmt_id(`qty_avail`), + `1`, + ); + } + }, + }} + /> + {/if} + </div> + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.lot`)}`, + }, + }} + > + <InputForm basis={{ - classes: `font-mono-display`, - options: [ - ...ls_trade_product_quantities.map((i) => ({ - value: fmt_trade_quantity_val(i), - label: `${i.mass} ${$t(`measurement.mass.unit.${i.mass_unit}_ab`, { default: i.mass_unit })} ${i.label}`, - })), - { - value: `other`, - label: `${$t(`common.other`)}`, - }, - ], - callback: async (val) => { - if (val === `other`) { - await toggle_show_qty_amt_other(true); - } else { - await kv.set(fmt_id(`qty_avail`), `1`); - } + id: fmt_id(`lot`), + sync: true, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`common.lot_name`)}`.toLowerCase() })}`, + field: { + charset: trade_product_form_fields.lot.charset, + validate: + trade_product_form_fields.lot.validation, + validate_keypress: true, }, }} /> + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`model_fields.process`)}`, + }, + }} + > + <InputForm + basis={{ + id: fmt_id(`process`), + sync: true, + placeholder: `Enter the process description`, + field: { + charset: + trade_product_form_fields.process.charset, + validate: + trade_product_form_fields.process + .validation, + validate_keypress: true, + }, + }} + /> + </LayoutTrellisLine> + </div> + <div + data-carousel-item={`form_1`} + class={`carousel-item carousel-item-trellis`} + > + {#if preview_trade_product && preview_trade_product_price_currency} + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`icu.*_details`, { value: `${$t(`common.product`)}` })}`, + }, + }} + > + <div + class={`relative flex flex-col h-[30rem] w-full px-4 py-2 gap-4 justify-around items-start bg-layer-1-surface rounded-3xl`} + > + <div + class={`flex flex-col w-full gap-5 justify-around items-center`} + > + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph`} + > + {`${$t(`common.product`)}:`} + </p> + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row flex-grow gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-2-glyph pr-2`} + > + {`-`} + </p> + + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`${preview_trade_product.key}`} + </p> + </div> + <div + class={`flex flex-row justify-end items-center`} + > + <button + class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} + on:click={async () => { + const el = el_id( + fmt_id(`key_wrap`), + ); + if (el) el.focus(); + await handle_carousel_prev(); + }} + > + <p + class={`font-mono font-[500] text-layer-2-glyph text-sm`} + > + {`${$t(`common.edit`)}`} + </p> + </button> + </div> + </div> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph`} + > + {`${$t(`common.price`)}:`} + </p> + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row flex-grow gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-2-glyph pr-2`} + > + {`-`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {preview_trade_product_price_currency.symbol} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`${(preview_trade_product_price_currency.val_i + preview_trade_product_price_currency.val_f).toFixed(2)}`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph lowercase`} + > + {`${$t(`common.per`)}`} + </p> + {#if preview_trade_product.price_qty_amt === `1`} + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`${$t(`measurement.mass.unit.${preview_trade_product.price_qty_unit}_ab`, { default: preview_trade_product.price_qty_unit })}`} + </p> + {/if} + </div> + <div + class={`flex flex-row justify-end items-center`} + > + <button + class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} + on:click={async () => { + const el = el_id( + fmt_id(`price_wrap`), + ); + if (el) el.focus(); + await handle_carousel_prev(); + }} + > + <p + class={`font-mono font-[500] text-layer-2-glyph text-sm`} + > + {`${$t(`common.edit`)}`} + </p> + </button> + </div> + </div> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph`} + > + {`${$t(`common.quantity`)}:`} + </p> + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row flex-grow gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-2-glyph pr-2`} + > + {`-`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {preview_trade_product.qty_amt} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })}`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {preview_trade_product.qty_label} + </p> + </div> + <div + class={`flex flex-row justify-end items-center`} + > + <button + class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} + on:click={async () => { + const el = el_id( + fmt_id(`qty_wrap`), + ); + if (el) el.focus(); + await handle_carousel_prev(); + }} + > + <p + class={`font-mono font-[500] text-layer-2-glyph text-sm`} + > + {`${$t(`common.edit`)}`} + </p> + </button> + </div> + </div> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph`} + > + {`${$t(`icu.*_available`, { value: `${$t(`common.quantity`)}` })}:`} + </p> + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row flex-grow gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-2-glyph pr-2`} + > + {`-`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`${preview_trade_product_qty_avail} ${preview_trade_product.qty_label}`} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`(${(preview_trade_product_qty_avail * num_trade_product_qty_amt).toFixed(2)} ${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })})`} + </p> + </div> + <div + class={`flex flex-row gap-[10px] justify-center items-center`} + > + <Glyph + basis={{ + key: `arrow-down`, + dim: `xs`, + weight: `bold`, + classes: `h-6 w-6 text-layer-2-glyph bg-layer-2-surface active:opacity-60 rounded-full transition-all`, + callback: async () => { + preview_trade_product_qty_avail = + int_step( + preview_trade_product_qty_avail, + `-`, + 1, + ); + await kv.set( + fmt_id(`qty_avail`), + preview_trade_product_qty_avail.toString(), + ); + }, + }} + /> + <Glyph + basis={{ + key: `arrow-up`, + dim: `xs`, + weight: `bold`, + classes: `h-6 w-6 text-layer-2-glyph bg-layer-2-surface active:opacity-60 rounded-full transition-all`, + callback: async () => { + preview_trade_product_qty_avail = + int_step( + preview_trade_product_qty_avail, + `+`, + ); + await kv.set( + fmt_id(`qty_avail`), + preview_trade_product_qty_avail.toString(), + ); + }, + }} + /> + </div> + </div> + </div> + </div> + <div + class={`flex flex-row w-full justify-start items-center`} + > + <div + class={` flex flex-row h-[1px] w-full justify-start items-center bg-layer-2-surface`} + > + <Fill /> + </div> + </div> + <div + class={`flex flex-col w-full gap-4 justify-start items-start`} + > + <div + class={`flex flex-col w-full gap-4 justify-start items-start`} + > + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph text-lg`} + > + {`${`${$t(`common.quantity`)}`}:`} + </p> + </div> + <div + class={`flex flex-row gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[500] text-layer-1-glyph/80 text-lg`} + > + {preview_trade_product_qty_avail} + </p> + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {`x`} + </p> + <p + class={`font-mono font-[500] text-layer-1-glyph/80 text-lg`} + > + {`${num_trade_product_qty_amt.toFixed(2)} ${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })}`} + </p> + <p + class={`font-mono font-[500] text-layer-1-glyph/80 text-lg`} + > + {preview_trade_product.qty_label} + </p> + </div> + </div> + <div + class={`flex flex-row w-full justify-between items-center`} + > + <div + class={`flex flex-row justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-1-glyph text-lg`} + > + {`${$t(`icu.total_*`, { value: `${$t(`common.price`)}`.toLowerCase() })}:`} + </p> + </div> + <div + class={`flex flex-row gap-2 justify-start items-center`} + > + <p + class={`font-mono font-[400] text-layer-2-glyph`} + > + {preview_trade_product_price_currency.symbol} + </p> + <p + class={`font-mono font-[600] text-layer-1-glyph/80 text-lg`} + > + {num_trade_product_price_total.toFixed( + 2, + )} + </p> + <p + class={`font-mono font-[600] text-layer-1-glyph/80 text-lg uppercase`} + > + {preview_trade_product_price_currency.currency} + </p> + </div> + </div> + </div> + </div> + </div> + </LayoutTrellisLine> {/if} </div> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `Price`, - }, - }} - > <div - bind:this={el_trellis_wrap_price} - id={fmt_id(`price_wrap`)} - tabindex={-1} - class={`relative form-line-active flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl transition-all`} + data-carousel-item={`form_1`} + class={`carousel-item carousel-item-trellis`} > - <InputForm + <LayoutTrellisLine basis={{ - id: fmt_id(`price_amt`), - layer: false, - sync: true, - sync_init: false, - classes: `font-mono-display`, - placeholder: `Enter price`, - field: { - charset: - trade_product_form_fields.price_amt.charset, - validate: - trade_product_form_fields.price_amt.validation, - validate_keypress: true, - }, - callback: async ({ val, pass }) => { - const lastchar = val[val.length - 1]; - const period_count = val.split(".").length - 1; - if ( - (pass && - lastchar !== `.` && - period_count < 2) || - val.length < 1 - ) { - el_trellis_wrap_price?.classList.remove( - `bg-layer-1-surface-err`, - ); - show_sel_price_currency = false; - } else { - el_trellis_wrap_price?.classList.add( - `bg-layer-1-surface-err`, - ); - show_sel_price_currency = true; - } + label: { + value: `${$t(`common.title`)}`, }, }} - /> - <div - class={`flex flex-row gap-2 pr-4 justify-end items-center text-layer-1-glyph/70`} > - <InputSelect - bind:value={sel_price_currency} + <InputForm basis={{ - id: fmt_id(`price_currency`), - classes: `w-fit font-mono-display font-[500] text-lg`, - layer: false, - hide_arrows: true, + id: fmt_id(`title`), sync: true, - options: fiat_currencies.map((i) => ({ - value: i, - label: `${$t(`currency.${i}.symbol`, { default: i })}`, - })), + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.listing_*`, { value: `${$t(`common.title`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.title.charset, + validate: + trade_product_form_fields.title.validation, + validate_keypress: true, + }, }} /> - <p - class={`font-sans font-[500] text-[1.1rem] -translate-y-[1px] scale-y-[110%]`} - > - {`/`} - </p> - <InputSelect - bind:value={sel_price_qty_unit} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.summary`)}`, + }, + }} + > + <TextareaElement basis={{ - id: fmt_id(`price_qty_unit`), - classes: `w-fit font-mono-display font-[600] text-[0.95rem] leading-[1rem]`, - layer: false, - hide_arrows: true, + classes: `h-[25rem] p-4 rounded-3xl`, + id: fmt_id(`summary`), sync: true, - options: mass_units.map((i) => ({ - value: i, - label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, - })), + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.listing_*`, { value: `${$t(`common.summary`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.summary.charset, + validate: + trade_product_form_fields.summary + .validation, + validate_keypress: true, + }, }} /> - </div> - {#if show_sel_price_currency} - <NotifyGlyph + </LayoutTrellisLine> + </div> + <div + data-carousel-item={`form_1`} + class={`carousel-item carousel-item-trellis`} + > + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.location`)}`, + }, + }} + > + <InputSelect + bind:value={sel_location_gcs_id} basis={{ - glyph: `warning-circle`, - layer: 2, - classes: `translate-x-[32px]`, + id_wrap: fmt_id(`location_gcs_id_wrap`), + id: fmt_id(`location_gcs_id`), + classes: `font-mono-display`, + sync: true, + loading: loading_location_gcs, + options: ls_models_location_gcs.length + ? [ + ...ls_models_location_gcs.map((i) => ({ + value: i.id, + label: `${i.label}`, + })), + { + value: `add-new`, + label: `${$t(`common.add_current_location`)}`, + }, + { + value: `add-map`, + label: `${$t(`common.add_map_location`)}`, + }, + ] + : [ + { + value: ``, + disabled: true, + selected: true, + label: `${$t(`common.no_locations_saved`)}`, + }, + { + value: `add-new`, + label: `${$t(`common.add_current_location`)}`, + }, + { + value: `add-map`, + label: `${$t(`common.add_map_location`)}`, + }, + ], + callback: async (val) => { + if (val === `add-new`) { + sel_location_gcs_id = ``; + await add_model_location_gcs(); + } else if (val === `add-map`) { + sel_location_gcs_id = ``; + await route(`/map/choose-location`); + } + }, }} /> - {/if} + </LayoutTrellisLine> </div> - </LayoutTrellisLine> + <div + data-carousel-item={`form_1`} + class={`carousel-item carousel-item-trellis`} + > + <div class={`flex flex-col gap-8 justify-start items-center`}> + <div + class={`flex flex-col gap-2 justify-start items-center`} + > + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`Trade Product`} + </p> + <div + class={`flex flex-row w-trellis_line justify-start items-start`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {JSON.stringify(review_trade_product, null, 4)} + </p> + </div> + </div> + <div + class={`flex flex-col gap-2 justify-start items-center`} + > + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`Location`} + </p> + <div + class={`flex flex-row w-trellis_line justify-start items-start`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {JSON.stringify(review_location_gcs, null, 4)} + </p> + </div> + </div> + </div> + </div> + </div> </LayoutTrellis> </LayoutView> <Nav @@ -535,6 +1281,14 @@ prev: { label: `${$t(`common.back`)}`, route: `/models/trade-product`, + prevent_route: + $carousel_index > 0 + ? { + callback: async () => { + handle_carousel_prev(); + }, + } + : undefined, callback: async () => { await trade_product_kv_init(fmt_id()); }, @@ -551,11 +1305,19 @@ option: { loading: loading_submit, label: { - value: `Preview`, - classes: `text-layer-1-glyph-hl tap-scale`, + value: carousel_conf.get($carousel_index)?.label_next || ``, + classes: `text-layer-1-glyph-hl`, + glyph: + $carousel_index > 0 && $carousel_index < $carousel_index_max + ? { + key: `caret-right`, + classes: `text-layer-1-glyph-hl`, + } + : undefined, }, callback: async () => { - await submit(); + if ($carousel_index === $carousel_index_max) await submit(); + else await handle_carousel_next(); }, }, }} diff --git a/src/routes/(app)/models/trade-product/add/preview/+page.svelte b/src/routes/(app)/models/trade-product/add/preview/+page.svelte @@ -1,469 +0,0 @@ -<script lang="ts"> - import { lc } from "$lib/client"; - import { trade_product_kv_vals } from "$lib/utils/trade_product"; - import { - type LocationGcs, - type TradeProductFormFields, - } from "@radroots/models"; - import { - Fill, - Glyph, - int_step, - kv, - LayoutView, - locale, - Nav, - route, - t, - } from "@radroots/svelte-lib"; - import { fmt_currency_tuple } from "@radroots/utils"; - import { onMount } from "svelte"; - - const kv_pref = `models-trade_product-add`; - - let preview_trade_product: TradeProductFormFields; - let preview_location_gcs: LocationGcs; - - let qty_avail = 0; - - $: num_qty_amt = preview_trade_product - ? Number(preview_trade_product.qty_amt) - : 0; - - $: num_price_amt = preview_trade_product - ? Number(preview_trade_product.price_amt) - : 0; - - let tup_price_amt: [string, string, string] = ["", "", ""]; - $: tup_price_amt = - num_price_amt > 0 - ? fmt_currency_tuple( - $locale, - preview_trade_product.price_currency, - num_price_amt, - ) - : ["", "", ""]; - - onMount(async () => { - try { - await kv.set(`new key`, 20); - qty_avail = await kv.get(`${kv_pref}-qty_avail`); - const vals = await trade_product_kv_vals({ - kv_pref, - }); - if (typeof vals === `string`) { - await lc.dialog.alert(`Invalid value ${vals}.`); - await route(`/models/trade-product/add`); - return; - } - - console.log(JSON.stringify(vals, null, 4), `vals`); - - const location_gcs_res = await lc.db.location_gcs_get({ - id: await kv.get(`${kv_pref}-location_gcs_id`), - }); - - console.log(`location_gcs_res `, location_gcs_res); - - if (typeof location_gcs_res === `string`) { - await lc.dialog.alert(`The product location is missing.`); - await route(`/models/trade-product/add`); - //@todo add focus - return; - } - - preview_trade_product = vals; - preview_location_gcs = location_gcs_res[0]; - } catch (e) { - } finally { - } - }); - - const submit = async (): Promise<void> => { - try { - const vals = await trade_product_kv_vals({ - kv_pref, - no_validation: [`year`, `price_qty_amt`], - }); - console.log(JSON.stringify(vals, null, 4), `vals`); - - if (typeof vals === `string`) { - lc.dialog.alert(`There was a problem adding the product`); - await route(`/models/trade-product/add`); - return; - } - - const res = await lc.db.trade_product_add(vals); - if (typeof res === `string`) { - lc.dialog.alert(res); - await route(`/models/trade-product/add`); - - return; - } else if (Array.isArray(res)) { - lc.dialog.alert(res.join(" ")); - await route(`/models/trade-product/add`); - return; - } - - const kv_each = await kv.each(kv_pref); - const kv_keys = kv_each.filter(([i]: [string, string]) => - i.startsWith(kv_pref), - ); - for (const key of kv_keys) await kv.delete(key); - - await route(`/models/trade-product`); - } catch (e) { - console.log(`(error) submit `, e); - } - }; -</script> - -<LayoutView> - {#if preview_trade_product} - <div - class={`flex flex-col w-full pt-4 px-4 justify-center items-center`} - > - <div - class={`relative flex flex-col w-full p-4 gap-2 justify-start items-start bg-layer-1-surface rounded-3xl`} - > - <div - class={`flex flex-row w-full h-6 justify-center items-center`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`Overview`} - </p> - </div> - <div - class={`flex flex-col w-full gap-3 justify-start items-center`} - > - <div - class={`flex flex-col w-full justify-start items-start`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${$t(`common.product`)}:`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph pr-2`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {preview_trade_product.key} - </p> - </div> - <div - class={`flex flex-row justify-end items-center`} - > - <button - class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} - on:click={async () => { - await kv.set( - `*-el-focus`, - `${kv_pref}-key_wrap`, - ); - await route( - `/models/trade-product/add`, - ); - }} - > - <p - class={`font-mono font-[500] text-layer-2-glyph text-sm`} - > - {`${$t(`common.edit`)}`} - </p> - </button> - </div> - </div> - </div> - <div - class={`flex flex-col w-full justify-start items-start`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${$t(`common.price`)}:`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph pr-2`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {tup_price_amt[0]} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {tup_price_amt.slice(1).join(".")} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph lowercase`} - > - {`${$t(`common.per`)}`} - </p> - {#if preview_trade_product.price_qty_amt === `1`} - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {`${$t(`measurement.mass.unit.${preview_trade_product.price_qty_unit}_ab`, { default: preview_trade_product.price_qty_unit })}`} - </p> - {/if} - </div> - <div - class={`flex flex-row justify-end items-center`} - > - <button - class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} - on:click={async () => { - await kv.set( - `*-el-focus`, - `${kv_pref}-price_wrap`, - ); - await route( - `/models/trade-product/add`, - ); - }} - > - <p - class={`font-mono font-[500] text-layer-2-glyph text-sm`} - > - {`${$t(`common.edit`)}`} - </p> - </button> - </div> - </div> - </div> - <div - class={`flex flex-col w-full justify-start items-start`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${$t(`common.quantity`)}:`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph pr-2`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {preview_trade_product.qty_amt} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {`${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })}`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {preview_trade_product.qty_label} - </p> - </div> - <div - class={`flex flex-row justify-end items-center`} - > - <button - class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} - on:click={async () => { - await kv.set( - `*-el-focus`, - `${kv_pref}-qty_wrap`, - ); - await route( - `/models/trade-product/add`, - ); - }} - > - <p - class={`font-mono font-[500] text-layer-2-glyph text-sm`} - > - {`${$t(`common.edit`)}`} - </p> - </button> - </div> - </div> - </div> - <div - class={`flex flex-col w-full justify-start items-start`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${$t(`icu.*_available`, { value: `${$t(`common.quantity`)}` })}`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph pr-2`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {qty_avail} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {`(${(qty_avail * num_qty_amt).toFixed(2)} ${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })} total)`} - </p> - </div> - <div - class={`flex flex-row gap-[10px] justify-center items-center`} - > - <Glyph - basis={{ - key: `arrow-down`, - dim: `xs`, - weight: `bold`, - classes: `h-5 w-5 text-layer-2-glyph bg-layer-2-surface active:opacity-60 rounded-full transition-all`, - callback: async () => { - qty_avail = int_step( - qty_avail, - `-`, - 1, - ); - await kv.set( - `${kv_pref}-qty_avail`, - qty_avail.toString(), - ); - }, - }} - /> - <Glyph - basis={{ - key: `arrow-up`, - dim: `xs`, - weight: `bold`, - classes: `h-5 w-5 text-layer-2-glyph bg-layer-2-surface active:opacity-60 rounded-full transition-all`, - callback: async () => { - qty_avail = int_step( - qty_avail, - `+`, - ); - await kv.set( - `${kv_pref}-qty_avail`, - qty_avail.toString(), - ); - }, - }} - /> - </div> - </div> - </div> - <div - class={`flex flex-col w-full justify-start items-start`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${$t(`common.location`)}:`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph pr-2`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-layer-2-glyph`} - > - {preview_location_gcs.label} - </p> - </div> - <div - class={`flex flex-row justify-end items-center`} - > - <button - class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} - on:click={async () => { - await kv.set( - `*-el-focus`, - `${kv_pref}-location_gcs_id_wrap`, - ); - await route( - `/models/trade-product/add`, - ); - }} - > - <p - class={`font-mono font-[500] text-layer-2-glyph text-sm`} - > - {`${$t(`common.edit`)}`} - </p> - </button> - </div> - </div> - </div> - </div> - <div - class={`flex flex-row w-full pt-4 pb-2 justify-start items-center`} - > - <div - class={` flex flex-row h-[1px] w-full justify-start items-center bg-stone-400`} - > - <Fill /> - </div> - </div> - <div - class={`flex flex-row w-full h-6 justify-center items-center`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`Product Fields`} - </p> - </div> - <div - class={`flex flex-col w-full justify-start items-center`} - ></div> - </div> - </div> - {/if} -</LayoutView> -<Nav - basis={{ - prev: { - label: `Product`, - route: `/models/trade-product/add`, - }, - title: { - label: { - value: `Preview`, - }, - }, - option: { - label: { - value: `Post`, - }, - callback: async () => { - await submit(); - }, - }, - }} -/> diff --git a/src/routes/(app)/nostr/notes/+page.svelte b/src/routes/(app)/nostr/notes/+page.svelte @@ -32,24 +32,20 @@ }); } - async function fetch_events(filter: NDKFilter) { + const fetch_events = async (filter: NDKFilter): Promise<void> => { try { events_store = $ndk.storeSubscribe(filter, { closeOnEose: true, groupable: false, autoStart: false, }); - if (events_store) { - events_store.onEose(() => {}); - } - } catch (error) { - console.error(error); + if (events_store) events_store.onEose(() => {}); + } catch (e) { + console.log(`(error) fetch_events `, e); } - } + }; - onDestroy(() => { - events_store?.unsubscribe(); - }); + onDestroy(() => events_store?.unsubscribe()); const parse_nostr_text_note = (ev: NDKEvent): ITrellisKindTouch[] => { const trellis_list: ITrellisKindTouch[] = []; diff --git a/src/routes/(app)/nostr/notes/post/+page.svelte b/src/routes/(app)/nostr/notes/post/+page.svelte @@ -7,11 +7,11 @@ LayoutView, Nav, ndk, - ndk_event, ndk_user, route, t, } from "@radroots/svelte-lib"; + import { ndk_event } from "@radroots/utils"; let loading = false; let value_note_content = ``; diff --git a/src/routes/(app)/nostr/profile/+page.svelte b/src/routes/(app)/nostr/profile/+page.svelte @@ -11,11 +11,11 @@ LayoutView, Nav, ndk, - ndk_event, ndk_user, t, Trellis, } from "@radroots/svelte-lib"; + import { ndk_event } from "@radroots/utils"; import { onDestroy } from "svelte"; let events_store: NDKEventStore<ExtendedBaseType<NDKEvent>>; diff --git a/src/routes/(app)/test/+page.svelte b/src/routes/(app)/test/+page.svelte @@ -1,8 +1,57 @@ <script lang="ts"> - import { LayoutView, Nav } from "@radroots/svelte-lib"; + import { + LayoutView, + Nav, + carousel_index, + carousel_index_max, + carousel_next, + carousel_prev, + } from "@radroots/svelte-lib"; + import { onMount } from "svelte"; + + onMount(async () => { + try { + carousel_index.set(0); + carousel_index_max.set(1); + } catch (e) { + } finally { + } + }); </script> -<LayoutView basis={{ classes: `px-4 gap-8` }}>test</LayoutView> +<LayoutView> + <div + data-carousel-container={`start`} + class={`carousel-container flex flex-grow h-full w-full`} + > + <div + data-carousel-item={`start`} + class={`carousel-item flex flex-col w-full`} + > + <button + class={`flex flex-row justify-center items-center`} + on:click={async () => { + await carousel_next(`start`); + }} + > + <p class={`font-sans font-[400] text-layer-0-glyph`}>one</p> + </button> + </div> + <div + data-carousel-item={`start`} + class={`carousel-item flex flex-col w-full`} + > + <button + class={`flex flex-row justify-center items-center`} + on:click={async () => { + await carousel_prev(`start`); + }} + > + <p class={`font-sans font-[400] text-layer-0-glyph`}>two</p> + </button> + </div> + </div> +</LayoutView> <Nav basis={{ prev: { diff --git a/src/routes/(conf)/init/+page.svelte b/src/routes/(conf)/init/+page.svelte @@ -4,7 +4,12 @@ import { _conf } from "$lib/conf"; import { restart } from "$lib/utils"; import { keystore_reset } from "$lib/utils/keystore"; - import { Glyph, LayoutView, sleep } from "@radroots/svelte-lib"; + import { + Glyph, + LayoutView, + sleep, + view_effect, + } from "@radroots/svelte-lib"; import { onMount } from "svelte"; const SLIDE_DURATION = 600; @@ -30,11 +35,7 @@ let view: View = `start`; $: { - for (const el of document.querySelectorAll(`[data-view]`)) - el.classList.toggle( - `hidden`, - el.getAttribute(`data-view`) !== view, - ); + view_effect<View>(view); } let slide_active = false; @@ -165,7 +166,7 @@ > <li data-carousel-item={`start`} - class={`carousel-item flex flex-col flex-fluid w-full py-32 justify-between items-center`} + class={`carousel-item flex flex-col w-full py-32 justify-between items-center`} > <div class={`flex flex-col w-40 gap-2 justify-start items-center`} @@ -311,7 +312,7 @@ </li> <li data-carousel-item={`start`} - class={`carousel-item flex flex-col flex-fluid w-full gap-12 justify-center items-center`} + class={`carousel-item flex flex-col w-full gap-12 justify-center items-center`} > <div class={`flex flex-col w-54 gap-4 justify-start items-center`} @@ -418,7 +419,7 @@ > <li data-carousel-item={`configure`} - class={`carousel-item flex flex-col flex-fluid w-full gap-12 justify-center items-center`} + class={`carousel-item flex flex-col w-full gap-12 justify-center items-center`} > <div class={`flex flex-col w-54 gap-4 justify-start items-center`} @@ -499,18 +500,3 @@ </div> </div> </LayoutView> - -<style> - .carousel-container { - display: flex; - overflow-x: hidden; - scroll-snap-type: x mandatory; - list-style: none; - scroll-behavior: smooth; - -webkit-overflow-scrolling: touch; - } - - .carousel-item { - scroll-snap-align: start; - } -</style> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -9,11 +9,12 @@ app_th, app_thc, } from "$lib/stores"; + import { defineCustomElements as pwaElements } from "@ionic/pwa-elements/loader"; import { app_config, app_notify, app_render, - AppConfig, + AppControls, CssStatic, CssStyles, route, @@ -21,21 +22,32 @@ theme_set, } from "@radroots/svelte-lib"; import { parse_color_mode, parse_theme_key } from "@radroots/theme"; + import { + applyPolyfills, + defineCustomElements as jeepSqlite, + } from "jeep-sqlite/loader"; import "../app.css"; - let render_pwa = browser && lc.platform === `web`; - if (render_pwa) { - const el = document.createElement(`jeep-sqlite`); - document.body.appendChild(el); - customElements - .whenDefined(`jeep-sqlite`) - .then(() => { - app_pwa_polyfills.set(true); - }) - .catch((e) => { - console.log(`(pwa polyfills) error `, e); - app_pwa_polyfills.set(false); + $: { + if (browser && lc.platform === `web`) { + applyPolyfills().then(() => { + pwaElements(window); + jeepSqlite(window); }); + const el = document.createElement(`jeep-sqlite`); + document.body.appendChild(el); + customElements + .whenDefined(`jeep-sqlite`) + .then(() => { + app_config.set(true); + }) + .catch((e) => { + console.log(`(pwa polyfills) error `, e); + app_pwa_polyfills.set(false); + }); + } else if (browser) { + app_config.set(true); + } } app_thc.subscribe((app_thc) => { @@ -52,12 +64,13 @@ app_sqlite.subscribe((app_sqlite) => { if (!app_sqlite) return; - console.log(`(app_sqlite) connected`); + console.log(`(app_sqlite) success`); }); app_config.subscribe(async (app_config) => { try { if (!app_config) return; + console.log(`app_config!`); const db_connected = await lc.db.connect(); if (!db_connected) { // @todo @@ -125,7 +138,7 @@ {#if $app_render} <slot /> {/if} -<AppConfig /> +<AppControls /> <CssStatic /> <CssStyles /> <div diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -21,6 +21,7 @@ const heights = { const widths = { line: `320px`, + trellis_line: `349px`, trellis_value: `180px`, trellis_display: `290px` }; @@ -51,7 +52,9 @@ const config: Config = { magda: [`Magda Text`], }, fontSize: { - line: ["1.05rem", { lineHeight: "1.33rem", fontWeight: 300 }], + lg_: ["1.0875rem", { lineHeight: "1.625", fontWeight: 400 }], + // font-size: 1.125rem; /* 18px */ + //line-height: 1.75rem; trellisTitle: ["0.8rem", { lineHeight: "1rem", fontWeight: 200 }], trellisTitleNote: ["0.76rem", { lineHeight: "1rem", fontWeight: 200 }], trellisLine: ["1.05rem", { lineHeight: "1.33rem", fontWeight: 300 }],