Spaces:
Running
Running
Upload 2 files
Browse files- templates/dashboard.html +55 -3
templates/dashboard.html
CHANGED
|
@@ -185,6 +185,9 @@
|
|
| 185 |
|
| 186 |
/* Duration styling */
|
| 187 |
.duration-text { font-size: 0.85rem; color: var(--text-medium); font-style: italic; }
|
|
|
|
|
|
|
|
|
|
| 188 |
</style>
|
| 189 |
</head>
|
| 190 |
<body>
|
|
@@ -267,6 +270,7 @@
|
|
| 267 |
<div id="column-visibility-menu" class="column-menu" style="display: none;">
|
| 268 |
<label><input type="checkbox" data-column="sl" checked> Sl</label>
|
| 269 |
<label><input type="checkbox" data-column="Name" checked> Patient Name</label>
|
|
|
|
| 270 |
<label><input type="checkbox" data-column="start_time" checked> Appointment Date & Time</label>
|
| 271 |
<label><input type="checkbox" data-column="notes" checked> Notes</label>
|
| 272 |
<label><input type="checkbox" data-column="reason" checked> Reason</label>
|
|
@@ -288,6 +292,7 @@
|
|
| 288 |
<tr>
|
| 289 |
<th class="sortable resizable" data-column="sl" style="width: 60px;">Sl <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 290 |
<th class="sortable resizable" data-column="Name" style="width: 150px;">Patient Name <span class="sort-icon"></span><div class="resizer"></div></th>
|
|
|
|
| 291 |
<th class="sortable resizable datetime-column" data-column="start_time">Appointment Date & Time <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 292 |
<th class="sortable resizable" data-column="notes" style="width: 200px;">Notes <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 293 |
<th class="sortable resizable" data-column="reason" style="width: 150px;">Reason <span class="sort-icon"></span><div class="resizer"></div></th>
|
|
@@ -558,6 +563,21 @@
|
|
| 558 |
return `${startFormatted} - ${endTimeFormatted}<br><span class="duration-text">${duration}</span>`;
|
| 559 |
}
|
| 560 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
// --- Inline Editing Functions ---
|
| 562 |
function makeEditable(element, recordId, fieldName, currentValue, inputType = 'text') {
|
| 563 |
if (element.classList.contains('editing')) return;
|
|
@@ -583,15 +603,43 @@
|
|
| 583 |
} else {
|
| 584 |
input = document.createElement('input');
|
| 585 |
input.type = inputType;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
}
|
| 587 |
|
| 588 |
-
input.value = currentValue || '';
|
| 589 |
element.innerHTML = '';
|
| 590 |
element.appendChild(input);
|
| 591 |
input.focus();
|
| 592 |
|
| 593 |
const saveEdit = async () => {
|
| 594 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
if (newValue === currentValue) {
|
| 596 |
cancelEdit();
|
| 597 |
return;
|
|
@@ -620,6 +668,9 @@
|
|
| 620 |
if (fieldName === 'start_time' || fieldName === 'end_time') {
|
| 621 |
element.innerHTML = formatAppointmentDateTime(appointment.start_time, appointment.end_time);
|
| 622 |
element.dataset.currentValue = newValue;
|
|
|
|
|
|
|
|
|
|
| 623 |
} else {
|
| 624 |
element.textContent = newValue;
|
| 625 |
}
|
|
@@ -1305,7 +1356,7 @@
|
|
| 1305 |
valA = a[appointmentsSort.column] || '';
|
| 1306 |
valB = b[appointmentsSort.column] || '';
|
| 1307 |
|
| 1308 |
-
if (['start_time', 'Created', 'Last Modified'].includes(appointmentsSort.column)) {
|
| 1309 |
valA = valA ? new Date(valA).getTime() : 0;
|
| 1310 |
valB = valB ? new Date(valB).getTime() : 0;
|
| 1311 |
}
|
|
@@ -1344,6 +1395,7 @@
|
|
| 1344 |
row.innerHTML = `
|
| 1345 |
<td>${index + 1}</td>
|
| 1346 |
<td class="editable" data-field-name="Name" data-input-type="text"><div class="edit-indicator"></div>${a.Name || ''}</td>
|
|
|
|
| 1347 |
<td class="editable" data-field-name="start_time" data-input-type="datetime-local" data-current-value="${a.start_time || ''}"><div class="edit-indicator"></div>${formatAppointmentDateTime(a.start_time, a.end_time)}</td>
|
| 1348 |
<td class="editable" data-field-name="notes" data-input-type="textarea"><div class="edit-indicator"></div>${a.notes || ''}</td>
|
| 1349 |
<td class="editable" data-field-name="reason" data-input-type="textarea"><div class="edit-indicator"></div>${a.reason || ''}</td>
|
|
|
|
| 185 |
|
| 186 |
/* Duration styling */
|
| 187 |
.duration-text { font-size: 0.85rem; color: var(--text-medium); font-style: italic; }
|
| 188 |
+
|
| 189 |
+
/* DOB column styling */
|
| 190 |
+
.dob-column { min-width: 140px; }
|
| 191 |
</style>
|
| 192 |
</head>
|
| 193 |
<body>
|
|
|
|
| 270 |
<div id="column-visibility-menu" class="column-menu" style="display: none;">
|
| 271 |
<label><input type="checkbox" data-column="sl" checked> Sl</label>
|
| 272 |
<label><input type="checkbox" data-column="Name" checked> Patient Name</label>
|
| 273 |
+
<label><input type="checkbox" data-column="dob" checked> Date of Birth</label>
|
| 274 |
<label><input type="checkbox" data-column="start_time" checked> Appointment Date & Time</label>
|
| 275 |
<label><input type="checkbox" data-column="notes" checked> Notes</label>
|
| 276 |
<label><input type="checkbox" data-column="reason" checked> Reason</label>
|
|
|
|
| 292 |
<tr>
|
| 293 |
<th class="sortable resizable" data-column="sl" style="width: 60px;">Sl <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 294 |
<th class="sortable resizable" data-column="Name" style="width: 150px;">Patient Name <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 295 |
+
<th class="sortable resizable dob-column" data-column="dob" style="width: 140px;">Date of Birth <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 296 |
<th class="sortable resizable datetime-column" data-column="start_time">Appointment Date & Time <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 297 |
<th class="sortable resizable" data-column="notes" style="width: 200px;">Notes <span class="sort-icon"></span><div class="resizer"></div></th>
|
| 298 |
<th class="sortable resizable" data-column="reason" style="width: 150px;">Reason <span class="sort-icon"></span><div class="resizer"></div></th>
|
|
|
|
| 563 |
return `${startFormatted} - ${endTimeFormatted}<br><span class="duration-text">${duration}</span>`;
|
| 564 |
}
|
| 565 |
|
| 566 |
+
function formatDateOfBirth(dob) {
|
| 567 |
+
if (!dob) return '';
|
| 568 |
+
try {
|
| 569 |
+
const date = new Date(dob);
|
| 570 |
+
if (isNaN(date.getTime())) return '';
|
| 571 |
+
return date.toLocaleDateString('en-US', {
|
| 572 |
+
year: 'numeric',
|
| 573 |
+
month: 'short',
|
| 574 |
+
day: 'numeric'
|
| 575 |
+
});
|
| 576 |
+
} catch (error) {
|
| 577 |
+
return '';
|
| 578 |
+
}
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
// --- Inline Editing Functions ---
|
| 582 |
function makeEditable(element, recordId, fieldName, currentValue, inputType = 'text') {
|
| 583 |
if (element.classList.contains('editing')) return;
|
|
|
|
| 603 |
} else {
|
| 604 |
input = document.createElement('input');
|
| 605 |
input.type = inputType;
|
| 606 |
+
|
| 607 |
+
// Handle date input special formatting
|
| 608 |
+
if (inputType === 'date' && currentValue) {
|
| 609 |
+
try {
|
| 610 |
+
const date = new Date(currentValue);
|
| 611 |
+
if (!isNaN(date.getTime())) {
|
| 612 |
+
input.value = date.toISOString().split('T')[0];
|
| 613 |
+
}
|
| 614 |
+
} catch (error) {
|
| 615 |
+
console.error('Date parsing error:', error);
|
| 616 |
+
}
|
| 617 |
+
}
|
| 618 |
+
}
|
| 619 |
+
|
| 620 |
+
if (inputType !== 'date') {
|
| 621 |
+
input.value = currentValue || '';
|
| 622 |
}
|
| 623 |
|
|
|
|
| 624 |
element.innerHTML = '';
|
| 625 |
element.appendChild(input);
|
| 626 |
input.focus();
|
| 627 |
|
| 628 |
const saveEdit = async () => {
|
| 629 |
+
let newValue = input.value.trim();
|
| 630 |
+
|
| 631 |
+
// Special handling for date fields
|
| 632 |
+
if (inputType === 'date' && newValue) {
|
| 633 |
+
try {
|
| 634 |
+
const dateObj = new Date(newValue);
|
| 635 |
+
if (!isNaN(dateObj.getTime())) {
|
| 636 |
+
newValue = dateObj.toISOString().split('T')[0];
|
| 637 |
+
}
|
| 638 |
+
} catch (error) {
|
| 639 |
+
console.error('Date formatting error:', error);
|
| 640 |
+
}
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
if (newValue === currentValue) {
|
| 644 |
cancelEdit();
|
| 645 |
return;
|
|
|
|
| 668 |
if (fieldName === 'start_time' || fieldName === 'end_time') {
|
| 669 |
element.innerHTML = formatAppointmentDateTime(appointment.start_time, appointment.end_time);
|
| 670 |
element.dataset.currentValue = newValue;
|
| 671 |
+
} else if (fieldName === 'dob') {
|
| 672 |
+
element.textContent = formatDateOfBirth(newValue);
|
| 673 |
+
element.dataset.currentValue = newValue;
|
| 674 |
} else {
|
| 675 |
element.textContent = newValue;
|
| 676 |
}
|
|
|
|
| 1356 |
valA = a[appointmentsSort.column] || '';
|
| 1357 |
valB = b[appointmentsSort.column] || '';
|
| 1358 |
|
| 1359 |
+
if (['start_time', 'Created', 'Last Modified', 'dob'].includes(appointmentsSort.column)) {
|
| 1360 |
valA = valA ? new Date(valA).getTime() : 0;
|
| 1361 |
valB = valB ? new Date(valB).getTime() : 0;
|
| 1362 |
}
|
|
|
|
| 1395 |
row.innerHTML = `
|
| 1396 |
<td>${index + 1}</td>
|
| 1397 |
<td class="editable" data-field-name="Name" data-input-type="text"><div class="edit-indicator"></div>${a.Name || ''}</td>
|
| 1398 |
+
<td class="editable" data-field-name="dob" data-input-type="date" data-current-value="${a.dob || ''}"><div class="edit-indicator"></div>${formatDateOfBirth(a.dob)}</td>
|
| 1399 |
<td class="editable" data-field-name="start_time" data-input-type="datetime-local" data-current-value="${a.start_time || ''}"><div class="edit-indicator"></div>${formatAppointmentDateTime(a.start_time, a.end_time)}</td>
|
| 1400 |
<td class="editable" data-field-name="notes" data-input-type="textarea"><div class="edit-indicator"></div>${a.notes || ''}</td>
|
| 1401 |
<td class="editable" data-field-name="reason" data-input-type="textarea"><div class="edit-indicator"></div>${a.reason || ''}</td>
|