mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 09:35:34 +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 ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||
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 { Button } from "$lib/components/ui/button/index.js";
|
||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||
import XIcon from "@lucide/svelte/icons/x";
|
||||
import Edit2Icon from "@lucide/svelte/icons/edit-2";
|
||||
// import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
@ -95,11 +89,6 @@
|
||||
const orig = originalMap.get(item.ContactDetID);
|
||||
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(
|
||||
key => item[key] !== orig[key]
|
||||
);
|
||||
@ -110,16 +99,6 @@
|
||||
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() {
|
||||
const currentPayload = buildContactPayload({
|
||||
mainForm: formState.form,
|
||||
@ -279,7 +258,7 @@
|
||||
function handleCancelEditDetail() {
|
||||
resetContactDetailForm();
|
||||
}
|
||||
$inspect(deletedDetailIds)
|
||||
|
||||
function handleRemoveDetail(id) {
|
||||
const row = tempDetailContact.find(r => r.id === id);
|
||||
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 = {
|
||||
TestSiteID: '',
|
||||
SiteID: '',
|
||||
@ -297,19 +274,6 @@ export const refTxtInitialForm = {
|
||||
Notes: ''
|
||||
};
|
||||
|
||||
export const testMapInitialForm = {
|
||||
TestMapID: '',
|
||||
HostType: '',
|
||||
HostID: '',
|
||||
HostTestCode: '',
|
||||
HostTestName: '',
|
||||
ClientType: '',
|
||||
ClientID: '',
|
||||
ClientTestCode: '',
|
||||
ClientTestName: '',
|
||||
ConDefID: ''
|
||||
};
|
||||
|
||||
export const testDefaultErrors = {
|
||||
TestSiteCode: 'Required',
|
||||
TestSiteName: 'Required',
|
||||
@ -340,11 +304,6 @@ export const refTxtDefaultErrors = {
|
||||
AgeEnd: null
|
||||
};
|
||||
|
||||
export const testMapDefaultErrors = {
|
||||
HostID: null,
|
||||
ClientID: null
|
||||
};
|
||||
|
||||
export const testFormFields = [
|
||||
{
|
||||
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) {
|
||||
return [
|
||||
{
|
||||
@ -1058,7 +922,6 @@ export function buildTestPayload({
|
||||
testType,
|
||||
refNumData,
|
||||
refTxtData,
|
||||
mapData
|
||||
}) {
|
||||
let payload = {
|
||||
...mainForm
|
||||
@ -1085,19 +948,12 @@ export function buildTestPayload({
|
||||
TestSiteID: Number(m.value)
|
||||
}))
|
||||
}
|
||||
} else if (key === 'map' && mapData?.length > 0) {
|
||||
payload.testmap = mapData;
|
||||
} else if (key === 'cal') {
|
||||
payload.testdefcal = {
|
||||
...state.form,
|
||||
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);
|
||||
|
||||
@ -20,10 +20,6 @@
|
||||
refTxtDefaultErrors,
|
||||
refTxtInitialForm,
|
||||
refTxtFormFields,
|
||||
testMapSchema,
|
||||
testMapInitialForm,
|
||||
testMapDefaultErrors,
|
||||
testMapFormFields,
|
||||
testGroupSchema,
|
||||
testGroupInitialForm,
|
||||
testGroupDefaultErrors,
|
||||
@ -33,7 +29,6 @@
|
||||
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';
|
||||
@ -42,10 +37,8 @@
|
||||
|
||||
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;
|
||||
|
||||
@ -75,13 +68,6 @@
|
||||
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 ?? '';
|
||||
@ -99,18 +85,15 @@
|
||||
case 'PARAM':
|
||||
return {
|
||||
...refState,
|
||||
map: mapFormState
|
||||
};
|
||||
case 'CALC':
|
||||
return {
|
||||
cal: calFormState,
|
||||
...refState,
|
||||
map: mapFormState
|
||||
};
|
||||
case 'GROUP':
|
||||
return {
|
||||
group: groupFormState,
|
||||
map: mapFormState
|
||||
}
|
||||
case 'TITLE':
|
||||
default:
|
||||
@ -137,7 +120,6 @@
|
||||
async function handleSave() {
|
||||
const mainForm = masterDetail.formState.form;
|
||||
const testType = mainForm.TestType;
|
||||
const cleanMapData = mapData.map(({ options, ...rest }) => rest);
|
||||
|
||||
const payload = buildTestPayload({
|
||||
mainForm,
|
||||
@ -145,7 +127,6 @@
|
||||
testType: testType,
|
||||
refNumData: refNumData,
|
||||
refTxtData: refTxtData,
|
||||
mapData: cleanMapData,
|
||||
});
|
||||
console.log(payload);
|
||||
|
||||
@ -172,11 +153,11 @@
|
||||
switch (testType) {
|
||||
case 'TEST':
|
||||
case 'PARAM':
|
||||
return ['definition', 'map', 'reference'];
|
||||
return ['definition', 'reference'];
|
||||
case 'CALC':
|
||||
return ['definition', 'calculation', 'map', 'reference'];
|
||||
return ['definition', 'calculation', 'reference'];
|
||||
case 'GROUP':
|
||||
return ['definition', 'group', 'map'];
|
||||
return ['definition', 'group'];
|
||||
default:
|
||||
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');
|
||||
|
||||
$effect(() => {
|
||||
@ -347,14 +253,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
// $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 ?? [])
|
||||
@ -440,11 +338,6 @@
|
||||
refTxtState.form.TxtRefType = value;
|
||||
}
|
||||
}
|
||||
|
||||
// $inspect({
|
||||
// definition: formState.errors,
|
||||
// active: Object.values(activeFormStates).map(fs => fs.errors)
|
||||
// });
|
||||
</script>
|
||||
|
||||
<FormPageContainer title="Create Test" {primaryAction} {secondaryActions} {actions}>
|
||||
@ -462,9 +355,6 @@
|
||||
{#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
|
||||
@ -485,9 +375,6 @@
|
||||
<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'}
|
||||
|
||||
@ -20,10 +20,6 @@
|
||||
refTxtDefaultErrors,
|
||||
refTxtInitialForm,
|
||||
refTxtFormFields,
|
||||
testMapSchema,
|
||||
testMapInitialForm,
|
||||
testMapDefaultErrors,
|
||||
testMapFormFields,
|
||||
testGroupSchema,
|
||||
testGroupInitialForm,
|
||||
testGroupDefaultErrors,
|
||||
@ -33,7 +29,6 @@
|
||||
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";
|
||||
@ -43,10 +38,8 @@
|
||||
|
||||
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;
|
||||
|
||||
@ -76,13 +69,6 @@
|
||||
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 ?? '';
|
||||
@ -100,13 +86,11 @@
|
||||
case 'PARAM':
|
||||
return {
|
||||
...refState,
|
||||
map: mapFormState
|
||||
};
|
||||
case 'CALC':
|
||||
return {
|
||||
cal: calFormState,
|
||||
...refState,
|
||||
map: mapFormState
|
||||
};
|
||||
case 'GROUP':
|
||||
return {
|
||||
@ -129,7 +113,6 @@
|
||||
async function handleEdit() {
|
||||
const mainForm = masterDetail.formState.form;
|
||||
const testType = mainForm.TestType;
|
||||
const cleanMapData = mapData.map(({ options, ...rest }) => rest);
|
||||
|
||||
const payload = buildTestPayload({
|
||||
mainForm,
|
||||
@ -137,7 +120,6 @@
|
||||
testType: testType,
|
||||
refNumData: refNumData,
|
||||
refTxtData: refTxtData,
|
||||
mapData: cleanMapData,
|
||||
});
|
||||
console.log(payload);
|
||||
|
||||
@ -162,9 +144,9 @@
|
||||
switch (testType) {
|
||||
case 'TEST':
|
||||
case 'PARAM':
|
||||
return ['definition', 'map', 'reference'];
|
||||
return ['definition', 'reference'];
|
||||
case 'CALC':
|
||||
return ['definition', 'calculation', 'map', 'reference'];
|
||||
return ['definition', 'calculation', 'reference'];
|
||||
case 'GROUP':
|
||||
return ['definition', 'group'];
|
||||
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 groupInitialized = $state(false);
|
||||
|
||||
@ -343,7 +251,7 @@
|
||||
AgeEnd: typeof row.AgeEnd === 'number' ? buildAgeText(daysToAge(row.AgeEnd)) : row.AgeEnd,
|
||||
}));
|
||||
}
|
||||
// groupFormState.form.Members = [];
|
||||
|
||||
if (!groupInitialized && mainForm.testdefgrp?.members && Array.isArray(mainForm.testdefgrp.members)) {
|
||||
groupFormState.form.Members = mainForm.testdefgrp.members.map((m, index) => ({
|
||||
id: m.id ?? index + 1,
|
||||
@ -351,12 +259,6 @@
|
||||
}));
|
||||
groupInitialized = true;
|
||||
}
|
||||
// if (mainForm.testmap && Array.isArray(mainForm.testmap)) {
|
||||
// mapData = mainForm.testmap.map((row, index) => ({
|
||||
// id: row.id ?? index + 1,
|
||||
// ...row,
|
||||
// }));
|
||||
// }
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
@ -453,14 +355,6 @@
|
||||
refTxtState.form.TxtRefType = value;
|
||||
}
|
||||
}
|
||||
|
||||
// $effect(() => {
|
||||
// if (formState.form.Factor && !formState.form.Unit2) {
|
||||
// formState.errors.Unit2 = 'Required';
|
||||
// } else {
|
||||
// formState.errors.Unit2 = null;
|
||||
// }
|
||||
// });
|
||||
|
||||
$effect(() => {
|
||||
const refType = formState.form.RefType;
|
||||
@ -488,9 +382,6 @@
|
||||
{#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
|
||||
@ -511,9 +402,6 @@
|
||||
<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'}
|
||||
|
||||
@ -30,7 +30,7 @@ export const detailSections = [
|
||||
{ key: "ClientTypeLabel", label: "Client Type" },
|
||||
{ key: "HostID", label: "Host 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 = {
|
||||
TestMapID: '',
|
||||
HostType: '',
|
||||
HostID: '',
|
||||
HostTestCode: '',
|
||||
HostTestName: '',
|
||||
ClientType: '',
|
||||
ClientID: '',
|
||||
};
|
||||
|
||||
export const testMapDetailInitialForm = {
|
||||
HostTestCode: '',
|
||||
HostTestName: '',
|
||||
ClientTestCode: '',
|
||||
ClientTestName: '',
|
||||
ConDefID: ''
|
||||
};
|
||||
}
|
||||
|
||||
export const testMapDefaultErrors = {
|
||||
HostID: null,
|
||||
ClientID: null
|
||||
};
|
||||
|
||||
export const testMapDetailDefaultErrors = {
|
||||
ConDefID: null,
|
||||
};
|
||||
|
||||
export const testMapFormFields = [
|
||||
{
|
||||
rows: [
|
||||
@ -89,6 +100,13 @@ export const testMapFormFields = [
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const testMapDetailFormFields = [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
type: 'row',
|
||||
columns: [
|
||||
@ -145,7 +163,7 @@ export const testMapFormFields = [
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export function getTestMapFormActions(handlers) {
|
||||
return [
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
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";
|
||||
@ -11,23 +13,30 @@
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { untrack } from "svelte";
|
||||
import { API } from '$lib/config/api';
|
||||
import { buildTestMapPayload } from "$lib/components/dictionary/testmap/config/testmap-form-config";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||
|
||||
const { formState } = masterDetail;
|
||||
|
||||
let editingId = $state(null);
|
||||
let idCounter = $state(0);
|
||||
let tempMap = $state([]);
|
||||
|
||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||
|
||||
const { formState } = masterDetail;
|
||||
const testMapDetailFormState = useForm({
|
||||
schema: testMapDetailSchema,
|
||||
initialForm: testMapDetailInitialForm,
|
||||
defaultErrors: testMapDetailDefaultErrors,
|
||||
});
|
||||
|
||||
const helpers = useDictionaryForm(formState);
|
||||
|
||||
const handlers = {
|
||||
clearForm: () => {
|
||||
formState.reset();
|
||||
resetTestMapDetailForm();
|
||||
tempMap = [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -37,7 +46,7 @@
|
||||
|
||||
function snapshotForm() {
|
||||
return untrack(() => {
|
||||
const f = formState.form;
|
||||
const f = testMapDetailFormState.form;
|
||||
return {
|
||||
HostTestCode: f.HostTestCode ?? "",
|
||||
HostTestName: f.HostTestName ?? "",
|
||||
@ -48,24 +57,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formState.reset();
|
||||
function resetTestMapDetailForm() {
|
||||
testMapDetailFormState.reset();
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function resetTest() {
|
||||
untrack(() => {
|
||||
const f = formState.form;
|
||||
f.HostTestCode = null;
|
||||
f.HostTestName = null;
|
||||
f.ClientTestCode = null;
|
||||
f.ClientTestName = null;
|
||||
f.ConDefID = null;
|
||||
});
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
function handleInsertDetail() {
|
||||
console.log('object');
|
||||
const row = {
|
||||
id: ++idCounter,
|
||||
...snapshotForm()
|
||||
@ -73,47 +71,38 @@
|
||||
|
||||
tempMap = [...tempMap, row];
|
||||
|
||||
resetTest();
|
||||
resetTestMapDetailForm();
|
||||
}
|
||||
|
||||
async function handleEdit(row) {
|
||||
async function handleEditDetail(row) {
|
||||
editingId = row.id;
|
||||
|
||||
untrack(() => {
|
||||
const f = formState.form;
|
||||
// f.HostType = row.HostType;
|
||||
// f.HostID = row.HostID;
|
||||
const f = testMapDetailFormState.form;
|
||||
|
||||
f.HostTestCode = row.HostTestCode;
|
||||
f.HostTestName = row.HostTestName;
|
||||
// f.ClientType = row.ClientType;
|
||||
// f.ClientID = row.ClientID;
|
||||
f.ClientTestCode = row.ClientTestCode;
|
||||
f.ClientTestName = row.ClientTestName;
|
||||
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) =>
|
||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||
);
|
||||
resetTest();
|
||||
resetTestMapDetailForm();
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetTest();
|
||||
function handleCancelEditDetail() {
|
||||
resetTestMapDetailForm();
|
||||
}
|
||||
|
||||
function handleRemove(id) {
|
||||
function handleRemoveDetail(id) {
|
||||
tempMap = tempMap.filter((row) => row.id !== id);
|
||||
if (editingId === id) {
|
||||
resetTest();
|
||||
resetTestMapDetailForm();
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,10 +114,10 @@
|
||||
tempMap,
|
||||
});
|
||||
console.log(payload)
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
// const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Map Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
// toast('Test Map Created!');
|
||||
// masterDetail?.exitForm(true);
|
||||
}
|
||||
|
||||
const primaryAction = $derived({
|
||||
@ -176,7 +165,15 @@
|
||||
}
|
||||
|
||||
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 {
|
||||
...col,
|
||||
type: 'select',
|
||||
@ -184,55 +181,61 @@
|
||||
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 };
|
||||
}
|
||||
|
||||
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;
|
||||
})
|
||||
}))
|
||||
}));
|
||||
});
|
||||
|
||||
// $effect(() => {
|
||||
// if (formState.form.ClientTestCode) {
|
||||
// formState.form.ClientTestName = 'nyaho';
|
||||
// }
|
||||
// })
|
||||
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
|
||||
})
|
||||
}))
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
<FormPageContainer title="Create Test Map" {primaryAction} {secondaryActions} {actions}>
|
||||
@ -241,17 +244,22 @@
|
||||
formFields={mapFormFieldsTransformed}
|
||||
mode="create"
|
||||
/>
|
||||
<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={handleUpdate}>
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleUpdateDetail}>
|
||||
Update
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEdit}>
|
||||
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEditDetail}>
|
||||
Cancel
|
||||
</Button>
|
||||
{:else}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsert}>
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsertDetail}>
|
||||
Insert
|
||||
</Button>
|
||||
{/if}
|
||||
@ -262,12 +270,8 @@
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<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 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 Name</Table.Head>
|
||||
<Table.Head>Container</Table.Head>
|
||||
@ -297,7 +301,7 @@
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleEdit(row)}
|
||||
onclick={() => handleEditDetail(row)}
|
||||
>
|
||||
<PencilIcon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
@ -305,7 +309,7 @@
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleRemove(row.id)}
|
||||
onclick={() => handleRemoveDetail(row.id)}
|
||||
>
|
||||
<Trash2Icon class="h-3.5 w-3.5" />
|
||||
</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 ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
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();
|
||||
|
||||
@ -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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<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">
|
||||
{#each "BACK TO TEST MAP".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
let testMap = $derived(masterDetail?.selectedItem?.data);
|
||||
|
||||
const handlers = {
|
||||
editTest: () => masterDetail.enterEdit("data"),
|
||||
editTestMap: () => masterDetail.enterEdit("data"),
|
||||
};
|
||||
|
||||
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 ActivityIcon from "@lucide/svelte/icons/activity";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
import NotepadTextIcon from "@lucide/svelte/icons/notepad-text";
|
||||
|
||||
export const searchFields = [
|
||||
@ -104,8 +105,13 @@ export const detailSections = [
|
||||
}
|
||||
]
|
||||
|
||||
export function patientActions(masterDetail, patientInitialForm) {
|
||||
export function patientActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Patient',
|
||||
|
||||
@ -7,13 +7,15 @@
|
||||
import ReusableSearchParam from "$lib/components/reusable/reusable-search-param.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.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();
|
||||
|
||||
const search = useSearch(searchFields, searchParam);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = patientActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => alert('velom visa, savar!'),
|
||||
};
|
||||
const actions = patientActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<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">
|
||||
{#each "BACK TO PATIENT".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
|
||||
@ -269,6 +269,7 @@
|
||||
formState.form.ClientID = '';
|
||||
formState.form.ClientTestCode = '';
|
||||
formState.form.ClientTestName = '';
|
||||
formState.selectOptions.ClientID = [];
|
||||
formState.selectOptions.ClientTestCode = [];
|
||||
formState.selectOptions.ClientTestName = [];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user