mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-24 10:08:07 +07:00
melanjutkan pusing pusing validate rule refnum
This commit is contained in:
parent
62b1d9fe45
commit
7fcb5d8507
@ -1,27 +1,60 @@
|
|||||||
const validationMode = {
|
const validationMode = {
|
||||||
create: (schema, field, value) => {
|
// create: (schema, field, value) => {
|
||||||
const result = schema.shape[field].safeParse(value);
|
// const result = schema.shape[field].safeParse(value);
|
||||||
return result.success ? null : result.error.issues[0].message;
|
// console.log(result);
|
||||||
|
// return result.success ? null : result.error.issues[0].message;
|
||||||
|
// },
|
||||||
|
|
||||||
|
create: (schema, field, form) => {
|
||||||
|
const result = schema.safeParse(form);
|
||||||
|
|
||||||
|
if (result.success) return null;
|
||||||
|
|
||||||
|
const fieldError = result.error.issues.find(
|
||||||
|
(issue) => issue.path[0] === field
|
||||||
|
);
|
||||||
|
|
||||||
|
return fieldError ? fieldError.message : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
edit: (schema, field, value, originalValue) => {
|
// edit: (schema, field, value, originalValue) => {
|
||||||
if (originalValue !== undefined && value === originalValue) {
|
// if (originalValue !== undefined && value === originalValue) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const result = schema.shape[field].safeParse(value);
|
||||||
|
// return result.success ? null : result.error.issues[0].message;
|
||||||
|
// }
|
||||||
|
edit: (schema, field, form, originalValue) => {
|
||||||
|
if (originalValue !== undefined && form[field] === originalValue) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = schema.shape[field].safeParse(value);
|
const result = schema.safeParse(form);
|
||||||
return result.success ? null : result.error.issues[0].message;
|
|
||||||
|
if (result.success) return null;
|
||||||
|
|
||||||
|
const fieldError = result.error.issues.find(
|
||||||
|
(issue) => issue.path[0] === field
|
||||||
|
);
|
||||||
|
|
||||||
|
return fieldError ? fieldError.message : null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useFormValidation(schema, form, defaultErrors, valMode) {
|
export function useFormValidation(schema, form, defaultErrors, valMode) {
|
||||||
const errors = $state({...defaultErrors})
|
const errors = $state({...defaultErrors})
|
||||||
|
|
||||||
|
// function validateField(field, originalValue) {
|
||||||
|
// const value = form[field];
|
||||||
|
// const valFn = validationMode[valMode];
|
||||||
|
// errors[field] = valFn(schema, field, value, originalValue);
|
||||||
|
// }
|
||||||
function validateField(field, originalValue) {
|
function validateField(field, originalValue) {
|
||||||
const value = form[field];
|
const valFn = validationMode[valMode];
|
||||||
const valFn = validationMode[valMode];
|
const error = valFn(schema, field, form, originalValue);
|
||||||
errors[field] = valFn(schema, field, value, originalValue);
|
errors[field] = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetErrors() {
|
function resetErrors() {
|
||||||
Object.assign(errors, defaultErrors);
|
Object.assign(errors, defaultErrors);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { API } from "$lib/config/api";
|
|||||||
import EraserIcon from "@lucide/svelte/icons/eraser";
|
import EraserIcon from "@lucide/svelte/icons/eraser";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
||||||
|
import { toDays } from "$lib/utils/ageUtils";
|
||||||
|
|
||||||
export const testSchema = z.object({
|
export const testSchema = z.object({
|
||||||
TestSiteCode: z.string().min(1, "Required"),
|
TestSiteCode: z.string().min(1, "Required"),
|
||||||
@ -13,7 +14,7 @@ export const testSchema = z.object({
|
|||||||
return Number(val);
|
return Number(val);
|
||||||
},
|
},
|
||||||
z.number({invalid_type_error: "Must be a number"}).min(0, "Min 0").max(7, "Max 7").optional()
|
z.number({invalid_type_error: "Must be a number"}).min(0, "Min 0").max(7, "Max 7").optional()
|
||||||
)
|
),
|
||||||
}).superRefine((data, ctx) => {
|
}).superRefine((data, ctx) => {
|
||||||
if (data.Factor && !data.Unit2) {
|
if (data.Factor && !data.Unit2) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
@ -42,6 +43,71 @@ export const testCalSchema = z.object({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const refNumSchema = z.object({
|
||||||
|
AgeStart: z.string().optional(),
|
||||||
|
AgeEnd: z.string().optional(),
|
||||||
|
Flag: z.string().min(1, "Required"),
|
||||||
|
Low: z.string().optional(),
|
||||||
|
High: z.string().optional(),
|
||||||
|
LowSign: z.string().optional(),
|
||||||
|
HighSign: z.string().optional(),
|
||||||
|
NumRefType: z.string().optional()
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
const start = toDays(data.AgeStart);
|
||||||
|
const end = toDays(data.AgeEnd);
|
||||||
|
|
||||||
|
// if (start !== null && start > 0 && end !== null && end <= start) {
|
||||||
|
if (start !== null && end !== null && end <= start) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "Age End must be greater than Age Start",
|
||||||
|
path: ["AgeEnd"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
// console.log(data.NumRefType);
|
||||||
|
if (data.NumRefType === "RANGE") {
|
||||||
|
const low = Number(data.Low);
|
||||||
|
const high = Number(data.High);
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.Low &&
|
||||||
|
data.High &&
|
||||||
|
!Number.isNaN(low) &&
|
||||||
|
!Number.isNaN(high) &&
|
||||||
|
(high <= low)
|
||||||
|
) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "High value must be greater than Low value",
|
||||||
|
path: ["High"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.NumRefType === "THOLD") {
|
||||||
|
if (data.Low && !data.LowSign) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "Math sign for Low is required",
|
||||||
|
path: ["LowSign"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
data.LowSign &&
|
||||||
|
data.HighSign &&
|
||||||
|
data.LowSign === data.HighSign
|
||||||
|
) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "High sign can't be the same as Low sign",
|
||||||
|
path: ["HighSign"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export const testInitialForm = {
|
export const testInitialForm = {
|
||||||
TestSiteID: "",
|
TestSiteID: "",
|
||||||
SiteID: "",
|
SiteID: "",
|
||||||
@ -123,7 +189,14 @@ export const testDefaultErrors = {
|
|||||||
|
|
||||||
export const testCalDefaultErrors = {};
|
export const testCalDefaultErrors = {};
|
||||||
|
|
||||||
export const refNumDefaultErrors = {};
|
export const refNumDefaultErrors = {
|
||||||
|
AgeStart: null,
|
||||||
|
AgeEnd: null,
|
||||||
|
Low: null,
|
||||||
|
High: null,
|
||||||
|
LowSign: null,
|
||||||
|
HighSign: null,
|
||||||
|
};
|
||||||
|
|
||||||
export const refTxtDefaultErrors = {};
|
export const refTxtDefaultErrors = {};
|
||||||
|
|
||||||
@ -482,12 +555,14 @@ export const refNumFormFields = [
|
|||||||
label: "Age Start",
|
label: "Age Start",
|
||||||
required: false,
|
required: false,
|
||||||
type: "agejoin",
|
type: "agejoin",
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "AgeEnd",
|
key: "AgeEnd",
|
||||||
label: "Age End",
|
label: "Age End",
|
||||||
required: false,
|
required: false,
|
||||||
type: "agejoin",
|
type: "agejoin",
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -524,6 +599,7 @@ export const refNumFormFields = [
|
|||||||
type: "signvalue",
|
type: "signvalue",
|
||||||
txtKey: "Low",
|
txtKey: "Low",
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "HighSign",
|
key: "HighSign",
|
||||||
@ -532,6 +608,7 @@ export const refNumFormFields = [
|
|||||||
type: "signvalue",
|
type: "signvalue",
|
||||||
txtKey: "High",
|
txtKey: "High",
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||||
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
||||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||||
import { buildTestPayload, testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields, refNumInitialForm, refNumFormFields, refTxtInitialForm, refTxtFormFields } from "$lib/components/dictionary/test/config/test-form-config";
|
import { buildTestPayload, testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields, refNumSchema, refNumDefaultErrors, refNumInitialForm, refNumFormFields, refTxtInitialForm, refTxtFormFields } from "$lib/components/dictionary/test/config/test-form-config";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import RefNum from "./tabs/ref-num.svelte";
|
import RefNum from "./tabs/ref-num.svelte";
|
||||||
import RefTxt from "./tabs/ref-txt.svelte";
|
import RefTxt from "./tabs/ref-txt.svelte";
|
||||||
@ -30,8 +30,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const refNumState = useForm({
|
const refNumState = useForm({
|
||||||
schema: null,
|
schema: refNumSchema,
|
||||||
initialForm: refNumInitialForm,
|
initialForm: refNumInitialForm,
|
||||||
|
defaultErrors: refNumDefaultErrors,
|
||||||
});
|
});
|
||||||
|
|
||||||
const refTxtState = useForm({
|
const refTxtState = useForm({
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
let disabledSign = $derived.by(() => {
|
let disabledSign = $derived.by(() => {
|
||||||
const refType = props.refType;
|
const refType = props.refNumState.form.NumRefType;
|
||||||
|
|
||||||
if (refType === "RANGE") return true;
|
if (refType === "RANGE") return true;
|
||||||
if (refType === "THOLD") return false;
|
if (refType === "THOLD") return false;
|
||||||
@ -62,9 +62,38 @@
|
|||||||
editingId = null;
|
editingId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isOverlapping(newLow, newHigh, existingRows) {
|
||||||
|
return existingRows.some(row => {
|
||||||
|
return !(newHigh <= row.Low || newLow >= row.High);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleInsert() {
|
function handleInsert() {
|
||||||
const row = { id: ++idCounter, ...snapshotForm() };
|
const low = Number(props.refNumState.form.Low);
|
||||||
|
const high = Number(props.refNumState.form.High);
|
||||||
|
console.log(`low: ${low}`);
|
||||||
|
// const row = { id: ++idCounter, ...snapshotForm() };
|
||||||
|
// tempNumeric = [...tempNumeric, row];
|
||||||
|
// resetForm();
|
||||||
|
const isOverlap = tempNumeric.some(row => {
|
||||||
|
const existingLow = Number(row.Low);
|
||||||
|
const existingHigh = Number(row.High);
|
||||||
|
|
||||||
|
return !(high < existingLow || low > existingHigh);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isOverlap) {
|
||||||
|
props.refNumState.errors.High = "Range overlaps with existing data";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = {
|
||||||
|
id: ++idCounter,
|
||||||
|
...snapshotForm()
|
||||||
|
};
|
||||||
|
|
||||||
tempNumeric = [...tempNumeric, row];
|
tempNumeric = [...tempNumeric, row];
|
||||||
|
|
||||||
resetForm();
|
resetForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +192,18 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
// Sinkronisasi Low dan LowSign
|
||||||
|
if (!props.refNumState.form.Low || props.refNumState.form.Low === "") {
|
||||||
|
props.refNumState.form.LowSign = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sinkronisasi High dan HighSign
|
||||||
|
if (!props.refNumState.form.High || props.refNumState.form.High === "") {
|
||||||
|
props.refNumState.form.HighSign = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4 w-full">
|
<div class="flex flex-col gap-4 w-full">
|
||||||
@ -230,8 +271,8 @@
|
|||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell class="font-medium flex justify-between">
|
<Table.Cell class="font-medium flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
{row.LowSign ? row.LowSign : ""} {row.Low || ""} –
|
{row.LowSign ? row.LowSign : ""} {row.Low || "null"} –
|
||||||
{row.HighSign ? row.HighSign : ""} {row.High || ""}
|
{row.HighSign ? row.HighSign : ""} {row.High || "null"}
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="outline" class="border-dashed border-primary border-2">{numRefTypeBadge(row.NumRefType)}</Badge>
|
<Badge variant="outline" class="border-dashed border-primary border-2">{numRefTypeBadge(row.NumRefType)}</Badge>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|||||||
@ -92,6 +92,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={formState.form[key]}
|
bind:value={formState.form[key]}
|
||||||
oninput={() => {
|
oninput={() => {
|
||||||
|
// console.log(`key: ${key}, form: ${formState.form[key]}`);
|
||||||
if (validateOn?.includes("input")) {
|
if (validateOn?.includes("input")) {
|
||||||
formState.validateField(key, formState.form[key], false);
|
formState.validateField(key, formState.form[key], false);
|
||||||
}
|
}
|
||||||
@ -215,79 +216,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
<!-- {:else if type === "selectmultiple"}
|
|
||||||
{@const filteredOptions = getFilteredOptions(key)}
|
|
||||||
{@const selectedValues = Array.isArray(formState.form[key]) ? formState.form[key] : []}
|
|
||||||
{@const selectedOptions = selectedValues
|
|
||||||
.map(val => formState.selectOptions?.[key]?.find(opt => opt.value === val))
|
|
||||||
.filter(Boolean)}
|
|
||||||
|
|
||||||
<Select.Root type="multiple" bind:value={formState.form[key]}
|
|
||||||
onValueChange={(vals) => {
|
|
||||||
formState.form[key] = vals;
|
|
||||||
if (validateOn?.includes("input")) {
|
|
||||||
formState.validateField?.(key, formState.form[key], false);
|
|
||||||
}
|
|
||||||
if (key === 'FormulaInput') {
|
|
||||||
formState.validateField?.('FormulaCode', formState.form['FormulaCode'], false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (open && optionsEndpoint) {
|
|
||||||
formState.fetchOptions?.(
|
|
||||||
{ key, optionsEndpoint, dependsOn, endpointParamKey, valueKey, labelKey },
|
|
||||||
formState.form
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Select.Trigger class="w-full min-h-[42px]">
|
|
||||||
{#if selectedOptions.length === 0}
|
|
||||||
<span class="text-muted-foreground">Choose</span>
|
|
||||||
{:else}
|
|
||||||
<div class="flex flex-wrap gap-1">
|
|
||||||
{#each selectedOptions as option}
|
|
||||||
<Badge variant="secondary" class="text-xs">
|
|
||||||
{option.label}
|
|
||||||
</Badge>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</Select.Trigger>
|
|
||||||
<Select.Content>
|
|
||||||
<div class="p-2">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search..."
|
|
||||||
class="w-full border rounded px-2 py-1 text-sm"
|
|
||||||
bind:value={searchQuery[key]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{#if formState.loadingOptions?.[key]}
|
|
||||||
<Select.Item disabled value="loading">Loading...</Select.Item>
|
|
||||||
{:else}
|
|
||||||
{#if !required}
|
|
||||||
<Select.Item value="">- None -</Select.Item>
|
|
||||||
{/if}
|
|
||||||
{#each filteredOptions as option}
|
|
||||||
<Select.Item value={option.value}
|
|
||||||
disabled={key === "ResultType" && disabledResultTypes.includes(option.value) ||
|
|
||||||
key === "RefType" && disabledReferenceTypes.includes(option.value)}
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</Select.Item>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</Select.Content>
|
|
||||||
{#if selectedValues.length > 0}
|
|
||||||
<button
|
|
||||||
class="w-full px-2 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground"
|
|
||||||
onclick={() => formState.form[key] = []}
|
|
||||||
>
|
|
||||||
Unselect All
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</Select.Root> -->
|
|
||||||
{:else if type === "date"}
|
{:else if type === "date"}
|
||||||
<ReusableCalendar
|
<ReusableCalendar
|
||||||
bind:value={formState.form[key]}
|
bind:value={formState.form[key]}
|
||||||
@ -303,6 +231,13 @@
|
|||||||
<InputGroup.Input
|
<InputGroup.Input
|
||||||
placeholder="Type here"
|
placeholder="Type here"
|
||||||
bind:value={formState.form[txtKey]}
|
bind:value={formState.form[txtKey]}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField("Low", formState.form[txtKey]);
|
||||||
|
formState.validateField("LowSign");
|
||||||
|
formState.validateField("High", formState.form[txtKey]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<InputGroup.Addon align="inline-start">
|
<InputGroup.Addon align="inline-start">
|
||||||
<DropdownMenu.Root
|
<DropdownMenu.Root
|
||||||
@ -322,9 +257,7 @@
|
|||||||
{...props}
|
{...props}
|
||||||
variant="ghost" disabled={disabledSign}
|
variant="ghost" disabled={disabledSign}
|
||||||
>
|
>
|
||||||
{formState.selectOptions?.[key]?.find(
|
{formState.selectOptions?.[key]?.find(opt => opt.value === formState.form[key])?.label || 'Choose'}
|
||||||
opt => opt.value === formState.form[key]
|
|
||||||
)?.label || 'Choose'}
|
|
||||||
<ChevronDownIcon />
|
<ChevronDownIcon />
|
||||||
</InputGroup.Button>
|
</InputGroup.Button>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -335,12 +268,27 @@
|
|||||||
Loading...
|
Loading...
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
{:else}
|
{:else}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
onSelect={() => {
|
||||||
|
formState.form[key] = "";
|
||||||
|
dropdownOpen[key] = false;
|
||||||
|
formState.validateField("LowSign");
|
||||||
|
formState.validateField("HighSign");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
- None -
|
||||||
|
</DropdownMenu.Item>
|
||||||
{#each formState.selectOptions?.[key] || [] as option}
|
{#each formState.selectOptions?.[key] || [] as option}
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
formState.form[key] = option.value;
|
formState.form[key] = option.value;
|
||||||
dropdownOpen[key] = false;
|
dropdownOpen[key] = false;
|
||||||
}}
|
}}
|
||||||
|
onSelect={() => {
|
||||||
|
formState.form[key] = option.value;
|
||||||
|
formState.validateField("LowSign");
|
||||||
|
formState.validateField("HighSign");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
@ -353,19 +301,43 @@
|
|||||||
{:else if type === "agejoin"}
|
{:else if type === "agejoin"}
|
||||||
<div class="flex items-center gap-2 w-full">
|
<div class="flex items-center gap-2 w-full">
|
||||||
<InputGroup.Root>
|
<InputGroup.Root>
|
||||||
<InputGroup.Input type="number" bind:value={joinFields[key].YY}/>
|
<InputGroup.Input type="number" bind:value={joinFields[key].YY}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
// formState.validateField(key, formState.form[key], false);
|
||||||
|
formState.validateField("AgeStart");
|
||||||
|
formState.validateField("AgeEnd");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<InputGroup.Addon align="inline-end">
|
<InputGroup.Addon align="inline-end">
|
||||||
<InputGroup.Text>Year</InputGroup.Text>
|
<InputGroup.Text>Year</InputGroup.Text>
|
||||||
</InputGroup.Addon>
|
</InputGroup.Addon>
|
||||||
</InputGroup.Root>
|
</InputGroup.Root>
|
||||||
<InputGroup.Root>
|
<InputGroup.Root>
|
||||||
<InputGroup.Input type="number" bind:value={joinFields[key].MM}/>
|
<InputGroup.Input type="number" bind:value={joinFields[key].MM}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
// formState.validateField(key, formState.form[key], false);
|
||||||
|
formState.validateField("AgeStart");
|
||||||
|
formState.validateField("AgeEnd");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<InputGroup.Addon align="inline-end">
|
<InputGroup.Addon align="inline-end">
|
||||||
<InputGroup.Text>Month</InputGroup.Text>
|
<InputGroup.Text>Month</InputGroup.Text>
|
||||||
</InputGroup.Addon>
|
</InputGroup.Addon>
|
||||||
</InputGroup.Root>
|
</InputGroup.Root>
|
||||||
<InputGroup.Root>
|
<InputGroup.Root>
|
||||||
<InputGroup.Input type="number" bind:value={joinFields[key].DD}/>
|
<InputGroup.Input type="number" bind:value={joinFields[key].DD}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
// formState.validateField(key, formState.form[key], false);
|
||||||
|
formState.validateField("AgeStart");
|
||||||
|
formState.validateField("AgeEnd");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<InputGroup.Addon align="inline-end">
|
<InputGroup.Addon align="inline-end">
|
||||||
<InputGroup.Text>Day</InputGroup.Text>
|
<InputGroup.Text>Day</InputGroup.Text>
|
||||||
</InputGroup.Addon>
|
</InputGroup.Addon>
|
||||||
@ -388,9 +360,9 @@
|
|||||||
{formState.errors[key]}
|
{formState.errors[key]}
|
||||||
</span>
|
</span>
|
||||||
{/if} -->
|
{/if} -->
|
||||||
{#if formState.errors[key]}
|
{#if formState.errors[key] || formState.errors[txtKey]}
|
||||||
<span class="text-destructive text-sm leading-none">
|
<span class="text-destructive text-sm leading-none">
|
||||||
{formState.errors[key]}
|
{formState.errors[key] ?? formState.errors[txtKey]}
|
||||||
</span>
|
</span>
|
||||||
<!-- {:else if dynamicRequiredFields.includes(key)}
|
<!-- {:else if dynamicRequiredFields.includes(key)}
|
||||||
<span class="text-destructive text-sm leading-none">
|
<span class="text-destructive text-sm leading-none">
|
||||||
|
|||||||
@ -32,3 +32,31 @@ export function daysToAge(totalDays) {
|
|||||||
|
|
||||||
return { YY, MM, DD };
|
return { YY, MM, DD };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export function toDays(ageString) {
|
||||||
|
// if (!ageString || typeof ageString !== 'string') return null;
|
||||||
|
|
||||||
|
// const match = ageString.match(/(\d+)\s*Y?\s*(\d+)\s*M?\s*(\d+)\s*D?/i);
|
||||||
|
|
||||||
|
// if (!match) return null;
|
||||||
|
|
||||||
|
// const YY = parseInt(match[1]) || 0;
|
||||||
|
// const MM = parseInt(match[2]) || 0;
|
||||||
|
// const DD = parseInt(match[3]) || 0;
|
||||||
|
|
||||||
|
// return YY * 365 + MM * 30 + DD;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function toDays(ageString) {
|
||||||
|
if (!ageString || typeof ageString !== "string") return null;
|
||||||
|
|
||||||
|
const match = ageString.match(/(?:(\d+)\s*Y)?\s*(?:(\d+)\s*M)?\s*(?:(\d+)\s*D)?/i);
|
||||||
|
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
const YY = parseInt(match[1] || 0);
|
||||||
|
const MM = parseInt(match[2] || 0);
|
||||||
|
const DD = parseInt(match[3] || 0);
|
||||||
|
|
||||||
|
return YY * 365 + MM * 30 + DD;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user