Files
barometer-server/website/js/pages/schedule.js
Tom Hempel c5c61f22ad
Some checks failed
PHPUnit / test (push) Has been cancelled
started work on scheduling
2026-07-01 10:07:20 +02:00

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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}