app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 5934eba13f9351afa291a9909f37d3cafbe6627b
parent 6e709b53201eac0c0c191a514417094f791ead7b
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Mon, 28 Oct 2024 03:28:38 +0000

Edit `models/trade-product/add` rewrite prior page adding writable stores, photo upload feature, simplified views, page param, handler functions. Add image upload display and image upload row components. Add validate fields util for `trade_product`. Add global.d.ts. Add/edit styles.

Diffstat:
Mcrates/tauri/capabilities/default.json | 1+
Msrc/app.d.ts | 2+-
Asrc/global.d.ts | 6++++++
Asrc/lib/components/image_upload_display.svelte | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/components/image_upload_row.svelte | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/utils/kv.ts | 24++++++++++++++++++++++--
Msrc/routes/(app)/+page.svelte | 4+---
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 1891+++++++++++++++++++++++++++----------------------------------------------------
Mtailwind.config.ts | 5++---
9 files changed, 852 insertions(+), 1258 deletions(-)

diff --git a/crates/tauri/capabilities/default.json b/crates/tauri/capabilities/default.json @@ -8,6 +8,7 @@ "permissions": [ "core:default", "dialog:default", + "fs:default", "geolocation:allow-check-permissions", "geolocation:allow-get-current-position", "geolocation:allow-request-permissions", diff --git a/src/app.d.ts b/src/app.d.ts @@ -1,5 +1,6 @@ declare global { namespace App { } + declare class Keyva { /** * An IDBKeyRange that has no upper or lower bounding. @@ -135,4 +136,3 @@ declare global { } export { }; - diff --git a/src/global.d.ts b/src/global.d.ts @@ -0,0 +1,6 @@ +declare module 'jshashes' { + export class SHA256 { + b64(input: string): string; + b64_hmac(key: string, input: string): string; + } +} diff --git a/src/lib/components/image_upload_display.svelte b/src/lib/components/image_upload_display.svelte @@ -0,0 +1,68 @@ +<script lang="ts"> + import { fs } from "$lib/client"; + import { + Glyph, + ImageBlob, + type CallbackPromise, + } from "@radroots/svelte-lib"; + + export let basis: { + loading: boolean; + list: { + file_path: string; + }[]; + callback_add: CallbackPromise; + }; +</script> + +<div + class={`flex flex-row h-[12rem] w-full pl-12 pr-4 gap-4 justify-start items-center`} +> + <div class={`flex flex-col h-[11rem] gap-1 justify-center items-center`}> + <div + class={`relative flex flex-col h-36 w-36 justify-center items-center rounded-full border-edge border-layer-0-glyph ${basis.list.length === 0 ? `group-active:border-layer-0-glyph/60 ` : ``} overflow-hidden delay-100 duration-300 ease-in-out transition-all`} + > + {#if basis.list[0]?.file_path} + {#await fs.read_bin(basis.list[0].file_path) then file_data} + <ImageBlob + basis={{ + data: file_data, + }} + /> + {/await} + {/if} + <button + class={`z-10 group absolute bottom-0 flex flex-row h-8 w-full justify-center items-start bg-layer-2-surface active:bg-layer-1-surface`} + on:click={async () => {}} + > + <p + class={`font-circ font-[500] text-[0.8rem] tracking-tight text-layer-0-glyph group-active:text-layer-0-glyph/60 el-re`} + > + {`Primary photo`} + </p> + </button> + </div> + </div> + <div + class={`grid grid-cols-12 flex flex-row w-full gap-y-2 justify-start items-center`} + > + {#each Array(6).fill(0) as li} + <div + class={`col-span-4 flex flex-row pr-2 justify-start items-center`} + > + <button + class={`group flex flex-row h-[2.8rem] w-[2.8rem] justify-center items-center rounded-full border-line border-layer-0-glyph/90 active:border-layer-0-glyph/60`} + on:click={async () => {}} + > + <Glyph + basis={{ + classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, + dim: `sm`, + key: `image-square`, + }} + /> + </button> + </div> + {/each} + </div> +</div> diff --git a/src/lib/components/image_upload_row.svelte b/src/lib/components/image_upload_row.svelte @@ -0,0 +1,109 @@ +<script lang="ts"> + import { fs } from "$lib/client"; + import { + Glyph, + ImageBlob, + Loading, + t, + type CallbackPromise, + } from "@radroots/svelte-lib"; + import { fade } from "svelte/transition"; + + export let basis: { + loading: boolean; + list: { + file_path: string; + }[]; + callback_add: CallbackPromise; + }; +</script> + +<div + class={`flex flex-row w-[100vw] ${basis.list.length > 1 ? `justify-start` : `justify-center`} items-center overflow-x-auto scroll-hide`} +> + <div + class={`flex flex-row ${basis.list.length > 1 ? `px-24` : ``} space-x-4 delay-[200ms] duration-[500ms] ease-in-out transition-all`} + > + <button + in:fade={{ duration: 200 }} + out:fade={{ delay: 0, duration: 200 }} + on:click={async () => { + if (basis.list.length === 0) await basis.callback_add(); + }} + class={`group flex flex-col gap-2 justify-start items-center`} + > + <div + class={`flex flex-row ${basis.list.length ? `h-52 w-52` : `h-36 w-36`} justify-center items-center rounded-full border-edge border-layer-0-glyph ${basis.list.length === 0 ? `group-active:border-layer-0-glyph/60 ` : ``} overflow-hidden delay-100 duration-300 ease-in-out transition-all`} + > + {#if basis.list.length} + {#await fs.read_bin(basis.list[0].file_path) then file_data} + <ImageBlob + basis={{ + data: file_data, + }} + /> + {/await} + {:else if basis.loading} + <div + in:fade={{ duration: 200 }} + out:fade={{ + delay: 0, + duration: 50, + }} + class={`flex flex-row justify-start items-center`} + > + <Loading /> + </div> + {:else} + <div + in:fade={{ duration: 200 }} + out:fade={{ + delay: 0, + duration: 50, + }} + class={`flex flex-row justify-start items-center`} + > + <Glyph + basis={{ + classes: `text-layer-0-glyph group-active:text-layer-0-glyph/60 el-re`, + dim: `lg`, + weight: `bold`, + key: `download-simple`, + }} + /> + </div> + {/if} + </div> + <div class={`flex flex-row justify-start items-center`}> + <button + class={`flex flex-row justify-center items-center`} + on:click={async () => { + await basis.callback_add(); + }} + > + <p + class={`font-sans font-[500] text-[1.1rem] tracking-tight text-layer-0-glyph group-active:text-layer-0-glyph/60 el-re`} + > + {`${$t(`icu.add_*`, { value: `${$t(`common.photo`)}`.toLowerCase() })}`} + </p> + </button> + </div> + </button> + {#if basis.list.length > 1} + {#each basis.list.slice(1) as li} + <button + class={`flex flex-row h-52 w-52 justify-center items-center rounded-full border-edge border-layer-0-glyph group-active:border-layer-0-glyph/60 overflow-hidden delay-100 duration-300 ease-in-out transition-all fade-in`} + on:click={async () => {}} + > + {#await fs.read_bin(li.file_path) then file_data} + <ImageBlob + basis={{ + data: file_data, + }} + /> + {/await} + </button> + {/each} + {/if} + </div> +</div> diff --git a/src/lib/utils/kv.ts b/src/lib/utils/kv.ts @@ -1,6 +1,6 @@ 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"; -import { err_msg, type ErrorMessage } from "@radroots/utils"; +import { err_msg, type ErrorMessage, type ResultPass } from "@radroots/utils"; export const kv_init_trade_product_fields = async (kv_pref: string): Promise<void> => { try { @@ -49,7 +49,27 @@ export const kv_validate_trade_product_fields = async (opts: { } return vals; } catch (e) { - console.log(`(error) trade_product_submit_preview `, e); + console.log(`(error) kv_validate_trade_product_fields `, e); + return err_msg(String(e)) + } +}; + +export const validate_trade_product_fields = async (opts: { + kv_pref: string; + fields: string[]; +}): Promise<ResultPass | ErrorMessage<string>> => { + try { + for (const field of opts.fields) { + const field_k = parse_trade_product_form_keys(field); + if (!field_k) return err_msg(field); + const field_id = `${opts.kv_pref}-${field_k}`; + const field_val = await kv.get(field_id); + console.log(`${field_k}: '${field_val}'`) + if (!trade_product_form_fields[field_k].validation.test(field_val)) return err_msg(field_k); + } + return { pass: true }; + } catch (e) { + console.log(`(error) validate_trade_product_fields `, e); return err_msg(String(e)) } }; \ No newline at end of file diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -240,9 +240,7 @@ { icon: `squares-four`, label: `Menu`, - callback: async () => { - await route(`/settings`); - }, + callback: async () => {}, }, ], }} diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,21 +1,13 @@ -<!-- svelte-ignore a11y-no-noninteractive-tabindex --> <script lang="ts"> - import { db, dialog, geol } from "$lib/client"; - import MapChooseLocation from "$lib/components/map_choose_location.svelte"; - import { cfg } from "$lib/conf"; + import { dialog } from "$lib/client"; + import ImageUploadDisplay from "$lib/components/image_upload_display.svelte"; + import ImageUploadRow from "$lib/components/image_upload_row.svelte"; import { kv_init_trade_product_fields, - kv_validate_trade_product_fields, + validate_trade_product_fields, } from "$lib/utils/kv"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; + import { mass_units, trade_product_form_fields } from "@radroots/models"; import { - mass_units, - trade_product_form_fields, - type LocationGcs, - type TradeProductFormFields, - } from "@radroots/models"; - import { - ButtonGlyph, carousel_dec, carousel_inc, carousel_index, @@ -26,241 +18,270 @@ EntryMultiline, EntrySelect, EntryWrap, - Fill, - fmt_geol_latitude, - fmt_geol_longitude, fmt_id, - Glyph, InputElement, - int_step, kv, LayoutTrellis, LayoutTrellisLine, LayoutView, - locale, Nav, - SelectEl, - sleep, + price_locale_fmt, + SelectElement, t, view_effect, } from "@radroots/svelte-lib"; import { fiat_currencies, - fmt_trade_quantity_val, - parse_currency_price, + fmt_trade_quantity_sel_val, parse_trade_key, - parse_trade_mass_tuple, + trade, trade_keys, - trade_quantities, - type CurrencyPrice, - type GeolocationCoordinatesPoint, type TradeKey, } from "@radroots/utils"; import { onMount } from "svelte"; - - const trade_key_default: TradeKey = `coffee`; + import { writable } from "svelte/store"; + import { fade } from "svelte/transition"; type CarouselParam = { label_prev?: string; label_next: string; }; - const carousel_param = new Map<number, CarouselParam>([ - [ - 0, - { - label_next: `${$t(`icu.add_*`, { value: `${$t(`common.product`)}` })}`, - }, - ], - [ - 1, - { - label_next: `${$t(`icu.add_*`, { value: `${$t(`common.location`)}` })}`, - }, - ], - [ - 2, - { - label_next: `${$t(`icu.add_*`, { value: `${$t(`common.listing`)}` })}`, - }, - ], - [ - 3, - { - label_next: `${$t(`common.preview`)}`, - }, - ], - [ - 4, - { - label_next: `${$t(`common.publish`)}`, - }, - ], - ]); + const page_param: { + trade_product: { + key_default: TradeKey; + }; + carousel: Map<number, CarouselParam>; + } = { + carousel: new Map<number, CarouselParam>([ + [ + 0, + { + label_next: `${$t(`common.add`)}`, + }, + ], + [ + 1, + { + label_next: `${$t(`icu.add_*`, { value: `${$t(`common.location`)}` })}`, + }, + ], + [ + 2, + { + label_next: `${$t(`icu.add_*`, { value: `${$t(`common.listing`)}` })}`, + }, + ], + [ + 3, + { + label_next: `${$t(`common.preview`)}`, + }, + ], + [ + 4, + { + label_next: `${$t(`common.publish`)}`, + }, + ], + ]), + trade_product: { + key_default: `coffee`, + }, + }; - type View = `load` | `form_1`; - let view: View = `load`; + type View = `main` | `finish`; + let view: View = `main`; $: { view_effect<View>(view); } - onMount(async () => { - try { - await kv.set(fmt_id(`price_amt`), `2`); - await handle_view(`load`); - carousel_index.set(0); - carousel_index_max.set(carousel_param.size - 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 { - await handle_view(`form_1`); - } - }); - - // let el_trellis_wrap_price: HTMLElement | null; - let loading_submit = false; - 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_models_location_gcs: LocationGcs[] = []; + let photo_add_loading = false; + let photo_add_list: { file_path: string }[] = []; - let map_choose_loc_loading = true; - let map_choose_loc_geoc_point: GeolocationCoordinatesPoint = - cfg.map.coords.default; - let map_choose_loc_geoc_point_select: GeolocationCoordinatesPoint = - cfg.map.coords.default; - let map_choose_loc_geoc_point_select_geoc: - | GeocoderReverseResult - | undefined = undefined; + let tradeproduct_key_sel_toggle = false; + const tradeproduct_key_sel = writable<string>(``); + $: tradeproduct_key_parsed = parse_trade_key($tradeproduct_key_sel); + $: ls_trade_product_quantities = tradeproduct_key_parsed + ? trade.key[tradeproduct_key_parsed].quantity + : trade.default.quantity; - let show_sel_trade_product_qty_tup_other = false; - let sel_trade_product_qty_tup = ``; + const tradeproduct_price_curr_sel = writable<string>(``); + const tradeproduct_price_qty_unit_sel = writable<string>(``); - let sel_trade_product_price_currency = ``; - let sel_trade_product_price_qty_unit = ``; + const tradeproduct_qty_unit_tup_sel = writable<string>(``); + let tradeproduct_qty_unit_tup_sel_toggle = false; - let sel_trade_product_qty_unit = ``; + const tradeproduct_process_sel = writable<string>(``); + let tradeproduct_process_sel_toggle = false; + $: ls_trade_product_processes = tradeproduct_key_parsed + ? trade.key[tradeproduct_key_parsed].process + : []; - let preview_trade_product: TradeProductFormFields | undefined = undefined; - let preview_trade_product_qty_avail = 0; + onMount(async () => { + try { + await kv_init(); + await handle_view(`main`); + carousel_index.set(0); + carousel_index_max.set(page_param.carousel.size - 1); + tradeproduct_price_curr_sel.set(`eur`); + tradeproduct_price_qty_unit_sel.set(`kg`); + await setup_test(); + } catch (e) { + } finally { + await handle_view(`main`); + } + }); - //let review_trade_product: TradeProductFormFields | undefined = undefined; - //let review_location_gcs: LocationGcs | undefined = undefined; + tradeproduct_key_sel.subscribe(async (_tradeproduct_key_sel) => { + await kv.set(fmt_id(`key`), _tradeproduct_key_sel); + }); - $: num_trade_product_qty_amt = preview_trade_product - ? Number(preview_trade_product.qty_amt) - : 0; + $: if (tradeproduct_key_parsed) { + tradeproduct_qty_unit_tup_sel.set( + fmt_trade_quantity_sel_val( + trade.key[tradeproduct_key_parsed].quantity[0], + ), + ); + tradeproduct_process_sel.set( + trade.key[tradeproduct_key_parsed].process[0], + ); + } - $: num_trade_product_price_amt = preview_trade_product - ? Number(preview_trade_product.price_amt) - : 0; + tradeproduct_price_curr_sel.subscribe( + async (_tradeproduct_price_curr_sel) => { + await kv.set( + fmt_id(`price_currency`), + _tradeproduct_price_curr_sel, + ); + }, + ); + + tradeproduct_price_qty_unit_sel.subscribe( + async (_tradeproduct_price_qty_unit_sel) => { + await kv.set( + fmt_id(`price_qty_unit`), + _tradeproduct_price_qty_unit_sel, + ); + }, + ); - $: num_trade_product_price_total = - num_trade_product_price_amt * - num_trade_product_qty_amt * - preview_trade_product_qty_avail; + tradeproduct_qty_unit_tup_sel.subscribe( + async (_tradeproduct_qty_unit_tup_sel) => { + await kv.set(fmt_id(`qty_unit`), _tradeproduct_qty_unit_tup_sel); + }, + ); - 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; + const setup_test = async (): Promise<void> => { + try { + tradeproduct_key_sel.set(page_param.trade_product.key_default); + + await kv.set( + fmt_id(`summary`), + [`This is the first line`, `This is the another line`].join( + `.\n`, + ), + ); + } catch (e) { + console.log(`(error) setup_test `, e); + } + }; - $: 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]); + const kv_init = async (): Promise<void> => { + try { + const kv_pref = fmt_id(); + const range = Keyva.prefix(kv_pref); + const kv_list = await kv.each({ range }, `keys`); + await Promise.all(kv_list.map((k) => kv.set(k, ``))); + } catch (e) { + console.log(`(error) kv_init `, e); + } + }; - $: if (ls_models_location_gcs.length && !sel_location_gcs_id) { - sel_location_gcs_id = ls_models_location_gcs[0].id; - } + const handle_view = async (new_view: View): Promise<void> => { + try { + view = new_view; + } catch (e) { + console.log(`(error) handle_view `, e); + } + }; - $: if (sel_location_gcs_id) { - (async () => { - try { - await kv.set(fmt_id(`location_gcs_id`), sel_location_gcs_id); - } catch (e) {} - })(); - } + const toggle_tradeproduct_key = async ( + visible_input: boolean, + ): Promise<void> => { + try { + tradeproduct_key_sel_toggle = visible_input; + if (visible_input) { + tradeproduct_key_sel.set(``); + } else { + //@todo tradeproduct_key_sel = trade_keys[0]; + } + } catch (e) { + console.log(`(error) toggle_tradeproduct_key `, e); + } + }; - $: if (sel_trade_product_qty_tup) { - (async () => { - try { - 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`); - } - } catch (e) {} - })(); - } + const toggle_tradeproduct_qty_amt = async ( + visible_input: boolean, + ): Promise<void> => { + try { + tradeproduct_qty_unit_tup_sel_toggle = visible_input; + if (visible_input) { + tradeproduct_qty_unit_tup_sel.set(mass_units[0]); + } else { + $tradeproduct_qty_unit_tup_sel = tradeproduct_key_parsed + ? fmt_trade_quantity_sel_val( + trade.key[tradeproduct_key_parsed].quantity[0], + ) + : ``; + } + } catch (e) { + console.log(`(error) toggle_tradeproduct_qty_amt `, e); + } + }; - $: if (sel_trade_product_price_currency) { - (async () => { - try { - await kv.set( - fmt_id(`price_currency`), - sel_trade_product_price_currency, + const toggle_tradeproduct_process = async ( + visible_input: boolean, + ): Promise<void> => { + try { + tradeproduct_process_sel_toggle = visible_input; + if (visible_input) { + tradeproduct_process_sel.set(``); + } else { + tradeproduct_process_sel.set( + ls_trade_product_processes.length + ? ls_trade_product_processes[0] + : ``, ); - } catch (e) {} - })(); - } - - carousel_index.subscribe(async (_carousel_index) => { - switch (view) { - case `form_1`: { - switch (_carousel_index) { - case 1: - { - await handle_render_map_initial(); - } - break; - case 2: - { - 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); - } - break; - } } + } catch (e) { + console.log(`(error) toggle_tradeproduct_process `, e); } - }); + }; - const handle_view = async (new_view: View): Promise<void> => { + const handle_photo_add = async (): Promise<void> => { try { - view = new_view; + photo_add_loading = true; + const photo_paths = await dialog.open_photos(); + if (!photo_paths) { + return; //@todo + } + const file_path = photo_paths.results[0]; + photo_add_list = [ + ...photo_add_list, + { + file_path, + }, + ]; } catch (e) { - console.log(`(error) handle_view `, e); + console.log(`(error) handle_photo_add `, e); + } finally { + photo_add_loading = false; } }; - const handle_back = async (num?: number): Promise<void> => { + const handle_back = async (carousel_offset?: number): Promise<void> => { try { switch ($carousel_index) { default: @@ -269,397 +290,444 @@ } break; } - if (num) { - carousel_num.set(num); - carousel_index.set($carousel_index - (num - 1)); + if (carousel_offset) { + carousel_num.set(carousel_offset); + carousel_index.set($carousel_index - (carousel_offset - 1)); } } catch (e) { console.log(`(error) handle_back `, e); } }; - const handle_continue = async (num?: number): Promise<void> => { + const handle_continue = async (): Promise<void> => { try { switch ($carousel_index) { case 0: { - const vals_1 = await kv_validate_trade_product_fields({ - kv_pref: fmt_id(), - no_validation: [ - `year`, - `price_qty_amt`, - `qty_avail`, - `summary`, - `title`, - ], - }); - if (`err` in vals_1) { + const validate_fields = + await validate_trade_product_fields({ + kv_pref: fmt_id(), + fields: [`key`, `summary`], + }); + if (`err` in validate_fields) { await dialog.alert( - `${$t(`trade.product.fields.${vals_1.err}.err_invalid`, { default: `${$t(`icu.invalid_*`, { value: vals_1.err })}` })}`, + `${$t(`icu.enter_a_*`, { value: `${$t(`model.trade_product.${validate_fields.err}`)}`.toLowerCase() })}`, ); return; } - if (!vals_1.year) - await kv.set( - fmt_id(`year`), - new Date().getFullYear().toString(), - ); - if (!vals_1.price_qty_amt) - await kv.set(fmt_id(`price_qty_amt`), `1`); - if (!vals_1.qty_avail) - await kv.set(fmt_id(`qty_avail`), `1`); - const vals_2 = await kv_validate_trade_product_fields({ - kv_pref: fmt_id(), - no_validation: [`summary`, `title`], - }); - if (`err` in vals_2) { - await dialog.alert( - `${$t(`trade.product.fields.${vals_2.err}.err_invalid`, { default: `${$t(`icu.invalid_*`, { value: vals_2.err })}` })}`, - ); - return; + if (photo_add_list.length < 1) { + await dialog.alert(`A primary photo is required`); + return; //@todo } - preview_trade_product = vals_2; + await carousel_inc(view); } break; case 1: { - await carousel_inc(view); - } - break; - case 2: - { - await carousel_inc(view); - } - break; - case 3: - { - const vals = await kv_validate_trade_product_fields({ - kv_pref: fmt_id(), - }); - if (`err` in vals) { - const el = el_id(fmt_id(`${vals.err}_wrap`)); + const validate_fields = + await validate_trade_product_fields({ + kv_pref: fmt_id(), + fields: [ + `price_amt`, + `price_currency`, + `price_qty_unit`, + ], + }); + if (`err` in validate_fields) { await dialog.alert( - `${$t(`icu.enter_the_*`, { value: `${$t(`common.${vals.err}`)}`.toLowerCase() })}`, + `${$t(`icu.enter_the_*`, { value: `${$t(`model.trade_product.${validate_fields.err}`)}`.toLowerCase() })}`, ); - el?.classList.add(`entry-layer-1-highlight`); - await sleep(cfg.delay.entry_focus); - el?.classList.remove(`entry-layer-1-highlight`); return; } - await carousel_inc(view); - } - break; - case 4: - { - await submit(); + console.log( + JSON.stringify(validate_fields, null, 4), + `validate_fields`, + ); } break; } - if (num) { - carousel_num.set(num); - } } catch (e) { console.log(`(error) handle_continue `, e); } }; - const toggle_show_key_other = async (visible: boolean): Promise<void> => { - try { - show_sel_trade_product_key_other = visible; - if (visible) { - await kv.set(fmt_id(`key`), ``); - } else { - sel_trade_product_key = trade_keys[0]; - } - } catch (e) { - console.log(`(error) toggle_show_key_other `, e); - } - }; - - const toggle_show_qty_amt_other = async ( - visible: boolean, - ): Promise<void> => { - try { - show_sel_trade_product_qty_tup_other = visible; - if (visible) { - sel_trade_product_price_qty_unit = mass_units[0]; - await kv.set(fmt_id(`qty_amt`), ``); - } else { - sel_trade_product_qty_tup = sel_trade_product_key_parsed - ? fmt_trade_quantity_val( - trade_quantities[sel_trade_product_key_parsed][0], - ) - : ``; - } - } catch (e) { - console.log(`(error) toggle_show_qty_amt_other `, e); - } - }; - - const fetch_models_models_location_gcs = async (): Promise<void> => { - try { - const res = await db.location_gcs_get({ - list: [`all`], - }); - if (`results` in res) ls_models_location_gcs = res.results; - } catch (e) { - console.log(`(error) fetch_models_models_location_gcs `, e); - } - }; - - const handle_render_map_initial = async (): Promise<void> => { - try { - const geolc = await geol.current(); - if (!(`err` in geolc)) { - map_choose_loc_geoc_point = { - ...geolc, - }; - map_choose_loc_geoc_point_select = map_choose_loc_geoc_point; - } - await sleep(600); - } catch (e) { - console.log(`(error) handle_render_map_initial `, e); - } finally { - map_choose_loc_loading = false; - } - }; - - const handle_map_reset = (): void => { - map_choose_loc_geoc_point_select = map_choose_loc_geoc_point; - }; - - const handle_entry_focus = async (id_key: string): Promise<void> => { - try { - const el = el_id(fmt_id(id_key)); - el?.classList.add(`entry-layer-1-highlight`); - el?.focus(); - await handle_back(2); - await sleep(1000); - el?.classList.remove(`entry-layer-1-highlight`); - } catch (e) { - console.log(`(error) handle_entry_focus `, e); - } - }; - const submit = async (): Promise<void> => { try { - loading_submit = true; - console.log(`@todo submit`); } catch (e) { console.log(`(error) submit `, e); - } finally { - loading_submit = false; } }; </script> <LayoutView> <div - data-view={`load`} - class={`flex flex-col h-full w-full justify-start items-start`} - > - <Fill /> - </div> - <div - data-view={`form_1`} - class={`hidden flex flex-col h-full w-full justify-start items-center`} + in:fade={{ delay: 100, duration: 200 }} + out:fade={{ delay: 0, duration: 200 }} + data-carousel-container={`main`} + class={`carousel-container flex h-full w-full`} > <div - data-carousel-container={`form_1`} - class={`carousel-container flex h-full w-full ${view === `form_1` ? `fade-in` : ``}`} + data-carousel-item={`main`} + class={`carousel-item flex flex-col w-full pt-4 justify-start items-center`} > - <div - data-carousel-item={`form_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis basis={{ classes: `gap-5` }}> - <LayoutTrellisLine + <LayoutTrellis> + <ImageUploadRow + basis={{ + loading: photo_add_loading, + list: photo_add_list, + callback_add: handle_photo_add, + }} + /> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.product`)}`, + }, + notify: tradeproduct_key_sel_toggle + ? { + label: { + value: `${$t(`common.back`)}`, + }, + glyph: { + key: `selection-foreground`, + weight: `bold`, + dim: `xs`, + }, + callback: async () => { + const kv_other = await kv.get( + fmt_id(`key`), + ); + if (kv_other) { + const confirm = await dialog.confirm({ + message: `${$t(`icu.the_current_entry_*_will_be_deleted`, { value: kv_other })}. ${$t(`common.do_you_want_to_continue_q`)}`, + }); + if (confirm === false) return; + } + await toggle_tradeproduct_key(false); + }, + } + : undefined, + }} + > + {#if !tradeproduct_key_sel_toggle} + <EntrySelect + bind:value={$tradeproduct_key_sel} + basis={{ + wrap: { + id: fmt_id(`key_wrap`), + }, + el: { + id: fmt_id(`key`), + layer: 1, + options: [ + { + entries: [ + { + value: ``, + label: `Choose product`, + disabled: true, + }, + ...trade_keys.map((i) => ({ + value: i, + label: `${$t(`trade.product.key.${i}.name`, { default: i })}`, + })), + { + value: `other`, + label: `${$t(`common.other`)}`, + }, + ], + }, + ], + callback: async (opt) => { + const el = el_id(fmt_id(`key_wrap`)); + el?.classList.remove( + `entry-layer-1-highlight`, + ); + if (opt.value === `other`) { + await toggle_tradeproduct_key(true); + } + }, + }, + }} + /> + {:else} + <EntryLine + basis={{ + wrap: { + id: fmt_id(`key_wrap`), + }, + el: { + classes: `fade-in-long`, + id: fmt_id(`key`), + sync: true, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_name`, { value: `${$t(`common.product`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.title + .charset, + validate: + trade_product_form_fields.title + .validation, + validate_keypress: true, + }, + }, + }} + /> + {/if} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.description`)}`, + }, + }} + > + <EntryMultiline basis={{ - label: { - value: `${$t(`common.product`)}`, + wrap: { + id: fmt_id(`summary_wrap`), + }, + el: { + classes: `h-[14rem]`, + id: fmt_id(`summary`), + sync: true, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_description`, { value: `${$t(`common.listing`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.summary + .charset, + validate: + trade_product_form_fields.summary + .validation, + validate_keypress: true, + }, }, - notify: show_sel_trade_product_key_other - ? { - classes: `w-full justify-end`, - label: { - value: `${$t(`icu.show_*`, { value: `${$t(`common.options`)}` })}`, - }, - callback: async () => { - const kv_other = await kv.get( - fmt_id(`key`), - ); - if (kv_other) { - const confirm = - await dialog.confirm({ - message: `${$t(`icu.the_current_entry_*_will_be_deleted`, { value: kv_other })}. ${$t(`common.do_you_want_to_continue_q`)}`, - }); - if (confirm === true) - await kv.set( - fmt_id(`key`), - trade_key_default, - ); - } - await toggle_show_key_other(false); - }, - } - : undefined, + }} + /> + </LayoutTrellisLine> + </LayoutTrellis> + </div> + <div + data-carousel-item={`main`} + class={`carousel-item flex flex-col w-full pt-4 justify-start items-center`} + > + <ImageUploadDisplay + basis={{ + loading: photo_add_loading, + list: photo_add_list, + callback_add: handle_photo_add, + }} + /> + <LayoutTrellis> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`icu.*_price`, { value: `${$t(`common.product`)}` })}`, + }, + }} + > + <EntryWrap + basis={{ + classes: `pl-3`, + id: fmt_id(`price_wrap`), }} > - {#if !show_sel_trade_product_key_other} - <EntrySelect - bind:value={sel_trade_product_key} + <div + class={`flex flex-row justify-start items-center pr-3`} + > + <SelectElement + bind:value={$tradeproduct_price_curr_sel} basis={{ - wrap: { - id: fmt_id(`key_wrap`), - }, - el: { - 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) => { - const el = el_id( - fmt_id(`key_wrap`), - ); - el?.classList.remove( - `entry-layer-1-highlight`, - ); - if (val === `other`) { - await toggle_show_key_other( - true, - ); - } + id: fmt_id(`price_currency`), + classes: `w-fit font-circ font-[500] text-[1.2rem] -translate-y-[1px]`, + layer: false, + options: [ + { + entries: fiat_currencies.map( + (i) => ({ + value: `${i}`, + label: `${$t(`currency.${i}.symbol`, { default: i })}`, + }), + ), }, - }, + ], }} /> - {:else} - <EntryLine + </div> + <InputElement + basis={{ + id: fmt_id(`price_amt`), + layer: 1, + sync: true, + sync_init: true, + placeholder: `${$t(`common.price`)}`, + field: { + charset: + trade_product_form_fields.price_amt + .charset, + validate: + trade_product_form_fields.price_amt + .validation, + validate_keypress: true, + }, + callback: async ({ value, pass }) => { + const lastchar = value[value.length - 1]; + const period_count = + value.split(".").length - 1; + if ( + (pass && + lastchar !== `.` && + period_count < 2) || + value.length < 1 + ) { + const el = el_id(fmt_id(`price_wrap`)); + el?.classList.remove( + `entry-layer-1-highlight`, + ); + } else { + const el = el_id(fmt_id(`price_wrap`)); + el?.classList.add( + `entry-layer-1-highlight`, + ); + } + }, + callback_blur: async ({ el }) => { + if (!el.value) return; + el.value = price_locale_fmt( + $tradeproduct_price_curr_sel, + el.value, + ).slice(1); + }, + }} + /> + <div + class={`flex flex-row gap-2 justify-end items-center text-layer-1-glyph/70`} + > + <p class={`font-circ font-[500] text-[1.05rem]`}> + {`${$t(`common.per`)}`} + </p> + <SelectElement + bind:value={$tradeproduct_price_qty_unit_sel} basis={{ - wrap: { - id: fmt_id(`key_wrap`), - }, - el: { - id: fmt_id(`key`), - sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_name`, { value: `${$t(`common.product`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.title - .charset, - validate: - trade_product_form_fields.title - .validation, - validate_keypress: true, + id: fmt_id(`price_qty_unit`), + classes: `w-fit font-circ font-[500] text-[1.05rem]`, + layer: false, + options: [ + { + entries: mass_units.map((i) => ({ + value: i, + label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`.toLowerCase(), + })), }, - }, + ], }} /> - {/if} - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`common.price`)}`, - }, - }} - > + </div> + </EntryWrap> + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.quantity`)}`, + }, + notify: tradeproduct_qty_unit_tup_sel_toggle + ? { + label: { + value: `${$t(`common.back`)}`, + }, + glyph: { + key: `selection-foreground`, + weight: `bold`, + dim: `xs`, + }, + callback: async () => { + await toggle_tradeproduct_qty_amt(false); + }, + } + : undefined, + }} + > + {#if !tradeproduct_qty_unit_tup_sel_toggle} + <EntrySelect + bind:value={$tradeproduct_qty_unit_tup_sel} + basis={{ + wrap: { + id: fmt_id(`qty_wrap`), + }, + el: { + layer: 1, + options: [ + { + entries: [ + ...ls_trade_product_quantities.map( + (i) => ({ + value: fmt_trade_quantity_sel_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 ({ value }) => { + el_id( + fmt_id(`qty_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (value === `other`) { + await toggle_tradeproduct_qty_amt( + true, + ); + } else { + await kv.set( + fmt_id(`qty_avail`), + `1`, + ); + } + }, + }, + }} + /> + {:else} <EntryWrap basis={{ - id: fmt_id(`price_wrap`), + id: fmt_id(`qty_wrap`), }} > <InputElement basis={{ - id: fmt_id(`price_amt`), + id: fmt_id(`qty_amt`), layer: 1, sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_*`, { value: `${$t(`common.price`)}`.toLowerCase() })}`, + placeholder: `${$t(`icu.enter_*_per_order`, { value: `${$t(`common.quantity`)}`.toLowerCase() })}`, field: { charset: - trade_product_form_fields.price_amt + trade_product_form_fields.qty_amt .charset, validate: - trade_product_form_fields.price_amt + trade_product_form_fields.qty_amt .validation, validate_keypress: true, }, - callback: async ({ value, pass }) => { - const lastchar = - value[value.length - 1]; - const period_count = - value.split(".").length - 1; - if ( - (pass && - lastchar !== `.` && - period_count < 2) || - value.length < 1 - ) { - const el = el_id( - fmt_id(`price_wrap`), - ); - el?.classList.remove( - `entry-layer-1-highlight`, - ); - } else { - const el = el_id( - fmt_id(`price_wrap`), - ); - el?.classList.add( - `entry-layer-1-highlight`, - ); - } - }, }} /> <div - class={`flex flex-row gap-2 justify-end items-center text-layer-1-glyph/70`} + class={`absolute top-0 right-0 flex flex-row h-full pr-4 justify-end items-center`} > - <SelectEl - bind:value={sel_trade_product_price_currency} + <SelectElement + bind:value={$tradeproduct_price_qty_unit_sel} basis={{ - id: fmt_id(`price_currency`), - classes: `w-fit font-circ font-[500] text-[1rem]`, + id: fmt_id(`qty_unit`), + classes: `w-fit font-circ font-[500] text-[1.1rem] text-layer-1-glyph/70`, layer: false, - //hide_arrows: true, - //sync: true, - options: [ - { - entries: 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-[120%]`} - > - {`/`} - </p> - <SelectEl - bind:value={sel_trade_product_price_qty_unit} - basis={{ - id: fmt_id(`price_qty_unit`), - classes: `w-fit font-circ font-[500] text-[1rem]`, - layer: false, - // hide_arrows: true, - // sync: true, options: [ { entries: mass_units.map( @@ -674,47 +742,47 @@ /> </div> </EntryWrap> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`common.quantity`)}`, - }, - notify: show_sel_trade_product_qty_tup_other - ? { - classes: `w-full justify-end`, - label: { - value: `${$t(`icu.show_*`, { value: `${$t(`common.options`)}` })}`, - }, - callback: async () => { - await toggle_show_qty_amt_other( - false, - ); - }, - } - : undefined, - }} - > - <div - id={fmt_id(`qty_wrap`)} - tabindex={-1} - class={`relative el-re flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl`} - > - {#if !show_sel_trade_product_qty_tup_other} - <EntrySelect - bind:value={sel_trade_product_qty_tup} - basis={{ - wrap: { - id: `tmp`, - }, - el: { - options: [ - ...ls_trade_product_quantities.map( + {/if} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.process`)}`, + }, + notify: tradeproduct_process_sel_toggle + ? { + label: { + value: `${$t(`common.back`)}`, + }, + glyph: { + key: `selection-foreground`, + weight: `bold`, + dim: `xs`, + }, + glyph_last: true, + callback: async () => { + await toggle_tradeproduct_process(false); + }, + } + : undefined, + }} + > + {#if !tradeproduct_process_sel_toggle} + <EntrySelect + bind:value={$tradeproduct_process_sel} + basis={{ + wrap: { + id: fmt_id(`process_wrap`), + }, + el: { + layer: 1, + options: [ + { + entries: [ + ...ls_trade_product_processes.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: i, + label: `${$t(`trade.product.key.${tradeproduct_key_parsed}.process.${i}`)}`, }), ), { @@ -722,116 +790,35 @@ label: `${$t(`common.other`)}`, }, ], - callback: async (val) => { - el_id( - fmt_id(`qty_wrap`), - )?.classList.remove( - `entry-layer-1-highlight`, - ); - if (val === `other`) { - await toggle_show_qty_amt_other( - true, - ); - } else { - await kv.set( - fmt_id(`qty_avail`), - `1`, - ); - } - }, }, - }} - /> - {:else} - <div - class={`relative flex flex-row w-full justify-center items-center`} - > - <InputElement - basis={{ - id: fmt_id(`qty_amt`), - layer: false, - sync: true, - placeholder: `${$t(`icu.enter_number_of_*_per_order`, { value: `${$t(`measurement.mass.unit.${sel_trade_product_price_qty_unit}_ab`, { default: sel_trade_product_price_qty_unit })}` })}`, - 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`} - > - <EntrySelect - bind:value={sel_trade_product_qty_unit} - basis={{ - wrap: { - id: `tmp2`, - }, - el: { - 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> - {/if} - </div> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`model.trade_product.lot`)}`, - }, - }} - > - <EntryLine - basis={{ - el: { - id: fmt_id(`lot`), - sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_*`, { value: `${$t(`common.lot_name`)}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.lot - .charset, - validate: - trade_product_form_fields.lot - .validation, - validate_keypress: true, + ], + callback: async ({ value }) => { + el_id( + fmt_id(`process_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (value === `other`) { + await toggle_tradeproduct_process( + true, + ); + } }, }, }} /> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`model.trade_product.process`)}`, - }, - }} - > - <EntryLine + {:else} + <EntryWrap basis={{ - el: { + id: fmt_id(`process_wrap`), + }} + > + <InputElement + basis={{ id: fmt_id(`process`), + layer: 1, sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_*`, { value: `${$t(`icu.*_description`, { value: `${$t(`common.process`)}` })}`.toLowerCase() })}`, + placeholder: `Enter the process`, field: { charset: trade_product_form_fields.process @@ -841,599 +828,12 @@ .validation, validate_keypress: true, }, - }, - }} - /> - </LayoutTrellisLine> - </LayoutTrellis> - </div> - <div - data-carousel-item={`form_1`} - class={`carousel-item flex flex-col w-full pt-4 gap-4 justify-start items-center`} - > - <MapChooseLocation - basis={{ - classes_map: `h-[280px] w-full`, - loading: map_choose_loc_loading, - }} - bind:map_point_center={map_choose_loc_geoc_point} - bind:map_point_select={map_choose_loc_geoc_point_select} - bind:map_point_select_geoc={map_choose_loc_geoc_point_select_geoc} - /> - <div - class={`grid grid-cols-12 flex flex-col w-full px-4 gap-1 justify-start items-center`} - > - <div - class={`col-span-9 flex flex-row w-full justify-start items-center`} - > - <p - class={`w-[5.3rem] font-circ font-[500] text-layer-1-glyph-shade tracking-tight`} - > - {`${$t(`common.latitude`)}:`} - </p> - <p class={`font-circ font-[400] text-layer-1-glyph`}> - {fmt_geol_latitude( - map_choose_loc_geoc_point_select.lat, - `dms`, - )} - </p> - </div> - <div - class={`col-span-3 flex flex-row w-full justify-end items-center`} - > - <button - class={`group flex flex-row gap-[6px] justify-center items-center`} - on:click={async () => { - handle_map_reset(); - }} - > - <p - class={`font-sg font-[500] text-layer-1-glyph tracking-tight group-active:opacity-80 el-re`} - > - {`${$t(`common.reset`)}`} - </p> - <Glyph - basis={{ - classes: `text-layer-1-glyph/80 group-active:opacity-60 el-re`, - dim: `sm`, - weight: `bold`, - key: `arrow-counter-clockwise`, }} /> - </button> - </div> - <div - class={`col-span-9 flex flex-row w-full justify-start items-center`} - > - <p - class={`w-[5.3rem] font-circ font-[500] text-layer-1-glyph-shade tracking-tight`} - > - {`${$t(`common.longitude`)}:`} - </p> - <p class={`font-circ font-[400] text-layer-1-glyph`}> - {fmt_geol_longitude( - map_choose_loc_geoc_point_select.lng, - `dms`, - )} - </p> - </div> - {#if map_choose_loc_geoc_point_select_geoc} - <div - class={`col-span-9 flex flex-row w-full justify-start items-center`} - > - <p - class={`w-[5.3rem] font-circ font-[500] text-layer-1-glyph-shade tracking-tight`} - > - {`${$t(`common.location`)}:`} - </p> - <p - class={`font-circ font-[400] text-layer-1-glyph`} - > - {`${map_choose_loc_geoc_point_select_geoc.admin1_name}, ${map_choose_loc_geoc_point_select_geoc.country_name}`} - </p> - </div> - {/if} - </div> - </div> - <div - data-carousel-item={`form_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - {#if preview_trade_product && preview_trade_product_price_currency} - <LayoutTrellisLine> - <div - class={`relative flex flex-col h-[36rem] w-full px-6 pb-4 gap-8 justify-around items-start bg-layer-0-surface rounded-touch`} - > - <div - class={`flex flex-col flex-grow w-full justify-around items-center`} - > - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[400] text-[1.25rem] 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-1 justify-start items-center`} - > - <p - class={`font-mono font-[600] text-layer-2-glyph/40 pr-2`} - > - {`-`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade capitalize`} - > - {`${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 handle_entry_focus( - `key_wrap`, - ); - }} - > - <p - class={`label-sg font-[600] text-[1rem] text-layer-2-glyph`} - > - {`${$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-[1.25rem] text-layer-1-glyph`} - > - {`${$t(`common.origin`)}:`} - </p> - <div - class={`flex flex-row w-full justify-between items-center`} - > - <div - class={`flex flex-row flex-grow gap-1 justify-start items-center`} - > - <p - class={`font-mono font-[600] text-layer-2-glyph/40 pr-2`} - > - {`-`} - </p> - {#if map_choose_loc_geoc_point_select_geoc} - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade capitalize`} - > - {`${map_choose_loc_geoc_point_select_geoc.admin1_name}, ${map_choose_loc_geoc_point_select_geoc.country_id}`} - </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 handle_back(); - }} - > - <p - class={`label-sg font-[600] text-[1rem] text-layer-2-glyph`} - > - {`${$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-[1.25rem] 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-1 justify-start items-center`} - > - <p - class={`font-mono font-[600] text-layer-2-glyph/40 pr-2`} - > - {`-`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {preview_trade_product_price_currency.symbol} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {`${(preview_trade_product_price_currency.val_i + preview_trade_product_price_currency.val_f).toFixed(2)}`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade lowercase`} - > - {`${$t(`common.per`)}`} - </p> - {#if preview_trade_product.price_qty_amt === `1`} - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {`${$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 handle_entry_focus( - `price_wrap`, - ); - /*const el = el_id( - fmt_id( - `price_wrap`, - ), - ); - console.log(`el `, el); - el?.classList.add( - `entry-layer-1-highlight`, - ); - el?.focus(); - await handle_back(2); - await sleep( - cfg.delay - .entry_focus, - ); - el?.classList.remove( - `entry-layer-1-highlight`, - );*/ - }} - > - <p - class={`label-sg font-[600] text-[1rem] text-layer-2-glyph`} - > - {`${$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-[1.25rem] 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-1 justify-start items-center`} - > - <p - class={`font-mono font-[600] text-layer-2-glyph/40 pr-2`} - > - {`-`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {preview_trade_product.qty_amt} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {`${$t(`measurement.mass.unit.${preview_trade_product.qty_unit}_ab`, { default: preview_trade_product.qty_unit })}`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {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`), - ); - el?.classList.add( - `entry-layer-1-highlight`, - ); - el?.focus(); - await handle_back(2); - await sleep( - cfg.delay - .entry_focus, - ); - el?.classList.remove( - `entry-layer-1-highlight`, - ); - }} - > - <p - class={`label-sg font-[600] text-[1rem] text-layer-2-glyph`} - > - {`${$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-[1.25rem] 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-1 justify-start items-center`} - > - <p - class={`font-mono font-[600] text-layer-2-glyph/40 pr-2`} - > - {`-`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {`${preview_trade_product_qty_avail} ${preview_trade_product.qty_label}`} - </p> - <p - class={`font-circ font-[400] text-[1.3rem] text-layer-1-glyph-shade`} - > - {`(${(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`} - > - <ButtonGlyph - basis={{ - key: `arrow-down`, - dim: `xs+`, - weight: `bold`, - classes: `h-8 w-8 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(), - ); - }, - }} - /> - <ButtonGlyph - basis={{ - key: `arrow-up`, - dim: `xs+`, - weight: `bold`, - classes: `h-8 w-8 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`)}` })}:`} - </p> - </div> - <div - class={`flex flex-row gap-2 justify-start items-center`} - > - <p - class={`font-mono font-[400] text-layer-2-glyph text-lg`} - > - {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> + </EntryWrap> {/if} - </LayoutTrellis> - </div> - <div - data-carousel-item={`form_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`common.title`)}`, - }, - }} - > - <EntryLine - basis={{ - wrap: { - id: fmt_id(`title_wrap`), - }, - el: { - id: fmt_id(`title`), - sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_title`, { value: `${$t(`common.listing`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.title - .charset, - validate: - trade_product_form_fields.title - .validation, - validate_keypress: true, - }, - }, - }} - /> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$t(`common.summary`)}`, - }, - }} - > - <EntryMultiline - basis={{ - id_wrap: fmt_id(`summary_wrap`), - el: { - classes: `h-[20rem]`, - id: fmt_id(`summary`), - sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_summary`, { value: `${$t(`common.listing`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.summary - .charset, - validate: - trade_product_form_fields.summary - .validation, - validate_keypress: true, - }, - }, - }} - /> - </LayoutTrellisLine> - </LayoutTrellis> - </div> - <div - data-carousel-item={`form_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {`@todo add preview`} - </p> - </LayoutTrellis> - </div> + </LayoutTrellisLine> + </LayoutTrellis> </div> </div> </LayoutView> @@ -1456,7 +856,7 @@ }, title: { label: { - value: `${$t(`common.product`)}`, + value: `${$t(`icu.new_*`, { value: `${$t(`common.product`)}` })}`, }, callback: async () => {}, }, @@ -1466,15 +866,8 @@ value: $carousel_num > 1 ? `${$t(`common.return`)}` - : carousel_param.get($carousel_index)?.label_next || ``, - classes: `text-layer-1-glyph-hl`, - glyph: - $carousel_num > 1 - ? { - key: `caret-right`, - classes: `text-layer-1-glyph-hl`, - } - : undefined, + : page_param.carousel.get($carousel_index) + ?.label_next || ``, }, callback: async () => { if ($carousel_index === $carousel_index_max) await submit(); diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -140,7 +140,8 @@ const config: Config = { ...Object.fromEntries(Object.entries(dimensions).map(([k, v]) => [`dim_${k}`, v])), }, borderWidth: { - "line": "1px" + "line": `1px`, + "edge": `2px` }, borderRadius: { input_form: "8px", @@ -154,8 +155,6 @@ const config: Config = { ], daisyui: { themes: [ - themes.theme_garden_light, - themes.theme_garden_dark, themes.theme_earth_light, themes.theme_earth_dark, themes.theme_os_dark,