mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-23 09:39:27 +07:00
510 lines
13 KiB
Svelte
510 lines
13 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,
|
|
testGroupSchema,
|
|
testGroupInitialForm,
|
|
testGroupDefaultErrors,
|
|
testGroupFormFields
|
|
} 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 Group from './tabs/group.svelte';
|
|
import { API } from '$lib/config/api';
|
|
import { untrack } from 'svelte';
|
|
|
|
let props = $props();
|
|
|
|
let resetRefNum = $state();
|
|
let resetRefTxt = $state();
|
|
let resetMap = $state();
|
|
let refNumData = $state([]);
|
|
let refTxtData = $state([]);
|
|
let mapData = $state([]);
|
|
|
|
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
|
|
|
const { formState } = masterDetail;
|
|
|
|
const calFormState = useForm({
|
|
schema: testCalSchema,
|
|
initialForm: testCalInitialForm,
|
|
defaultErrors: testCalDefaultErrors
|
|
});
|
|
|
|
const groupFormState = useForm({
|
|
schema: testGroupSchema,
|
|
initialForm: testGroupInitialForm,
|
|
defaultErrors: testGroupDefaultErrors,
|
|
});
|
|
|
|
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(() => {
|
|
const testType = formState.form.TestType ?? '';
|
|
const refType = formState.form.RefType ?? '';
|
|
|
|
let refState = {};
|
|
|
|
if (refType === 'RANGE' || refType === 'THOLD') {
|
|
refState = { refNum: refNumState };
|
|
} else if (refType === 'TEXT' || refType === 'VSET') {
|
|
refState = { refTxt: refTxtState };
|
|
}
|
|
|
|
switch (testType) {
|
|
case 'TEST':
|
|
case 'PARAM':
|
|
return {
|
|
...refState,
|
|
map: mapFormState
|
|
};
|
|
case 'CALC':
|
|
return {
|
|
cal: calFormState,
|
|
...refState,
|
|
map: mapFormState
|
|
};
|
|
case 'GROUP':
|
|
return {
|
|
group: groupFormState
|
|
}
|
|
case 'TITLE':
|
|
default:
|
|
return {};
|
|
}
|
|
});
|
|
|
|
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 testType = mainForm.TestType;
|
|
const cleanMapData = mapData.map(({ options, ...rest }) => rest);
|
|
|
|
const payload = buildTestPayload({
|
|
mainForm,
|
|
activeFormStates,
|
|
testType: testType,
|
|
refNumData: refNumData,
|
|
refTxtData: refTxtData,
|
|
mapData: cleanMapData,
|
|
});
|
|
console.log(payload);
|
|
|
|
const result = await formState.save(masterDetail.mode, payload);
|
|
|
|
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: Object.values(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}
|
|
mode="create"
|
|
{disabledResultTypes}
|
|
{disabledReferenceTypes}
|
|
{hiddenFields}
|
|
{handleTestTypeChange}
|
|
{handleResultTypeChange}
|
|
{handleRefTypeChange}
|
|
/>
|
|
</Tabs.Content>
|
|
<Tabs.Content value="calculation">
|
|
<Calculation {calFormState} {testCalFormFields} />
|
|
</Tabs.Content>
|
|
<Tabs.Content value="group">
|
|
<Group {groupFormState} {testGroupFormFields} />
|
|
</Tabs.Content>
|
|
<Tabs.Content value="map">
|
|
<Map {mapFormState} testMapFormFields={testMapFormFieldsTransformed} bind:tempMap={mapData} bind:resetMap />
|
|
</Tabs.Content>
|
|
<Tabs.Content value="reference">
|
|
<div class="w-full h-full flex items-start">
|
|
{#if refComponent === 'numeric'}
|
|
<RefNum {refNumState} {refNumFormFields} bind:tempNumeric={refNumData} bind:resetRefNum />
|
|
{:else if refComponent === 'text'}
|
|
<RefTxt {refTxtState} refTxtFormFields={refTxtFormFieldsTransformed} bind:tempTxt={refTxtData} bind: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}
|
|
/>
|