mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-26 02:46:32 +07:00
continue testmap & deleting testmap on test tab
This commit is contained in:
parent
801c0a54f1
commit
80d59f3120
@ -7,16 +7,10 @@
|
|||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
|
||||||
import * as Card from "$lib/components/ui/card/index.js";
|
|
||||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
|
||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
|
||||||
import { contactDetailSchema, contactDetailInitialForm, contactDetailDefaultErrors, contactDetailFormFields, buildContactPayload } from "$lib/components/dictionary/contact/config/contact-form-config";
|
import { contactDetailSchema, contactDetailInitialForm, contactDetailDefaultErrors, contactDetailFormFields, buildContactPayload } from "$lib/components/dictionary/contact/config/contact-form-config";
|
||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||||
import XIcon from "@lucide/svelte/icons/x";
|
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||||
import Edit2Icon from "@lucide/svelte/icons/edit-2";
|
|
||||||
// import { getChangedFields } from "$lib/utils/getChangedFields";
|
|
||||||
import * as Table from '$lib/components/ui/table/index.js';
|
import * as Table from '$lib/components/ui/table/index.js';
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||||
@ -95,11 +89,6 @@
|
|||||||
const orig = originalMap.get(item.ContactDetID);
|
const orig = originalMap.get(item.ContactDetID);
|
||||||
if (!orig) continue;
|
if (!orig) continue;
|
||||||
|
|
||||||
// console.log('ITEM:', item);
|
|
||||||
// console.log('ORIG:', orig);
|
|
||||||
// console.log('KEYS current:', Object.keys(item));
|
|
||||||
// console.log('KEYS original:', Object.keys(orig));
|
|
||||||
|
|
||||||
const changed = Object.keys(item).some(
|
const changed = Object.keys(item).some(
|
||||||
key => item[key] !== orig[key]
|
key => item[key] !== orig[key]
|
||||||
);
|
);
|
||||||
@ -110,16 +99,6 @@
|
|||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getChangedFields(original, current) {
|
|
||||||
const changed = {};
|
|
||||||
for (const key in current) {
|
|
||||||
if (JSON.stringify(current[key]) !== JSON.stringify(original[key])) {
|
|
||||||
changed[key] = current[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleEdit() {
|
async function handleEdit() {
|
||||||
const currentPayload = buildContactPayload({
|
const currentPayload = buildContactPayload({
|
||||||
mainForm: formState.form,
|
mainForm: formState.form,
|
||||||
@ -279,7 +258,7 @@
|
|||||||
function handleCancelEditDetail() {
|
function handleCancelEditDetail() {
|
||||||
resetContactDetailForm();
|
resetContactDetailForm();
|
||||||
}
|
}
|
||||||
$inspect(deletedDetailIds)
|
|
||||||
function handleRemoveDetail(id) {
|
function handleRemoveDetail(id) {
|
||||||
const row = tempDetailContact.find(r => r.id === id);
|
const row = tempDetailContact.find(r => r.id === id);
|
||||||
if (row?.ContactDetID) {
|
if (row?.ContactDetID) {
|
||||||
|
|||||||
@ -195,29 +195,6 @@ export const refTxtSchema = z
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const testMapSchema = z
|
|
||||||
.object({
|
|
||||||
HostID: z.string().optional(),
|
|
||||||
ClientID: z.string().optional()
|
|
||||||
})
|
|
||||||
.superRefine((data, ctx) => {
|
|
||||||
const hostID = data.HostID;
|
|
||||||
const clientID = data.ClientID;
|
|
||||||
|
|
||||||
if (hostID && clientID && hostID === clientID) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: 'ClientID must be different from HostID',
|
|
||||||
path: ['ClientID']
|
|
||||||
});
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: 'HostID must be different from ClientID',
|
|
||||||
path: ['HostID']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const testInitialForm = {
|
export const testInitialForm = {
|
||||||
TestSiteID: '',
|
TestSiteID: '',
|
||||||
SiteID: '',
|
SiteID: '',
|
||||||
@ -297,19 +274,6 @@ export const refTxtInitialForm = {
|
|||||||
Notes: ''
|
Notes: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testMapInitialForm = {
|
|
||||||
TestMapID: '',
|
|
||||||
HostType: '',
|
|
||||||
HostID: '',
|
|
||||||
HostTestCode: '',
|
|
||||||
HostTestName: '',
|
|
||||||
ClientType: '',
|
|
||||||
ClientID: '',
|
|
||||||
ClientTestCode: '',
|
|
||||||
ClientTestName: '',
|
|
||||||
ConDefID: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testDefaultErrors = {
|
export const testDefaultErrors = {
|
||||||
TestSiteCode: 'Required',
|
TestSiteCode: 'Required',
|
||||||
TestSiteName: 'Required',
|
TestSiteName: 'Required',
|
||||||
@ -340,11 +304,6 @@ export const refTxtDefaultErrors = {
|
|||||||
AgeEnd: null
|
AgeEnd: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testMapDefaultErrors = {
|
|
||||||
HostID: null,
|
|
||||||
ClientID: null
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testFormFields = [
|
export const testFormFields = [
|
||||||
{
|
{
|
||||||
title: 'Basic Information',
|
title: 'Basic Information',
|
||||||
@ -947,101 +906,6 @@ export const refTxtFormFields = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const testMapFormFields = [
|
|
||||||
{
|
|
||||||
title: 'Host & Client Information',
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
type: 'row',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
key: 'HostType',
|
|
||||||
label: 'Host Type',
|
|
||||||
required: false,
|
|
||||||
type: 'select',
|
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ClientType',
|
|
||||||
label: 'Client Type',
|
|
||||||
required: false,
|
|
||||||
type: 'select',
|
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'row',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
key: 'HostID',
|
|
||||||
label: 'Host ID',
|
|
||||||
required: false,
|
|
||||||
type: 'text',
|
|
||||||
validateOn: ['input']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ClientID',
|
|
||||||
label: 'Client ID',
|
|
||||||
required: false,
|
|
||||||
type: 'text',
|
|
||||||
validateOn: ['input']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'row',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
key: 'HostTestCode',
|
|
||||||
label: 'Host Test Code',
|
|
||||||
required: false,
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ClientTestCode',
|
|
||||||
label: 'Client Test Code',
|
|
||||||
required: false,
|
|
||||||
type: 'text'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'row',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
key: 'HostTestName',
|
|
||||||
label: 'Host Test Name',
|
|
||||||
required: false,
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ClientTestName',
|
|
||||||
label: 'Client Test Name',
|
|
||||||
required: false,
|
|
||||||
type: 'text'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'row',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
key: 'ConDefID',
|
|
||||||
label: 'Container Definition',
|
|
||||||
required: false,
|
|
||||||
type: 'select',
|
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.CONTAINER}`,
|
|
||||||
valueKey: 'ConDefID',
|
|
||||||
labelKey: (item) => `${item.ConCode} - ${item.ConName}`,
|
|
||||||
fullWidth: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getTestFormActions(handlers) {
|
export function getTestFormActions(handlers) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -1058,7 +922,6 @@ export function buildTestPayload({
|
|||||||
testType,
|
testType,
|
||||||
refNumData,
|
refNumData,
|
||||||
refTxtData,
|
refTxtData,
|
||||||
mapData
|
|
||||||
}) {
|
}) {
|
||||||
let payload = {
|
let payload = {
|
||||||
...mainForm
|
...mainForm
|
||||||
@ -1085,19 +948,12 @@ export function buildTestPayload({
|
|||||||
TestSiteID: Number(m.value)
|
TestSiteID: Number(m.value)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else if (key === 'map' && mapData?.length > 0) {
|
|
||||||
payload.testmap = mapData;
|
|
||||||
} else if (key === 'cal') {
|
} else if (key === 'cal') {
|
||||||
payload.testdefcal = {
|
payload.testdefcal = {
|
||||||
...state.form,
|
...state.form,
|
||||||
FormulaInput: state.form?.FormulaInput?.map((m) => ({ TestSiteID: Number(m.testid) }))
|
FormulaInput: state.form?.FormulaInput?.map((m) => ({ TestSiteID: Number(m.testid) }))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// else if ((key === 'cal' || key === 'map') && state?.form) {
|
|
||||||
// payload[key] = {
|
|
||||||
// ...state.form
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanEmptyStrings(payload);
|
return cleanEmptyStrings(payload);
|
||||||
|
|||||||
@ -20,10 +20,6 @@
|
|||||||
refTxtDefaultErrors,
|
refTxtDefaultErrors,
|
||||||
refTxtInitialForm,
|
refTxtInitialForm,
|
||||||
refTxtFormFields,
|
refTxtFormFields,
|
||||||
testMapSchema,
|
|
||||||
testMapInitialForm,
|
|
||||||
testMapDefaultErrors,
|
|
||||||
testMapFormFields,
|
|
||||||
testGroupSchema,
|
testGroupSchema,
|
||||||
testGroupInitialForm,
|
testGroupInitialForm,
|
||||||
testGroupDefaultErrors,
|
testGroupDefaultErrors,
|
||||||
@ -33,7 +29,6 @@
|
|||||||
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 Map from './tabs/map.svelte';
|
|
||||||
import Group from './tabs/group.svelte';
|
import Group from './tabs/group.svelte';
|
||||||
import { API } from '$lib/config/api';
|
import { API } from '$lib/config/api';
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
@ -42,10 +37,8 @@
|
|||||||
|
|
||||||
let resetRefNum = $state();
|
let resetRefNum = $state();
|
||||||
let resetRefTxt = $state();
|
let resetRefTxt = $state();
|
||||||
let resetMap = $state();
|
|
||||||
let refNumData = $state([]);
|
let refNumData = $state([]);
|
||||||
let refTxtData = $state([]);
|
let refTxtData = $state([]);
|
||||||
let mapData = $state([]);
|
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||||
|
|
||||||
@ -75,13 +68,6 @@
|
|||||||
defaultErrors: refTxtDefaultErrors
|
defaultErrors: refTxtDefaultErrors
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapFormState = useForm({
|
|
||||||
schema: testMapSchema,
|
|
||||||
initialForm: testMapInitialForm,
|
|
||||||
defaultErrors: testMapDefaultErrors,
|
|
||||||
modeOpt: 'cascade'
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeFormStates = $derived.by(() => {
|
const activeFormStates = $derived.by(() => {
|
||||||
const testType = formState.form.TestType ?? '';
|
const testType = formState.form.TestType ?? '';
|
||||||
const refType = formState.form.RefType ?? '';
|
const refType = formState.form.RefType ?? '';
|
||||||
@ -99,18 +85,15 @@
|
|||||||
case 'PARAM':
|
case 'PARAM':
|
||||||
return {
|
return {
|
||||||
...refState,
|
...refState,
|
||||||
map: mapFormState
|
|
||||||
};
|
};
|
||||||
case 'CALC':
|
case 'CALC':
|
||||||
return {
|
return {
|
||||||
cal: calFormState,
|
cal: calFormState,
|
||||||
...refState,
|
...refState,
|
||||||
map: mapFormState
|
|
||||||
};
|
};
|
||||||
case 'GROUP':
|
case 'GROUP':
|
||||||
return {
|
return {
|
||||||
group: groupFormState,
|
group: groupFormState,
|
||||||
map: mapFormState
|
|
||||||
}
|
}
|
||||||
case 'TITLE':
|
case 'TITLE':
|
||||||
default:
|
default:
|
||||||
@ -137,7 +120,6 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const mainForm = masterDetail.formState.form;
|
const mainForm = masterDetail.formState.form;
|
||||||
const testType = mainForm.TestType;
|
const testType = mainForm.TestType;
|
||||||
const cleanMapData = mapData.map(({ options, ...rest }) => rest);
|
|
||||||
|
|
||||||
const payload = buildTestPayload({
|
const payload = buildTestPayload({
|
||||||
mainForm,
|
mainForm,
|
||||||
@ -145,7 +127,6 @@
|
|||||||
testType: testType,
|
testType: testType,
|
||||||
refNumData: refNumData,
|
refNumData: refNumData,
|
||||||
refTxtData: refTxtData,
|
refTxtData: refTxtData,
|
||||||
mapData: cleanMapData,
|
|
||||||
});
|
});
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
|
|
||||||
@ -172,11 +153,11 @@
|
|||||||
switch (testType) {
|
switch (testType) {
|
||||||
case 'TEST':
|
case 'TEST':
|
||||||
case 'PARAM':
|
case 'PARAM':
|
||||||
return ['definition', 'map', 'reference'];
|
return ['definition', 'reference'];
|
||||||
case 'CALC':
|
case 'CALC':
|
||||||
return ['definition', 'calculation', 'map', 'reference'];
|
return ['definition', 'calculation', 'reference'];
|
||||||
case 'GROUP':
|
case 'GROUP':
|
||||||
return ['definition', 'group', 'map'];
|
return ['definition', 'group'];
|
||||||
default:
|
default:
|
||||||
return ['definition'];
|
return ['definition'];
|
||||||
}
|
}
|
||||||
@ -258,81 +239,6 @@
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
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');
|
let activeTab = $state('definition');
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@ -347,14 +253,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// $effect(() => {
|
|
||||||
// if (formState.form.Factor && !formState.form.Unit2) {
|
|
||||||
// formState.errors.Unit2 = 'Required';
|
|
||||||
// } else {
|
|
||||||
// formState.errors.Unit2 = null;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const allColumns = formFields.flatMap((section) =>
|
const allColumns = formFields.flatMap((section) =>
|
||||||
section.rows.flatMap((row) => row.columns ?? [])
|
section.rows.flatMap((row) => row.columns ?? [])
|
||||||
@ -440,11 +338,6 @@
|
|||||||
refTxtState.form.TxtRefType = value;
|
refTxtState.form.TxtRefType = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// $inspect({
|
|
||||||
// definition: formState.errors,
|
|
||||||
// active: Object.values(activeFormStates).map(fs => fs.errors)
|
|
||||||
// });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
|
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
|
||||||
@ -462,9 +355,6 @@
|
|||||||
{#if availableTabs.includes('reference')}
|
{#if availableTabs.includes('reference')}
|
||||||
<Tabs.Trigger value="reference">Reference</Tabs.Trigger>
|
<Tabs.Trigger value="reference">Reference</Tabs.Trigger>
|
||||||
{/if}
|
{/if}
|
||||||
{#if availableTabs.includes('map')}
|
|
||||||
<Tabs.Trigger value="map">Map</Tabs.Trigger>
|
|
||||||
{/if}
|
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Content value="definition">
|
<Tabs.Content value="definition">
|
||||||
<DictionaryFormRenderer
|
<DictionaryFormRenderer
|
||||||
@ -485,9 +375,6 @@
|
|||||||
<Tabs.Content value="group">
|
<Tabs.Content value="group">
|
||||||
<Group {groupFormState} {testGroupFormFields} />
|
<Group {groupFormState} {testGroupFormFields} />
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
<Tabs.Content value="map">
|
|
||||||
<Map {mapFormState} testMapFormFields={testMapFormFieldsTransformed} bind:tempMap={mapData} bind:resetMap />
|
|
||||||
</Tabs.Content>
|
|
||||||
<Tabs.Content value="reference">
|
<Tabs.Content value="reference">
|
||||||
<div class="w-full h-full flex items-start">
|
<div class="w-full h-full flex items-start">
|
||||||
{#if refComponent === 'numeric'}
|
{#if refComponent === 'numeric'}
|
||||||
|
|||||||
@ -20,10 +20,6 @@
|
|||||||
refTxtDefaultErrors,
|
refTxtDefaultErrors,
|
||||||
refTxtInitialForm,
|
refTxtInitialForm,
|
||||||
refTxtFormFields,
|
refTxtFormFields,
|
||||||
testMapSchema,
|
|
||||||
testMapInitialForm,
|
|
||||||
testMapDefaultErrors,
|
|
||||||
testMapFormFields,
|
|
||||||
testGroupSchema,
|
testGroupSchema,
|
||||||
testGroupInitialForm,
|
testGroupInitialForm,
|
||||||
testGroupDefaultErrors,
|
testGroupDefaultErrors,
|
||||||
@ -33,7 +29,6 @@
|
|||||||
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 Map from './tabs/map.svelte';
|
|
||||||
import Group from './tabs/group.svelte';
|
import Group from './tabs/group.svelte';
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
import { untrack } from "svelte";
|
import { untrack } from "svelte";
|
||||||
@ -43,10 +38,8 @@
|
|||||||
|
|
||||||
let resetRefNum = $state();
|
let resetRefNum = $state();
|
||||||
let resetRefTxt = $state();
|
let resetRefTxt = $state();
|
||||||
let resetMap = $state();
|
|
||||||
let refNumData = $state([]);
|
let refNumData = $state([]);
|
||||||
let refTxtData = $state([]);
|
let refTxtData = $state([]);
|
||||||
let mapData = $state([]);
|
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||||
|
|
||||||
@ -76,13 +69,6 @@
|
|||||||
defaultErrors: refTxtDefaultErrors
|
defaultErrors: refTxtDefaultErrors
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapFormState = useForm({
|
|
||||||
schema: testMapSchema,
|
|
||||||
initialForm: testMapInitialForm,
|
|
||||||
defaultErrors: testMapDefaultErrors,
|
|
||||||
modeOpt: 'cascade'
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeFormStates = $derived.by(() => {
|
const activeFormStates = $derived.by(() => {
|
||||||
const testType = formState.form.TestType ?? '';
|
const testType = formState.form.TestType ?? '';
|
||||||
const refType = formState.form.RefType ?? '';
|
const refType = formState.form.RefType ?? '';
|
||||||
@ -100,13 +86,11 @@
|
|||||||
case 'PARAM':
|
case 'PARAM':
|
||||||
return {
|
return {
|
||||||
...refState,
|
...refState,
|
||||||
map: mapFormState
|
|
||||||
};
|
};
|
||||||
case 'CALC':
|
case 'CALC':
|
||||||
return {
|
return {
|
||||||
cal: calFormState,
|
cal: calFormState,
|
||||||
...refState,
|
...refState,
|
||||||
map: mapFormState
|
|
||||||
};
|
};
|
||||||
case 'GROUP':
|
case 'GROUP':
|
||||||
return {
|
return {
|
||||||
@ -129,7 +113,6 @@
|
|||||||
async function handleEdit() {
|
async function handleEdit() {
|
||||||
const mainForm = masterDetail.formState.form;
|
const mainForm = masterDetail.formState.form;
|
||||||
const testType = mainForm.TestType;
|
const testType = mainForm.TestType;
|
||||||
const cleanMapData = mapData.map(({ options, ...rest }) => rest);
|
|
||||||
|
|
||||||
const payload = buildTestPayload({
|
const payload = buildTestPayload({
|
||||||
mainForm,
|
mainForm,
|
||||||
@ -137,7 +120,6 @@
|
|||||||
testType: testType,
|
testType: testType,
|
||||||
refNumData: refNumData,
|
refNumData: refNumData,
|
||||||
refTxtData: refTxtData,
|
refTxtData: refTxtData,
|
||||||
mapData: cleanMapData,
|
|
||||||
});
|
});
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
|
|
||||||
@ -162,9 +144,9 @@
|
|||||||
switch (testType) {
|
switch (testType) {
|
||||||
case 'TEST':
|
case 'TEST':
|
||||||
case 'PARAM':
|
case 'PARAM':
|
||||||
return ['definition', 'map', 'reference'];
|
return ['definition', 'reference'];
|
||||||
case 'CALC':
|
case 'CALC':
|
||||||
return ['definition', 'calculation', 'map', 'reference'];
|
return ['definition', 'calculation', 'reference'];
|
||||||
case 'GROUP':
|
case 'GROUP':
|
||||||
return ['definition', 'group'];
|
return ['definition', 'group'];
|
||||||
default:
|
default:
|
||||||
@ -248,80 +230,6 @@
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
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;
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
let activeTab = $state('definition');
|
let activeTab = $state('definition');
|
||||||
let groupInitialized = $state(false);
|
let groupInitialized = $state(false);
|
||||||
|
|
||||||
@ -343,7 +251,7 @@
|
|||||||
AgeEnd: typeof row.AgeEnd === 'number' ? buildAgeText(daysToAge(row.AgeEnd)) : row.AgeEnd,
|
AgeEnd: typeof row.AgeEnd === 'number' ? buildAgeText(daysToAge(row.AgeEnd)) : row.AgeEnd,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// groupFormState.form.Members = [];
|
|
||||||
if (!groupInitialized && mainForm.testdefgrp?.members && Array.isArray(mainForm.testdefgrp.members)) {
|
if (!groupInitialized && mainForm.testdefgrp?.members && Array.isArray(mainForm.testdefgrp.members)) {
|
||||||
groupFormState.form.Members = mainForm.testdefgrp.members.map((m, index) => ({
|
groupFormState.form.Members = mainForm.testdefgrp.members.map((m, index) => ({
|
||||||
id: m.id ?? index + 1,
|
id: m.id ?? index + 1,
|
||||||
@ -351,12 +259,6 @@
|
|||||||
}));
|
}));
|
||||||
groupInitialized = true;
|
groupInitialized = true;
|
||||||
}
|
}
|
||||||
// if (mainForm.testmap && Array.isArray(mainForm.testmap)) {
|
|
||||||
// mapData = mainForm.testmap.map((row, index) => ({
|
|
||||||
// id: row.id ?? index + 1,
|
|
||||||
// ...row,
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@ -454,14 +356,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// $effect(() => {
|
|
||||||
// if (formState.form.Factor && !formState.form.Unit2) {
|
|
||||||
// formState.errors.Unit2 = 'Required';
|
|
||||||
// } else {
|
|
||||||
// formState.errors.Unit2 = null;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const refType = formState.form.RefType;
|
const refType = formState.form.RefType;
|
||||||
if (refType === 'RANGE' || refType === 'THOLD') {
|
if (refType === 'RANGE' || refType === 'THOLD') {
|
||||||
@ -488,9 +382,6 @@
|
|||||||
{#if availableTabs.includes('reference')}
|
{#if availableTabs.includes('reference')}
|
||||||
<Tabs.Trigger value="reference">Reference</Tabs.Trigger>
|
<Tabs.Trigger value="reference">Reference</Tabs.Trigger>
|
||||||
{/if}
|
{/if}
|
||||||
{#if availableTabs.includes('map')}
|
|
||||||
<Tabs.Trigger value="map">Map</Tabs.Trigger>
|
|
||||||
{/if}
|
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Content value="definition">
|
<Tabs.Content value="definition">
|
||||||
<DictionaryFormRenderer
|
<DictionaryFormRenderer
|
||||||
@ -511,9 +402,6 @@
|
|||||||
<Tabs.Content value="group">
|
<Tabs.Content value="group">
|
||||||
<Group {groupFormState} {testGroupFormFields} />
|
<Group {groupFormState} {testGroupFormFields} />
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
<Tabs.Content value="map">
|
|
||||||
<Map {mapFormState} testMapFormFields={testMapFormFieldsTransformed} bind:tempMap={mapData} bind:resetMap />
|
|
||||||
</Tabs.Content>
|
|
||||||
<Tabs.Content value="reference">
|
<Tabs.Content value="reference">
|
||||||
<div class="w-full h-full flex items-start">
|
<div class="w-full h-full flex items-start">
|
||||||
{#if refComponent === 'numeric'}
|
{#if refComponent === 'numeric'}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export const detailSections = [
|
|||||||
{ key: "ClientTypeLabel", label: "Client Type" },
|
{ key: "ClientTypeLabel", label: "Client Type" },
|
||||||
{ key: "HostID", label: "Host ID" },
|
{ key: "HostID", label: "Host ID" },
|
||||||
{ key: "ClientID", label: "Client ID" },
|
{ key: "ClientID", label: "Client ID" },
|
||||||
{ key: "Details", label: "Details", fullWidth: true },
|
{ key: "details", label: "Details", fullWidth: true },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,185 @@
|
|||||||
|
import { API } from '$lib/config/api';
|
||||||
|
import EraserIcon from '@lucide/svelte/icons/eraser';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { cleanEmptyStrings } from '$lib/utils/cleanEmptyStrings';
|
||||||
|
|
||||||
|
export const testMapSchema = z
|
||||||
|
.object({
|
||||||
|
HostID: z.string().optional(),
|
||||||
|
ClientID: z.string().optional()
|
||||||
|
})
|
||||||
|
// .superRefine((data, ctx) => {
|
||||||
|
// const hostID = data.HostID;
|
||||||
|
// const clientID = data.ClientID;
|
||||||
|
|
||||||
|
// if (hostID && clientID && hostID === clientID) {
|
||||||
|
// ctx.addIssue({
|
||||||
|
// code: z.ZodIssueCode.custom,
|
||||||
|
// message: 'ClientID must be different from HostID',
|
||||||
|
// path: ['ClientID']
|
||||||
|
// });
|
||||||
|
// ctx.addIssue({
|
||||||
|
// code: z.ZodIssueCode.custom,
|
||||||
|
// message: 'HostID must be different from ClientID',
|
||||||
|
// path: ['HostID']
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const testMapDetailSchema = z.object({});
|
||||||
|
|
||||||
|
export const testMapInitialForm = {
|
||||||
|
TestMapID: '',
|
||||||
|
HostType: '',
|
||||||
|
HostID: '',
|
||||||
|
ClientType: '',
|
||||||
|
ClientID: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testMapDetailInitialForm = {
|
||||||
|
HostTestCode: '',
|
||||||
|
HostTestName: '',
|
||||||
|
ClientTestCode: '',
|
||||||
|
ClientTestName: '',
|
||||||
|
ConDefID: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export const testMapDefaultErrors = {
|
||||||
|
HostID: null,
|
||||||
|
ClientID: null
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testMapDetailDefaultErrors = {};
|
||||||
|
|
||||||
|
export const testMapFormFields = [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'HostType',
|
||||||
|
label: 'Host Type',
|
||||||
|
required: false,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`,
|
||||||
|
tabIndex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ClientType',
|
||||||
|
label: 'Client Type',
|
||||||
|
required: false,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`,
|
||||||
|
tabIndex: 5,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'HostID',
|
||||||
|
label: 'Host ID',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
validateOn: ['input'],
|
||||||
|
tabIndex: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ClientID',
|
||||||
|
label: 'Client ID',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
validateOn: ['input'],
|
||||||
|
tabIndex: 6,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'HostTestCode',
|
||||||
|
label: 'Host Test Code',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
tabIndex: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ClientTestCode',
|
||||||
|
label: 'Client Test Code',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
tabIndex: 7,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'HostTestName',
|
||||||
|
label: 'Host Test Name',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
tabIndex: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ClientTestName',
|
||||||
|
label: 'Client Test Name',
|
||||||
|
required: false,
|
||||||
|
type: 'text',
|
||||||
|
tabIndex: 8,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'row',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'ConDefID',
|
||||||
|
label: 'Container Definition',
|
||||||
|
required: false,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.CONTAINER}`,
|
||||||
|
valueKey: 'ConDefID',
|
||||||
|
labelKey: (item) => `${item.ConCode} - ${item.ConName}`,
|
||||||
|
fullWidth: false,
|
||||||
|
tabIndex: 9,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getTestMapFormActions(handlers) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
Icon: EraserIcon,
|
||||||
|
label: 'Clear Form',
|
||||||
|
onClick: handlers.clearForm
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTestMapPayload({
|
||||||
|
mainForm,
|
||||||
|
tempMap,
|
||||||
|
}) {
|
||||||
|
const { HostTestCode, HostTestName, ClientTestCode, ClientTestName, ConDefID, ...rest } = mainForm;
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
...rest,
|
||||||
|
details: tempMap.map((item) => ({
|
||||||
|
HostTestCode: item.HostTestCode,
|
||||||
|
HostTestName: item.HostTestName,
|
||||||
|
ClientTestCode: item.ClientTestCode,
|
||||||
|
ClientTestName: item.ClientTestName,
|
||||||
|
ConDefID: item.ConDefID,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
return cleanEmptyStrings(payload);
|
||||||
|
}
|
||||||
@ -26,24 +26,35 @@ export const testMapSchema = z
|
|||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
export const testMapDetailSchema = z.object({
|
||||||
|
ConDefID: z.string().trim().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
export const testMapInitialForm = {
|
export const testMapInitialForm = {
|
||||||
TestMapID: '',
|
TestMapID: '',
|
||||||
HostType: '',
|
HostType: '',
|
||||||
HostID: '',
|
HostID: '',
|
||||||
HostTestCode: '',
|
|
||||||
HostTestName: '',
|
|
||||||
ClientType: '',
|
ClientType: '',
|
||||||
ClientID: '',
|
ClientID: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testMapDetailInitialForm = {
|
||||||
|
HostTestCode: '',
|
||||||
|
HostTestName: '',
|
||||||
ClientTestCode: '',
|
ClientTestCode: '',
|
||||||
ClientTestName: '',
|
ClientTestName: '',
|
||||||
ConDefID: ''
|
ConDefID: ''
|
||||||
};
|
}
|
||||||
|
|
||||||
export const testMapDefaultErrors = {
|
export const testMapDefaultErrors = {
|
||||||
HostID: null,
|
HostID: null,
|
||||||
ClientID: null
|
ClientID: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const testMapDetailDefaultErrors = {
|
||||||
|
ConDefID: null,
|
||||||
|
};
|
||||||
|
|
||||||
export const testMapFormFields = [
|
export const testMapFormFields = [
|
||||||
{
|
{
|
||||||
rows: [
|
rows: [
|
||||||
@ -89,6 +100,13 @@ export const testMapFormFields = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const testMapDetailFormFields = [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
{
|
{
|
||||||
type: 'row',
|
type: 'row',
|
||||||
columns: [
|
columns: [
|
||||||
@ -145,7 +163,7 @@ export const testMapFormFields = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
export function getTestMapFormActions(handlers) {
|
export function getTestMapFormActions(handlers) {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||||
|
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||||
|
import { testMapDetailSchema, testMapDetailInitialForm, testMapDetailDefaultErrors, testMapDetailFormFields, buildTestMapPayload } from "$lib/components/dictionary/testmap/config/testmap-form-config";
|
||||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||||
@ -11,23 +13,30 @@
|
|||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
import { untrack } from "svelte";
|
import { untrack } from "svelte";
|
||||||
import { API } from '$lib/config/api';
|
import { API } from '$lib/config/api';
|
||||||
import { buildTestMapPayload } from "$lib/components/dictionary/testmap/config/testmap-form-config";
|
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
|
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||||
|
|
||||||
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
let editingId = $state(null);
|
let editingId = $state(null);
|
||||||
let idCounter = $state(0);
|
let idCounter = $state(0);
|
||||||
let tempMap = $state([]);
|
let tempMap = $state([]);
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
const testMapDetailFormState = useForm({
|
||||||
|
schema: testMapDetailSchema,
|
||||||
const { formState } = masterDetail;
|
initialForm: testMapDetailInitialForm,
|
||||||
|
defaultErrors: testMapDetailDefaultErrors,
|
||||||
|
});
|
||||||
|
|
||||||
const helpers = useDictionaryForm(formState);
|
const helpers = useDictionaryForm(formState);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
clearForm: () => {
|
clearForm: () => {
|
||||||
formState.reset();
|
formState.reset();
|
||||||
|
resetTestMapDetailForm();
|
||||||
|
tempMap = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,7 +46,7 @@
|
|||||||
|
|
||||||
function snapshotForm() {
|
function snapshotForm() {
|
||||||
return untrack(() => {
|
return untrack(() => {
|
||||||
const f = formState.form;
|
const f = testMapDetailFormState.form;
|
||||||
return {
|
return {
|
||||||
HostTestCode: f.HostTestCode ?? "",
|
HostTestCode: f.HostTestCode ?? "",
|
||||||
HostTestName: f.HostTestName ?? "",
|
HostTestName: f.HostTestName ?? "",
|
||||||
@ -48,24 +57,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetTestMapDetailForm() {
|
||||||
formState.reset();
|
testMapDetailFormState.reset();
|
||||||
editingId = null;
|
editingId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetTest() {
|
function handleInsertDetail() {
|
||||||
untrack(() => {
|
console.log('object');
|
||||||
const f = formState.form;
|
|
||||||
f.HostTestCode = null;
|
|
||||||
f.HostTestName = null;
|
|
||||||
f.ClientTestCode = null;
|
|
||||||
f.ClientTestName = null;
|
|
||||||
f.ConDefID = null;
|
|
||||||
});
|
|
||||||
editingId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleInsert() {
|
|
||||||
const row = {
|
const row = {
|
||||||
id: ++idCounter,
|
id: ++idCounter,
|
||||||
...snapshotForm()
|
...snapshotForm()
|
||||||
@ -73,47 +71,38 @@
|
|||||||
|
|
||||||
tempMap = [...tempMap, row];
|
tempMap = [...tempMap, row];
|
||||||
|
|
||||||
resetTest();
|
resetTestMapDetailForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEdit(row) {
|
async function handleEditDetail(row) {
|
||||||
editingId = row.id;
|
editingId = row.id;
|
||||||
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
const f = formState.form;
|
const f = testMapDetailFormState.form;
|
||||||
// f.HostType = row.HostType;
|
|
||||||
// f.HostID = row.HostID;
|
|
||||||
f.HostTestCode = row.HostTestCode;
|
f.HostTestCode = row.HostTestCode;
|
||||||
f.HostTestName = row.HostTestName;
|
f.HostTestName = row.HostTestName;
|
||||||
// f.ClientType = row.ClientType;
|
|
||||||
// f.ClientID = row.ClientID;
|
|
||||||
f.ClientTestCode = row.ClientTestCode;
|
f.ClientTestCode = row.ClientTestCode;
|
||||||
f.ClientTestName = row.ClientTestName;
|
f.ClientTestName = row.ClientTestName;
|
||||||
f.ConDefID = row.ConDefID;
|
f.ConDefID = row.ConDefID;
|
||||||
|
|
||||||
// if (row.options) {
|
|
||||||
// for (const key in row.options) {
|
|
||||||
// masterDetail.formState.selectOption[key] = row.options[key];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUpdate() {
|
function handleUpdateDetail() {
|
||||||
tempMap = tempMap.map((row) =>
|
tempMap = tempMap.map((row) =>
|
||||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||||
);
|
);
|
||||||
resetTest();
|
resetTestMapDetailForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancelEdit() {
|
function handleCancelEditDetail() {
|
||||||
resetTest();
|
resetTestMapDetailForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemove(id) {
|
function handleRemoveDetail(id) {
|
||||||
tempMap = tempMap.filter((row) => row.id !== id);
|
tempMap = tempMap.filter((row) => row.id !== id);
|
||||||
if (editingId === id) {
|
if (editingId === id) {
|
||||||
resetTest();
|
resetTestMapDetailForm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,10 +114,10 @@
|
|||||||
tempMap,
|
tempMap,
|
||||||
});
|
});
|
||||||
console.log(payload)
|
console.log(payload)
|
||||||
const result = await formState.save(masterDetail.mode, payload);
|
// const result = await formState.save(masterDetail.mode, payload);
|
||||||
|
|
||||||
toast('Test Map Created!');
|
// toast('Test Map Created!');
|
||||||
masterDetail?.exitForm(true);
|
// masterDetail?.exitForm(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
@ -176,7 +165,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (col.key === 'ClientID') {
|
if (col.key === 'ClientID') {
|
||||||
if (formState.form.ClientType === 'SITE') {
|
if (formState.form.ClientType === 'HIS') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.HOSTAPP}`,
|
||||||
|
valueKey: 'HostAppID',
|
||||||
|
labelKey: 'HostAppName'
|
||||||
|
}
|
||||||
|
} else if (formState.form.ClientType === 'SITE') {
|
||||||
return {
|
return {
|
||||||
...col,
|
...col,
|
||||||
type: 'select',
|
type: 'select',
|
||||||
@ -184,55 +181,61 @@
|
|||||||
valueKey: 'SiteID',
|
valueKey: 'SiteID',
|
||||||
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`
|
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`
|
||||||
};
|
};
|
||||||
|
} else if (formState.form.ClientType === 'WST') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.WORKSTATION}`,
|
||||||
|
valueKey: 'WorkstationID',
|
||||||
|
labelKey: 'WorkstationName'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { ...col, type: 'text', optionsEndpoint: undefined };
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col.key === 'HostTestCode') {
|
|
||||||
if (formState.form.HostType === 'SITE' && formState.form.HostID) {
|
|
||||||
return {
|
|
||||||
...col,
|
|
||||||
type: 'select',
|
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.HostID}`,
|
|
||||||
valueKey: 'TestSiteID',
|
|
||||||
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...col,
|
|
||||||
type: 'text',
|
|
||||||
optionsEndpoint: undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (col.key === 'ClientTestCode') {
|
|
||||||
if (formState.form.ClientType === 'SITE' && formState.form.ClientID) {
|
|
||||||
return {
|
|
||||||
...col,
|
|
||||||
type: 'select',
|
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.ClientID}`,
|
|
||||||
valueKey: 'TestSiteID',
|
|
||||||
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...col,
|
|
||||||
type: 'text',
|
|
||||||
optionsEndpoint: undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return col;
|
return col;
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// $effect(() => {
|
const mapDetailFormFieldsTransformed = $derived.by(() => {
|
||||||
// if (formState.form.ClientTestCode) {
|
return testMapDetailFormFields.map((group) => ({
|
||||||
// formState.form.ClientTestName = 'nyaho';
|
...group,
|
||||||
// }
|
rows: group.rows.map((row) => ({
|
||||||
// })
|
...row,
|
||||||
|
columns: row.columns.map((col) => {
|
||||||
|
if (col.key === 'HostTestCode') {
|
||||||
|
if (formState.form.HostType === 'SITE' && formState.form.HostID) {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.HostID}`,
|
||||||
|
valueKey: 'TestSiteCode',
|
||||||
|
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col.key === 'ClientTestCode') {
|
||||||
|
if (formState.form.ClientType === 'SITE' && formState.form.ClientID) {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.ClientID}`,
|
||||||
|
valueKey: 'TestSiteCode',
|
||||||
|
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return col
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormPageContainer title="Create Test Map" {primaryAction} {secondaryActions} {actions}>
|
<FormPageContainer title="Create Test Map" {primaryAction} {secondaryActions} {actions}>
|
||||||
@ -241,17 +244,22 @@
|
|||||||
formFields={mapFormFieldsTransformed}
|
formFields={mapFormFieldsTransformed}
|
||||||
mode="create"
|
mode="create"
|
||||||
/>
|
/>
|
||||||
|
<DictionaryFormRenderer
|
||||||
|
formState={testMapDetailFormState}
|
||||||
|
formFields={mapDetailFormFieldsTransformed}
|
||||||
|
mode="create"
|
||||||
|
/>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex gap-2 mt-1 ms-2">
|
<div class="flex gap-2 mt-1 ms-2">
|
||||||
{#if editingId !== null}
|
{#if editingId !== null}
|
||||||
<Button size="sm" class="cursor-pointer" onclick={handleUpdate}>
|
<Button size="sm" class="cursor-pointer" onclick={handleUpdateDetail}>
|
||||||
Update
|
Update
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEdit}>
|
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEditDetail}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<Button size="sm" class="cursor-pointer" onclick={handleInsert}>
|
<Button size="sm" class="cursor-pointer" onclick={handleInsertDetail}>
|
||||||
Insert
|
Insert
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
@ -262,12 +270,8 @@
|
|||||||
<Table.Root>
|
<Table.Root>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row class="hover:bg-transparent">
|
<Table.Row class="hover:bg-transparent">
|
||||||
<!-- <Table.Head>Host Type</Table.Head>
|
|
||||||
<Table.Head>Host ID</Table.Head> -->
|
|
||||||
<Table.Head>Host Test Code</Table.Head>
|
<Table.Head>Host Test Code</Table.Head>
|
||||||
<Table.Head>Host Test Name</Table.Head>
|
<Table.Head>Host Test Name</Table.Head>
|
||||||
<!-- <Table.Head>Client Type</Table.Head>
|
|
||||||
<Table.Head>Client ID</Table.Head> -->
|
|
||||||
<Table.Head>Client Test Code</Table.Head>
|
<Table.Head>Client Test Code</Table.Head>
|
||||||
<Table.Head>Client Test Name</Table.Head>
|
<Table.Head>Client Test Name</Table.Head>
|
||||||
<Table.Head>Container</Table.Head>
|
<Table.Head>Container</Table.Head>
|
||||||
@ -297,7 +301,7 @@
|
|||||||
size="icon"
|
size="icon"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="h-7 w-7 cursor-pointer"
|
class="h-7 w-7 cursor-pointer"
|
||||||
onclick={() => handleEdit(row)}
|
onclick={() => handleEditDetail(row)}
|
||||||
>
|
>
|
||||||
<PencilIcon class="h-3.5 w-3.5" />
|
<PencilIcon class="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
@ -305,7 +309,7 @@
|
|||||||
size="icon"
|
size="icon"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="h-7 w-7 cursor-pointer"
|
class="h-7 w-7 cursor-pointer"
|
||||||
onclick={() => handleRemove(row.id)}
|
onclick={() => handleRemoveDetail(row.id)}
|
||||||
>
|
>
|
||||||
<Trash2Icon class="h-3.5 w-3.5" />
|
<Trash2Icon class="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1 +1,445 @@
|
|||||||
ed
|
<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 { useForm } from "$lib/components/composable/use-form.svelte";
|
||||||
|
import { testMapDetailSchema, testMapDetailInitialForm, testMapDetailDefaultErrors, testMapDetailFormFields, buildTestMapPayload } from "$lib/components/dictionary/testmap/config/testmap-form-config";
|
||||||
|
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||||
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
|
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||||
|
import * as Table from '$lib/components/ui/table/index.js';
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import { untrack } from "svelte";
|
||||||
|
import { API } from "$lib/config/api";
|
||||||
|
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
|
||||||
|
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||||
|
|
||||||
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
|
let editingId = $state(null);
|
||||||
|
let idCounter = $state(0);
|
||||||
|
let tempMap = $state([]);
|
||||||
|
let deletedDetailIds = $state([]);
|
||||||
|
|
||||||
|
const testMapDetailFormState = useForm({
|
||||||
|
schema: testMapDetailSchema,
|
||||||
|
initialForm: testMapDetailInitialForm,
|
||||||
|
defaultErrors: testMapDetailDefaultErrors,
|
||||||
|
});
|
||||||
|
|
||||||
|
const helpers = useDictionaryForm(formState);
|
||||||
|
|
||||||
|
let showConfirm = $state(false);
|
||||||
|
|
||||||
|
function snapshotForm() {
|
||||||
|
return untrack(() => {
|
||||||
|
const f = testMapDetailFormState.form;
|
||||||
|
return {
|
||||||
|
HostTestCode: f.HostTestCode ?? "",
|
||||||
|
HostTestName: f.HostTestName ?? "",
|
||||||
|
ClientTestCode: f.ClientTestCode ?? "",
|
||||||
|
ClientTestName: f.ClientTestName ?? "",
|
||||||
|
ConDefID: f.ConDefID ?? "",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTestMapDetailForm() {
|
||||||
|
testMapDetailFormState.reset();
|
||||||
|
editingId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInsertDetail() {
|
||||||
|
const row = {
|
||||||
|
id: ++idCounter,
|
||||||
|
TestMapDetailID: null,
|
||||||
|
...snapshotForm()
|
||||||
|
};
|
||||||
|
|
||||||
|
tempMap = [...tempMap, row];
|
||||||
|
|
||||||
|
resetTestMapDetailForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEditDetail(row) {
|
||||||
|
editingId = row.id;
|
||||||
|
|
||||||
|
untrack(() => {
|
||||||
|
const f = testMapDetailFormState.form;
|
||||||
|
|
||||||
|
f.HostTestCode = row.HostTestCode;
|
||||||
|
f.HostTestName = row.HostTestName;
|
||||||
|
f.ClientTestCode = row.ClientTestCode;
|
||||||
|
f.ClientTestName = row.ClientTestName;
|
||||||
|
f.ConDefID = row.ConDefID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateDetail() {
|
||||||
|
const updated = snapshotForm();
|
||||||
|
|
||||||
|
tempMap = tempMap.map((row) =>
|
||||||
|
row.id === editingId ? {
|
||||||
|
...row,
|
||||||
|
...updated,
|
||||||
|
TestMapDetailID: row.TestMapDetailID ?? null
|
||||||
|
} : row
|
||||||
|
);
|
||||||
|
resetTestMapDetailForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelEditDetail() {
|
||||||
|
resetTestMapDetailForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveDetail(id) {
|
||||||
|
const row = tempMap.find(r => r.id === id);
|
||||||
|
if (row?.TestMapDetailID) {
|
||||||
|
deletedDetailIds.push(row.TestMapDetailID);
|
||||||
|
}
|
||||||
|
tempMap = tempMap.filter((row) => row.id !== id);
|
||||||
|
if (editingId === id) {
|
||||||
|
resetTestMapDetailForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const fields = mapFormFieldsTransformed;
|
||||||
|
untrack(() => {
|
||||||
|
fields.forEach(group => {
|
||||||
|
group.rows.forEach(row => {
|
||||||
|
row.columns.forEach(col => {
|
||||||
|
if (col.type === 'select' && col.optionsEndpoint) {
|
||||||
|
formState.fetchOptions(col, formState.form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const fields = mapDetailFormFieldsTransformed;
|
||||||
|
|
||||||
|
untrack(() => {
|
||||||
|
fields.forEach(group => {
|
||||||
|
group.rows.forEach(row => {
|
||||||
|
row.columns.forEach(col => {
|
||||||
|
if (col.type === 'select' && col.optionsEndpoint) {
|
||||||
|
testMapDetailFormState.fetchOptions(col, testMapDetailFormState.form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function diffDetails(currentRows, originalRows) {
|
||||||
|
const originalMap = new Map(
|
||||||
|
originalRows
|
||||||
|
.filter(item => item.TestMapDetailID)
|
||||||
|
.map(item => [item.TestMapDetailID, item])
|
||||||
|
);
|
||||||
|
|
||||||
|
const updated = [];
|
||||||
|
const detailKeys = ['HostTestCode', 'HostTestName', 'ClientTestCode', 'ClientTestName', 'ConDefID'];
|
||||||
|
|
||||||
|
for (const item of currentRows) {
|
||||||
|
if (!item.TestMapDetailID) continue;
|
||||||
|
|
||||||
|
const orig = originalMap.get(item.TestMapDetailID);
|
||||||
|
if (!orig) continue;
|
||||||
|
|
||||||
|
const changed = detailKeys.some(
|
||||||
|
key => item[key] !== orig[key]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (changed) updated.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEdit() {
|
||||||
|
const currentPayload = buildTestMapPayload({
|
||||||
|
mainForm: formState.form,
|
||||||
|
tempMap
|
||||||
|
});
|
||||||
|
const originalPayload = buildTestMapPayload({
|
||||||
|
mainForm: masterDetail.formSnapshot,
|
||||||
|
tempMap: masterDetail.formSnapshot.details ?? []
|
||||||
|
});
|
||||||
|
|
||||||
|
const originalRows = masterDetail.formSnapshot.details ?? [];
|
||||||
|
const updatedDetails = diffDetails(tempMap, originalRows);
|
||||||
|
|
||||||
|
const changedFields = getChangedFields(originalPayload, currentPayload);
|
||||||
|
const hasMainChanges = Object.keys(changedFields).length > 0;
|
||||||
|
const hasDetailChanges = updatedDetails.length > 0 || tempMap.some(r => !r.TestMapDetailID) || deletedDetailIds.length > 0;
|
||||||
|
|
||||||
|
if (!hasMainChanges && !hasDetailChanges) {
|
||||||
|
toast('No changes detected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalPayload = {
|
||||||
|
TestMapID: formState.form.TestMapID,
|
||||||
|
...changedFields,
|
||||||
|
...(hasDetailChanges && {
|
||||||
|
details: {
|
||||||
|
created: tempMap.filter(r => !r.TestMapDetailID),
|
||||||
|
edited: updatedDetails,
|
||||||
|
deleted: deletedDetailIds
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(finalPayload);
|
||||||
|
|
||||||
|
const result = await formState.save(masterDetail.mode, finalPayload);
|
||||||
|
|
||||||
|
if (result.status === 'success') {
|
||||||
|
console.log('Test Map updated successfully');
|
||||||
|
toast('Test Map Updated!');
|
||||||
|
masterDetail.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to update test map:', result.message);
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to update test map';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryAction = $derived({
|
||||||
|
label: 'Edit',
|
||||||
|
onClick: handleEdit,
|
||||||
|
disabled: helpers.hasErrors || formState.isSaving.current,
|
||||||
|
loading: formState.isSaving.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const secondaryActions = [];
|
||||||
|
|
||||||
|
const mapFormFieldsTransformed = $derived.by(() => {
|
||||||
|
return formFields.map((group) => ({
|
||||||
|
...group,
|
||||||
|
rows: group.rows.map((row) => ({
|
||||||
|
...row,
|
||||||
|
columns: row.columns.map((col) => {
|
||||||
|
if (col.key === 'HostID') {
|
||||||
|
if (formState.form.HostType === 'HIS') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.HOSTAPP}`,
|
||||||
|
valueKey: 'HostAppID',
|
||||||
|
labelKey: 'HostAppName'
|
||||||
|
}
|
||||||
|
} else if (formState.form.HostType === 'SITE') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
|
||||||
|
valueKey: 'SiteID',
|
||||||
|
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`
|
||||||
|
};
|
||||||
|
} else if (formState.form.HostType === 'WST') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.WORKSTATION}`,
|
||||||
|
valueKey: 'WorkstationID',
|
||||||
|
labelKey: 'WorkstationName'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col.key === 'ClientID') {
|
||||||
|
if (formState.form.ClientType === 'HIS') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.HOSTAPP}`,
|
||||||
|
valueKey: 'HostAppID',
|
||||||
|
labelKey: 'HostAppName'
|
||||||
|
}
|
||||||
|
} else if (formState.form.ClientType === 'SITE') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
|
||||||
|
valueKey: 'SiteID',
|
||||||
|
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`
|
||||||
|
};
|
||||||
|
} else if (formState.form.ClientType === 'WST') {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.WORKSTATION}`,
|
||||||
|
valueKey: 'WorkstationID',
|
||||||
|
labelKey: 'WorkstationName'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return col;
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDetailFormFieldsTransformed = $derived.by(() => {
|
||||||
|
return testMapDetailFormFields.map((group) => ({
|
||||||
|
...group,
|
||||||
|
rows: group.rows.map((row) => ({
|
||||||
|
...row,
|
||||||
|
columns: row.columns.map((col) => {
|
||||||
|
if (col.key === 'HostTestCode') {
|
||||||
|
if (formState.form.HostType === 'SITE' && formState.form.HostID) {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.HostID}`,
|
||||||
|
valueKey: 'TestSiteCode',
|
||||||
|
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col.key === 'ClientTestCode') {
|
||||||
|
if (formState.form.ClientType === 'SITE' && formState.form.ClientID) {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
type: 'select',
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.TEST}?SiteID=${formState.form.ClientID}`,
|
||||||
|
valueKey: 'TestSiteCode',
|
||||||
|
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...col, type: 'text', optionsEndpoint: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return col
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const mainForm = formState.form;
|
||||||
|
if (mainForm.details && Array.isArray(mainForm.details)) {
|
||||||
|
tempMap = mainForm.details.map((row, index) => ({
|
||||||
|
id: row.id ?? index + 1,
|
||||||
|
...row,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const maxId = tempMap.reduce((max, row) => {
|
||||||
|
const rowId = typeof row.id === 'number' ? row.id : 0;
|
||||||
|
return rowId > max ? rowId : max;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
if (maxId > idCounter) {
|
||||||
|
idCounter = maxId;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPageContainer title="Edit Test Map" {primaryAction} {secondaryActions}>
|
||||||
|
<DictionaryFormRenderer
|
||||||
|
{formState}
|
||||||
|
formFields={mapFormFieldsTransformed}
|
||||||
|
mode="edit"
|
||||||
|
/>
|
||||||
|
<DictionaryFormRenderer
|
||||||
|
formState={testMapDetailFormState}
|
||||||
|
formFields={mapDetailFormFieldsTransformed}
|
||||||
|
mode="create"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="flex gap-2 mt-1 ms-2">
|
||||||
|
{#if editingId !== null}
|
||||||
|
<Button size="sm" class="cursor-pointer" onclick={handleUpdateDetail}>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEditDetail}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
{:else}
|
||||||
|
<Button size="sm" class="cursor-pointer" onclick={handleInsertDetail}>
|
||||||
|
Insert
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Separator />
|
||||||
|
<Table.Root>
|
||||||
|
<Table.Header>
|
||||||
|
<Table.Row class="hover:bg-transparent">
|
||||||
|
<Table.Head>Host Test Code</Table.Head>
|
||||||
|
<Table.Head>Host Test Name</Table.Head>
|
||||||
|
<Table.Head>Client Test Code</Table.Head>
|
||||||
|
<Table.Head>Client Test Name</Table.Head>
|
||||||
|
<Table.Head>Container</Table.Head>
|
||||||
|
<Table.Head class="w-[80px]"></Table.Head>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
<Table.Body>
|
||||||
|
{#if tempMap.length === 0}
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Cell colspan={9} class="text-center text-muted-foreground py-6">
|
||||||
|
No data. Fill the form above and click Insert.
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
{:else}
|
||||||
|
{#each tempMap as row (row.id)}
|
||||||
|
<Table.Row
|
||||||
|
class="cursor-pointer hover:bg-muted/50"
|
||||||
|
>
|
||||||
|
<Table.Cell>{row.HostTestCode}</Table.Cell>
|
||||||
|
<Table.Cell>{row.HostTestName}</Table.Cell>
|
||||||
|
<Table.Cell>{row.ClientTestCode}</Table.Cell>
|
||||||
|
<Table.Cell>{row.ClientTestName}</Table.Cell>
|
||||||
|
<Table.Cell>{row.ConDefID}</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-7 w-7 cursor-pointer"
|
||||||
|
onclick={() => handleEditDetail(row)}
|
||||||
|
>
|
||||||
|
<PencilIcon class="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-7 w-7 cursor-pointer"
|
||||||
|
onclick={() => handleRemoveDetail(row.id)}
|
||||||
|
>
|
||||||
|
<Trash2Icon class="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FormPageContainer>
|
||||||
|
|
||||||
|
<ReusableAlertDialog
|
||||||
|
bind:open={masterDetail.showExitConfirm}
|
||||||
|
onConfirm={masterDetail.confirmExit}
|
||||||
|
/>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import MapIcon from "@lucide/svelte/icons/map";
|
import MapIcon from "@lucide/svelte/icons/map";
|
||||||
import ArrowLeftIcon from "@lucide/svelte/icons/arrow-left";
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
<ArrowLeftIcon />
|
<MoveLeftIcon />
|
||||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{#each "BACK TO TEST MAP".split("") as c}
|
{#each "BACK TO TEST MAP".split("") as c}
|
||||||
<span class="leading-none">{c}</span>
|
<span class="leading-none">{c}</span>
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
let testMap = $derived(masterDetail?.selectedItem?.data);
|
let testMap = $derived(masterDetail?.selectedItem?.data);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
editTest: () => masterDetail.enterEdit("data"),
|
editTestMap: () => masterDetail.enterEdit("data"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = viewActions(handlers);
|
const actions = viewActions(handlers);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
|||||||
import FlaskConicalIcon from "@lucide/svelte/icons/flask-conical";
|
import FlaskConicalIcon from "@lucide/svelte/icons/flask-conical";
|
||||||
import ActivityIcon from "@lucide/svelte/icons/activity";
|
import ActivityIcon from "@lucide/svelte/icons/activity";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
import NotepadTextIcon from "@lucide/svelte/icons/notepad-text";
|
import NotepadTextIcon from "@lucide/svelte/icons/notepad-text";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
@ -104,8 +105,13 @@ export const detailSections = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export function patientActions(masterDetail, patientInitialForm) {
|
export function patientActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Patient',
|
label: 'Add Patient',
|
||||||
|
|||||||
@ -7,13 +7,15 @@
|
|||||||
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import ArrowLeftIcon from "@lucide/svelte/icons/arrow-left";
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, searchParam);
|
const search = useSearch(searchFields, searchParam);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = patientActions(props.masterDetail, initialForm)
|
refresh: () => alert('velom visa, savar!'),
|
||||||
|
};
|
||||||
|
const actions = patientActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
@ -36,7 +38,7 @@
|
|||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
<ArrowLeftIcon />
|
<MoveLeftIcon />
|
||||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{#each "BACK TO PATIENT".split("") as c}
|
{#each "BACK TO PATIENT".split("") as c}
|
||||||
<span class="leading-none">{c}</span>
|
<span class="leading-none">{c}</span>
|
||||||
|
|||||||
@ -269,6 +269,7 @@
|
|||||||
formState.form.ClientID = '';
|
formState.form.ClientID = '';
|
||||||
formState.form.ClientTestCode = '';
|
formState.form.ClientTestCode = '';
|
||||||
formState.form.ClientTestName = '';
|
formState.form.ClientTestName = '';
|
||||||
|
formState.selectOptions.ClientID = [];
|
||||||
formState.selectOptions.ClientTestCode = [];
|
formState.selectOptions.ClientTestCode = [];
|
||||||
formState.selectOptions.ClientTestName = [];
|
formState.selectOptions.ClientTestName = [];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user