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.string()), FormulaCode: z.string() }).refine( (data) => { if (data.FormulaInput.length === 0) return true; function hasExactKeyword(input, keyword) { const regex = new RegExp(`\\b${keyword}\\b`, 'i'); return regex.test(input); } return data.FormulaInput.every((v) => hasExactKeyword(data.FormulaCode, v)); }, { message: 'Formula must contain all selected input parameters', path: ['FormulaCode'] } ); 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 = {}; 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: false, type: "selectmultiple", optionsEndpoint: `${API.BASE_URL}${API.TEST}`, valueKey: "TestSiteID", labelKey: (item) => `${item.TestSiteCode} - ${item.TestSiteName}`, }, ] }, { type: "row", columns: [ { key: "FormulaCode", label: "Formula", required: false, type: "textarea", validateOn: ["input"] } ] }, ] }, ]; 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"] }, ] }, ] }, { 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); }