467 lines
16 KiB
Svelte

<script>
import { useDictionaryForm } from "$lib/components/composable/use-dictionary-form.svelte";
import FormPageContainer from "$lib/components/reusable/form/form-page-container.svelte";
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
import { toast } from "svelte-sonner";
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 { buildTestPayload,
testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields,
refNumSchema, refNumDefaultErrors, refNumInitialForm, refNumFormFields,
refTxtSchema, refTxtDefaultErrors, refTxtInitialForm, refTxtFormFields,
testMapSchema, testMapInitialForm, testMapDefaultErrors, testMapFormFields
} from "$lib/components/dictionary/test/config/test-form-config";
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
import RefNum from "./tabs/ref-num.svelte";
import RefTxt from "./tabs/ref-txt.svelte";
import Calculation from "./tabs/calculation.svelte";
import Map from "./tabs/map.svelte";
import { API } from "$lib/config/api";
import { untrack } from "svelte";
let props = $props();
let resetRefNum = $state();
let resetRefTxt = $state();
let resetMap = $state();
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
const { formState } = masterDetail;
const calFormState = useForm({
schema: testCalSchema,
initialForm: testCalInitialForm,
defaultErrors: testCalDefaultErrors,
});
const refNumState = useForm({
schema: refNumSchema,
initialForm: refNumInitialForm,
defaultErrors: refNumDefaultErrors,
});
const refTxtState = useForm({
schema: refTxtSchema,
initialForm: refTxtInitialForm,
defaultErrors: refTxtDefaultErrors,
});
const mapFormState = useForm({
schema: testMapSchema,
initialForm: testMapInitialForm,
defaultErrors: testMapDefaultErrors,
modeOpt: 'cascade'
})
const activeFormStates = $derived.by(() => {
switch (formState.form.TestType) {
case "TEST":
case "PARAM":
return [refNumState, refTxtState, mapFormState];
case "CALC":
return [calFormState, refNumState, refTxtState, mapFormState];
case "GROUP":
return [];
case "TITLE":
return [];
default:
return [];
}
});
// const helpers = useDictionaryForm(formState);
const helpers = useDictionaryForm(formState, () => activeFormStates);
const handlers = {
clearForm: () => {
formState.reset();
}
};
const actions = formActions(handlers);
const allColumns = formFields.flatMap(
(section) => section.rows.flatMap(
(row) => row.columns ?? []
)
);
let showConfirm = $state(false);
async function handleSave() {
const mainForm = masterDetail.formState.form;
const calForm = calFormState.form;
const refForm = refNumState.form;
const payload = buildTestPayload({
mainForm,
calForm,
refForm,
testType: mainForm.TestType
});
console.log(payload);
// const result = await formState.save(masterDetail.mode);
// toast('Test Created!');
// masterDetail?.exitForm(true);
}
const primaryAction = $derived({
label: 'Save',
onClick: handleSave,
disabled: helpers.hasErrors || formState.isSaving.current,
loading: formState.isSaving.current
});
const secondaryActions = [];
// ==================================================================
let availableTabs = $derived.by(() => {
const testType = formState?.form?.TestType;
switch(testType) {
case 'TEST':
case 'PARAM':
return ['definition', 'map', 'reference'];
case 'CALC':
return ['definition', 'calculation', 'map', 'reference'];
case 'GROUP':
return ['definition', 'group'];
default:
return ['definition'];
}
});
let disabledResultTypes = $derived.by(() => {
const testType = formState?.form?.TestType;
switch(testType) {
case 'TEST':
case 'PARAM':
return [];
case 'CALC':
return ['RANGE', 'TEXT', 'VSET', 'NORES'];
case 'GROUP':
return ['NMRIC', 'RANGE', 'TEXT', 'VSET', 'NORES'];
case 'TITLE':
return ['NMRIC', 'RANGE', 'TEXT', 'VSET', 'NORES'];
default:
return [];
}
});
let disabledReferenceTypes = $derived.by(() => {
const resultType = formState?.form?.ResultType;
switch(resultType) {
case 'NMRIC':
return ['TEXT', 'VSET', 'NOREF'];
case 'RANGE':
return ['TEXT', 'VSET', 'NOREF'];
case 'TEXT':
return ['RANGE', 'THOLD', 'VSET', 'NOREF'];
case 'VSET':
return ['RANGE', 'THOLD', 'TEXT', 'NOREF'];
case 'NORES':
return ['RANGE', 'THOLD', 'TEXT', 'VSET'];
default:
return ['RANGE', 'THOLD', 'TEXT', 'VSET', 'NOREF'];
}
});
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';
if (refType === 'TEXT' || refType === 'VSET') return 'text';
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
};
})
}))
}));
});
const testMapFormFieldsTransformed = $derived.by(() => {
return testMapFormFields.map(group => ({
...group,
rows: group.rows.map(row => ({
...row,
columns: row.columns.map(col => {
if (col.key === "HostID") {
if (mapFormState.form.HostType === "SITE") {
return {
...col,
type: "select",
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
valueKey: "SiteID",
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`,
};
}
return col;
}
if (col.key === "ClientID") {
if (mapFormState.form.ClientType === "SITE") {
return {
...col,
type: "select",
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
valueKey: "SiteID",
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`,
};
}
return col;
}
if (col.key === "HostTestCode" || col.key === "HostTestName") {
if (mapFormState.form.HostType === "SITE" && mapFormState.form.HostID) {
return {
...col,
type: "select",
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${mapFormState.form.HostID}`,
valueKey: "TestSiteID",
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`,
};
}
// kalau belum pilih HostID, kembalikan default (misal input biasa)
return {
...col,
type: "text",
optionsEndpoint: undefined
};
}
if (col.key === "ClientTestCode" || col.key === "ClientTestName") {
if (mapFormState.form.ClientType === "SITE" && mapFormState.form.ClientID) {
return {
...col,
type: "select",
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${mapFormState.form.ClientID}`,
valueKey: "TestSiteID",
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`,
};
}
// kalau belum pilih HostID, kembalikan default (misal input biasa)
return {
...col,
type: "text",
optionsEndpoint: undefined
};
}
return col;
})
}))
}));
});
// $inspect(activeFormState.errors)
let activeTab = $state('definition');
$effect(() => {
if (!availableTabs.includes(activeTab)) {
activeTab = availableTabs[0];
}
});
$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
);
}
})
});
function handleTestTypeChange(value) {
formState.form.TestType = value;
formState.form.ResultType = "";
formState.errors.ResultType = null;
formState.form.RefType = "";
formState.errors.RefType = null;
calFormState.reset();
refNumState.reset();
refTxtState.reset();
resetRefNum?.();
resetRefTxt?.();
}
function handleResultTypeChange(value) {
formState.form.ResultType = value;
formState.form.RefType = "";
formState.errors.RefType = null;
calFormState.reset();
refNumState.reset();
refTxtState.reset();
resetRefNum?.();
resetRefTxt?.();
let newRefType = "";
if (value === 'TEXT') {
newRefType = 'TEXT';
}
if (value === 'VSET') {
newRefType = 'VSET';
}
if (value === 'NORES') {
newRefType = 'NOREF';
}
if (newRefType) {
formState.form.RefType = newRefType;
handleRefTypeChange(newRefType);
}
}
function handleRefTypeChange(value) {
formState.form.RefType = value;
refNumState.reset();
refTxtState.reset();
resetRefNum?.();
resetRefTxt?.();
if (value === 'RANGE' || value === 'THOLD') {
refNumState.form.NumRefType = value;
}
if (value === 'TEXT' || value === 'VSET') {
refTxtState.form.TxtRefType = value;
}
}
// $inspect({
// definition: formState.errors,
// active: activeFormStates.map(fs => fs.errors)
// });
</script>
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
<Tabs.Root bind:value={activeTab} class="w-full h-full">
<Tabs.List>
{#if availableTabs.includes('definition')}
<Tabs.Trigger value="definition">Definition</Tabs.Trigger>
{/if}
{#if availableTabs.includes('calculation')}
<Tabs.Trigger value="calculation">Calculation</Tabs.Trigger>
{/if}
{#if availableTabs.includes('group')}
<Tabs.Trigger value="group">Group</Tabs.Trigger>
{/if}
{#if availableTabs.includes('reference')}
<Tabs.Trigger value="reference">Reference</Tabs.Trigger>
{/if}
{#if availableTabs.includes('map')}
<Tabs.Trigger value="map">Map</Tabs.Trigger>
{/if}
</Tabs.List>
<Tabs.Content value="definition">
<DictionaryFormRenderer
{formState}
formFields={formFields}
mode="create"
{disabledResultTypes}
{disabledReferenceTypes}
{hiddenFields}
{handleTestTypeChange}
{handleResultTypeChange}
{handleRefTypeChange}
/>
</Tabs.Content>
<Tabs.Content value="calculation">
<Calculation {calFormState} {testCalFormFields} />
</Tabs.Content>
<Tabs.Content value="group">
group
</Tabs.Content>
<Tabs.Content value="map">
<Map {mapFormState} testMapFormFields={testMapFormFieldsTransformed} bind:resetMap={resetMap}/>
</Tabs.Content>
<Tabs.Content value="reference">
<div class="w-full h-full flex items-start">
{#if refComponent === 'numeric'}
<RefNum {refNumState} {refNumFormFields} bind:resetRefNum={resetRefNum}/>
{:else if refComponent === 'text'}
<RefTxt {refTxtState} refTxtFormFields={refTxtFormFieldsTransformed} bind:resetRefTxt={resetRefTxt} />
{:else}
<div class="h-full w-full flex items-center">
<ReusableEmpty desc="Select a Reference Type" />
</div>
{/if}
</div>
</Tabs.Content>
</Tabs.Root>
</FormPageContainer>
<ReusableAlertDialog
bind:open={masterDetail.showExitConfirm}
onConfirm={masterDetail.confirmExit}
/>