clqms-fe1/src/routes/(app)/visits/VisitList.svelte
mahdahar ad1618efec Refactor patients and visits modules
- Extract patient utilities to src/lib/utils/patients.js
- Split patient page into modular components
- Create dedicated visits page and route
- Move visit-related modals to visits directory
- Add Sidebar navigation for visits
2026-02-25 07:12:24 +07:00

166 lines
5.4 KiB
Svelte

<script>
import { ChevronLeft, ChevronRight, Calendar } from 'lucide-svelte';
import VisitCard from './VisitCard.svelte';
import DataTable from '$lib/components/DataTable.svelte';
import { formatPatientName } from '$lib/utils/patients.js';
/**
* @typedef {Object} Visit
* @property {string} InternalPVID
* @property {string} [PVID]
* @property {string} [PatientID]
* @property {string} [PatientName]
* @property {string} [PVCreateDate]
* @property {string} [ADTCode]
* @property {string} [LocCode]
* @property {string} [EndDate]
*/
/** @type {{
* visits: Visit[],
* loading: boolean,
* viewMode: 'table' | 'cards',
* currentPage: number,
* totalPages: number,
* totalItems: number,
* perPage: number,
* onPageChange: (page: number) => void,
* onEditVisit: (visit: Visit) => void,
* onDeleteVisit: (visit: Visit) => void,
* onViewHistory: (visit: Visit) => void,
* onDischarge: (visit: Visit) => void
* }} */
let {
visits = [],
loading = false,
viewMode = 'table',
currentPage = 1,
totalPages = 1,
totalItems = 0,
perPage = 20,
onPageChange,
onEditVisit,
onDeleteVisit,
onViewHistory,
onDischarge
} = $props();
const columns = [
{ key: 'PVID', label: 'Visit ID', class: 'font-medium w-24' },
{ key: 'PatientID', label: 'Patient ID', class: 'w-24' },
{ key: 'PatientName', label: 'Patient Name', class: 'min-w-32' },
{ key: 'PVCreateDate', label: 'Date', class: 'w-28' },
{ key: 'ADTCode', label: 'Type', class: 'w-20' },
{ key: 'LocCode', label: 'Location', class: 'w-24' },
{ key: 'Status', label: 'Status', class: 'w-20 text-center' },
{ key: 'actions', label: 'Actions', class: 'w-28 text-center' },
];
function getStatusBadge(visit) {
if (!visit.EndDate && !visit.ArchivedDate) {
return '<span class="badge badge-sm badge-success">Active</span>';
}
return '<span class="badge badge-sm badge-ghost">Closed</span>';
}
</script>
<div class="bg-base-100 rounded-lg shadow border border-base-200 flex flex-col h-full overflow-hidden">
{#if !loading && visits.length === 0}
<!-- Empty State -->
<div class="flex-1 flex items-center justify-center bg-base-100">
<div class="text-center text-gray-500 p-6">
<Calendar class="w-12 h-12 mx-auto mb-2 opacity-50" />
<p class="text-sm">No visits found</p>
<p class="text-xs text-gray-400">Use search criteria to find visits</p>
</div>
</div>
{:else}
{#if viewMode === 'table'}
<!-- Table View -->
<div class="flex-1 overflow-auto">
<DataTable
{columns}
data={visits}
{loading}
emptyMessage="No visits found"
hover={true}
>
{#snippet cell({ column, row })}
{#if column.key === 'Status'}
{@html getStatusBadge(row)}
{:else if column.key === 'actions'}
<div class="flex justify-center gap-1">
<button
class="btn btn-xs btn-ghost"
title="Edit"
onclick={() => onEditVisit(row)}
>
<span class="text-xs">Edit</span>
</button>
<button
class="btn btn-xs btn-ghost"
title="History"
onclick={() => onViewHistory(row)}
>
<span class="text-xs">Hist</span>
</button>
</div>
{:else}
<span class="truncate block">{row[column.key] || '-'}</span>
{/if}
{/snippet}
</DataTable>
</div>
{:else}
<!-- Card View -->
<div class="flex-1 overflow-auto p-3">
{#if loading}
<div class="flex items-center justify-center py-12">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
{:else}
<div class="space-y-2">
{#each visits as visit (visit.InternalPVID)}
<VisitCard
{visit}
onEdit={() => onEditVisit(visit)}
onDelete={() => onDeleteVisit(visit)}
onViewHistory={() => onViewHistory(visit)}
onDischarge={() => onDischarge(visit)}
/>
{/each}
</div>
{/if}
</div>
{/if}
<!-- Pagination -->
{#if totalPages > 1}
<div class="flex items-center justify-between px-3 py-2 border-t border-base-200 bg-base-100">
<div class="text-xs text-gray-600">
{(currentPage - 1) * perPage + 1} - {Math.min(currentPage * perPage, totalItems)} of {totalItems}
</div>
<div class="flex gap-1">
<button
class="btn btn-xs btn-ghost"
onclick={() => onPageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ChevronLeft class="w-3 h-3" />
</button>
<span class="btn btn-xs btn-ghost no-animation text-xs">
{currentPage} / {totalPages}
</span>
<button
class="btn btn-xs btn-ghost"
onclick={() => onPageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<ChevronRight class="w-3 h-3" />
</button>
</div>
</div>
{/if}
{/if}
</div>