clqms-be/app/Views/v2/patient-view.php
2025-12-22 16:54:19 +07:00

306 lines
10 KiB
PHP

<?= $this->extend('layouts/v2') ?>
<?= $this->section('content') ?>
<div x-data="patientView">
<!-- Loading State -->
<template x-if="isLoading">
<div class="flex justify-center items-center py-24">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
</template>
<!-- Error State -->
<template x-if="error && !isLoading">
<div class="max-w-lg mx-auto">
<div class="alert alert-error">
<i data-lucide="alert-circle" class="w-5 h-5"></i>
<span x-text="error"></span>
</div>
<div class="text-center mt-4">
<a href="<?= site_url('v2/patients') ?>" class="btn btn-ghost">Back to Patient List</a>
</div>
</div>
</template>
<!-- Patient Detail -->
<template x-if="!isLoading && !error && patient">
<div>
<!-- Page Header -->
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6">
<div class="flex items-center gap-4">
<a href="<?= site_url('v2/patients') ?>" class="btn btn-ghost btn-sm btn-square">
<i data-lucide="arrow-left" class="w-5 h-5"></i>
</a>
<div>
<h2 class="text-2xl font-bold" x-text="fullName"></h2>
<div class="flex items-center gap-2 text-base-content/60">
<span class="badge badge-primary" x-text="patient.PatientID || 'No MRN'"></span>
<span x-text="patient.Gender || ''"></span>
<span>•</span>
<span x-text="patient.Age || ''"></span>
</div>
</div>
</div>
<div class="flex gap-2">
<a :href="'<?= site_url('v2/patients/edit/') ?>' + patient.InternalPID" class="btn btn-primary gap-2">
<i data-lucide="pencil" class="w-4 h-4"></i>
Edit
</a>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Left Column: Main Info -->
<div class="lg:col-span-2 space-y-6">
<!-- Personal Information -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="user" class="w-5 h-5"></i>
Personal Information
</h3>
<div class="grid grid-cols-2 md:grid-cols-3 gap-4 mt-4">
<div>
<div class="text-sm text-base-content/60">Full Name</div>
<div class="font-medium" x-text="fullName"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Gender</div>
<div class="font-medium" x-text="patient.Gender || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Birthdate</div>
<div class="font-medium" x-text="patient.BirthdateConversion || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Age</div>
<div class="font-medium" x-text="patient.Age || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Place of Birth</div>
<div class="font-medium" x-text="patient.PlaceOfBirth || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Marital Status</div>
<div class="font-medium" x-text="patient.MaritalStatus || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Religion</div>
<div class="font-medium" x-text="patient.Religion || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Ethnic</div>
<div class="font-medium" x-text="patient.Ethnic || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Citizenship</div>
<div class="font-medium" x-text="patient.Citizenship || '-'"></div>
</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="phone" class="w-5 h-5"></i>
Contact Information
</h3>
<div class="grid grid-cols-2 gap-4 mt-4">
<div>
<div class="text-sm text-base-content/60">Mobile Phone</div>
<div class="font-medium" x-text="patient.MobilePhone || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Phone</div>
<div class="font-medium" x-text="patient.Phone || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Email</div>
<div class="font-medium" x-text="patient.EmailAddress1 || '-'"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Alternate Email</div>
<div class="font-medium" x-text="patient.EmailAddress2 || '-'"></div>
</div>
</div>
</div>
</div>
<!-- Address -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="map-pin" class="w-5 h-5"></i>
Address
</h3>
<div class="mt-4">
<div class="font-medium" x-text="fullAddress || 'No address on file'"></div>
</div>
</div>
</div>
</div>
<!-- Right Column: Sidebar Info -->
<div class="space-y-6">
<!-- Identifier -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="id-card" class="w-5 h-5"></i>
Identifier
</h3>
<template x-if="patient.PatIdt">
<div class="mt-4">
<div class="text-sm text-base-content/60" x-text="patient.PatIdt.IdentifierType"></div>
<div class="font-mono font-medium text-lg" x-text="patient.PatIdt.Identifier"></div>
</div>
</template>
<template x-if="!patient.PatIdt">
<div class="text-base-content/50 mt-4">No identifier on file</div>
</template>
</div>
</div>
<!-- System Info -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="info" class="w-5 h-5"></i>
System Info
</h3>
<div class="space-y-3 mt-4">
<div>
<div class="text-sm text-base-content/60">Internal ID</div>
<div class="font-mono" x-text="patient.InternalPID"></div>
</div>
<div>
<div class="text-sm text-base-content/60">Created</div>
<div x-text="patient.CreateDate || '-'"></div>
</div>
<template x-if="patient.LinkTo">
<div>
<div class="text-sm text-base-content/60">Linked Patients</div>
<template x-for="link in patient.LinkTo" :key="link.InternalPID">
<a
:href="'<?= site_url('v2/patients/') ?>' + link.InternalPID"
class="link link-primary"
x-text="link.PatientID"
></a>
</template>
</div>
</template>
</div>
</div>
</div>
<!-- Comments -->
<template x-if="patient.PatCom">
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="card-title text-lg">
<i data-lucide="message-square" class="w-5 h-5"></i>
Comments
</h3>
<div class="mt-4 whitespace-pre-wrap" x-text="patient.PatCom"></div>
</div>
</div>
</template>
</div>
</div>
</div>
</template>
</div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('patientView', () => ({
patient: null,
isLoading: true,
error: null,
async init() {
await this.loadPatient();
},
get fullName() {
if (!this.patient) return '';
return [
this.patient.Prefix,
this.patient.NameFirst,
this.patient.NameMiddle,
this.patient.NameLast,
this.patient.Suffix
].filter(Boolean).join(' ');
},
get fullAddress() {
if (!this.patient) return '';
return [
this.patient.Street_1,
this.patient.Street_2,
this.patient.Street_3,
this.patient.City,
this.patient.Province,
this.patient.ZIP,
this.patient.Country
].filter(Boolean).join(', ');
},
async loadPatient() {
const patientId = <?= json_encode($patientId ?? null) ?>;
if (!patientId) {
this.error = 'Patient ID not provided';
this.isLoading = false;
return;
}
try {
const response = await fetch('<?= site_url('api/patient/') ?>' + patientId, {
credentials: 'include'
});
const data = await response.json();
if (data.status === 'success') {
this.patient = data.data;
} else {
this.error = data.message || 'Failed to load patient';
}
} catch (err) {
this.error = 'Connection error: ' + err.message;
} finally {
this.isLoading = false;
// Re-init icons when loading state changes
setTimeout(() => {
if(window.lucide) window.lucide.createIcons();
}, 50);
}
}
}));
});
</script>
<?= $this->endSection() ?>