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 9f2d136..0ac6e67 100644
--- a/src/lib/components/dictionary/test/config/test-form-config.js
+++ b/src/lib/components/dictionary/test/config/test-form-config.js
@@ -73,6 +73,10 @@ export const testCalSchema = z
}
});
+export const testGroupSchema = z.object({
+ Member: z.string().optional()
+ });
+
export const refNumSchema = z
.object({
AgeStart: z.string().optional(),
@@ -212,6 +216,12 @@ export const testCalInitialForm = {
FormulaCode: ''
};
+export const testGroupInitialForm = {
+ TestGrpID: '',
+ TestSiteID: '',
+ Member: '',
+}
+
export const refNumInitialForm = {
RefNumID: '',
SiteID: '',
@@ -270,9 +280,13 @@ export const testDefaultErrors = {
export const testCalDefaultErrors = {
FormulaInput: 'Required',
- FormulaCode: null
+ FormulaCode: null,
};
+export const testGroupDefaultErrors = {
+ Member: null,
+}
+
export const refNumDefaultErrors = {
AgeStart: null,
AgeEnd: null,
@@ -597,6 +611,28 @@ export const testCalFormFields = [
}
];
+export const testGroupFormFields = [
+ {
+ rows: [
+ {
+ type: 'row',
+ columns: [
+ {
+ key: 'Member',
+ label: 'Member Test',
+ required: false,
+ type: 'members',
+ optionsEndpoint: `${API.BASE_URL}${API.TEST}`,
+ valueKey: 'TestSiteCode',
+ labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`,
+ validateOn: ['input']
+ }
+ ]
+ },
+ ]
+ }
+];
+
export const refNumFormFields = [
{
rows: [
@@ -1013,21 +1049,25 @@ export function getTestFormActions(handlers) {
// return cleanEmptyStrings(payload);
// }
-export function buildTestPayload({ mainForm, activeFormStates, testType }) {
- let payload = {
- ...mainForm
- };
+export function buildTestPayload({ mainForm, activeFormStates, testType, refNumData, refTxtData }) {
+ let payload = {
+ ...mainForm
+ };
- for (const key in activeFormStates) {
+ for (const key in activeFormStates) {
const state = activeFormStates[key];
- if (state?.form) {
+ if (key === 'refNum' && refNumData?.length > 0) {
+ payload[key] = refNumData;
+ } else if (key === 'refTxt' && refTxtData?.length > 0) {
+ payload[key] = refTxtData;
+
+ } else if (state?.form) {
payload[key] = {
...state.form
};
}
}
-
- return cleanEmptyStrings(payload);
+ return cleanEmptyStrings(payload);
}
diff --git a/src/lib/components/dictionary/test/page/create-page.svelte b/src/lib/components/dictionary/test/page/create-page.svelte
index 3154a92..b09f8e8 100644
--- a/src/lib/components/dictionary/test/page/create-page.svelte
+++ b/src/lib/components/dictionary/test/page/create-page.svelte
@@ -1,502 +1,524 @@
-
-
- {#if availableTabs.includes('definition')}
- Definition
- {/if}
- {#if availableTabs.includes('calculation')}
- Calculation
- {/if}
- {#if availableTabs.includes('group')}
- Group
- {/if}
- {#if availableTabs.includes('reference')}
- Reference
- {/if}
- {#if availableTabs.includes('map')}
- Map
- {/if}
-
-
-
+
+
+ {#if availableTabs.includes('definition')}
+ Definition
+ {/if}
+ {#if availableTabs.includes('calculation')}
+ Calculation
+ {/if}
+ {#if availableTabs.includes('group')}
+ Group
+ {/if}
+ {#if availableTabs.includes('reference')}
+ Reference
+ {/if}
+ {#if availableTabs.includes('map')}
+ Map
+ {/if}
+
+
+
+
+
+
+
+
+
-
-
-
-
- group
-
-
-
-
-
-
- {#if refComponent === 'numeric'}
-
- {:else if refComponent === 'text'}
-
- {:else}
-
-
-
- {/if}
-
-
-
-
+
+
+
+
+
+ {#if refComponent === 'numeric'}
+
+ {:else if refComponent === 'text'}
+
+ {:else}
+
+
+
+ {/if}
+
+
+
\ No newline at end of file
+ bind:open={masterDetail.showExitConfirm}
+ onConfirm={masterDetail.confirmExit}
+/>
diff --git a/src/lib/components/dictionary/test/page/tabs/calculation.svelte b/src/lib/components/dictionary/test/page/tabs/calculation.svelte
index 077d943..87945ba 100644
--- a/src/lib/components/dictionary/test/page/tabs/calculation.svelte
+++ b/src/lib/components/dictionary/test/page/tabs/calculation.svelte
@@ -9,154 +9,11 @@
import DictionaryFormRenderer from '$lib/components/reusable/form/dictionary-form-renderer.svelte';
let props = $props();
-
- // const formState = props.calFormState;
-
- // let options = $state([]);
- // let isLoading = $state(false);
- // let errors = $state({});
-
- // function hasExactKeyword(input, keyword) {
- // const regex = new RegExp(`\\b${keyword}\\b`, 'i');
- // return regex.test(input);
- // }
-
- // // 🔹 FETCH OPTIONS
- // async function fetchTests() {
- // isLoading = true;
- // try {
- // const res = await fetch(`${API.BASE_URL}${API.TEST}`);
- // const data = await res.json();
- // console.log(data);
-
- // options = data.data.map((item) => ({
- // value: item.TestSiteCode,
- // label: `${item.TestSiteCode} - ${item.TestSiteName}`
- // }));
- // } catch (err) {
- // console.error('Failed to fetch tests', err);
- // } finally {
- // isLoading = false;
- // }
- // }
-
- // $effect(() => {
- // fetchTests();
- // });
-
- // // 🔹 VALIDATION
- // $effect(() => {
- // const result = testCalSchema.safeParse(formState.form);
-
- // if (!result.success) {
- // const fieldErrors = {};
- // for (const issue of result.error.issues) {
- // fieldErrors[issue.path[0]] = issue.message;
- // }
- // errors = fieldErrors;
- // } else {
- // errors = {};
- // }
- // });
-
- // // 🔹 Badge status
- // const inputStatus = $derived.by(() => {
- // const inputs = formState.form.FormulaInput || [];
- // const code = formState.form.FormulaCode || '';
-
- // return inputs.map((v) => ({
- // value: v,
- // done: hasExactKeyword(code, v)
- // }));
- // });
-
- // function unselectAll() {
- // formState.form.FormulaInput = [];
- // errors = {};
- // }
-
-
-
diff --git a/src/lib/components/dictionary/test/page/tabs/group.svelte b/src/lib/components/dictionary/test/page/tabs/group.svelte
new file mode 100644
index 0000000..75ecc4f
--- /dev/null
+++ b/src/lib/components/dictionary/test/page/tabs/group.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/dictionary/test/page/tabs/ref-num.svelte b/src/lib/components/dictionary/test/page/tabs/ref-num.svelte
index a5054e1..ada7670 100644
--- a/src/lib/components/dictionary/test/page/tabs/ref-num.svelte
+++ b/src/lib/components/dictionary/test/page/tabs/ref-num.svelte
@@ -1,307 +1,301 @@
-
-
-
- {#if editingId !== null}
-
- Update
-
-
- Cancel
-
- {:else}
-
- Insert
-
- {/if}
-
-
+
+
+
+ {#if editingId !== null}
+ Update
+
+ Cancel
+
+ {:else}
+ Insert
+ {/if}
+
+
-
+
-
-
-
-
- Specimen Type
- Sex
- Age Range
- Type
- Range/Threshold
- Flag
- Interpretation
- Notes
-
-
-
-
- {#if tempNumeric.length === 0}
-
-
- No data. Fill the form above and click Insert.
-
-
- {:else}
- {#each tempNumeric as row (row.id)}
-
- {row.SpcType ? getLabel("SpcType", row.SpcType) : "-"}
- {row.Sex ? getLabel("Sex", row.Sex) : "-"}
- {row.AgeStart} – {row.AgeEnd}
-
- {#if rangeTypeBadge(row.RangeType)}
- {rangeTypeBadge(row.RangeType)}
- {:else}
- -
- {/if}
-
-
-
- {row.LowSign ? row.LowSign : ""} {row.Low || "null"} –
- {row.HighSign ? row.HighSign : ""} {row.High || "null"}
-
- {numRefTypeBadge(row.NumRefType)}
-
- {row.Flag}
- {row.Interpretation}
- {row.Notes}
-
-
-
handleEdit(row)}
- >
-
-
-
handleRemove(row.id)}
- >
-
-
-
-
-
- {/each}
- {/if}
-
-
-
-
\ No newline at end of file
+
+
+
+
+ Specimen Type
+ Sex
+ Age Range
+ Type
+ Range/Threshold
+ Flag
+ Interpretation
+ Notes
+
+
+
+
+ {#if tempNumeric.length === 0}
+
+
+ No data. Fill the form above and click Insert.
+
+
+ {:else}
+ {#each tempNumeric as row (row.id)}
+
+ {row.SpcType ? getLabel('SpcType', row.SpcType) : '-'}
+ {row.Sex ? getLabel('Sex', row.Sex) : '-'}
+ {row.AgeStart} – {row.AgeEnd}
+
+ {#if rangeTypeBadge(row.RangeType)}
+ {rangeTypeBadge(row.RangeType)}
+ {:else}
+ -
+ {/if}
+
+
+
+ {row.LowSign ? row.LowSign : ''}
+ {row.Low || 'null'} –
+ {row.HighSign ? row.HighSign : ''}
+ {row.High || 'null'}
+
+ {numRefTypeBadge(row.NumRefType)}
+
+ {row.Flag}
+ {row.Interpretation}
+ {row.Notes}
+
+
+
handleEdit(row)}
+ >
+
+
+
handleRemove(row.id)}
+ >
+
+
+
+
+
+ {/each}
+ {/if}
+
+
+
+
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 2bd687f..09eb594 100644
--- a/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte
+++ b/src/lib/components/dictionary/test/page/tabs/ref-txt.svelte
@@ -10,9 +10,8 @@
import { untrack } from "svelte";
import { toDays } from "$lib/utils/ageUtils";
- let { resetRefTxt = $bindable(), ...props } = $props()
+ let { tempTxt = $bindable([]), resetRefTxt = $bindable(), ...props } = $props()
- let tempTxt = $state([]);
let editingId = $state(null);
let idCounter = $state(0);
diff --git a/src/lib/components/reusable/form/dictionary-form-renderer.svelte b/src/lib/components/reusable/form/dictionary-form-renderer.svelte
index c628dd8..29fa9bd 100644
--- a/src/lib/components/reusable/form/dictionary-form-renderer.svelte
+++ b/src/lib/components/reusable/form/dictionary-form-renderer.svelte
@@ -15,6 +15,8 @@
import MoveRightIcon from '@lucide/svelte/icons/move-right';
import BrushCleaningIcon from '@lucide/svelte/icons/brush-cleaning';
import DeleteIcon from '@lucide/svelte/icons/delete';
+ import Trash2Icon from '@lucide/svelte/icons/trash-2';
+ import PlusIcon from '@lucide/svelte/icons/plus';
let {
formState,
@@ -27,7 +29,10 @@
hiddenFields,
handleTestTypeChange,
handleResultTypeChange,
- handleRefTypeChange
+ handleRefTypeChange,
+ members = [],
+ onAddMember,
+ onRemoveMember,
} = $props();
const operators = ['+', '-', '*', '/', '^', '(', ')'];
@@ -610,6 +615,69 @@
{/if}
+ {:else if type === "members"}
+ {@const filteredOptions = getFilteredOptions(key)}
+
+ {#each members as member, index (member.id)}
+ {@const selectedLabel =
+ formState.selectOptions?.[key]?.find(
+ (opt) => opt.value === member.value
+ )?.label || 'Choose'}
+
+
+ {index + 1}
+
+
+
+
{
+ if (open && optionsEndpoint) {
+ formState.fetchOptions?.(
+ { key, optionsEndpoint, dependsOn, endpointParamKey, valueKey, labelKey },
+ formState.form
+ );
+ }
+ }}
+ >
+
+ {selectedLabel}
+
+
+
+
+
+ {#if formState.loadingOptions?.[key]}
+ Loading...
+ {:else}
+ {#if !required}
+ - None -
+ {/if}
+ {#each filteredOptions as option}
+
+ {option.label}
+
+ {/each}
+ {/if}
+
+
+
+
onRemoveMember(member.id)}>
+
+
+
+ {/each}
+
+
+ Add Test
+
+
{:else}