web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

commit e90dcbc825884e6621c89911e9f50fb76a6b0a61
parent 9777d622d9023b748e95bf2ec09d2b617d1c82ae
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sun, 27 Apr 2025 05:32:51 +0000

apps-lib: add farms add view, edit farms view, add validation utils

Diffstat:
Mapps-lib/package.json | 3++-
Mapps-lib/src/lib/index.ts | 2++
Aapps-lib/src/lib/util/validation/farm.ts | 13+++++++++++++
Aapps-lib/src/lib/view/farms-add.svelte | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/view/farms.svelte | 93++++++++++++++++++++++++++++++++++++++-----------------------------------------
5 files changed, 282 insertions(+), 49 deletions(-)

diff --git a/apps-lib/package.json b/apps-lib/package.json @@ -57,6 +57,7 @@ "@radroots/util": "workspace:*", "luxon": "^3.5.0", "svelte-maplibre": "1.0.0-next.12", - "sveltekit-search-params": "^3.0.0" + "sveltekit-search-params": "^3.0.0", + "zod": "^3.23.8" } } \ No newline at end of file diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts @@ -112,7 +112,9 @@ export * from "./util/nostr/nostr-poll-relays.js" export * from "./util/nostr/nostr-sync.js" export * from "./util/service/nostr-event-classified.js" export * from "./util/service/nostr-sync.js" +export * from "./util/validation/farm.js" export * from "./util/view.js" +export { default as FarmsAdd } from "./view/farms-add.svelte" export { default as Farms } from "./view/farms.svelte" export { default as Home } from "./view/home.svelte" export { default as Notifications } from "./view/notifications.svelte" diff --git a/apps-lib/src/lib/util/validation/farm.ts b/apps-lib/src/lib/util/validation/farm.ts @@ -0,0 +1,12 @@ +import type { IViewFarmsAddSubmission } from "$lib/types/view/farm"; +import { form_fields, schema_geolocation_address, schema_geolocation_point, zf_numf_pos } from "@radroots/util"; +import { z } from "zod"; + +export const schema_view_farms_add_submission: z.ZodSchema<IViewFarmsAddSubmission> = z.object({ + farm_name: z.string().regex(form_fields.farm_name.validate), + farm_area: zf_numf_pos, + farm_area_unit: z.string().regex(form_fields.area_unit.validate), + farm_contact_name: z.string().regex(form_fields.contact_name.validate), + geolocation_point: schema_geolocation_point, + geolocation_address: schema_geolocation_address, +}); +\ No newline at end of file diff --git a/apps-lib/src/lib/view/farms-add.svelte b/apps-lib/src/lib/view/farms-add.svelte @@ -0,0 +1,220 @@ +<script lang="ts"> + import LayoutBottomButton from "$lib/components/layout/layout-bottom-button.svelte"; + import Fade from "$lib/components/lib/fade.svelte"; + import { schema_view_farms_add_submission } from "$lib/util/validation/farm"; + import { + app_platform, + ButtonLayoutPair, + Carousel, + casl_dec, + casl_i, + casl_inc, + casl_init, + FarmsAddCasliDetail, + FarmsAddCasliMap, + Glyph, + handle_err, + LayoutView, + PageToolbar, + type CallbackRoute, + type IViewFarmsAddSubmission, + type LcGeocodeCallback, + type LcGeocodeCurrentCallback, + type LcGuiAlertCallback, + } from "$root"; + import { + geol_lat_fmt, + geol_lng_fmt, + parse_float, + parse_geocode_address, + type CallbackPromiseGeneric, + type GeocoderReverseResult, + type GeolocationAddress, + type GeolocationPoint, + type I18nTranslateFunction, + type I18nTranslateLocale, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + ls, + locale, + }: { + basis: { + callback_route?: CallbackRoute<string>; + lc_gui_alert: LcGuiAlertCallback; + lc_geop_current: LcGeocodeCurrentCallback; + lc_geocode: LcGeocodeCallback; + lc_submit: CallbackPromiseGeneric<{ + data_s: IViewFarmsAddSubmission; + }>; + }; + ls: I18nTranslateFunction; + locale: I18nTranslateLocale; + } = $props(); + + let loading = $state(false); + + let map_geop: GeolocationPoint = $state({ lat: 0, lng: 0 }); + let map_geoc: GeocoderReverseResult | undefined = $state(undefined); + + let val_farmname = $state(``); + let val_farmcontact = $state(``); + let val_farmarea = $state(``); + let val_farmarea_unit = $state(`ac`); + + const disabled_submit = $derived($casl_i === 1 && !val_farmname); + + onMount(async () => { + try { + casl_init(0, 2); + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const farm_geop_lat = $derived( + map_geop ? geol_lat_fmt(map_geop.lat, `dms`, $locale, 3) : ``, + ); + + const farm_geop_lng = $derived( + map_geop ? geol_lng_fmt(map_geop.lng, `dms`, $locale, 3) : ``, + ); + + const farm_geolocation_address: GeolocationAddress | undefined = $derived( + parse_geocode_address(map_geoc), + ); + + const handle_continue_0 = async (): Promise<void> => { + await casl_inc(); + }; + + const handle_continue_1 = async (): Promise<void> => { + if (!map_geop) + return void basis.lc_gui_alert(`No farm location provided.`); //@todo + if (!farm_geolocation_address) + return void basis.lc_gui_alert(`No farm address provided.`); //@todo + const farms_add_submission = schema_view_farms_add_submission.safeParse( + { + farm_name: val_farmname, + farm_area: parse_float(val_farmarea), + farm_area_unit: val_farmarea_unit, + farm_contact_name: val_farmcontact, + geolocation_point: map_geop, + geolocation_address: farm_geolocation_address, + } satisfies IViewFarmsAddSubmission, + ); + + if (!farms_add_submission.success) { + return void basis.lc_gui_alert( + `Request invalid: ${farms_add_submission.error}`, + ); //@todo + } + loading = true; + await basis.lc_submit({ data_s: farms_add_submission.data }); + loading = false; + }; + + const handle_continue = async (): Promise<void> => { + switch ($casl_i) { + case 0: + return await handle_continue_0(); + case 1: + return await handle_continue_1(); + } + }; + + const handle_back = async (): Promise<void> => { + switch ($casl_i) { + default: + return await casl_dec(); + } + }; +</script> + +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `Farm / Add`, + callback_route: basis.callback_route, + }, + }} + > + {#snippet header_option()} + {#if $casl_i > 0} + <Fade> + <button + class={`flex flex-row pr-3 justify-center items-center`} + onclick={async () => { + await handle_back(); + }} + > + <p + class={`font-sans font-[600] text-lg text-layer-0-glyph`} + > + {`${$ls(`common.back`)}`} + </p> + </button> + </Fade> + {/if} + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + await handle_continue(); + }} + > + <p class={`font-sans font-[600] text-lg text-layer-0-glyph-hl`}> + {`${$ls(`common.details`)}`} + </p> + <Glyph + basis={{ + classes: `text-layer-0-glyph-hl`, + dim: `md`, + key: `caret-right`, + }} + /> + </button> + {/snippet} + </PageToolbar> + <Carousel> + <FarmsAddCasliMap + bind:map_geop + bind:map_geoc + {farm_geop_lat} + {farm_geop_lng} + basis={{ + lc_geocode: basis.lc_geocode, + lc_geop_current: basis.lc_geop_current, + }} + /> + <FarmsAddCasliDetail + bind:val_farmname + bind:val_farmcontact + bind:val_farmarea + bind:val_farmarea_unit + {farm_geop_lat} + {farm_geop_lng} + {ls} + /> + </Carousel> +</LayoutView> +{#if $app_platform?.browser !== `safari`} + <LayoutBottomButton> + <ButtonLayoutPair + basis={{ + continue: { + label: `${$ls(`common.continue`)}`, + disabled: disabled_submit, + callback: handle_continue, + }, + back: { + label: `${$ls(`common.back`)}`, + visible: $casl_i > 0, + callback: handle_back, + }, + }} + /> + </LayoutBottomButton> +{/if} diff --git a/apps-lib/src/lib/view/farms.svelte b/apps-lib/src/lib/view/farms.svelte @@ -47,53 +47,50 @@ }); </script> -{#if basis.data} - {@const { data: basis_data } = basis} - <LayoutView> - <PageToolbar - basis={{ - header: { - label: `${$ls(`common.farms`)}`, - callback_route: basis.callback_route, - }, - }} - > - {#snippet header_option()} - {#if basis_data.list.length} - <Fade> - <GlyphButtonSimple - basis={{ - label: `${$ls(`icu.add_*`, { value: `${$ls(`common.farm`)}` })}`, - callback: async () => { - await basis.lc_handle_farm_add(); - }, - }} - /> - </Fade> - {/if} - {/snippet} - </PageToolbar> - <LayoutPage> - {#if basis_data.list.length} - {#each basis_data.list as li} - <FarmsDisplayLiEl - basis={li} - lc_geocode={basis.lc_geocode} - lc_handle_farm_view={basis.lc_handle_farm_view} - {ls} - {locale} +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `${$ls(`common.farms`)}`, + callback_route: basis.callback_route, + }, + }} + > + {#snippet header_option()} + {#if basis.data?.list.length} + <Fade> + <GlyphButtonSimple + basis={{ + label: `${$ls(`icu.add_*`, { value: `${$ls(`common.farm`)}` })}`, + callback: async () => { + await basis.lc_handle_farm_add(); + }, + }} /> - {/each} - {:else} - <ButtonLabelDashed - basis={{ - label: `Add farm`, - callback: async () => { - await basis.lc_handle_farm_add(); - }, - }} - /> + </Fade> {/if} - </LayoutPage> - </LayoutView> -{/if} + {/snippet} + </PageToolbar> + <LayoutPage> + {#if basis.data?.list.length} + {#each basis.data?.list as li} + <FarmsDisplayLiEl + basis={li} + lc_geocode={basis.lc_geocode} + lc_handle_farm_view={basis.lc_handle_farm_view} + {ls} + {locale} + /> + {/each} + {:else} + <ButtonLabelDashed + basis={{ + label: `Add farm`, + callback: async () => { + await basis.lc_handle_farm_add(); + }, + }} + /> + {/if} + </LayoutPage> +</LayoutView>