refactor zo zod and felte
This commit is contained in:
@@ -1,41 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { type SummitLog } from "$lib/models/summit_log";
|
||||
import {
|
||||
SummitLogCreateSchema,
|
||||
} from "$lib/models/api/summit_log_schema";
|
||||
import GPX from "$lib/models/gpx/gpx";
|
||||
import { summitLog } from "$lib/stores/summit_log_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { createForm } from "felte";
|
||||
import { validator } from "@felte/validator-zod";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { date, object, string } from "yup";
|
||||
import { z } from "zod";
|
||||
import Datepicker from "../base/datepicker.svelte";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import Textarea from "../base/textarea.svelte";
|
||||
import TrailPicker from "../trail/trail_picker.svelte";
|
||||
import GPX from "$lib/models/gpx/gpx";
|
||||
import PhotoPicker from "../trail/photo_picker.svelte";
|
||||
import TrailPicker from "../trail/trail_picker.svelte";
|
||||
import { cloneDeep } from "$lib/util/deep_util";
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const summitLogSchema = object<SummitLog>({
|
||||
id: string().optional(),
|
||||
date: date().required("Required").typeError($_("invalid-date")),
|
||||
text: string().optional(),
|
||||
const ClientSummitLogCreateSchema = SummitLogCreateSchema.extend({
|
||||
_photos: z.array(z.instanceof(File)).optional(),
|
||||
_gpx: z.instanceof(File).optional().nullable(),
|
||||
expand: z
|
||||
.object({
|
||||
gpx_data: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<SummitLog>({
|
||||
const { form, errors, data, setFields } = createForm<
|
||||
z.infer<typeof ClientSummitLogCreateSchema>
|
||||
>({
|
||||
initialValues: $summitLog,
|
||||
validationSchema: summitLogSchema,
|
||||
onSubmit: async (submittedValues) => {
|
||||
if (!$form.expand.gpx_data) {
|
||||
$form.gpx = "";
|
||||
extend: validator({ schema: ClientSummitLogCreateSchema }),
|
||||
onSubmit: async (form) => {
|
||||
if (!form.expand?.gpx_data) {
|
||||
form.gpx = "";
|
||||
}
|
||||
|
||||
if (
|
||||
!$form._photos?.length &&
|
||||
!$form.photos?.length &&
|
||||
$form.expand?.gpx_data
|
||||
!form._photos?.length &&
|
||||
!form.photos?.length &&
|
||||
form.expand?.gpx_data
|
||||
) {
|
||||
const canvas = document.querySelector(
|
||||
"#trail-picker-map .maplibregl-canvas",
|
||||
@@ -44,25 +53,25 @@
|
||||
const dataURL = canvas.toDataURL();
|
||||
const response = await fetch(dataURL);
|
||||
const blob = await response.blob();
|
||||
$form._photos = [new File([blob], "route")];
|
||||
form._photos = [new File([blob], "route")];
|
||||
}
|
||||
|
||||
dispatch("save", submittedValues);
|
||||
dispatch("save", form);
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
$: form.set(util.cloneDeep($summitLog));
|
||||
$: setFields(cloneDeep($summitLog));
|
||||
|
||||
$: if ($summitLog._gpx) {
|
||||
$form._gpx = $summitLog._gpx;
|
||||
$data._gpx = $summitLog._gpx;
|
||||
}
|
||||
|
||||
async function handleTrailSelection(trailData: string | null) {
|
||||
if (!trailData) {
|
||||
$form.duration = undefined;
|
||||
$form.elevation_gain = undefined;
|
||||
$form.elevation_loss = undefined;
|
||||
$form.distance = undefined;
|
||||
$data.duration = undefined;
|
||||
$data.elevation_gain = undefined;
|
||||
$data.elevation_loss = undefined;
|
||||
$data.distance = undefined;
|
||||
return;
|
||||
}
|
||||
const gpxObject = await GPX.parse(trailData);
|
||||
@@ -72,16 +81,15 @@
|
||||
|
||||
const totals = gpxObject.getTotals();
|
||||
|
||||
$form.duration = totals.duration / 1000;
|
||||
$form.elevation_gain = totals.elevationGain;
|
||||
$form.elevation_loss = totals.elevationLoss;
|
||||
$form.distance = totals.distance;
|
||||
$data.duration = totals.duration / 1000;
|
||||
$data.elevation_gain = totals.elevationGain;
|
||||
$data.elevation_loss = totals.elevationLoss;
|
||||
$data.distance = totals.distance;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
id="summit-log-modal"
|
||||
title={$form.id ? $_("edit-entry") : $_("add-entry")}
|
||||
title={$data.id ? $_("edit-entry") : $_("add-entry")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -91,15 +99,10 @@
|
||||
id="summit-log-form"
|
||||
slot="content"
|
||||
class="modal-content space-y-4"
|
||||
on:submit={handleSubmit}
|
||||
use:form
|
||||
>
|
||||
<div class="flex">
|
||||
<Datepicker
|
||||
name="date"
|
||||
label={$_("date")}
|
||||
bind:value={$form.date}
|
||||
error={$errors.date}
|
||||
on:change={handleChange}
|
||||
<Datepicker name="date" label={$_("date")} error={$errors.date}
|
||||
></Datepicker>
|
||||
</div>
|
||||
<div>
|
||||
@@ -108,27 +111,27 @@
|
||||
</label>
|
||||
<PhotoPicker
|
||||
id="summitlog-photo-input"
|
||||
parent={$form}
|
||||
bind:photos={$form.photos}
|
||||
bind:photoFiles={$form._photos}
|
||||
parent={$data}
|
||||
bind:photos={$data.photos}
|
||||
bind:photoFiles={$data._photos}
|
||||
showThumbnailControls={false}
|
||||
></PhotoPicker>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<TrailPicker
|
||||
bind:trailFile={$form._gpx}
|
||||
bind:trailData={$form.expand.gpx_data}
|
||||
label={$_("trail", { values: { n: 1 } })}
|
||||
on:change={(e) => handleTrailSelection(e.detail)}
|
||||
></TrailPicker>
|
||||
{#if $data.expand}
|
||||
<TrailPicker
|
||||
bind:trailFile={$data._gpx}
|
||||
bind:trailData={$data.expand.gpx_data}
|
||||
label={$_("trail", { values: { n: 1 } })}
|
||||
on:change={(e) => handleTrailSelection(e.detail)}
|
||||
></TrailPicker>
|
||||
{/if}
|
||||
<div class="basis-full">
|
||||
<Textarea
|
||||
name="text"
|
||||
extraClasses="h-28"
|
||||
label={$_("text")}
|
||||
bind:value={$form.text}
|
||||
error={$errors.text}
|
||||
on:change={handleChange}
|
||||
></Textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user