1017 lines
18 KiB
JavaScript

import { API } from '$lib/config/api';
import EraserIcon from '@lucide/svelte/icons/eraser';
import { z } from 'zod';
import { cleanEmptyStrings } from '$lib/utils/cleanEmptyStrings';
import { toDays } from '$lib/utils/ageUtils';
export const testSchema = z
.object({
TestSiteCode: z.string().min(1, 'Required'),
TestSiteName: z.string().min(1, 'Required'),
Factor: z.string().optional(),
Unit2: z.string().optional(),
Decimal: z.preprocess(
(val) => {
if (val === '' || val === null || val === undefined) return undefined;
return Number(val);
},
z
.number({ invalid_type_error: 'Must be a number' })
.min(0, 'Min 0')
.max(7, 'Max 7')
.optional()
)
})
.superRefine((data, ctx) => {
if (data.Factor && !data.Unit2) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Required',
path: ['Unit2']
});
}
});
export const testCalSchema = z
.object({
FormulaInput: z
.array(
z.object({
value: z.string(),
level: z.preprocess((val) => (val ? Number(val) : 0), z.number())
})
)
.min(1, 'Required'),
FormulaCode: z.string().optional()
})
.superRefine((data, ctx) => {
if (data.FormulaInput.length === 0) return;
const hasExactKeyword = (input, keyword) => {
const regex = new RegExp(`\\b${keyword}\\b`, 'i');
return regex.test(input);
};
const invalid = data.FormulaInput.some((v) => !hasExactKeyword(data.FormulaCode, v.value));
if (invalid) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Formula must contain all selected input parameters:${data.FormulaInput.map((f) => f.value).join(',')}`,
path: ['FormulaCode']
});
}
})
.superRefine((data, ctx) => {
const totalLevel = data.FormulaInput.reduce((sum, item) => sum + (item.level || 0), 0);
if (totalLevel > 5) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Level must not exceeded 5 (${totalLevel})`,
path: ['FormulaInput']
});
}
});
export const refNumSchema = z
.object({
AgeStart: z.string().optional(),
AgeEnd: z.string().optional(),
Low: z.string().optional(),
High: z.string().optional(),
LowSign: z.string().optional(),
HighSign: z.string().optional(),
NumRefType: z.string().optional()
})
.superRefine((data, ctx) => {
const start = toDays(data.AgeStart);
const end = toDays(data.AgeEnd);
// if (start !== null && start > 0 && end !== null && end <= start) {
if (start !== null && end !== null && end <= start) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Age End must be greater than Age Start',
path: ['AgeEnd']
});
}
})
.superRefine((data, ctx) => {
// console.log(data.NumRefType);
if (data.NumRefType === 'RANGE') {
const low = Number(data.Low);
const high = Number(data.High);
if (data.Low && data.High && !Number.isNaN(low) && !Number.isNaN(high) && high <= low) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'High value must be greater than Low value',
path: ['High']
});
}
}
if (data.NumRefType === 'THOLD') {
if (data.Low && !data.LowSign) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Math sign for Low is required',
path: ['LowSign']
});
}
if (data.High && !data.HighSign) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Math sign for High is required',
path: ['HighSign']
});
}
if (data.LowSign && data.HighSign && data.LowSign === data.HighSign) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "High sign can't be the same as Low sign",
path: ['HighSign']
});
}
}
});
export const refTxtSchema = z
.object({
AgeStart: z.string().optional(),
AgeEnd: z.string().optional()
})
.superRefine((data, ctx) => {
const start = toDays(data.AgeStart);
const end = toDays(data.AgeEnd);
if (start !== null && end !== null && end <= start) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Age End must be greater than Age Start',
path: ['AgeEnd']
});
}
});
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: '',
TestSiteCode: '',
TestSiteName: '',
TestType: '',
Description: '',
DisciplineID: '',
DepartmentID: '',
ResultType: '',
RefType: '',
Vset: '',
Unit1: '',
Factor: '',
Unit2: '',
Decimal: '',
ReqQty: '',
ReqQtyUnit: '',
CollReq: '',
Method: '',
ExpectedTAT: '',
SeqScr: '',
SeqRpt: '',
VisibleScr: '',
VisibleRpt: '',
CountStat: '',
Level: ''
};
export const testCalInitialForm = {
TestCalID: '',
TestSiteID: '',
FormulaInput: [],
FormulaCode: ''
};
export const refNumInitialForm = {
RefNumID: '',
SiteID: '',
TestSiteID: '',
SpcType: '',
Sex: '',
Criteria: '',
AgeStart: '',
AgeEnd: '',
NumRefType: '',
RangeType: '',
LowSign: '',
Low: '',
HighSign: '',
High: '',
Display: '',
Flag: '',
Interpretation: '',
Notes: ''
};
export const refTxtInitialForm = {
RefTxtID: '',
SiteID: '',
TestSiteID: '',
SpcType: '',
Sex: '',
Criteria: '',
AgeStart: '',
AgeEnd: '',
TxtRefType: '',
RefTxt: '',
Flag: '',
Notes: ''
};
export const testMapInitialForm = {
TestMapID: '',
HostType: '',
HostID: '',
HostTestCode: '',
HostTestName: '',
ClientType: '',
ClientID: '',
ClientTestCode: '',
ClientTestName: '',
ConDefID: ''
};
export const testDefaultErrors = {
TestSiteCode: 'Required',
TestSiteName: 'Required',
Unit2: null,
Decimal: null
};
export const testCalDefaultErrors = {
FormulaInput: 'Required',
FormulaCode: null
};
export const refNumDefaultErrors = {
AgeStart: null,
AgeEnd: null,
Low: null,
High: null,
LowSign: null,
HighSign: null
};
export const refTxtDefaultErrors = {
AgeStart: null,
AgeEnd: null
};
export const testMapDefaultErrors = {
HostID: null,
ClientID: null
};
export const testFormFields = [
{
title: 'Basic Information',
rows: [
{
type: 'row',
columns: [
{
key: 'SiteID',
label: 'Site ID',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
valueKey: 'SiteID',
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`
},
{
key: 'TestSiteCode',
label: 'Test Code',
required: true,
type: 'text',
validateOn: ['input']
}
]
},
{
type: 'row',
columns: [
{
key: 'TestSiteName',
label: 'Test Name',
required: true,
type: 'text',
validateOn: ['input']
},
{
key: 'TestType',
label: 'Test Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/test_type`
}
]
},
{
type: 'row',
columns: [
{
key: 'Description',
label: 'Description',
required: false,
type: 'textarea'
}
]
}
]
},
{
title: 'Organizational Assignment',
rows: [
{
type: 'row',
columns: [
{
key: 'DisciplineID',
label: 'Discipline',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.DISCIPLINE}`,
valueKey: 'DisciplineID',
labelKey: 'DisciplineName'
},
{
key: 'DepartmentID',
label: 'Department',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.DEPARTMENT}`,
valueKey: 'DepartmentID',
labelKey: 'DepartmentName'
}
]
}
]
},
{
title: 'Result Configuration',
rows: [
{
type: 'row',
columns: [
{
key: 'ResultType',
label: 'Result Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/result_type`
},
{
key: 'RefType',
label: 'Reference Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/reference_type`
}
]
},
{
type: 'row',
columns: [
{
key: 'VSet',
label: 'Value Set',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}`,
fullWidth: false,
autoFetch: false
}
]
},
{
type: 'row',
columns: [
{
key: 'Unit1',
label: 'Unit 1',
required: false,
type: 'text'
},
{
key: 'Factor',
label: 'Factor',
required: false,
type: 'text'
}
]
},
{
type: 'row',
columns: [
{
key: 'Unit2',
label: 'Unit 2',
required: false,
type: 'text'
},
{
key: 'Decimal',
label: 'Decimal',
required: false,
type: 'number',
validateOn: ['input']
}
]
},
{
type: 'row',
columns: [
{
key: 'ExpectedTAT',
label: 'Expected TAT',
required: false,
type: 'text',
fullWidth: false
}
]
}
]
},
{
title: 'Specimen Requirements',
rows: [
{
type: 'row',
columns: [
{
key: 'ReqQty',
label: 'Required Quantity',
required: false,
type: 'text'
},
{
key: 'CollReq',
label: 'Collection Requirement',
required: false,
type: 'text'
}
]
},
{
type: 'row',
columns: [
{
key: 'ReqQtyUnit',
label: 'Quantity Unit',
required: false,
type: 'text'
},
{
key: 'Method',
label: 'Method',
required: false,
type: 'text'
}
]
}
]
},
{
title: 'Display Settings',
rows: [
{
type: 'row',
columns: [
{
key: 'SeqScr',
label: 'Sequence on Screen',
required: false,
type: 'text'
},
{
key: 'VisibleScr',
label: 'Visible on Screen',
required: false,
type: 'text'
}
]
},
{
type: 'row',
columns: [
{
key: 'SeqRpt',
label: 'Sequence on Report',
required: false,
type: 'text'
},
{
key: 'VisibleRpt',
label: 'Visible on Report',
required: false,
type: 'text'
}
]
}
]
},
{
title: 'Other Settings',
rows: [
{
type: 'row',
columns: [
{
key: 'CountStat',
label: 'Statistic',
required: false,
type: 'text'
},
{
key: 'Level',
label: 'Level',
required: false,
type: 'text'
}
]
}
]
}
];
export const testCalFormFields = [
{
rows: [
{
type: 'row',
columns: [
{
key: 'FormulaInput',
label: 'Input Parameter',
required: true,
type: 'selectmultiple',
optionsEndpoint: `${API.BASE_URL}${API.TEST}`,
valueKey: 'TestSiteCode',
labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`,
validateOn: ['input']
}
]
},
{
type: 'row',
columns: [
{
key: 'FormulaCode',
label: 'Formula Code',
required: false,
type: 'formulabuilder'
}
]
}
]
}
];
export const refNumFormFields = [
{
rows: [
{
type: 'row',
columns: [
{
key: 'SiteID',
label: 'Site',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
valueKey: 'SiteID',
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`,
fullWidth: false
}
]
}
]
},
{
title: 'Criteria Definition',
rows: [
{
type: 'row',
columns: [
{
key: 'Sex',
label: 'Sex',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/sex`
},
{
key: 'SpcType',
label: 'Specimen Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/specimen_type`
}
]
},
{
type: 'row',
columns: [
{
key: 'AgeStart',
label: 'Age Start',
required: false,
type: 'agejoin',
validateOn: ['input']
},
{
key: 'AgeEnd',
label: 'Age End',
required: false,
type: 'agejoin',
validateOn: ['input']
}
]
}
]
},
{
title: 'Reference Range Configuration',
rows: [
{
type: 'row',
columns: [
{
key: 'NumRefType',
label: 'Reference Type',
required: false,
type: 'text'
},
{
key: 'RangeType',
label: 'Range Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/range_type`
}
]
},
{
type: 'row',
columns: [
{
key: 'LowSign',
label: 'Low Sign - Value',
required: false,
type: 'signvalue',
txtKey: 'Low',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
validateOn: ['input']
},
{
key: 'HighSign',
label: 'High Sign - Value',
required: false,
type: 'signvalue',
txtKey: 'High',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/math_sign`,
validateOn: ['input']
}
]
},
{
type: 'row',
columns: [
{
key: 'Flag',
label: 'Flag',
required: false,
type: 'text'
},
{
key: 'Interpretation',
label: 'Interpretation',
required: false,
type: 'text'
}
]
},
{
type: 'row',
columns: [
{
key: 'Notes',
label: 'Notes',
required: false,
type: 'textarea'
}
]
}
]
}
// {
// title: "Flag & Interpretation",
// rows: [
// {
// type: "row",
// columns: [
// {
// key: "Flag",
// label: "Flag",
// required: false,
// type: "text",
// },
// {
// key: "Interpretation",
// label: "Interpretation",
// required: false,
// type: "text",
// },
// ]
// },
// {
// type: "row",
// columns: [
// {
// key: "Notes",
// label: "Notes",
// required: false,
// type: "textarea",
// },
// ]
// },
// ]
// },
];
export const refTxtFormFields = [
{
rows: [
{
type: 'row',
columns: [
{
key: 'SiteID',
label: 'Site',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
valueKey: 'SiteID',
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`,
fullWidth: false
}
]
}
]
},
{
title: 'Criteria Definition',
rows: [
{
type: 'row',
columns: [
{
key: 'Sex',
label: 'Sex',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/sex`
},
{
key: 'SpcType',
label: 'Specimen Type',
required: false,
type: 'select',
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/specimen_type`
}
]
},
{
type: 'row',
columns: [
{
key: 'AgeStart',
label: 'Age Start',
required: false,
type: 'agejoin',
validateOn: ['input']
},
{
key: 'AgeEnd',
label: 'Age End',
required: false,
type: 'agejoin',
validateOn: ['input']
}
]
}
]
},
{
title: 'Reference Range Configuration',
rows: [
{
type: 'row',
columns: [
{
key: 'TxtRefType',
label: 'Reference Type',
required: false,
type: 'text',
fullWidth: false
}
]
},
{
type: 'row',
columns: [
{
key: 'RefTxt',
label: 'Reference Text',
required: false,
type: 'textarea'
}
]
}
]
},
{
title: 'Flag & Notes',
rows: [
{
type: 'row',
columns: [
{
key: 'Flag',
label: 'Flag',
required: false,
type: 'text',
fullWidth: false
}
]
},
{
type: 'row',
columns: [
{
key: 'Notes',
label: 'Notes',
required: false,
type: 'textarea'
}
]
}
]
}
];
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 [
{
Icon: EraserIcon,
label: 'Clear Form',
onClick: handlers.clearForm
}
];
}
export function buildTestPayload({ mainForm, calForm, refForm, testType }) {
let payload = {
...mainForm
};
if (testType === 'CALC') {
payload = {
...payload,
...calForm
};
} else if (testType === 'TEST' || 'PARAM') {
payload = {
...payload,
...refForm
};
}
return cleanEmptyStrings(payload);
}