diff --git a/src/lib/components/composable/use-form.svelte.js b/src/lib/components/composable/use-form.svelte.js index d84231d..02c893e 100644 --- a/src/lib/components/composable/use-form.svelte.js +++ b/src/lib/components/composable/use-form.svelte.js @@ -2,7 +2,7 @@ import { useFormState } from "./use-form-state.svelte"; import { useFormOptions } from "./use-form-option.svelte"; import { useFormValidation } from "./use-form-validation.svelte"; -export function useForm({schema, initialForm, defaultErrors, mode, modeOpt, saveEndpoint, editEndpoint}) { +export function useForm({schema, initialForm, defaultErrors = {}, mode = 'create', modeOpt = 'default', saveEndpoint = null, editEndpoint = null}) { const state = useFormState(initialForm); const val = useFormValidation(schema, state.form, defaultErrors, mode); const options = useFormOptions(modeOpt); 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 ff664b8..6dc2080 100644 --- a/src/lib/components/dictionary/test/config/test-form-config.js +++ b/src/lib/components/dictionary/test/config/test-form-config.js @@ -3,7 +3,30 @@ import EraserIcon from "@lucide/svelte/icons/eraser"; import { z } from "zod"; import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings"; -export const testSchema = z.object({}); +export const testSchema = z.object({ + TestSiteCode: z.string().min(1, "Required"), + TestSiteName: z.string().min(1, "Required"), +}); + +export const testCalSchema = z.object({ + FormulaInput: z.array(z.string()), + FormulaCode: z.string() +}).refine( + (data) => { + if (data.FormulaInput.length === 0) return true; + function hasExactKeyword(input, keyword) { + const regex = new RegExp(`\\b${keyword}\\b`, 'i'); + return regex.test(input); + } + return data.FormulaInput.every((v) => hasExactKeyword(data.FormulaCode, v)); + }, + { + message: 'Formula must contain all selected input parameters', + path: ['FormulaCode'] + } +); + + export const testInitialForm = { TestSiteID: "", @@ -62,7 +85,10 @@ export const refNumInitialForm = { Notes: "", } -export const testDefaultErrors = {}; +export const testDefaultErrors = { + TestSiteCode: "Required", + TestSiteName: "Required", +}; export const testCalDefaultErrors = {}; @@ -106,7 +132,7 @@ export const testFormFields = [ { key: "TestType", label: "Test Type", - required: true, + required: false, type: "select", optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/test_type`, } @@ -363,6 +389,7 @@ export const testCalFormFields = [ label: "Formula", required: false, type: "textarea", + validateOn: ["input"] } ] }, @@ -518,4 +545,23 @@ export function getTestFormActions(handlers) { onClick: handlers.clearForm, }, ]; +} + +export function buildTestPayload({ + mainForm, + calForm, + testType +}) { + let payload = { + ...mainForm + }; + + if (testType === 'CALC') { + payload = { + ...payload, + ...calForm + }; + } + + return cleanEmptyStrings(payload); } \ No newline at end of file diff --git a/src/lib/components/dictionary/test/page/create-page.svelte b/src/lib/components/dictionary/test/page/create-page.svelte index d6faf98..9c0f19a 100644 --- a/src/lib/components/dictionary/test/page/create-page.svelte +++ b/src/lib/components/dictionary/test/page/create-page.svelte @@ -6,10 +6,11 @@ import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte"; import * as Tabs from "$lib/components/ui/tabs/index.js"; import { useForm } from "$lib/components/composable/use-form.svelte"; - import { testCalInitialForm, testCalDefaultErrors, testCalFormFields, refNumInitialForm, refNumFormFields } from "$lib/components/dictionary/test/config/test-form-config"; + import { buildTestPayload, testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields, refNumInitialForm, refNumFormFields } from "$lib/components/dictionary/test/config/test-form-config"; import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte"; - import RefNum from "./reference/ref-num.svelte"; - import RefTxt from "./reference/ref-txt.svelte"; + import RefNum from "./tabs/ref-num.svelte"; + import RefTxt from "./tabs/ref-txt.svelte"; + import Calculation from "./tabs/calculation.svelte"; let props = $props(); @@ -20,11 +21,6 @@ const calFormState = useForm({ schema: null, initialForm: testCalInitialForm, - defaultErrors: {}, - mode: 'create', - modeOpt: 'default', - saveEndpoint: null, - editEndpoint: null, }); const refNumState = useForm({ @@ -50,10 +46,19 @@ let showConfirm = $state(false); async function handleSave() { - const result = await formState.save(masterDetail.mode); + const mainForm = masterDetail.formState.form; + const calForm = calFormState.form; - toast('Test Created!'); - masterDetail?.exitForm(true); + const payload = buildTestPayload({ + mainForm, + calForm, + testType: mainForm.TestType + }); + console.log(payload); + // const result = await formState.save(masterDetail.mode); + + // toast('Test Created!'); + // masterDetail?.exitForm(true); } const primaryAction = $derived({ @@ -134,6 +139,7 @@ activeTab = availableTabs[0]; } }); + @@ -165,10 +171,7 @@ /> - + group diff --git a/src/lib/components/dictionary/test/page/tabs/calculation.svelte b/src/lib/components/dictionary/test/page/tabs/calculation.svelte new file mode 100644 index 0000000..c16c88f --- /dev/null +++ b/src/lib/components/dictionary/test/page/tabs/calculation.svelte @@ -0,0 +1,158 @@ + + +
+
+
+
+ +
+
+ + + {formState.form.FormulaInput?.length + ? options + .filter(o => formState.form.FormulaInput.includes(o.value)) + .map(o => o.label) + .join(', ') + : 'Select parameters'} + + + + + {#if isLoading} +
+ Loading... +
+ {:else} + {#if formState.form.FormulaInput.length > 0} + + + {/if} + {#each options as opt (opt.value)} + + {opt.label} + + {/each} + {/if} +
+ +
+
+
+
+ +
+
+ +
+
+ + /> {:else if type === "select"} {@const selectedLabel = formState.selectOptions?.[key]?.find(opt => opt.value === formState.form[key])?.label || "Choose"} {@const filteredOptions = getFilteredOptions(key)} @@ -232,6 +238,9 @@ 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) { @@ -280,6 +289,14 @@ {/each} {/if} + {#if selectedValues.length > 0} + + {/if} {:else if type === "date"} {/if} -
- {#if formState.errors[key]} +
+ + {#if key === 'FormulaCode' && formState.errors[key]} +
+ {formState.errors[key]}: +
+ {#each formState.form.FormulaInput || [] as item} + {@const hasItem = hasExactKeyword(formState.form.FormulaCode, item)} + + {item} + + {/each} +
+
+ {:else if formState.errors[key]} {formState.errors[key]} {/if} +
diff --git a/src/routes/dictionary/test/+page.svelte b/src/routes/dictionary/test/+page.svelte index 839f1d3..805e51b 100644 --- a/src/routes/dictionary/test/+page.svelte +++ b/src/routes/dictionary/test/+page.svelte @@ -6,7 +6,7 @@ import ViewPage from "$lib/components/dictionary/test/page/view-page.svelte"; import CreatePage from "$lib/components/dictionary/test/page/create-page.svelte"; import EditPage from "$lib/components/dictionary/test/page/edit-page.svelte"; - import { testSchema, testInitialForm, testDefaultErrors, testFormFields, getTestFormActions } from "$lib/components/dictionary/test/config/test-form-config"; + import { testSchema, testInitialForm, testDefaultErrors, testFormFields, getTestFormActions, testCalDefaultErrors } from "$lib/components/dictionary/test/config/test-form-config"; const masterDetail = useMasterDetail({ onSelect: async (row) => {