- Rename all controllers from X.php to XController.php format - Add new RefTxtModel for text-based reference ranges - Rename group_dialog.php to grp_dialog.php and remove title_dialog.php - Add comprehensive test suite for v2/master/TestDef module - Update Routes.php to reflect controller renames - Remove obsolete data files (clqms_v2.sql, lab.dbml)
417 lines
20 KiB
PHP
417 lines
20 KiB
PHP
<!-- Parameter Dialog (for PARAM type) -->
|
|
<div x-show="showModal && (getTypeCode(form.TestType) === 'PARAM' || form.TypeCode === 'PARAM')" x-cloak
|
|
class="modal-overlay" @click.self="closeModal()" x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0">
|
|
<div class="modal-content p-6 max-w-3xl w-full max-h-[90vh] overflow-y-auto" @click.stop
|
|
x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 transform scale-95"
|
|
x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-95">
|
|
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-emerald-100 flex items-center justify-center">
|
|
<i class="fa-solid fa-sliders text-emerald-600 text-lg"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-bold text-lg" style="color: rgb(var(--color-text));">
|
|
<span x-text="isEditing ? 'Edit Parameter' : 'New Parameter'"></span>
|
|
</h3>
|
|
<p class="text-xs" style="color: rgb(var(--color-text-muted));">Parameter/Component Definition</p>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-ghost btn-sm btn-square" @click="closeModal()">
|
|
<i class="fa-solid fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Parameter Type Badge -->
|
|
<div class="mb-4">
|
|
<span class="badge badge-success gap-1">
|
|
<i class="fa-solid fa-sliders"></i>
|
|
<span x-text="form.TypeCode || getTypeName(form.TestType)"></span>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Tabs Navigation -->
|
|
<div class="flex flex-wrap gap-1 mb-4 p-1 rounded-lg"
|
|
style="background: rgb(var(--color-bg-secondary)); border: 1px solid rgb(var(--color-border));">
|
|
<button class="flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all duration-200"
|
|
:class="activeTab === 'basic' ? 'bg-emerald-500 text-white shadow' : 'hover:bg-opacity-50'"
|
|
style="color: rgb(var(--color-text));" @click="activeTab = 'basic'">
|
|
<i class="fa-solid fa-info-circle mr-1"></i> Basic
|
|
</button>
|
|
<button class="flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all duration-200"
|
|
:class="activeTab === 'results' ? 'bg-emerald-500 text-white shadow' : 'hover:bg-opacity-50'"
|
|
style="color: rgb(var(--color-text));" @click="activeTab = 'results'">
|
|
<i class="fa-solid fa-flask mr-1"></i> Results
|
|
</button>
|
|
<button class="flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all duration-200"
|
|
:class="activeTab === 'reference' ? 'bg-emerald-500 text-white shadow' : 'hover:bg-opacity-50'"
|
|
style="color: rgb(var(--color-text));" @click="activeTab = 'reference'">
|
|
<i class="fa-solid fa-ruler-combined mr-1"></i> Ref
|
|
</button>
|
|
<button class="flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all duration-200"
|
|
:class="activeTab === 'valueset' ? 'bg-emerald-500 text-white shadow' : 'hover:bg-opacity-50'"
|
|
style="color: rgb(var(--color-text));" @click="activeTab = 'valueset'">
|
|
<i class="fa-solid fa-list-ul mr-1"></i> VSet
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Form -->
|
|
<div class="space-y-4">
|
|
|
|
<!-- Tab: Basic Information (includes Org, Sample, and Seq) -->
|
|
<div x-show="activeTab === 'basic'" x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 transform -translate-x-4"
|
|
x-transition:enter-end="opacity-100 transform translate-x-0">
|
|
|
|
<!-- Basic Info Section -->
|
|
<div class="mb-4">
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-info-circle text-emerald-500"></i> Basic Information
|
|
</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Parameter Code <span class="text-error">*</span></span>
|
|
</label>
|
|
<input type="text" class="input input-bordered font-mono uppercase w-full"
|
|
:class="errors.TestSiteCode && 'input-error'" x-model="form.TestSiteCode"
|
|
placeholder="e.g., RBC, WBC, HGB" maxlength="10" />
|
|
<label class="label" x-show="errors.TestSiteCode">
|
|
<span class="label-text-alt text-error text-xs" x-text="errors.TestSiteCode"></span>
|
|
</label>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Parameter Name <span class="text-error">*</span></span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" :class="errors.TestSiteName && 'input-error'"
|
|
x-model="form.TestSiteName" placeholder="e.g., Red Blood Cell Count" />
|
|
<label class="label" x-show="errors.TestSiteName">
|
|
<span class="label-text-alt text-error text-xs">Parameter name is required</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Description</span>
|
|
</label>
|
|
<textarea class="textarea textarea-bordered w-full" x-model="form.Description"
|
|
placeholder="Optional description..." rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Organization Section -->
|
|
<div class="mb-4">
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-building text-emerald-500"></i> Organization
|
|
</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Discipline</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.DisciplineID">
|
|
<option value="">Select Discipline</option>
|
|
<option value="1">Hematology</option>
|
|
<option value="2">Chemistry</option>
|
|
<option value="3">Microbiology</option>
|
|
<option value="4">Urinalysis</option>
|
|
<option value="5">Immunology</option>
|
|
<option value="6">Serology</option>
|
|
<option value="10">General</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Department</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.DepartmentID">
|
|
<option value="">Select Department</option>
|
|
<option value="1">Lab Hematology</option>
|
|
<option value="2">Lab Chemistry</option>
|
|
<option value="3">Lab Microbiology</option>
|
|
<option value="4">Lab Urinalysis</option>
|
|
<option value="5">Lab Immunology</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sample Section -->
|
|
<div class="mb-4">
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-vial text-emerald-500"></i> Sample
|
|
</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Sample Type</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.SampleType">
|
|
<option value="">Select Sample Type</option>
|
|
<option value="SERUM">Serum</option>
|
|
<option value="PLASMA">Plasma</option>
|
|
<option value="BLOOD">Whole Blood</option>
|
|
<option value="URINE">Urine</option>
|
|
<option value="CSF">CSF</option>
|
|
<option value="OTHER">Other</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Method</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.Method"
|
|
placeholder="e.g., Automated Cell Counter" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sequencing Section -->
|
|
<div>
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-list-ol text-emerald-500"></i> Sequencing & Visibility
|
|
</h4>
|
|
<div class="grid grid-cols-4 gap-3">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Seq (Screen)</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.SeqScr" placeholder="0" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Seq (Report)</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.SeqRpt" placeholder="0" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Indent</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.IndentLeft"
|
|
placeholder="0" />
|
|
</div>
|
|
<div class="flex items-center">
|
|
<label class="label cursor-pointer justify-start gap-2">
|
|
<input type="checkbox" class="checkbox checkbox-sm" x-model="form.CountStat" :true-value="1"
|
|
:false-value="0" />
|
|
<span class="label-text text-sm">Count in Statistics</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-4 mt-3">
|
|
<label class="label cursor-pointer justify-start gap-2">
|
|
<input type="checkbox" class="checkbox checkbox-sm" x-model="form.VisibleScr" :true-value="1"
|
|
:false-value="0" />
|
|
<span class="label-text text-sm">Visible on Screen</span>
|
|
</label>
|
|
<label class="label cursor-pointer justify-start gap-2">
|
|
<input type="checkbox" class="checkbox checkbox-sm" x-model="form.VisibleRpt" :true-value="1"
|
|
:false-value="0" />
|
|
<span class="label-text text-sm">Visible on Report</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Result Configuration (includes Sample & Method) -->
|
|
<div x-show="activeTab === 'results'" x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 transform -translate-x-4"
|
|
x-transition:enter-end="opacity-100 transform translate-x-0">
|
|
|
|
<!-- Result Configuration -->
|
|
<div class="mb-4">
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-flask text-emerald-500"></i> Result Configuration
|
|
</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Result Type</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.ResultType">
|
|
<option value="">Select Result Type</option>
|
|
<option value="NMRIC">Numeric</option>
|
|
<option value="TEXT">Text</option>
|
|
<option value="VSET">Value Set (Select)</option>
|
|
<option value="RANGE">Range with Reference</option>
|
|
<option value="DTTM">Date/Time</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Reference Type</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.RefType">
|
|
<option value="">Select Reference Type</option>
|
|
<option value="NMRC">Numeric Range</option>
|
|
<option value="TEXT">Text Reference</option>
|
|
<option value="AGE">Age-based</option>
|
|
<option value="GENDER">Gender-based</option>
|
|
<option value="NONE">No Reference</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Unit 1</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.Unit1"
|
|
placeholder="e.g., 10^6/µL, g/dL" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Unit 2 (SI)</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.Unit2"
|
|
placeholder="e.g., 10^12/L, g/L" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Decimal Places</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.Decimal" placeholder="2"
|
|
min="0" max="10" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Expected TAT (min)</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.ExpectedTAT"
|
|
placeholder="30" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sample & Method -->
|
|
<div>
|
|
<h4 class="font-medium text-sm mb-3 flex items-center gap-2">
|
|
<i class="fa-solid fa-vial text-emerald-500"></i> Sample & Method
|
|
</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Sample Type</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.SampleType">
|
|
<option value="">Select Sample Type</option>
|
|
<option value="SERUM">Serum</option>
|
|
<option value="PLASMA">Plasma</option>
|
|
<option value="BLOOD">Whole Blood</option>
|
|
<option value="URINE">Urine</option>
|
|
<option value="CSF">CSF</option>
|
|
<option value="OTHER">Other</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Method</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.Method"
|
|
placeholder="e.g., Automated Cell Counter" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Reference Range -->
|
|
<div x-show="activeTab === 'reference'" x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 transform -translate-x-4"
|
|
x-transition:enter-end="opacity-100 transform translate-x-0">
|
|
<div class="grid grid-cols-4 gap-3">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Min Normal</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.RefMin" placeholder="0"
|
|
step="any" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Max Normal</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.RefMax" placeholder="100"
|
|
step="any" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Critical Low</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.CriticalLow"
|
|
placeholder="Alert low value" step="any" />
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Critical High</span>
|
|
</label>
|
|
<input type="number" class="input input-bordered w-full" x-model.number="form.CriticalHigh"
|
|
placeholder="Alert high value" step="any" />
|
|
</div>
|
|
</div>
|
|
<div class="mt-4">
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Reference Text (for text-based reference)</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.RefText"
|
|
placeholder="e.g., Negative/Positive, Normal/Abnormal" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Value Set Selection -->
|
|
<div x-show="activeTab === 'valueset'" x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 transform -translate-x-4"
|
|
x-transition:enter-end="opacity-100 transform translate-x-0">
|
|
<template x-if="form.ResultType === 'VSET'">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Value Set</span>
|
|
</label>
|
|
<select class="select select-bordered w-full" x-model="form.ValueSetID">
|
|
<option value="">Select Value Set</option>
|
|
<option value="1">Positive/Negative</option>
|
|
<option value="2">+1 to +4</option>
|
|
<option value="3">Absent/Present</option>
|
|
<option value="4">Normal/Abnormal</option>
|
|
<option value="5">Trace/+/++/+++</option>
|
|
<option value="6">Yes/No</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="label">
|
|
<span class="label-text font-medium text-sm">Default Value</span>
|
|
</label>
|
|
<input type="text" class="input input-bordered w-full" x-model="form.DefaultValue"
|
|
placeholder="Default selection" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template x-if="form.ResultType !== 'VSET'">
|
|
<div class="p-8 text-center rounded-lg border bg-opacity-30"
|
|
style="background: rgb(var(--color-bg-secondary)); border-color: rgb(var(--color-border));">
|
|
<i class="fa-solid fa-list-ul text-4xl opacity-40 mb-2"></i>
|
|
<p class="opacity-60">Value Set configuration is only available when Result Type is "Value Set (Select)"</p>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="flex gap-3 mt-6 pt-4 border-t" style="border-color: rgb(var(--color-border));">
|
|
<button class="btn btn-ghost flex-1" @click="closeModal()">
|
|
<i class="fa-solid fa-times mr-2"></i> Cancel
|
|
</button>
|
|
<button class="btn btn-success flex-1" @click="save()" :disabled="saving">
|
|
<span x-show="saving" class="loading loading-spinner loading-sm mr-2"></span>
|
|
<i x-show="!saving" class="fa-solid fa-save mr-2"></i>
|
|
<span x-text="saving ? 'Saving...' : (isEditing ? 'Update Parameter' : 'Create Parameter')"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div> |