clqms-be/app/Views/v2/master/tests/grp_dialog.php
mahdahar cd65e91db1 refactor: Rename controllers to follow CodeIgniter 4 naming convention
- 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)
2026-01-05 16:55:34 +07:00

305 lines
16 KiB
PHP

<!-- Group Dialog (for GROUP type) -->
<div x-show="showModal && (getTypeCode(form.TestType) === 'GROUP' || form.TypeCode === 'GROUP')" 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-4xl 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-primary bg-opacity-20 flex items-center justify-center">
<i class="fa-solid fa-layer-group text-primary text-lg"></i>
</div>
<div>
<h3 class="font-bold text-lg" style="color: rgb(var(--color-text));">
<span x-text="isEditing ? 'Edit Test Group' : 'New Test Group'"></span>
</h3>
<p class="text-xs" style="color: rgb(var(--color-text-muted));">Group/Panel Definition</p>
</div>
</div>
<button class="btn btn-ghost btn-sm btn-square" @click="closeModal()">
<i class="fa-solid fa-times"></i>
</button>
</div>
<!-- Group Type Badge -->
<div class="mb-4">
<span class="badge badge-primary gap-1">
<i class="fa-solid fa-layer-group"></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-4 py-2 text-sm font-medium rounded-md transition-all duration-200"
:class="activeTab === 'basic' ? 'bg-primary 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-4 py-2 text-sm font-medium rounded-md transition-all duration-200"
:class="activeTab === 'members' ? 'bg-primary text-white shadow' : 'hover:bg-opacity-50'"
style="color: rgb(var(--color-text));" @click="activeTab = 'members'">
<i class="fa-solid fa-users mr-1"></i> Members
</button>
</div>
<!-- Form -->
<div class="space-y-4">
<!-- Tab: Basic Information (includes 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-primary"></i> Basic Information
</h4>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label">
<span class="label-text font-medium text-sm">Group Code <span class="text-error">*</span></span>
<span class="label-text-alt text-xs">Auto-generated from name</span>
</label>
<input type="text" class="input input-bordered font-mono uppercase w-full"
:class="errors.TestSiteCode && 'input-error'" x-model="form.TestSiteCode" placeholder="Auto-generated"
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">Group 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., Lipid Profile, CBC Panel" />
<label class="label" x-show="errors.TestSiteName">
<span class="label-text-alt text-error text-xs">Group 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="e.g., Comprehensive lipid analysis panel..." rows="2"></textarea>
</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-primary"></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: Group Members -->
<div x-show="activeTab === 'members'" 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="space-y-4">
<div class="flex items-center justify-between p-3 rounded-lg bg-primary bg-opacity-10"
style="border: 1px solid rgb(var(--color-primary));">
<div class="flex items-center gap-2">
<i class="fa-solid fa-users text-primary"></i>
<span class="font-medium text-sm">Group Members (<span
x-text="form.groupMembers?.length || 0"></span>)</span>
</div>
<button class="btn btn-sm btn-primary" @click="showMemberSelector = true">
<i class="fa-solid fa-plus mr-1"></i> Add Member
</button>
</div>
<!-- Member List -->
<template x-if="!form.groupMembers || form.groupMembers.length === 0">
<div class="text-center py-8 rounded-lg border border-dashed"
style="border-color: rgb(var(--color-border));">
<i class="fa-solid fa-inbox text-3xl opacity-40 mb-2"></i>
<p class="opacity-60">No members added yet</p>
<p class="text-xs opacity-50">Click "Add Member" to add tests to this group</p>
</div>
</template>
<template x-if="form.groupMembers && form.groupMembers.length > 0">
<div class="overflow-x-auto">
<table class="table table-xs">
<thead>
<tr class="bg-base-200">
<th>Code</th>
<th>Name</th>
<th>Type</th>
<th>Seq</th>
<th class="w-10">Actions</th>
</tr>
</thead>
<tbody>
<template x-for="(member, index) in form.groupMembers" :key="index">
<tr class="hover">
<td><code class="text-xs" x-text="member.TestSiteCode"></code></td>
<td x-text="member.TestSiteName"></td>
<td>
<span class="badge badge-xs" :class="{
'badge-info': member.MemberTypeCode === 'TEST',
'badge-success': member.MemberTypeCode === 'PARAM'
}" x-text="member.MemberTypeCode || 'TEST'"></span>
</td>
<td>
<input type="number" class="input input-xs w-16" x-model.number="member.SeqScr"
placeholder="0" />
</td>
<td>
<button class="btn btn-ghost btn-xs btn-square text-error" @click="removeMember(index)">
<i class="fa-solid fa-times"></i>
</button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
<!-- Quick Add Common Tests -->
<div class="p-3 rounded-lg border border-dashed" style="border-color: rgb(var(--color-border));">
<h4 class="font-medium text-sm mb-2 flex items-center gap-2">
<i class="fa-solid fa-bolt text-amber-500"></i>
Quick Add Common Tests
</h4>
<div class="flex flex-wrap gap-2">
<button class="btn btn-xs btn-outline" @click="addCommonMember('HBA1C', 'TEST')">HbA1c</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('GLU_R', 'TEST')">Glucose (Random)</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('GLU_F', 'TEST')">Glucose
(Fasting)</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('CHOL', 'TEST')">Cholesterol</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('TG', 'TEST')">Triglycerides</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('HDL', 'TEST')">HDL</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('LDL', 'TEST')">LDL</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('VLDL', 'TEST')">VLDL</button>
</div>
<div class="mt-2 flex flex-wrap gap-2">
<button class="btn btn-xs btn-outline" @click="addCommonMember('RBC', 'PARAM')">RBC</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('WBC', 'PARAM')">WBC</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('HGB', 'PARAM')">HGB</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('HCT', 'PARAM')">HCT</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('PLT', 'PARAM')">PLT</button>
<button class="btn btn-xs btn-outline" @click="addCommonMember('MCV', 'PARAM')">MCV</button>
</div>
</div>
</div>
</div>
</div>
<!-- Member Selector Modal (outside tabs) -->
<div x-show="showMemberSelector" x-cloak class="modal-overlay" x-transition>
<div class="modal-content p-6 max-w-3xl w-full max-h-[80vh] 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">
<div class="flex items-center justify-between mb-4">
<h4 class="font-bold text-lg">Select Test Members</h4>
<button class="btn btn-ghost btn-sm btn-square" @click="showMemberSelector = false">
<i class="fa-solid fa-times"></i>
</button>
</div>
<div class="mb-4">
<input type="text" class="input input-bordered w-full" placeholder="Search tests..." x-model="memberSearch" />
</div>
<div class="space-y-2 max-h-96 overflow-y-auto">
<template
x-for="test in availableTests.filter(t => t.TestSiteName?.toLowerCase().includes(memberSearch?.toLowerCase() || ''))"
:key="test.TestSiteID">
<label
class="flex items-center gap-3 p-3 rounded-lg border cursor-pointer hover:bg-opacity-50 transition-colors"
style="background: rgb(var(--color-bg-secondary)); border-color: rgb(var(--color-border));">
<input type="checkbox" class="checkbox checkbox-sm"
:checked="form.groupMembers?.some(m => m.TestSiteID === test.TestSiteID)"
@change="toggleMember(test)" />
<div class="flex-1">
<div class="font-medium" x-text="test.TestSiteName"></div>
<div class="text-xs flex items-center gap-2">
<code x-text="test.TestSiteCode"></code>
<span class="badge badge-xs" :class="{
'badge-info': test.TypeCode === 'TEST',
'badge-success': test.TypeCode === 'PARAM'
}" x-text="test.TypeCode"></span>
</div>
</div>
</label>
</template>
</div>
<div class="flex justify-end gap-2 mt-4 pt-4 border-t" style="border-color: rgb(var(--color-border));">
<button class="btn btn-ghost" @click="showMemberSelector = false">Cancel</button>
<button class="btn btn-primary" @click="showMemberSelector = false; $forceUpdate()">Done</button>
</div>
</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-primary 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 Group' : 'Create Group')"></span>
</button>
</div>
</div>
</div>