thors1's picture
Initial DeepSite commit
3edd5cd verified
// Sample patient data
let patients = [
{
id: 1,
name: "Emma Thompson",
avatar: "http://static.photos/people/200x200/1",
tags: ["Critical", "VIP"],
created: "2024-01-15",
approved: "2024-01-16",
expires: "2025-01-16",
notes: "Severe allergy to penicillin. Requires constant monitoring.",
phone: "+1 (555) 123-4567",
email: "emma.t@email.com",
status: "approved"
},
{
id: 2,
name: "James Wilson",
avatar: "http://static.photos/people/200x200/2",
tags: ["New Patient"],
created: "2024-03-20",
approved: "2024-03-21",
expires: "2024-09-21",
notes: "Post-surgery recovery. Physical therapy scheduled.",
phone: "+1 (555) 234-5678",
email: "j.wilson@email.com",
status: "expired"
},
{
id: 3,
name: "Sarah Chen",
avatar: "http://static.photos/people/200x200/3",
tags: ["Follow-up"],
created: "2024-02-10",
approved: "2024-02-11",
expires: "2025-02-11",
notes: "Routine diabetes check. Blood sugar levels stable.",
phone: "+1 (555) 345-6789",
email: "sarah.chen@email.com",
status: "approved"
},
{
id: 4,
name: "Michael Brown",
avatar: "http://static.photos/people/200x200/4",
tags: ["Urgent"],
created: "2024-04-05",
approved: "2024-04-05",
expires: "2024-10-05",
notes: "Cardiac monitoring required. High blood pressure history.",
phone: "+1 (555) 456-7890",
email: "mbrown@email.com",
status: "expired"
},
{
id: 5,
name: "Lisa Anderson",
avatar: "http://static.photos/people/200x200/5",
tags: ["Regular"],
created: "2024-01-28",
approved: "2024-01-29",
expires: "2025-01-29",
notes: "Annual physical completed. All vitals normal.",
phone: "+1 (555) 567-8901",
email: "lisa.a@email.com",
status: "approved"
},
{
id: 6,
name: "David Martinez",
avatar: "http://static.photos/people/200x200/6",
tags: ["New Patient", "Insurance Pending"],
created: "2024-05-12",
approved: "2024-05-13",
expires: "2025-05-13",
notes: "Initial consultation completed. Awaiting test results.",
phone: "+1 (555) 678-9012",
email: "d.martinez@email.com",
status: "approved"
},
{
id: 7,
name: "Jennifer Taylor",
avatar: "http://static.photos/people/200x200/7",
tags: ["Critical"],
created: "2023-11-15",
approved: "2023-11-16",
expires: "2024-05-16",
notes: "Oncology patient. Chemotherapy cycle 4 of 6.",
phone: "+1 (555) 789-0123",
email: "jtaylor@email.com",
status: "expired"
},
{
id: 8,
name: "Robert Johnson",
avatar: "http://static.photos/people/200x200/8",
tags: ["Senior"],
created: "2024-03-01",
approved: "2024-03-02",
expires: "2025-03-02",
notes: "Arthritis management. Mobility assistance required.",
phone: "+1 (555) 890-1234",
email: "r.johnson@email.com",
status: "approved"
},
{
id: 9,
name: "Amanda White",
avatar: "http://static.photos/people/200x200/9",
tags: ["Pediatric"],
created: "2024-04-20",
approved: "2024-04-21",
expires: "2025-04-21",
notes: "Vaccination schedule up to date. Growth chart normal.",
phone: "+1 (555) 901-2345",
email: "amanda.w@email.com",
status: "approved"
},
{
id: 10,
name: "Christopher Lee",
avatar: "http://static.photos/people/200x200/10",
tags: ["Follow-up", "Physical Therapy"],
created: "2024-02-28",
approved: "2024-02-29",
expires: "2024-08-29",
notes: "Knee replacement recovery. PT exercises 3x weekly.",
phone: "+1 (555) 012-3456",
email: "chris.lee@email.com",
status: "expired"
},
{
id: 11,
name: "Maria Garcia",
avatar: "http://static.photos/people/200x200/11",
tags: ["Prenatal"],
created: "2024-01-10",
approved: "2024-01-11",
expires: "2024-10-11",
notes: "Second trimester. Ultrasound scheduled next week.",
phone: "+1 (555) 111-2222",
email: "maria.g@email.com",
status: "approved"
},
{
id: 12,
name: "Kevin Davis",
avatar: "http://static.photos/people/200x200/12",
tags: ["Emergency"],
created: "2024-05-15",
approved: "2024-05-15",
expires: "2024-11-15",
notes: "Fractured wrist. Cast applied, follow-up in 2 weeks.",
phone: "+1 (555) 222-3333",
email: "kevin.d@email.com",
status: "expired"
}
];
// State management
let currentPage = 1;
let itemsPerPage = 10;
let sortColumn = null;
let sortDirection = 'asc';
let filteredData = [...patients];
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderTable();
lucide.createIcons();
});
// Sidebar toggle for mobile
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('mobile-overlay');
const isClosed = sidebar.classList.contains('-translate-x-full');
if (isClosed) {
sidebar.classList.remove('-translate-x-full');
overlay.classList.remove('hidden');
} else {
sidebar.classList.add('-translate-x-full');
overlay.classList.add('hidden');
}
}
// Sorting functionality
function sortTable(column) {
// Reset all sort icons
document.querySelectorAll('.sort-icon').forEach(icon => {
icon.setAttribute('data-lucide', 'chevrons-up-down');
icon.classList.remove('sort-asc', 'sort-desc');
});
// Toggle direction if same column
if (sortColumn === column) {
sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
} else {
sortColumn = column;
sortDirection = 'asc';
}
// Update icon for current column
const currentIcon = document.getElementById(`sort-${column}`);
if (currentIcon) {
currentIcon.setAttribute('data-lucide', sortDirection === 'asc' ? 'chevron-up' : 'chevron-down');
currentIcon.classList.add(sortDirection === 'asc' ? 'sort-asc' : 'sort-desc');
}
// Sort data
filteredData.sort((a, b) => {
let valA = a[column];
let valB = b[column];
if (column === 'name') {
valA = a.name.toLowerCase();
valB = b.name.toLowerCase();
}
if (valA < valB) return sortDirection === 'asc' ? -1 : 1;
if (valA > valB) return sortDirection === 'asc' ? 1 : -1;
return 0;
});
lucide.createIcons();
renderTable();
}
// Filter functionality
function filterTable() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
const statusFilter = document.getElementById('statusFilter').value;
filteredData = patients.filter(patient => {
const matchesSearch = patient.name.toLowerCase().includes(searchTerm) ||
patient.email.toLowerCase().includes(searchTerm) ||
patient.phone.includes(searchTerm);
const matchesStatus = statusFilter === 'all' || patient.status === statusFilter;
return matchesSearch && matchesStatus;
});
currentPage = 1;
renderTable();
}
// Render table
function renderTable() {
const tbody = document.getElementById('tableBody');
const start = (currentPage - 1) * itemsPerPage;
const end = start + itemsPerPage;
const paginatedData = filteredData.slice(start, end);
tbody.innerHTML = paginatedData.map(patient => {
const statusColors = {
approved: 'bg-emerald-100 text-emerald-800 border-emerald-200',
expired: 'bg-rose-100 text-rose-800 border-rose-200'
};
const statusIcons = {
approved: 'check-circle',
expired: 'x-circle'
};
const tagColors = {
'Critical': 'bg-red-100 text-red-700',
'VIP': 'bg-purple-100 text-purple-700',
'New Patient': 'bg-blue-100 text-blue-700',
'Follow-up': 'bg-amber-100 text-amber-700',
'Urgent': 'bg-orange-100 text-orange-700',
'Regular': 'bg-gray-100 text-gray-700',
'Insurance Pending': 'bg-yellow-100 text-yellow-700',
'Senior': 'bg-indigo-100 text-indigo-700',
'Pediatric': 'bg-pink-100 text-pink-700',
'Prenatal': 'bg-teal-100 text-teal-700',
'Emergency': 'bg-red-100 text-red-700',
'Physical Therapy': 'bg-cyan-100 text-cyan-700'
};
return `
<tr class="table-row transition-colors border-b border-gray-100 last:border-0">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<img class="h-10 w-10 rounded-full object-cover border-2 border-gray-200" src="${patient.avatar}" alt="">
<div class="ml-4">
<div class="text-sm font-semibold text-gray-900">${patient.name}</div>
<div class="text-xs text-gray-500">ID: #${String(patient.id).padStart(4, '0')}</div>
</div>
</div>
</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
${patient.tags.map(tag => `
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${tagColors[tag] || 'bg-gray-100 text-gray-700'}">
${tag}
</span>
`).join('')}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
${formatDate(patient.created)}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
${formatDate(patient.approved)}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
${formatDate(patient.expires)}
</td>
<td class="px-6 py-4">
<div class="text-sm text-gray-600 max-w-xs truncate" title="${patient.notes}">
${patient.notes}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">${patient.phone}</div>
<div class="text-sm text-gray-500">${patient.email}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium border ${statusColors[patient.status]} capitalize">
<i data-lucide="${statusIcons[patient.status]}" class="w-3 h-3 mr-1"></i>
${patient.status}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div class="flex items-center justify-end space-x-2">
<button onclick="viewPatient(${patient.id})" class="text-blue-600 hover:text-blue-900 p-1 hover:bg-blue-50 rounded transition-colors" title="View">
<i data-lucide="eye" class="w-4 h-4"></i>
</button>
<button onclick="editPatient(${patient.id})" class="text-gray-600 hover:text-gray-900 p-1 hover:bg-gray-100 rounded transition-colors" title="Edit">
<i data-lucide="edit-2" class="w-4 h-4"></i>
</button>
<button onclick="deletePatient(${patient.id})" class="text-red-600 hover:text-red-900 p-1 hover:bg-red-50 rounded transition-colors" title="Delete">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
</td>
</tr>
`;
}).join('');
// Update pagination info
document.getElementById('showingStart').textContent = filteredData.length > 0 ? start + 1 : 0;
document.getElementById('showingEnd').textContent = Math.min(end, filteredData.length);
document.getElementById('totalItems').textContent = filteredData.length;
// Update pagination buttons
document.getElementById('prevBtn').disabled = currentPage === 1;
document.getElementById('nextBtn').disabled = end >= filteredData.length;
renderPaginationNumbers();
lucide.createIcons();
}
function renderPaginationNumbers() {
const totalPages = Math.ceil(filteredData.length / itemsPerPage);
const container = document.getElementById('paginationNumbers');
let html = '';
for (let i = 1; i <= totalPages; i++) {
if (i === 1 || i === totalPages || (i >= currentPage - 1 && i <= currentPage + 1)) {
html += `
<button onclick="goToPage(${i})" class="w-8 h-8 rounded-md text-sm font-medium transition-colors ${currentPage === i ? 'bg-blue-600 text-white' : 'text-gray-700 hover:bg-gray-100 border border-gray-300'}">
${i}
</button>
`;
} else if (i === currentPage - 2 || i === currentPage + 2) {
html += `<span class="px-2 text-gray-400">...</span>`;
}
}
container.innerHTML = html;
}
function changePage(direction) {
const totalPages = Math.ceil(filteredData.length / itemsPerPage);
const newPage = currentPage + direction;
if (newPage >= 1 && newPage <= totalPages) {
currentPage = newPage;
renderTable();
}
}
function goToPage(page) {
currentPage = page;
renderTable();
}
function formatDate(dateString) {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
return new Date(dateString).toLocaleDateString('en-US', options);
}
// Action handlers
function viewPatient(id) {
showToast('Viewing patient details...');
}
function editPatient(id) {
showToast('Opening edit mode...');
}
function deletePatient(id) {
if (confirm('Are you sure you want to delete this patient record?')) {
patients = patients.filter(p => p.id !== id);
filterTable();
showToast('Patient deleted successfully');
}
}
function openAddModal() {
document.getElementById('addModal').classList.remove('hidden');
}
function closeAddModal() {
document.getElementById('addModal').classList.add('hidden');
}
function savePatient() {
closeAddModal();
showToast('New patient added successfully');
}
function exportData() {
showToast('Exporting patient data to CSV...');
}
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('translate-y-20', 'opacity-0');
setTimeout(() => {
toast.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
// Close modal on escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeAddModal();
}
});