commit 7adf36d33c38d5d04e6bdabc19db65a6f3e1463b
parent dcec3215bdcdc44a8e78c0ca81e646cf354cb947
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sun, 10 Nov 2024 05:03:19 +0000
Edit `nostr_profile_relay` model fields. Edit `/models/trade-product/add` update photo upload component, add photo upload handlers. Update `/cfg/init` components. Edit map components. Add geocode uitls. Add/edit styles, utils.
Diffstat:
17 files changed, 746 insertions(+), 1131 deletions(-)
diff --git a/crates/tauri/migrations/0005_nostr_profile_relay.sql b/crates/tauri/migrations/0005_nostr_profile_relay.sql
@@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS nostr_profile_relay (
- tb_pr_rl_0 CHAR(36)
- tb_pr_rl_1 CHAR(36)
- FOREIGN KEY (tb_pr_rl_0) REFERENCES nostr_profile(id) ON DELETE CASCADE
- FOREIGN KEY (tb_pr_rl_1) REFERENCES nostr_relay(id) ON DELETE CASCADE
+ tb_pr_rl_0 CHAR(36),
+ tb_pr_rl_1 CHAR(36),
+ FOREIGN KEY (tb_pr_rl_0) REFERENCES nostr_profile(id) ON DELETE CASCADE,
+ FOREIGN KEY (tb_pr_rl_1) REFERENCES nostr_relay(id) ON DELETE CASCADE,
PRIMARY KEY (tb_pr_rl_0, tb_pr_rl_1)
);
\ No newline at end of file
diff --git a/package.json b/package.json
@@ -22,6 +22,7 @@
"@sveltejs/adapter-static": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@tailwindcss/aspect-ratio": "^0.4.2",
"@tauri-apps/cli": "2.0.4",
"@types/node": "^20.12.7",
"@types/sql.js": "^1.4.9",
diff --git a/src/app.css b/src/app.css
@@ -8,6 +8,7 @@
@import "/static/webfonts/lust/styles.css";
@import "/static/webfonts/magda-text/styles.css";
@import "/static/webfonts/sf-pro-display/styles.css";
+@import "/static/webfonts/sf-pro-rounded/styles.css";
@import "/static/webfonts/circular/styles.css";
@import "/static/webfonts/archivo/styles.css";
@import "/static/webfonts/space-grotesk/styles.css";
diff --git a/src/lib/components/image_upload_display.svelte b/src/lib/components/image_upload_display.svelte
@@ -1,84 +0,0 @@
-<script lang="ts">
- import { fs } from "$lib/client";
- import {
- Glyph,
- ImageBlob,
- type CallbackPromise,
- type CallbackPromiseGeneric,
- } from "@radroots/svelte-lib";
-
- export let basis: {
- loading: boolean;
- file_paths: string[];
- callback_add: CallbackPromise;
- callback_edit: CallbackPromiseGeneric<number>;
- };
-</script>
-
-<div
- class={`flex flex-row h-[12rem] w-full px-4 gap-4 justify-center items-center`}
->
- <div class={`flex flex-col h-[11rem] gap-1 justify-center items-center`}>
- <button
- on:click={async () => {
- await basis.callback_edit(0);
- }}
- class={`group relative flex flex-col h-36 w-36 justify-center items-center rounded-[4.2rem] border-edge border-layer-0-glyph active:border-layer-0-glyph/60 overflow-hidden el-re`}
- >
- {#if basis.file_paths[0]}
- {#await fs.read_bin(basis.file_paths[0]) then file_data}
- <ImageBlob
- basis={{
- data: file_data,
- }}
- />
- {/await}
- {/if}
- <div
- class={`z-10 absolute bottom-0 flex flex-row h-8 w-full justify-center items-start bg-layer-2-surface active:bg-layer-1-surface`}
- >
- <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>
- </div>
- </button>
- </div>
- <div
- class={`grid grid-cols-12 flex flex-row gap-y-2 justify-start items-center`}
- >
- {#each Array(6).fill(0) as _, li_i}
- <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-touch border-line border-layer-0-glyph/90 active:border-layer-0-glyph/60 overflow-hidden`}
- on:click={async () => {
- if (basis.file_paths[li_i + 1])
- await basis.callback_edit(li_i + 1);
- else await basis.callback_add();
- }}
- >
- {#if basis.file_paths[li_i + 1]}
- {#await fs.read_bin(basis.file_paths[li_i + 1]) then file_data}
- <ImageBlob
- basis={{
- data: file_data,
- }}
- />
- {/await}
- {:else}
- <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`,
- }}
- />
- {/if}
- </button>
- </div>
- {/each}
- </div>
-</div>
diff --git a/src/lib/components/image_upload_multi.svelte b/src/lib/components/image_upload_multi.svelte
@@ -0,0 +1,397 @@
+<script lang="ts">
+ import { dialog, fs } from "$lib/client";
+ import {
+ app_layout,
+ envelope_visible,
+ EnvelopeLower,
+ Glyph,
+ ImageBlob,
+ t,
+ time_iso,
+ } from "@radroots/svelte-lib";
+ import {
+ format_file_bytes,
+ list_assign,
+ list_move_index,
+ parse_file_name,
+ } from "@radroots/utils";
+ import { fade } from "svelte/transition";
+
+ export let photo_paths: string[];
+
+ let photo_file_path_0: string;
+ let photo_file_path_1: string;
+ let photo_file_path_2: string;
+ let photo_file_path_3: string;
+ let photo_file_path_4: string;
+ let photo_file_path_5: string;
+ let photo_file_path_6: string;
+ let photo_edit_envelope: { index: number; file_path: string } | undefined;
+
+ $: envelope_visible.set(!!photo_edit_envelope);
+
+ $: if (photo_paths.length > 0) {
+ if (photo_paths[0]) photo_file_path_0 = photo_paths[0];
+ if (photo_paths[1]) photo_file_path_1 = photo_paths[1];
+ if (photo_paths[2]) photo_file_path_2 = photo_paths[2];
+ if (photo_paths[3]) photo_file_path_3 = photo_paths[3];
+ if (photo_paths[4]) photo_file_path_4 = photo_paths[4];
+ if (photo_paths[5]) photo_file_path_5 = photo_paths[5];
+ if (photo_paths[6]) photo_file_path_6 = photo_paths[6];
+ }
+
+ const handle_photo_add = async (): Promise<void> => {
+ try {
+ const photo_paths_select = await dialog.open_photos();
+ if (!photo_paths_select) return;
+ photo_paths = list_assign(photo_paths, photo_paths_select.results);
+ } catch (e) {
+ console.log(`(error) handle_photo_add `, e);
+ }
+ };
+
+ const handle_photo_envelope_edit = async (
+ opts_photo_index: number,
+ ): Promise<void> => {
+ try {
+ if (!photo_paths[opts_photo_index]) {
+ photo_edit_envelope = undefined;
+ return;
+ }
+ photo_edit_envelope = {
+ index: opts_photo_index,
+ file_path: photo_paths[opts_photo_index],
+ };
+ } catch (e) {
+ console.log(`(error) handle_photo_envelope_edit `, e);
+ }
+ };
+
+ const handle_photo_envelope_edit_move = async (
+ opts_photo_index: number,
+ ): Promise<void> => {
+ try {
+ photo_paths = list_move_index(photo_paths, opts_photo_index, 0);
+ } catch (e) {
+ console.log(`(error) handle_photo_envelope_edit_move `, e);
+ }
+ };
+</script>
+
+<div class={`flex flex-col h-[13rem] w-full px-4 justify-center items-center`}>
+ <div
+ class={`flex flex-row h-[11rem] w-[22rem] justify-center items-center bg-layer-1-surface rounded-[2rem] overflow-hidden`}
+ >
+ <button
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_0) await handle_photo_envelope_edit(0);
+ else await handle_photo_add();
+ }}
+ class={`group relative flex flex-col h-[11rem] w-[11rem] justify-center items-center border-r-line border-layer-0-glyph_d rounded-tl-3xl rounded-bl-3xl overflow-hidden el-re`}
+ >
+ {#if photo_file_path_0}
+ {#await fs.read_bin(photo_file_path_0) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {: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 delay-100 duration-300 ease-in-out transition-all`,
+ dim: `lg`,
+ key: `camera`,
+ }}
+ />
+ </div>
+ {/if}
+ </button>
+ <div
+ class={`flex flex-row flex-wrap h-full w-[11rem] justify-start items-start`}
+ >
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-r-line border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_1) await handle_photo_envelope_edit(1);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_1}
+ {#await fs.read_bin(photo_file_path_1) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-r-line border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_2) await handle_photo_envelope_edit(2);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_2}
+ {#await fs.read_bin(photo_file_path_2) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_3) await handle_photo_envelope_edit(3);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_3}
+ {#await fs.read_bin(photo_file_path_3) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-r-line border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_4) await handle_photo_envelope_edit(4);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_4}
+ {#await fs.read_bin(photo_file_path_4) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-r-line border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_5) await handle_photo_envelope_edit(5);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_5}
+ {#await fs.read_bin(photo_file_path_5) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ <button
+ class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-layer-0-glyph_d overflow-hidden`}
+ on:click|stopPropagation={async () => {
+ if (photo_file_path_6) await handle_photo_envelope_edit(6);
+ else await handle_photo_add();
+ }}
+ >
+ {#if photo_file_path_6}
+ {#await fs.read_bin(photo_file_path_6) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`,
+ dim: `sm`,
+ key: `plus`,
+ }}
+ />
+ {/if}
+ </button>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row h-8 w-${$app_layout} justify-start items-center`}
+ >
+ <div class={`flex flex-row w-[11rem] justify-center items-center`}>
+ <p class={`font-sans font-[500] text-sm text-layer-0-glyph`}>
+ {`${$t(`icu.primary_*`, { value: `${$t(`common.photo`)}` })}`}
+ </p>
+ </div>
+ </div>
+</div>
+<EnvelopeLower
+ basis={{
+ close: async () => {
+ photo_edit_envelope = undefined;
+ },
+ }}
+>
+ {#if photo_edit_envelope}
+ <div
+ class={`flex flex-col w-full px-4 gap-4 justify-start items-center`}
+ >
+ <div
+ class={`flex flex-row w-full justify-center items-center round-44 overflow-hidden`}
+ >
+ {#await fs.read_bin(photo_edit_envelope.file_path) then file_data}
+ <ImageBlob
+ basis={{
+ data: file_data,
+ }}
+ />
+ {/await}
+ </div>
+ <div
+ class={`flex flex-col w-full pb-16 gap-4 justify-center items-center`}
+ >
+ <button
+ class={`flex flex-row h-touch_guide w-full justify-center items-center rounded-2xl bg-layer-1-glyph-hl`}
+ on:click={async () => {
+ if (!photo_edit_envelope) {
+ photo_edit_envelope = undefined;
+ return;
+ } else if (photo_edit_envelope.index === 0) return;
+ await handle_photo_envelope_edit_move(
+ photo_edit_envelope.index,
+ );
+ photo_edit_envelope = undefined;
+ }}
+ >
+ <p class={`font-sans font-[600] text-lg text-white`}>
+ {#if photo_edit_envelope.index === 0}
+ {`${$t(`icu.primary_*`, { value: `${$t(`common.photo`)}`.toLowerCase() })}`}
+ {:else}
+ {`${$t(`common.make_primary`)}`}
+ {/if}
+ </p>
+ </button>
+ {#await fs.info(photo_edit_envelope.file_path) then fs_info}
+ {#if fs_info}
+ <div
+ class={`flex flex-col w-full px-4 gap-3 justify-start items-start`}
+ >
+ <div
+ class={`flex flex-col gap-1 justify-start items-start`}
+ >
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${$t(`common.file_name`)}:`}
+ </p>
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${parse_file_name(photo_edit_envelope.file_path)}`}
+ </p>
+ </div>
+ <div
+ class={`flex flex-col gap-1 justify-start items-start`}
+ >
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${$t(`common.file_size`)}:`}
+ </p>
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${format_file_bytes(fs_info.size, `mb`)}`}
+ </p>
+ </div>
+ <div
+ class={`flex flex-col gap-1 justify-start items-start`}
+ >
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${$t(`common.date_created`)}:`}
+ </p>
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${time_iso(fs_info.birthtime?.toISOString(), `file_info`).replaceAll(`,`, ` ${`${$t(`common.at`)}`.toLowerCase()}`)}`}
+ </p>
+ </div>
+ <div
+ class={`flex flex-col gap-1 justify-start items-start`}
+ >
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${$t(`common.date_modified`)}:`}
+ </p>
+ <p
+ class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`}
+ >
+ {`${time_iso(fs_info.mtime?.toISOString(), `file_info`).replaceAll(`,`, ` ${`${$t(`common.at`)}`.toLowerCase()}`)}`}
+ </p>
+ </div>
+ </div>
+ {/if}
+ {/await}
+ </div>
+ </div>
+ {/if}
+</EnvelopeLower>
diff --git a/src/lib/components/image_upload_row.svelte b/src/lib/components/image_upload_row.svelte
@@ -1,107 +0,0 @@
-<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;
- file_paths: string[];
- callback_add: CallbackPromise;
- };
-</script>
-
-<div
- class={`flex flex-row w-[100vw] ${basis.file_paths.length > 1 ? `justify-start` : `justify-center`} items-center overflow-x-auto scroll-hide`}
->
- <div
- class={`flex flex-row ${basis.file_paths.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.file_paths.length === 0) await basis.callback_add();
- }}
- class={`group flex flex-col gap-2 justify-start items-center`}
- >
- <div
- class={`flex flex-row ${basis.file_paths.length ? `h-52 w-52` : `h-36 w-36`} justify-center items-center rounded-full border-edge border-layer-0-glyph ${basis.file_paths.length === 0 ? `group-active:border-layer-0-glyph/60 ` : ``} overflow-hidden delay-100 duration-300 ease-in-out transition-all`}
- >
- {#if basis.file_paths.length}
- {#await fs.read_bin(basis.file_paths[0]) 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.file_paths.length > 1}
- {#each basis.file_paths.slice(1) as file_path}
- <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(file_path) then file_data}
- <ImageBlob
- basis={{
- data: file_data,
- }}
- />
- {/await}
- </button>
- {/each}
- {/if}
- </div>
-</div>
diff --git a/src/lib/components/map_choose_location.svelte b/src/lib/components/map_choose_location.svelte
@@ -1,102 +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 MapPopupLocationInfo from "./map_popup_location_info.svelte";
-
- export let basis: {
- classes_wrap?: string;
- classes?: string;
- };
- $: basis = basis;
-
- let map_visible = false;
-
- //export let map_point_center: GeolocationCoordinatesPoint;
- export let map_point_select: GeolocationCoordinatesPoint;
- export let map_point_select_geoc: GeocoderReverseResult | undefined =
- undefined;
-
- let map_point_center: GeolocationCoordinatesPoint | undefined = undefined;
-
- onMount(async () => {
- try {
- map_point_center = {
- ...map_point_select,
- };
- await sleep(300);
- } catch (e) {
- } finally {
- map_visible = true;
- }
- });
-
- $: {
- 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_center,
- });
- 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 () => {}}
- >
- <MapPopupLocationInfo
- basis={{
- point: map_point_select,
- geoc: map_point_select_geoc,
- }}
- />
- </button>
- <MapMarkerDot />
- </Marker>
- </MapLibre>
- </div>
-</div>
diff --git a/src/lib/components/map_popup_location_info.svelte b/src/lib/components/map_popup_point_geolocation.svelte
diff --git a/src/lib/components/map_select_point.svelte b/src/lib/components/map_select_point.svelte
@@ -0,0 +1,105 @@
+<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/lib/utils/geocode.ts b/src/lib/utils/geocode.ts
@@ -0,0 +1,13 @@
+import { geoc } from "$lib/client";
+import type { GeocoderReverseResult } from "@radroots/geocoder";
+import type { GeolocationCoordinatesPoint } from "@radroots/utils";
+
+export const geoc_rev = async (point: GeolocationCoordinatesPoint): Promise<GeocoderReverseResult | undefined> => {
+ try {
+ const geoc_res = await geoc.reverse({ point });
+ if (`results` in geoc_res && geoc_res.results.length > 0)
+ return geoc_res.results[0];
+ } catch (e) {
+ console.log(`(error) geoc_rev `, e);
+ }
+};
+\ No newline at end of file
diff --git a/src/lib/utils/kv.ts b/src/lib/utils/kv.ts
@@ -1,6 +1,4 @@
-import { parse_trade_product_form_keys, trade_product_form_fields } from "@radroots/models";
import { fmt_id, kv } from "@radroots/svelte-lib";
-import { err_msg, type ErrorMessage, type ResultPass } from "@radroots/utils";
export const kv_init_page = async (): Promise<void> => {
try {
@@ -13,38 +11,13 @@ export const kv_init_page = async (): Promise<void> => {
}
};
-export const kv_init_trade_product_fields = async (kv_pref: string): Promise<void> => {
+export const kv_sync = async (list: [string, string][]): Promise<void> => {
try {
- for (const k of Object.keys(
- trade_product_form_fields,
- )) {
- const field_k = parse_trade_product_form_keys(k);
- if (!field_k) continue;
- const field_id = `${kv_pref}-${field_k}`
- await kv.delete(field_id);
+ for (const [key, val] of list) {
+ await kv.set(key, val);
+ // await sleep(50);
}
-
- } catch (e) {
- console.log(`(error) kv_init_trade_product_fields `, 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))
+ console.log(`(error) kv_sync `, e);
}
};
\ No newline at end of file
diff --git a/src/lib/utils/trade_product.ts b/src/lib/utils/trade_product.ts
@@ -1,6 +1,6 @@
import { parse_trade_product_form_keys, trade_product_form_fields, trade_product_form_vals, type IModelsForm, type TradeProductFormFields } from "@radroots/models";
import { fmt_id, kv } from "@radroots/svelte-lib";
-import { err_msg, type ErrorMessage } from "@radroots/utils";
+import { err_msg, type ErrorMessage, type ResultPass } from "@radroots/utils";
const trade_products_field_validate = (field: IModelsForm, field_val: string): boolean => {
if (
@@ -72,7 +72,7 @@ export const trade_product_fields_validate = async (opts: {
}
};
-export const trade_product_fields_kv_validate = async (opts?: {
+export const tradeproduct_validate_kv = async (opts?: {
kv_pref?: string;
fields_pass?: string[] | true;
}): Promise<TradeProductFormFields | ErrorMessage<string>> => {
@@ -96,7 +96,42 @@ export const trade_product_fields_kv_validate = async (opts?: {
}
return vals;
} catch (e) {
- console.log(`(error) trade_product_fields_kv_validate `, e);
+ console.log(`(error) tradeproduct_validate_kv `, e);
return err_msg(String(e))
}
};
+
+
+export const tradeproduct_init_kv = async (kv_pref: string): Promise<void> => {
+ try {
+ for (const k of Object.keys(
+ trade_product_form_fields,
+ )) {
+ const field_k = parse_trade_product_form_keys(k);
+ if (!field_k) continue;
+ const field_id = `${kv_pref}-${field_k}`
+ await kv.delete(field_id);
+ }
+ } catch (e) {
+ console.log(`(error) tradeproduct_init_kv `, e);
+ }
+};
+
+export const tradeproduct_validate_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);
+ if (!trade_product_form_fields[field_k].validation.test(field_val)) return err_msg(field_k);
+ }
+ return { pass: true };
+ } catch (e) {
+ console.log(`(error) tradeproduct_validate_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,7 +240,9 @@
{
icon: `squares-four`,
label: `Menu`,
- callback: async () => {},
+ callback: async () => {
+ await route(`/settings`);
+ },
},
],
}}
diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte
@@ -1,300 +1,120 @@
<script lang="ts">
- import { dialog } from "$lib/client";
- import ImageUploadDisplay from "$lib/components/image_upload_display.svelte";
- import ImageUploadRow from "$lib/components/image_upload_row.svelte";
+ import ImageUploadMulti from "$lib/components/image_upload_multi.svelte";
+ import { tradeproduct_init_kv } from "$lib/utils/trade_product";
import {
- kv_init_trade_product_fields,
- validate_trade_product_fields,
- } from "$lib/utils/kv";
- import { trade_product_form_fields } from "@radroots/models";
- import {
- carousel_dec,
- carousel_inc,
carousel_index,
carousel_index_max,
carousel_num,
- el_id,
- EntryLine,
- EntryMultiline,
- EntrySelect,
- EntryWrap,
fmt_id,
- fmt_price,
- InputElement,
- kv,
+ layout_view_cover,
LayoutTrellis,
- LayoutTrellisLine,
LayoutView,
Nav,
- SelectElement,
t,
view_effect,
} from "@radroots/svelte-lib";
- import {
- fiat_currencies,
- fmt_trade_quantity_tup,
- mass_units,
- parse_trade_key,
- trade,
- trade_keys,
- type TradeKey,
- } from "@radroots/utils";
+ import { type TradeKey } from "@radroots/utils";
import { onMount } from "svelte";
- import { writable } from "svelte/store";
import { fade } from "svelte/transition";
+ const CAROUSEL_INDEX_MAP = 2;
+
type CarouselParam = {
label_prev?: string;
label_next: string;
};
const page_param: {
+ carousel: Record<View, Map<number, CarouselParam>>;
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`)}`,
- },
- ],
- ]),
+ carousel: {
+ fl_1: new Map<number, CarouselParam>([
+ [
+ 0,
+ {
+ label_next: `${$t(`common.add`)}`,
+ },
+ ],
+ [
+ 1,
+ {
+ label_next: `${$t(`common.location`)}`,
+ },
+ ],
+ [
+ 2,
+ {
+ label_next: `*`,
+ },
+ ],
+ [
+ 3,
+ {
+ label_next: `Post`,
+ },
+ ],
+ ]),
+ },
trade_product: {
key_default: `coffee`,
},
};
- type View = `main` | `finish`;
- let view: View = `main`;
+ let view_init: View = `fl_1`;
+ type View = `fl_1`;
+ let view: View = view_init;
$: {
view_effect<View>(view);
}
- let loading_submit = false;
-
- let photo_add_loading = false;
- let photo_add_list: { file_path: string }[] = [];
+ let load_submit = false;
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;
-
- const tradeproduct_price_curr_sel = writable<string>(``);
- const tradeproduct_price_qty_unit_sel = writable<string>(``);
+ let tradeproduct_key_sel = ``;
- const tradeproduct_qty_unit_tup_sel = writable<string>(``);
- let tradeproduct_qty_unit_tup_sel_toggle = false;
-
- 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 photo_add_paths: string[] = [];
onMount(async () => {
try {
- await kv_init();
- await handle_view(`main`);
+ await handle_view(view_init);
+ layout_view_cover.set(false);
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();
+ carousel_index_max.set(page_param.carousel[view].size - 1);
+ await setup_tests();
} catch (e) {
} finally {
- await handle_view(`main`);
}
});
- tradeproduct_key_sel.subscribe(async (_tradeproduct_key_sel) => {
- await kv.set(fmt_id(`key`), _tradeproduct_key_sel);
- });
-
- $: if (tradeproduct_key_parsed) {
- tradeproduct_qty_unit_tup_sel.set(
- fmt_trade_quantity_tup(
- trade.key[tradeproduct_key_parsed].quantity[0],
- ),
- );
- tradeproduct_process_sel.set(
- trade.key[tradeproduct_key_parsed].process[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,
- );
- },
- );
-
- tradeproduct_qty_unit_tup_sel.subscribe(
- async (_tradeproduct_qty_unit_tup_sel) => {
- await kv.set(fmt_id(`qty_unit`), _tradeproduct_qty_unit_tup_sel);
- },
- );
-
- 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);
- }
- };
-
- const kv_init = async (): Promise<void> => {
+ const setup_tests = 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, ``)));
+ //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) kv_init `, e);
+ console.log(`(error) setup_tests `, e);
}
};
- const handle_view = async (new_view: View): Promise<void> => {
+ const handle_view = async (view_new: View): Promise<void> => {
try {
- view = new_view;
+ /*
+ 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) {
console.log(`(error) handle_view `, 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);
- }
- };
-
- 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_tup(
- trade.key[tradeproduct_key_parsed].quantity[0],
- )
- : ``;
- }
- } catch (e) {
- console.log(`(error) toggle_tradeproduct_qty_amt `, e);
- }
- };
-
- 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) {
- console.log(`(error) toggle_tradeproduct_process `, e);
- }
- };
-
- const handle_photo_add = async (): Promise<void> => {
- try {
- 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_photo_add `, e);
- } finally {
- photo_add_loading = false;
- }
- };
-
- const handle_back = async (carousel_offset?: number): Promise<void> => {
+ const handle_back = async (): Promise<void> => {
try {
- switch ($carousel_index) {
- default:
- {
- await carousel_dec(view);
- }
- break;
- }
- if (carousel_offset) {
- carousel_num.set(carousel_offset);
- carousel_index.set($carousel_index - (carousel_offset - 1));
- }
} catch (e) {
console.log(`(error) handle_back `, e);
}
@@ -302,50 +122,23 @@
const handle_continue = async (): Promise<void> => {
try {
- switch ($carousel_index) {
- case 0:
- {
- const validate_fields =
- await validate_trade_product_fields({
- kv_pref: fmt_id(),
- fields: [`key`, `summary`],
- });
- if (`err` in validate_fields) {
- await dialog.alert(
- `${$t(`icu.enter_a_*`, { value: `${$t(`model.trade_product.${validate_fields.err}`)}`.toLowerCase() })}`,
- );
- return;
- }
-
- if (photo_add_list.length < 1) {
- await dialog.alert(`A primary photo is required`);
- return; //@todo
- }
-
- await carousel_inc(view);
- }
- break;
- case 1:
+ switch (view) {
+ case `fl_1`:
{
- 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(`model.trade_product.${validate_fields.err}`)}`.toLowerCase() })}`,
- );
- return;
+ switch ($carousel_index) {
+ case 0:
+ {
+ console.log(
+ JSON.stringify(
+ photo_add_paths,
+ null,
+ 4,
+ ),
+ `photo_add_paths`,
+ );
+ }
+ break;
}
- console.log(
- JSON.stringify(validate_fields, null, 4),
- `validate_fields`,
- );
}
break;
}
@@ -364,521 +157,83 @@
<LayoutView>
<div
- in:fade={{ delay: 100, duration: 200 }}
- out:fade={{ delay: 0, duration: 200 }}
- data-carousel-container={`main`}
- class={`carousel-container flex h-full w-full`}
+ data-view={`fl_1`}
+ class={`flex flex-col h-full w-full justify-start items-center`}
>
<div
- data-carousel-item={`main`}
- class={`carousel-item flex flex-col w-full pt-4 justify-start items-center`}
- >
- <LayoutTrellis>
- <ImageUploadRow
- basis={{
- loading: photo_add_loading,
- file_paths: photo_add_list.map(
- ({ file_path }) => file_path,
- ),
- 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={{
- 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,
- },
- },
- }}
- />
- </LayoutTrellisLine>
- </LayoutTrellis>
- </div>
- <div
- data-carousel-item={`main`}
- class={`carousel-item flex flex-col w-full pt-4 justify-start items-center`}
+ data-carousel-container={`fl_1`}
+ class={`carousel-container flex h-full w-full`}
>
- <ImageUploadDisplay
- basis={{
- loading: photo_add_loading,
- file_paths: photo_add_list.map(
- ({ file_path }) => file_path,
- ),
- callback_add: handle_photo_add,
- callback_edit: async () => {},
- }}
- />
- <LayoutTrellis>
- <LayoutTrellisLine
- basis={{
- label: {
- value: `${$t(`icu.*_price`, { value: `${$t(`common.product`)}` })}`,
- },
- }}
- >
- <EntryWrap
- basis={{
- classes: `pl-3`,
- id: fmt_id(`price_wrap`),
- }}
- >
- <div
- class={`flex flex-row justify-start items-center pr-3`}
- >
- <SelectElement
- bind:value={$tradeproduct_price_curr_sel}
- basis={{
- 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 })}`,
- }),
- ),
- },
- ],
- }}
- />
- </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 = fmt_price(
- $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={{
- 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(),
- })),
- },
- ],
- }}
- />
- </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_tup(
- 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(`qty_wrap`),
- }}
- >
- <InputElement
- basis={{
- id: fmt_id(`qty_amt`),
- layer: 1,
- sync: true,
- placeholder: `${$t(`icu.enter_*_per_order`, { value: `${$t(`common.quantity`)}`.toLowerCase() })}`,
- 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 pr-4 justify-end items-center`}
- >
- <SelectElement
- bind:value={$tradeproduct_price_qty_unit_sel}
- basis={{
- id: fmt_id(`qty_unit`),
- classes: `w-fit font-circ font-[500] text-[1.1rem] text-layer-1-glyph/70`,
- layer: false,
- options: [
- {
- entries: mass_units.map(
- (i) => ({
- value: i,
- label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`,
- }),
- ),
- },
- ],
- }}
- />
- </div>
- </EntryWrap>
- {/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: i,
- label: `${$t(`trade.product.key.${tradeproduct_key_parsed}.process.${i}`)}`,
- }),
- ),
- {
- value: `other`,
- label: `${$t(`common.other`)}`,
- },
- ],
- },
- ],
- callback: async ({ value }) => {
- el_id(
- fmt_id(`process_wrap`),
- )?.classList.remove(
- `entry-layer-1-highlight`,
- );
- if (value === `other`) {
- await toggle_tradeproduct_process(
- true,
- );
- }
- },
- },
- }}
- />
- {:else}
- <EntryWrap
- basis={{
- id: fmt_id(`process_wrap`),
- }}
- >
- <InputElement
- basis={{
- id: fmt_id(`process`),
- layer: 1,
- sync: true,
- placeholder: `Enter the process`,
- field: {
- charset:
- trade_product_form_fields.process
- .charset,
- validate:
- trade_product_form_fields.process
- .validation,
- validate_keypress: true,
- },
- }}
- />
- </EntryWrap>
- {/if}
- </LayoutTrellisLine>
- </LayoutTrellis>
+ <div
+ data-carousel-item={`fl_1`}
+ class={`carousel-item flex flex-col w-full justify-start items-center`}
+ >
+ <LayoutTrellis>
+ <ImageUploadMulti bind:photo_paths={photo_add_paths} />
+ </LayoutTrellis>
+ </div>
</div>
</div>
</LayoutView>
-<Nav
- basis={{
- prev: {
- label: `${$t(`common.back`)}`,
- route: `/models/trade-product`,
- prevent_route:
- $carousel_index > 0
- ? {
- callback: async () => {
- await handle_back();
+{#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
+ ? undefined
+ : {
+ label: {
+ value: `${$t(`icu.new_*`, { value: `${$t(`common.product`)}` })}`,
+ },
+ callback: async () => {},
},
- }
- : undefined,
- callback: async () => {
- await kv_init_trade_product_fields(fmt_id());
- },
- },
- title: {
- label: {
- value: `${$t(`icu.new_*`, { value: `${$t(`common.product`)}` })}`,
- },
- callback: async () => {},
- },
- option: {
- loading: loading_submit,
- label: {
- value:
- $carousel_num > 1
- ? `${$t(`common.return`)}`
- : page_param.carousel.get($carousel_index)
- ?.label_next || ``,
- },
- callback: async () => {
- if ($carousel_index === $carousel_index_max) await submit();
- else await handle_continue();
- },
- },
- }}
-/>
+ 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();
+ },
+ },
+ }}
+ />
+ </div>
+{/if}
diff --git a/src/routes/(app)/test/+page.svelte b/src/routes/(app)/test/+page.svelte
@@ -1,4 +1,7 @@
<script lang="ts">
+ import { LayoutView } from "@radroots/svelte-lib";
</script>
-<div>test</div>
+<LayoutView>
+ <div class={`flex flex-col w-full justify-center items-center`}></div>
+</LayoutView>
diff --git a/src/routes/(cfg)/cfg/init/+page.svelte b/src/routes/(cfg)/cfg/init/+page.svelte
@@ -820,7 +820,6 @@
classes: `h-entry_guide w-${$app_layout} bg-layer-1-surface rounded-touch font-mono text-lg placeholder:opacity-60 items-end text-center`,
id: fmt_id(page_param.kv.nostr_secretkey),
sync: true,
- sync_init: true,
placeholder: `${$t(`icu.enter_*`, { value: `nostr nsec/hex` })}`,
field: {
charset: regex.profile_name_ch,
@@ -891,7 +890,6 @@
classes: `font-mono text-lg text-center placeholder:opacity-60`,
id: fmt_id(page_param.kv.nostr_profilename),
sync: true,
- sync_init: true,
placeholder: `${$t(`icu.enter_*`, { value: `${$t(`common.profile_name`)}`.toLowerCase() })}`,
field: {
charset: regex.profile_name_ch,
diff --git a/tailwind.config.ts b/tailwind.config.ts
@@ -1,5 +1,6 @@
import { theme_colors, themes } from "@radroots/theme";
import { wind } from "@radroots/utils";
+import aspect_ratio from "@tailwindcss/aspect-ratio";
import daisyui from "daisyui";
import type { Config } from "tailwindcss";
import tailwind_default from "tailwindcss/defaultTheme";
@@ -70,6 +71,27 @@ const config: Config = {
mobile_y: { raw: `(orientation: portrait) and (min-height: ${wind.app.layout.mobile_y}px)` },
mobile_base: { raw: `(orientation: portrait) and (max-height: ${wind.app.layout.mobile_base}px)` },
},
+ aspectRatio: {
+ auto: 'auto',
+ square: '1 / 1',
+ video: '16 / 9',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13',
+ 14: '14',
+ 15: '15',
+ 16: '16',
+ },
extend: {
colors: {
...theme_colors,
@@ -77,7 +99,7 @@ const config: Config = {
'chart-red': 'var(--chart-color-red)',
},
fontFamily: {
- sans: ['SF Pro Display', ...tw_font.sans],
+ sans: ['SF Pro Rounded', ...tw_font.sans],
serif: [...tw_font.serif],
mono: [...tw_font.mono],
apercu: ['Apercu Mono Pro'],
@@ -152,7 +174,8 @@ const config: Config = {
}
},
plugins: [
- daisyui
+ daisyui,
+ aspect_ratio
],
daisyui: {
themes: [