109 lines
4.0 KiB
JavaScript
109 lines
4.0 KiB
JavaScript
import { apiGet } from '../api.js';
|
|
import { pageHeaderHTML, showToast } from '../app.js';
|
|
|
|
export async function schedulePage() {
|
|
const app = document.getElementById('app');
|
|
app.innerHTML = `
|
|
${pageHeaderHTML('Schedule', '', {
|
|
subtitle: 'Upcoming session appointments set by counselors in the app',
|
|
})}
|
|
<div id="scheduleContent"><div class="spinner"></div></div>
|
|
`;
|
|
|
|
try {
|
|
const data = await apiGet('schedule');
|
|
renderSchedule(data.entries || [], data.byDate || {});
|
|
} catch (e) {
|
|
showToast(e.message, 'error');
|
|
document.getElementById('scheduleContent').innerHTML =
|
|
`<p class="error-text">${esc(e.message)}</p>`;
|
|
}
|
|
}
|
|
|
|
function renderSchedule(entries, byDate) {
|
|
const el = document.getElementById('scheduleContent');
|
|
const todayStart = new Date();
|
|
todayStart.setHours(0, 0, 0, 0);
|
|
const futureEntries = entries.filter(e => parseDdMmYyyy(e.scheduledDate) > todayStart.getTime());
|
|
const futureByDate = {};
|
|
futureEntries.forEach(e => {
|
|
(futureByDate[e.scheduledDate] ||= []).push(e);
|
|
});
|
|
|
|
if (!futureEntries.length) {
|
|
el.innerHTML = `
|
|
<div class="card">
|
|
<p class="scoring-empty-hint" style="margin:0">No upcoming session appointments.</p>
|
|
</div>`;
|
|
return;
|
|
}
|
|
|
|
const dates = Object.keys(futureByDate).sort((a, b) => parseDdMmYyyy(a) - parseDdMmYyyy(b));
|
|
|
|
el.innerHTML = `
|
|
<div class="card" style="margin-bottom:16px">
|
|
<p class="data-toolbar-hint" style="margin:0">
|
|
${futureEntries.length} scheduled client${futureEntries.length === 1 ? '' : 's'} across ${dates.length} date${dates.length === 1 ? '' : 's'}.
|
|
Counselors set dates in the mobile app.
|
|
</p>
|
|
</div>
|
|
${dates.map(date => dateGroupHTML(date, futureByDate[date] || [])).join('')}
|
|
`;
|
|
}
|
|
|
|
function dateGroupHTML(scheduledDate, rows) {
|
|
const sorted = [...rows].sort((a, b) =>
|
|
String(a.coachUsername || '').localeCompare(String(b.coachUsername || ''))
|
|
|| String(a.clientCode).localeCompare(String(b.clientCode)),
|
|
);
|
|
return `
|
|
<div class="card scoring-profiles-card" style="margin-bottom:16px">
|
|
<div class="scoring-profile-header">
|
|
<div>
|
|
<h3 class="scoring-section-title">${esc(scheduledDate)}</h3>
|
|
<p class="scoring-section-desc">
|
|
${sorted.length} client${sorted.length === 1 ? '' : 's'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Client</th>
|
|
<th>Cycle</th>
|
|
<th>Session</th>
|
|
<th>Counselor</th>
|
|
<th>Supervisor</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${sorted.map(row => `
|
|
<tr>
|
|
<td>${esc(row.clientCode)}</td>
|
|
<td>${esc(String(row.programCycleNumber))}</td>
|
|
<td>${esc(String(row.programSessionNumber))}</td>
|
|
<td>${esc(row.coachUsername || row.coachID || '—')}</td>
|
|
<td>${esc(row.supervisorUsername || '—')}</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
function parseDdMmYyyy(date) {
|
|
const m = /^(\d{2})-(\d{2})-(\d{4})$/.exec(String(date));
|
|
if (!m) return 0;
|
|
return new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1])).getTime();
|
|
}
|
|
|
|
function esc(value) {
|
|
return String(value ?? '')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|