From a54e1b9d2317ade4abdd0e430ee998fc778d2e1a Mon Sep 17 00:00:00 2001 From: faiztyanirh Date: Fri, 20 Feb 2026 13:52:01 +0700 Subject: [PATCH] continue dict test reference --- .../test/config/test-form-config.js | 163 ++++++++++++++++++ .../dictionary/test/page/create-page.svelte | 36 +++- .../test/page/reference/ref-num.svelte | 55 ++++++ .../test/page/reference/ref-txt.svelte | 1 + .../form/dictionary-form-renderer.svelte | 132 ++++++++------ src/lib/components/ui/input-group/index.js | 22 +++ .../ui/input-group/input-group-addon.svelte | 49 ++++++ .../ui/input-group/input-group-button.svelte | 44 +++++ .../ui/input-group/input-group-input.svelte | 22 +++ .../ui/input-group/input-group-text.svelte | 20 +++ .../input-group/input-group-textarea.svelte | 21 +++ .../ui/input-group/input-group.svelte | 36 ++++ src/lib/components/ui/textarea/index.js | 7 + .../components/ui/textarea/textarea.svelte | 21 +++ 14 files changed, 571 insertions(+), 58 deletions(-) create mode 100644 src/lib/components/dictionary/test/page/reference/ref-num.svelte create mode 100644 src/lib/components/dictionary/test/page/reference/ref-txt.svelte create mode 100644 src/lib/components/ui/input-group/index.js create mode 100644 src/lib/components/ui/input-group/input-group-addon.svelte create mode 100644 src/lib/components/ui/input-group/input-group-button.svelte create mode 100644 src/lib/components/ui/input-group/input-group-input.svelte create mode 100644 src/lib/components/ui/input-group/input-group-text.svelte create mode 100644 src/lib/components/ui/input-group/input-group-textarea.svelte create mode 100644 src/lib/components/ui/input-group/input-group.svelte create mode 100644 src/lib/components/ui/textarea/index.js create mode 100644 src/lib/components/ui/textarea/textarea.svelte 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 6b42102..aac173f 100644 --- a/src/lib/components/dictionary/test/config/test-form-config.js +++ b/src/lib/components/dictionary/test/config/test-form-config.js @@ -41,10 +41,33 @@ export const testCalInitialForm = { FormulaCode: "" } +export const refNumInitialForm = { + RefNumID: "", + SiteID: "", + TestSiteID: "", + SpcType: "", + Sex: "", + Criteria: "", + AgeStart: "", + AgeEnd: "", + NumRefType: "", + RangeType: "", + LowSign: "", + Low: "", + HighSign: "", + High: "", + Display: "", + Flag: "", + Interpretation: "", + Notes: "", +} + export const testDefaultErrors = {}; export const testCalDefaultErrors = {}; +export const refNumDefaultErrors = {}; + export const testFormFields = [ { title: "Basic Information", @@ -347,6 +370,146 @@ export const testCalFormFields = [ }, ]; +export const refNumFormFields = [ + { + rows: [ + { + type: "row", + columns: [ + { + key: "SiteID", + label: "Site", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.SITE}`, + valueKey: "SiteID", + labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`, + fullWidth: false + }, + ] + } + ] + }, + { + title: "Criteria Definition", + rows: [ + { + type: "row", + columns: [ + { + key: "Sex", + label: "Sex", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/sex`, + }, + { + key: "SpcType", + label: "Specimen Type", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/specimen_type`, + }, + ] + }, + { + type: "row", + columns: [ + { + key: "AgeStart", + label: "Age Start", + required: false, + type: "agejoin", + }, + { + key: "AgeEnd", + label: "Age End", + required: false, + type: "agejoin", + }, + ] + }, + ] + }, + { + title: "Reference Range Configuration", + rows: [ + { + type: "row", + columns: [ + { + key: "NumRefType", + label: "Reference Type", + required: false, + type: "text", + }, + { + key: "RangeType", + label: "Range Type", + required: false, + type: "select", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/range_type`, + }, + ] + }, + { + type: "row", + columns: [ + { + key: "LowSign", + label: "Low Sign - Value", + required: false, + type: "signvalue", + txtKey: "Low", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`, + }, + { + key: "HighSign", + label: "High Sign - Value", + required: false, + type: "signvalue", + txtKey: "High", + optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`, + }, + ] + }, + ] + }, + { + title: "Flag & Interpretation", + rows: [ + { + type: "row", + columns: [ + { + key: "Flag", + label: "Flag", + required: false, + type: "text", + }, + { + key: "Interpretation", + label: "Interpretation", + required: false, + type: "text", + }, + ] + }, + { + type: "row", + columns: [ + { + key: "Notes", + label: "Notes", + required: false, + type: "textarea", + }, + ] + }, + ] + }, +]; + export function getTestFormActions(handlers) { return [ { diff --git a/src/lib/components/dictionary/test/page/create-page.svelte b/src/lib/components/dictionary/test/page/create-page.svelte index 4bff877..18c21e2 100644 --- a/src/lib/components/dictionary/test/page/create-page.svelte +++ b/src/lib/components/dictionary/test/page/create-page.svelte @@ -6,7 +6,10 @@ 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 } from "$lib/components/dictionary/test/config/test-form-config"; + import { 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"; let props = $props(); @@ -24,6 +27,16 @@ editEndpoint: null, }); + const refNumState = useForm({ + schema: null, + initialForm: refNumInitialForm, + defaultErrors: {}, + mode: 'create', + modeOpt: 'default', + saveEndpoint: null, + editEndpoint: null, + }) + const helpers = useDictionaryForm(formState); const handlers = { @@ -107,6 +120,13 @@ } }); + let refComponent = $derived.by(() => { + const refType = formState.form.RefType; + if (refType === 'RANGE' || refType === 'THOLD') return 'numeric'; + if (refType === 'TEXT' || refType === 'VSET') return 'text'; + return null; + }); + let activeTab = $state('definition'); $effect(() => { @@ -117,7 +137,7 @@ - + {#if availableTabs.includes('definition')} Definition @@ -157,7 +177,17 @@ map - ref +
+ {#if refComponent === 'numeric'} + + {:else if refComponent === 'text'} + + {:else} +
+ +
+ {/if} +
diff --git a/src/lib/components/dictionary/test/page/reference/ref-num.svelte b/src/lib/components/dictionary/test/page/reference/ref-num.svelte new file mode 100644 index 0000000..ea9a7a8 --- /dev/null +++ b/src/lib/components/dictionary/test/page/reference/ref-num.svelte @@ -0,0 +1,55 @@ + + +
+
+ + +
+ +
+ + + + Specimen Type + Sex + Age Range + Type + Range/Threshold + Flag + Interpretation + Notes + + + + + Whole Blood + Female + 1Y 0M 0D - 10Y 0M 0D + Reference Range + Range + L + Interpretation + Testing + + + +
+
\ No newline at end of file diff --git a/src/lib/components/dictionary/test/page/reference/ref-txt.svelte b/src/lib/components/dictionary/test/page/reference/ref-txt.svelte new file mode 100644 index 0000000..84c22fd --- /dev/null +++ b/src/lib/components/dictionary/test/page/reference/ref-txt.svelte @@ -0,0 +1 @@ +txt \ No newline at end of file diff --git a/src/lib/components/reusable/form/dictionary-form-renderer.svelte b/src/lib/components/reusable/form/dictionary-form-renderer.svelte index f4116fc..d3c16da 100644 --- a/src/lib/components/reusable/form/dictionary-form-renderer.svelte +++ b/src/lib/components/reusable/form/dictionary-form-renderer.svelte @@ -8,6 +8,8 @@ import { Spinner } from "$lib/components/ui/spinner/index.js"; import ReusableCalendar from "$lib/components/reusable/reusable-calendar.svelte"; import { Badge } from "$lib/components/ui/badge/index.js"; + import * as InputGroup from "$lib/components/ui/input-group/index.js"; + import ChevronDownIcon from "@lucide/svelte/icons/chevron-down"; let { formState, @@ -19,6 +21,7 @@ } = $props(); let searchQuery = $state({}); + let dropdownOpen = $state({}); function getFilteredOptions(key) { const query = searchQuery[key] || ""; @@ -60,7 +63,7 @@ } -{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey, valueKey, labelKey })} +{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey, valueKey, labelKey, txtKey })}
@@ -277,60 +280,6 @@ {/if} - - {:else if type === "date"} + {:else if type === "signvalue"} + + + + { + dropdownOpen[key] = open; + if (open && optionsEndpoint) { + formState.fetchOptions?.( + { key, optionsEndpoint, valueKey, labelKey }, + formState.form + ); + } + }} + > + + {#snippet child({ props })} + + {formState.selectOptions?.[key]?.find( + opt => opt.value === formState.form[key] + )?.label || 'Choose'} + + + {/snippet} + + + {#if formState.loadingOptions?.[key]} + + Loading... + + {:else} + {#each formState.selectOptions?.[key] || [] as option} + { + formState.form[key] = option.value; + dropdownOpen[key] = false; + }} + > + {option.label} + + {/each} + {/if} + + + + + {:else if type === "agejoin"} +
+ + + + Year + + + + + + Month + + + + + + Day + + +
{:else} + import { tv } from "tailwind-variants"; + export const inputGroupAddonVariants = tv({ + base: "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", + variants: { + align: { + "inline-start": + "order-first ps-3 has-[>button]:ms-[-0.45rem] has-[>kbd]:ms-[-0.35rem]", + "inline-end": + "order-last pe-3 has-[>button]:me-[-0.45rem] has-[>kbd]:me-[-0.35rem]", + "block-start": + "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3", + "block-end": + "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3", + }, + }, + defaultVariants: { + align: "inline-start", + }, + }); + + + + +
{ + if ((e.target).closest("button")) { + return; + } + e.currentTarget.parentElement?.querySelector("input")?.focus(); + }} + {...restProps} +> + {@render children?.()} +
\ No newline at end of file diff --git a/src/lib/components/ui/input-group/input-group-button.svelte b/src/lib/components/ui/input-group/input-group-button.svelte new file mode 100644 index 0000000..685e5ff --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-button.svelte @@ -0,0 +1,44 @@ + + + + + \ No newline at end of file diff --git a/src/lib/components/ui/input-group/input-group-input.svelte b/src/lib/components/ui/input-group/input-group-input.svelte new file mode 100644 index 0000000..a4aa1bf --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-input.svelte @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/src/lib/components/ui/input-group/input-group-text.svelte b/src/lib/components/ui/input-group/input-group-text.svelte new file mode 100644 index 0000000..b3739b7 --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-text.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + \ No newline at end of file diff --git a/src/lib/components/ui/input-group/input-group-textarea.svelte b/src/lib/components/ui/input-group/input-group-textarea.svelte new file mode 100644 index 0000000..79dd442 --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-textarea.svelte @@ -0,0 +1,21 @@ + + + \ No newline at end of file