mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-29 01:58:01 +07:00
continue dict test refnum/reftxt. add validation rule
This commit is contained in:
parent
a139c979e3
commit
795251d911
@ -6,6 +6,22 @@ import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
|||||||
export const testSchema = z.object({
|
export const testSchema = z.object({
|
||||||
TestSiteCode: z.string().min(1, "Required"),
|
TestSiteCode: z.string().min(1, "Required"),
|
||||||
TestSiteName: 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({
|
export const testCalSchema = z.object({
|
||||||
@ -101,6 +117,8 @@ export const refTxtInitialForm = {
|
|||||||
export const testDefaultErrors = {
|
export const testDefaultErrors = {
|
||||||
TestSiteCode: "Required",
|
TestSiteCode: "Required",
|
||||||
TestSiteName: "Required",
|
TestSiteName: "Required",
|
||||||
|
Unit2: null,
|
||||||
|
Decimal: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testCalDefaultErrors = {};
|
export const testCalDefaultErrors = {};
|
||||||
@ -224,8 +242,9 @@ export const testFormFields = [
|
|||||||
label: "Value Set",
|
label: "Value Set",
|
||||||
required: false,
|
required: false,
|
||||||
type: "select",
|
type: "select",
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/v_category`,
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}`,
|
||||||
fullWidth: false
|
fullWidth: false,
|
||||||
|
autoFetch: false
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -259,7 +278,8 @@ export const testFormFields = [
|
|||||||
key: "Decimal",
|
key: "Decimal",
|
||||||
label: "Decimal",
|
label: "Decimal",
|
||||||
required: false,
|
required: false,
|
||||||
type: "text",
|
type: "number",
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -11,10 +11,16 @@
|
|||||||
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";
|
||||||
import Calculation from "./tabs/calculation.svelte";
|
import Calculation from "./tabs/calculation.svelte";
|
||||||
|
import { API } from "$lib/config/api";
|
||||||
|
import { untrack } from "svelte";
|
||||||
|
|
||||||
let props = $props();
|
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;
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
@ -43,6 +49,12 @@
|
|||||||
|
|
||||||
const actions = formActions(handlers);
|
const actions = formActions(handlers);
|
||||||
|
|
||||||
|
const allColumns = formFields.flatMap(
|
||||||
|
(section) => section.rows.flatMap(
|
||||||
|
(row) => row.columns ?? []
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
let showConfirm = $state(false);
|
let showConfirm = $state(false);
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
@ -127,6 +139,11 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let hiddenFields = $derived.by(() => {
|
||||||
|
const resultType = formState?.form?.ResultType;
|
||||||
|
return resultType !== "VSET" ? ["VSet"] : [];
|
||||||
|
});
|
||||||
|
|
||||||
let refComponent = $derived.by(() => {
|
let refComponent = $derived.by(() => {
|
||||||
const refType = formState.form.RefType;
|
const refType = formState.form.RefType;
|
||||||
if (refType === 'RANGE' || refType === 'THOLD') return 'numeric';
|
if (refType === 'RANGE' || refType === 'THOLD') return 'numeric';
|
||||||
@ -134,6 +151,33 @@
|
|||||||
return null;
|
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');
|
let activeTab = $state('definition');
|
||||||
|
|
||||||
$effect(() => {
|
$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;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
|
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
|
||||||
@ -170,6 +277,7 @@
|
|||||||
mode="create"
|
mode="create"
|
||||||
{disabledResultTypes}
|
{disabledResultTypes}
|
||||||
{disabledReferenceTypes}
|
{disabledReferenceTypes}
|
||||||
|
{hiddenFields}
|
||||||
/>
|
/>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
<Tabs.Content value="calculation">
|
<Tabs.Content value="calculation">
|
||||||
@ -186,7 +294,7 @@
|
|||||||
{#if refComponent === 'numeric'}
|
{#if refComponent === 'numeric'}
|
||||||
<RefNum {refNumState} {refNumFormFields} refType={formState.form.RefType}/>
|
<RefNum {refNumState} {refNumFormFields} refType={formState.form.RefType}/>
|
||||||
{:else if refComponent === 'text'}
|
{:else if refComponent === 'text'}
|
||||||
<RefTxt {refTxtState} {refTxtFormFields} refType={formState.form.RefType}/>
|
<RefTxt {refTxtState} refTxtFormFields={refTxtFormFieldsTransformed} refType={formState.form.RefType}/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="h-full w-full flex items-center">
|
<div class="h-full w-full flex items-center">
|
||||||
<ReusableEmpty desc="Select a Reference Type" />
|
<ReusableEmpty desc="Select a Reference Type" />
|
||||||
|
|||||||
@ -99,7 +99,7 @@
|
|||||||
return found ? found.code : value;
|
return found ? found.code : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const txtRefTypeBadge = (type) => ({ TEXT: "TX", VSET: "V" }[type] ?? null);;
|
const txtRefTypeBadge = (type) => ({ TEXT: "TX", VSET: "VS" }[type] ?? null);;
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (props.refType) {
|
if (props.refType) {
|
||||||
|
|||||||
@ -19,9 +19,10 @@
|
|||||||
disabledResultTypes = [],
|
disabledResultTypes = [],
|
||||||
disabledReferenceTypes = [],
|
disabledReferenceTypes = [],
|
||||||
disabledSign = false,
|
disabledSign = false,
|
||||||
joinFields = $bindable()
|
joinFields = $bindable(),
|
||||||
|
hiddenFields,
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let searchQuery = $state({});
|
let searchQuery = $state({});
|
||||||
let dropdownOpen = $state({});
|
let dropdownOpen = $state({});
|
||||||
|
|
||||||
@ -77,6 +78,9 @@
|
|||||||
{#if required}
|
{#if required}
|
||||||
<span class="text-destructive text-xl leading-none h-3.5">*</span>
|
<span class="text-destructive text-xl leading-none h-3.5">*</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
<!-- {#if required || dynamicRequiredFields.includes(key)}
|
||||||
|
<span class="text-destructive text-xl leading-none h-3.5">*</span>
|
||||||
|
{/if} -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="relative flex flex-col items-center w-full">
|
<div class="relative flex flex-col items-center w-full">
|
||||||
@ -94,7 +98,7 @@
|
|||||||
validateFieldAsync(key, mode, originalData?.[key]);
|
validateFieldAsync(key, mode, originalData?.[key]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
readonly={key === "NumRefType" || key === "TxtRefType"}
|
readonly={key === "NumRefType" || key === "TxtRefType" || key === "Level"}
|
||||||
/>
|
/>
|
||||||
{:else if type === "email"}
|
{:else if type === "email"}
|
||||||
<Input
|
<Input
|
||||||
@ -125,6 +129,7 @@
|
|||||||
formState.validateField(key, formState.form[key], false);
|
formState.validateField(key, formState.form[key], false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onkeydown={(e) => ['e', 'E', '+', '-'].includes(e.key) && e.preventDefault()}
|
||||||
/>
|
/>
|
||||||
{:else if type === "textarea"}
|
{:else if type === "textarea"}
|
||||||
<Textarea
|
<Textarea
|
||||||
@ -218,7 +223,7 @@
|
|||||||
{#each filteredOptions as option}
|
{#each filteredOptions as option}
|
||||||
<Select.Item value={option.value}
|
<Select.Item value={option.value}
|
||||||
disabled={key === "ResultType" && disabledResultTypes.includes(option.value) ||
|
disabled={key === "ResultType" && disabledResultTypes.includes(option.value) ||
|
||||||
key === "RefType" && disabledReferenceTypes.includes(option.value)}
|
key === "RefType" && disabledReferenceTypes.includes(option.value)}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
@ -394,10 +399,19 @@
|
|||||||
key === 'FormulaCode' ? 'top-20' : 'top-8'
|
key === 'FormulaCode' ? 'top-20' : 'top-8'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
<!-- {#if formState.errors[key] || dynamicRequiredFields.includes(key)}
|
||||||
|
<span class="text-destructive text-sm leading-none">
|
||||||
|
{formState.errors[key]}
|
||||||
|
</span>
|
||||||
|
{/if} -->
|
||||||
{#if formState.errors[key]}
|
{#if formState.errors[key]}
|
||||||
<span class="text-destructive text-sm leading-none">
|
<span class="text-destructive text-sm leading-none">
|
||||||
{formState.errors[key]}
|
{formState.errors[key]}
|
||||||
</span>
|
</span>
|
||||||
|
<!-- {:else if dynamicRequiredFields.includes(key)}
|
||||||
|
<span class="text-destructive text-sm leading-none">
|
||||||
|
Required
|
||||||
|
</span> -->
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -405,6 +419,52 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<div class="p-2 space-y-6">
|
<div class="p-2 space-y-6">
|
||||||
|
{#each formFields as group}
|
||||||
|
<div class="space-y-6">
|
||||||
|
{#if group.title}
|
||||||
|
<div class="text-md 2xl:text-lg font-semibold italic">
|
||||||
|
<span class="border-b-2 border-primary">{group.title}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each group.rows as row}
|
||||||
|
{@const visibleColumns = row.columns.filter(col => !hiddenFields?.includes(col.key))}
|
||||||
|
|
||||||
|
{#if visibleColumns.length > 0}
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-1 space-y-2 gap-6 md:gap-4"
|
||||||
|
class:md:grid-cols-1={visibleColumns.length === 1 && visibleColumns[0].fullWidth !== false}
|
||||||
|
class:md:grid-cols-2={visibleColumns.length === 2 || (visibleColumns.length === 1 && visibleColumns[0].fullWidth === false)}
|
||||||
|
class:md:grid-cols-3={visibleColumns.length === 3}
|
||||||
|
>
|
||||||
|
{#each visibleColumns as col}
|
||||||
|
{#if col.type === "group"}
|
||||||
|
{@const visibleChildColumns = col.columns.filter(child => !hiddenFields?.includes(child.key))}
|
||||||
|
|
||||||
|
{#if visibleChildColumns.length > 0}
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-1 gap-6 md:gap-2"
|
||||||
|
class:md:grid-cols-1={visibleChildColumns.length === 1 && visibleChildColumns[0].fullWidth !== false}
|
||||||
|
class:md:grid-cols-2={visibleChildColumns.length === 2 || (visibleChildColumns.length === 1 && visibleChildColumns[0].fullWidth === false)}
|
||||||
|
class:md:grid-cols-3={visibleChildColumns.length === 3}
|
||||||
|
>
|
||||||
|
{#each visibleChildColumns as child}
|
||||||
|
{@render Fieldset(child)}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
{@render Fieldset(col)}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="p-2 space-y-6">
|
||||||
{#each formFields as group}
|
{#each formFields as group}
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
{#if group.title}
|
{#if group.title}
|
||||||
@ -440,4 +500,4 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div> -->
|
||||||
@ -36,6 +36,9 @@ export async function load({ url }) {
|
|||||||
'/dictionary/workstation': {
|
'/dictionary/workstation': {
|
||||||
title: 'Workstation'
|
title: 'Workstation'
|
||||||
},
|
},
|
||||||
|
'/dictionary/test': {
|
||||||
|
title: 'Test'
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = routeConfig[url.pathname] || {
|
const config = routeConfig[url.pathname] || {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user