web


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

commit 5b5b105959c112b9c0dc5d7b9d420408f03aaa58
parent 7adf36d33c38d5d04e6bdabc19db65a6f3e1463b
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sun, 10 Nov 2024 06:58:37 +0000

Edit `models/trade-product/add` add model fields input and select elements, add map location point selection envelope.

Diffstat:
Asrc/lib/components/map_point_select_envelope.svelte | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/components/map_select_point.svelte | 105-------------------------------------------------------------------------------
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 511+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
3 files changed, 561 insertions(+), 178 deletions(-)

diff --git a/src/lib/components/map_point_select_envelope.svelte b/src/lib/components/map_point_select_envelope.svelte @@ -0,0 +1,123 @@ +<script lang="ts"> + import { geoc } from "$lib/client"; + import { cfg } from "$lib/conf"; + import type { GeocoderReverseResult } from "@radroots/geocoder"; + import { + app_thc, + envelope_visible, + EnvelopeLower, + fmt_geol_latitude, + fmt_geol_longitude, + t, + type CallbackPromise, + } from "@radroots/svelte-lib"; + import { MapLibre, Marker } from "@radroots/svelte-maplibre"; + import type { GeolocationCoordinatesPoint } from "@radroots/utils"; + import MapMarkerDot from "./map_marker_dot.svelte"; + import MapPopupPointGeolocation from "./map_popup_point_geolocation.svelte"; + + export let map_point_select: GeolocationCoordinatesPoint | undefined; + export let map_point_select_geoc: GeocoderReverseResult | undefined = + undefined; + + export let basis: { + visible: Boolean; + close: CallbackPromise; + }; + + let map_point_center: GeolocationCoordinatesPoint = cfg.map.coords.default; + + $: envelope_visible.set(!!basis.visible); + + $: if ( + map_point_select && + map_point_center.lat === 0 && + map_point_center.lng === 0 + ) { + map_point_center = { + lat: map_point_select.lat, + lng: map_point_select.lng - 0.065, + }; + } + + $: { + if ( + map_point_select && + map_point_center && + map_point_center.lat !== 0 && + map_point_center.lng !== 0 + ) { + (async () => { + try { + const geoc_res = await geoc.reverse({ + point: map_point_select, + }); + if (`results` in geoc_res && geoc_res.results.length > 0) + map_point_select_geoc = geoc_res.results[0]; + else map_point_select_geoc = undefined; + } catch (e) { + console.log(`(error) map choose location`, e); + } + })(); + } + } +</script> + +<EnvelopeLower + basis={{ + full_cover: true, + close: async () => { + await basis.close(); + }, + }} +> + {#if basis.visible && map_point_select} + <MapLibre + center={map_point_center} + zoom={10} + class={`h-full w-full`} + style={cfg.map.styles.base[$app_thc]} + attributionControl={false} + > + <Marker + bind:lngLat={map_point_select} + draggable + on:dragend={async () => { + if (!map_point_select) return; + const geoc_res = await geoc.reverse({ + point: map_point_select, + limit: 1, + }); + if (`results` in geoc_res && geoc_res.results.length > 0) + map_point_select_geoc = geoc_res.results[0]; + }} + > + <button + class={`flex flex-row justify-center items-center transform -translate-x-[42%]`} + on:click={async () => {}} + > + <MapPopupPointGeolocation + basis={{ + point: map_point_select, + geoc: map_point_select_geoc, + }} + /> + </button> + <MapMarkerDot /> + </Marker> + </MapLibre> + <div + class={`absolute top-6 left-4 flex flex-col min-w-[180px] px-4 py-1 justify-start items-start bg-layer-1-surface rounded-xl shadow-md`} + > + <p class={`font-sans font-[400] text-layer-0-glyph capitalize`}> + {`${`${$t(`common.current_location`)}`}:`} + </p> + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`${fmt_geol_latitude(map_point_select.lat, `dms`)}`} + </p> + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`${fmt_geol_longitude(map_point_select.lng, `dms`)}`} + </p> + </div> + {/if} +</EnvelopeLower> diff --git a/src/lib/components/map_select_point.svelte b/src/lib/components/map_select_point.svelte @@ -1,105 +0,0 @@ -<script lang="ts"> - import { geoc } from "$lib/client"; - import { cfg } from "$lib/conf"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { app_thc, fmt_cl, sleep } from "@radroots/svelte-lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; - import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - import { onMount } from "svelte"; - import MapMarkerDot from "./map_marker_dot.svelte"; - import MapPopupPointGeolocation from "./map_popup_point_geolocation.svelte"; - - export let basis: { - classes_wrap?: string; - classes?: string; - }; - $: basis = basis; - - let map_visible = false; - - export let map_point_select: GeolocationCoordinatesPoint; - export let map_point_select_geoc: GeocoderReverseResult | undefined = - undefined; - - let map_point_center: GeolocationCoordinatesPoint = cfg.map.coords.default; - - onMount(async () => { - try { - await sleep(300); - } catch (e) { - } finally { - map_visible = true; - } - }); - - $: if (!map_visible) { - map_point_center = { - lat: map_point_select.lat, - lng: map_point_select.lng - 0.065, - }; - } - - $: { - if ( - map_point_center && - map_point_center.lat !== 0 && - map_point_center.lng !== 0 - ) { - (async () => { - try { - const geoc_res = await geoc.reverse({ - point: map_point_select, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_point_select_geoc = geoc_res.results[0]; - else map_point_select_geoc = undefined; - } catch (e) { - console.log(`(error) map choose location`, e); - } - })(); - } - } -</script> - -<div - class={`${fmt_cl(basis.classes_wrap)} flex flex-col justify-start items-center`} -> - <div - class={`relative flex flex-col w-full justify-center items-center bg-layer-1-surface overflow-hidden`} - > - <MapLibre - center={map_point_center} - zoom={10} - class={`${fmt_cl(basis.classes || `h-full w-full`)} ${map_visible ? `fade-in` : `hidden`}`} - style={cfg.map.styles.base[$app_thc]} - attributionControl={false} - > - <Marker - bind:lngLat={map_point_select} - draggable - on:dragend={async () => { - if (!map_point_select) return; - const geoc_res = await geoc.reverse({ - point: map_point_select, - limit: 1, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_point_select_geoc = geoc_res.results[0]; - }} - > - <button - class={`flex flex-row justify-center items-center transform -translate-x-[42%]`} - on:click={async () => {}} - > - <MapPopupPointGeolocation - basis={{ - point: map_point_select, - geoc: map_point_select_geoc, - }} - /> - </button> - <MapMarkerDot /> - </Marker> - </MapLibre> - </div> -</div> diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,24 +1,37 @@ <script lang="ts"> + import { dialog, geol } from "$lib/client"; import ImageUploadMulti from "$lib/components/image_upload_multi.svelte"; + import MapPointSelectEnvelope from "$lib/components/map_point_select_envelope.svelte"; import { tradeproduct_init_kv } from "$lib/utils/trade_product"; + import type { GeocoderReverseResult } from "@radroots/geocoder"; + import { trade_product_form_fields } from "@radroots/models"; import { carousel_index, carousel_index_max, carousel_num, + el_id, + EntryLine, + EntrySelect, fmt_id, + kv, layout_view_cover, LayoutTrellis, + LayoutTrellisLine, LayoutView, Nav, t, view_effect, } from "@radroots/svelte-lib"; - import { type TradeKey } from "@radroots/utils"; + import { + parse_trade_key, + trade, + trade_keys, + type GeolocationCoordinatesPoint, + type TradeKey, + } from "@radroots/utils"; import { onMount } from "svelte"; import { fade } from "svelte/transition"; - const CAROUSEL_INDEX_MAP = 2; - type CarouselParam = { label_prev?: string; label_next: string; @@ -71,10 +84,28 @@ let load_submit = false; + let tradeproduct_photo_paths: string[] = []; + let tradeproduct_key_sel_toggle = false; let tradeproduct_key_sel = ``; + $: tradeproduct_key_parsed = parse_trade_key(tradeproduct_key_sel); + $: tradeproduct_key_quantities_list = tradeproduct_key_parsed + ? trade.key[tradeproduct_key_parsed].quantity + : trade.default.quantity; - let photo_add_paths: string[] = []; + let tradeproduct_process_sel = ``; + let tradeproduct_process_sel_toggle = false; + $: tradeproduct_process_list = tradeproduct_key_parsed + ? trade.key[tradeproduct_key_parsed].process + : []; + + let tradeproduct_location_sel = ``; + let tradeproduct_location_select_vis = false; + let tradeproduct_location_select_point: + | GeolocationCoordinatesPoint + | undefined = undefined; + let tradeproduct_location_select_geoc: GeocoderReverseResult | undefined = + undefined; onMount(async () => { try { @@ -90,8 +121,6 @@ const setup_tests = async (): Promise<void> => { try { - //const photo1 = `file:///Users/treesap/Library/Developer/CoreSimulator/Devices/04252089-B59A-4955-B1E0-84ECBAC1C28D/data/Containers/Data/Application/73E35A31-5CCD-4C1A-97C3-B5FA617DDB80/Library/Caches/IMG_0111.jpeg`; - //photo_add_list_paths = [photo1]; } catch (e) { console.log(`(error) setup_tests `, e); } @@ -99,13 +128,6 @@ const handle_view = async (view_new: View): Promise<void> => { try { - /* - const index_max_new = page_param.carousel[view_new].size - 1; - carousel_index_max.set(index_max_new); - if (view === `fl_2` && view_new === `fl_1`) - carousel_index.set(index_max_new); - else carousel_index.set(0); - */ carousel_index.set(0); view = view_new; } catch (e) { @@ -113,6 +135,60 @@ } }; + const handle_tradeproduct_key_toggle = async ( + vis_input: boolean, + ): Promise<void> => { + try { + const kv_curr = await kv.get(fmt_id(`key`)); + if (kv_curr) { + const confirm = await dialog.confirm({ + message: `${$t(`icu.the_current_entry_*_will_be_deleted`, { value: kv_curr })}. ${$t(`common.do_you_want_to_continue_q`)}`, + }); + if (!confirm) return; + } + tradeproduct_key_sel_toggle = vis_input; + if (vis_input) tradeproduct_key_sel = ``; + } catch (e) { + console.log(`(error) handle_tradeproduct_key_toggle `, e); + } + }; + + const handle_tradeproduct_process_toggle = async ( + vis_input: boolean, + ): Promise<void> => { + try { + const kv_curr = await kv.get(fmt_id(`process`)); + if (kv_curr) { + const confirm = await dialog.confirm({ + message: `${$t(`icu.the_current_entry_*_will_be_deleted`, { value: kv_curr })}. ${$t(`common.do_you_want_to_continue_q`)}`, + }); + if (!confirm) return; + } + tradeproduct_process_sel_toggle = vis_input; + if (vis_input) tradeproduct_process_sel = ``; + } catch (e) { + console.log(`(error) handle_tradeproduct_process_toggle `, e); + } + }; + + const handle_tradeproduct_location_sel_map_select = + async (): Promise<void> => { + try { + const geolc = await geol.current(); + if (`err` in geolc) return; //@todo + tradeproduct_location_select_vis = true; + tradeproduct_location_select_point = { + lat: geolc.lat, + lng: geolc.lng, + }; + } catch (e) { + console.log( + `(error) handle_tradeproduct_location_sel_map_select `, + e, + ); + } + }; + const handle_back = async (): Promise<void> => { try { } catch (e) { @@ -130,11 +206,11 @@ { console.log( JSON.stringify( - photo_add_paths, + tradeproduct_photo_paths, null, 4, ), - `photo_add_paths`, + `tradeproduct_photo_paths`, ); } break; @@ -169,71 +245,360 @@ class={`carousel-item flex flex-col w-full justify-start items-center`} > <LayoutTrellis> - <ImageUploadMulti bind:photo_paths={photo_add_paths} /> + <ImageUploadMulti + bind:photo_paths={tradeproduct_photo_paths} + /> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.product`)}`, + }, + notify: tradeproduct_key_sel_toggle + ? { + label: { + value: `${$t(`common.close`)}`, + }, + callback: async () => { + await handle_tradeproduct_key_toggle( + false, + ); + }, + } + : undefined, + }} + > + {#if !tradeproduct_key_sel_toggle} + <EntrySelect + bind:value={tradeproduct_key_sel} + basis={{ + wrap: { + id: fmt_id(`key_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`key`), + sync: true, + layer: 1, + options: [ + { + entries: [ + { + value: ``, + label: `${$t(`icu.choose_*`, { value: `${$t(`common.product`)}`.toLowerCase() })}`, + disabled: true, + }, + ...trade_keys.map((i) => ({ + value: i, + label: `${$t(`trade.product.key.${i}.name`, { default: i })}`, + })), + { + value: ``, + label: `${$t(`common.other`)}`, + }, + ], + }, + ], + callback: async (opt) => { + el_id( + fmt_id(`key_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (!opt.value) { + await handle_tradeproduct_key_toggle( + true, + ); + tradeproduct_key_sel = ``; + tradeproduct_process_sel = ``; + } else { + tradeproduct_process_sel = ``; + } + }, + }, + }} + /> + {:else} + <EntryLine + basis={{ + wrap: { + id: fmt_id(`key_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`key`), + layer: 1, + sync: true, + classes: `fade-in-long`, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_name`, { value: `${$t(`common.product`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.key + .charset, + validate: + trade_product_form_fields.key + .validation, + validate_keypress: true, + }, + }, + }} + /> + {/if} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.lot`)}`, + }, + }} + > + <EntryLine + basis={{ + wrap: { + id: fmt_id(`lot_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`lot`), + layer: 1, + sync: true, + classes: `fade-in-long`, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_name`, { value: `${$t(`common.lot`)}` })}`.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(`common.process`)}`, + }, + notify: tradeproduct_process_sel_toggle + ? { + label: { + value: `${$t(`common.close`)}`, + }, + callback: async () => { + await handle_tradeproduct_process_toggle( + false, + ); + }, + } + : undefined, + }} + > + {#if !tradeproduct_process_sel_toggle} + <EntrySelect + bind:value={tradeproduct_process_sel} + basis={{ + wrap: { + id: fmt_id(`process_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`process`), + sync: true, + layer: 1, + options: [ + { + entries: [ + { + value: ``, + label: `${$t(`icu.choose_*`, { value: `${$t(`common.process`)}`.toLowerCase() })}`, + disabled: true, + }, + ...tradeproduct_process_list.map( + (i) => ({ + value: i, + label: `${$t(`trade.product.key.${tradeproduct_key_parsed}.process.${i}`)}`, + }), + ), + { + value: ``, + label: `${$t(`common.other`)}`, + }, + ], + }, + ], + callback: async ({ value }) => { + el_id( + fmt_id(`process_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (!value) { + await handle_tradeproduct_process_toggle( + true, + ); + } + }, + }, + }} + /> + {:else} + <EntryLine + basis={{ + wrap: { + id: fmt_id(`process_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`process`), + layer: 1, + sync: true, + classes: `fade-in-long`, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`common.process`)}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields + .process.charset, + validate: + trade_product_form_fields + .process.validation, + validate_keypress: true, + }, + }, + }} + /> + {/if} + </LayoutTrellisLine> + <LayoutTrellisLine + basis={{ + label: { + value: `${$t(`common.location`)}`, + }, + }} + > + <EntrySelect + bind:value={tradeproduct_location_sel} + basis={{ + wrap: { + id: fmt_id(`tradeproduct_location_wrap`), + layer: 1, + }, + el: { + id: fmt_id(`tradeproduct_location`), + sync: true, + layer: 1, + options: [ + { + entries: + tradeproduct_location_select_geoc + ? [ + { + value: `map-select`, + label: `${tradeproduct_location_select_geoc.name}, ${tradeproduct_location_select_geoc.admin1_name}, ${tradeproduct_location_select_geoc.country_id}`, + }, + { + value: `map-select`, + label: `Choose new location`, + }, + ] + : [ + { + value: ``, + label: `${$t(`icu.choose_*`, { value: `${$t(`common.location`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `map-select`, + label: `Choose on map`, + }, + { + value: ``, + label: `Clear`, + }, + ], + }, + ], + callback: async ({ value }) => { + el_id( + fmt_id(`process_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (!value) { + //@todo + } else if (value === `map-select`) { + await handle_tradeproduct_location_sel_map_select(); + } + }, + }, + }} + /> + </LayoutTrellisLine> </LayoutTrellis> </div> </div> </div> </LayoutView> -{#if $carousel_index !== CAROUSEL_INDEX_MAP} - <div - in:fade={{ delay: 0, duration: 50 }} - out:fade={{ delay: 50, duration: 200 }} - class={`flex flex-col w-full justify-start items-center fade-in`} - > - <Nav - basis={{ - prev: { - label: `${$t(`common.back`)}`, - route: `/models/trade-product`, - prevent_route: - view === `fl_1` && $carousel_index === 0 - ? undefined - : { - callback: async () => { - await handle_back(); - }, - }, - callback: async () => { - await tradeproduct_init_kv(fmt_id()); - }, - }, - title: - $carousel_index === CAROUSEL_INDEX_MAP +<div + in:fade={{ delay: 0, duration: 50 }} + out:fade={{ delay: 50, duration: 200 }} + class={`flex flex-col w-full justify-start items-center fade-in`} +> + <Nav + basis={{ + prev: { + label: `${$t(`common.back`)}`, + route: `/models/trade-product`, + prevent_route: + view === `fl_1` && $carousel_index === 0 ? undefined : { - label: { - value: `${$t(`icu.new_*`, { value: `${$t(`common.product`)}` })}`, + callback: async () => { + await handle_back(); }, - callback: async () => {}, }, - option: { - loading: load_submit, - label: - $carousel_index === CAROUSEL_INDEX_MAP - ? undefined - : { - value: - $carousel_num > 1 - ? `${$t(`common.return`)}` - : page_param.carousel[view].get( - $carousel_index, - )?.label_next || ``, - glyph: - $carousel_index > 0 - ? { - key: `caret-right`, - classes: `text-layer-1-glyph-hl`, - } - : undefined, - }, - callback: async () => { - if ($carousel_index === $carousel_index_max) - await submit(); - else await handle_continue(); - }, + callback: async () => { + await tradeproduct_init_kv(fmt_id()); }, - }} - /> - </div> -{/if} + }, + title: { + label: { + value: `${$t(`icu.new_*`, { value: `${$t(`common.product`)}` })}`, + }, + callback: async () => {}, + }, + option: { + loading: load_submit, + label: { + value: + $carousel_num > 1 + ? `${$t(`common.return`)}` + : page_param.carousel[view].get($carousel_index) + ?.label_next || ``, + glyph: + $carousel_index > 0 + ? { + key: `caret-right`, + classes: `text-layer-1-glyph-hl`, + } + : undefined, + }, + callback: async () => { + if ($carousel_index === $carousel_index_max) await submit(); + else await handle_continue(); + }, + }, + }} + /> +</div> +<MapPointSelectEnvelope + bind:map_point_select={tradeproduct_location_select_point} + bind:map_point_select_geoc={tradeproduct_location_select_geoc} + basis={{ + visible: tradeproduct_location_select_vis, + close: async () => { + tradeproduct_location_select_vis = false; + }, + }} +/>