From 795251d91130f5fadc5e3921c91f0fa1e74ec14e Mon Sep 17 00:00:00 2001 From: faiztyanirh Date: Mon, 23 Feb 2026 17:01:50 +0700 Subject: [PATCH] continue dict test refnum/reftxt. add validation rule --- .../test/config/test-form-config.js | 26 +++- .../dictionary/test/page/create-page.svelte | 112 +++++++++++++++++- .../dictionary/test/page/tabs/ref-txt.svelte | 2 +- .../form/dictionary-form-renderer.svelte | 70 ++++++++++- src/routes/+layout.server.js | 3 + 5 files changed, 202 insertions(+), 11 deletions(-) diff --git a/src/lib/components/dictionary/test/config/test-form-config.js b/src/lib/components/dictionary/test/config/test-form-config.js index 677fb49..fb9a74d 100644 --- a/src/lib/components/dictionary/test/config/test-form-config.js +++ b/src/lib/components/dictionary/test/config/test-form-config.js @@ -6,6 +6,22 @@ import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings"; export const testSchema = z.object({ TestSiteCode: z.string().min(1, "Required"), TestSiteName: z.string().min(1, "Required"), + Factor: z.string().optional(), + Unit2: z.string().optional(), + Decimal: z.preprocess((val) => { + if (val === "" || val === null || val === undefined) return undefined; + return Number(val); + }, + z.number({invalid_type_error: "Must be a number"}).min(0, "Min 0").max(7, "Max 7").optional() + ) +}).superRefine((data, ctx) => { + if (data.Factor && !data.Unit2) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Required", + path: ["Unit2"], + }); + } }); export const testCalSchema = z.object({ @@ -101,6 +117,8 @@ export const refTxtInitialForm = { export const testDefaultErrors = { TestSiteCode: "Required", TestSiteName: "Required", + Unit2: null, + Decimal: null, }; export const testCalDefaultErrors = {}; @@ -224,8 +242,9 @@ export const testFormFields = [ label: "Value Set", required: false, type: "select", - optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/v_category`, - fullWidth: false + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}`, + fullWidth: false, + autoFetch: false }, ] }, @@ -259,7 +278,8 @@ export const testFormFields = [ key: "Decimal", label: "Decimal", required: false, - type: "text", + type: "number", + validateOn: ["input"] }, ] }, diff --git a/src/lib/components/dictionary/test/page/create-page.svelte b/src/lib/components/dictionary/test/page/create-page.svelte index c2ce1d2..68a2493 100644 --- a/src/lib/components/dictionary/test/page/create-page.svelte +++ b/src/lib/components/dictionary/test/page/create-page.svelte @@ -11,10 +11,16 @@ import RefNum from "./tabs/ref-num.svelte"; import RefTxt from "./tabs/ref-txt.svelte"; import Calculation from "./tabs/calculation.svelte"; + import { API } from "$lib/config/api"; + import { untrack } from "svelte"; let props = $props(); - const { masterDetail, formFields, formActions, schema } = props.context; + let previousTestType = $state(''); + + let previousRefType = $state(''); + + const { masterDetail, formFields, formActions, schema, initialForm } = props.context; const { formState } = masterDetail; @@ -43,6 +49,12 @@ const actions = formActions(handlers); + const allColumns = formFields.flatMap( + (section) => section.rows.flatMap( + (row) => row.columns ?? [] + ) + ); + let showConfirm = $state(false); async function handleSave() { @@ -127,6 +139,11 @@ } }); + let hiddenFields = $derived.by(() => { + const resultType = formState?.form?.ResultType; + return resultType !== "VSET" ? ["VSet"] : []; + }); + let refComponent = $derived.by(() => { const refType = formState.form.RefType; if (refType === 'RANGE' || refType === 'THOLD') return 'numeric'; @@ -134,6 +151,33 @@ return null; }); + const refTxtFormFieldsTransformed = $derived.by(() => { + return refTxtFormFields.map(group => ({ + ...group, + rows: group.rows.map(row => ({ + ...row, + columns: row.columns.map(col => { + if (col.key !== "RefTxt") return col; + + if (formState.form.ResultType !== "VSET" || !formState.form.VSet) { + return { + ...col, + type: "textarea", + optionsEndpoint: undefined + }; + } + + return { + ...col, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/${formState.form.VSet}`, + fullWidth: false + }; + }) + })) + })); + }); + let activeTab = $state('definition'); $effect(() => { @@ -142,6 +186,69 @@ } }); + $effect(() => { + for (const key of hiddenFields) { + formState.form[key] = ""; + } + }); + + $effect(() => { + if (formState.form.Factor && !formState.form.Unit2) { + formState.errors.Unit2 = "Required"; + } else { + formState.errors.Unit2 = null; + } + }); + + $effect(() => { + const allColumns = formFields.flatMap( + (section) => section.rows.flatMap( + (row) => row.columns ?? [] + ) + ); + + untrack(() => { + for (const col of allColumns) { + if (!col.optionsEndpoint) continue; + if (!col.optionsEndpoint || col.autoFetch === false) continue; + + formState.fetchOptions?.( + { + key: col.key, + optionsEndpoint: col.optionsEndpoint, + valueKey: col.valueKey, + labelKey: col.labelKey, + }, + formState.form + ); + } + }) + }); + + $effect(() => { + const currentTestType = formState.form.TestType; + + if (previousTestType && currentTestType !== previousTestType) { + calFormState.reset(); + refNumState.reset(); + refTxtState.reset(); + } + + previousTestType = currentTestType; + }); + + //masih error cek dulu + $effect(() => { + const currentRefType = formState.form.RefType; + + if (previousRefType && currentRefType !== previousRefType) { + console.log('rst'); + refNumState.reset(); + refTxtState.reset(); + } + + previousRefType = currentRefType; + }); @@ -170,6 +277,7 @@ mode="create" {disabledResultTypes} {disabledReferenceTypes} + {hiddenFields} /> @@ -186,7 +294,7 @@ {#if refComponent === 'numeric'} {:else if refComponent === 'text'} - + {:else}
diff --git a/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte b/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte index 09a4930..3fdd5dc 100644 --- a/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte +++ b/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte @@ -99,7 +99,7 @@ return found ? found.code : value; } - const txtRefTypeBadge = (type) => ({ TEXT: "TX", VSET: "V" }[type] ?? null);; + const txtRefTypeBadge = (type) => ({ TEXT: "TX", VSET: "VS" }[type] ?? null);; $effect(() => { if (props.refType) { diff --git a/src/lib/components/reusable/form/dictionary-form-renderer.svelte b/src/lib/components/reusable/form/dictionary-form-renderer.svelte index 6d558f9..3420f92 100644 --- a/src/lib/components/reusable/form/dictionary-form-renderer.svelte +++ b/src/lib/components/reusable/form/dictionary-form-renderer.svelte @@ -19,9 +19,10 @@ disabledResultTypes = [], disabledReferenceTypes = [], disabledSign = false, - joinFields = $bindable() + joinFields = $bindable(), + hiddenFields, } = $props(); - + let searchQuery = $state({}); let dropdownOpen = $state({}); @@ -77,6 +78,9 @@ {#if required} * {/if} +
@@ -94,7 +98,7 @@ validateFieldAsync(key, mode, originalData?.[key]); } }} - readonly={key === "NumRefType" || key === "TxtRefType"} + readonly={key === "NumRefType" || key === "TxtRefType" || key === "Level"} /> {:else if type === "email"} ['e', 'E', '+', '-'].includes(e.key) && e.preventDefault()} /> {:else if type === "textarea"}