update: description in ics shared calendar, and realtive display depending on the votes

This commit is contained in:
Nathan FONTEYNE 2026-07-31 11:51:05 +02:00
parent 5759a1f248
commit 212016e88f
10 changed files with 92 additions and 25 deletions

View file

@ -189,6 +189,7 @@ async function loadSlotSettingsForm() {
form.marginMinutes.value = settings.marginMinutes;
form.concertStart.value = settings.concertStart;
form.concertEnd.value = settings.concertEnd;
form.rehearsalConfirmThreshold.value = settings.rehearsalConfirmThreshold;
}
async function onSaveSlotSettings(e) {
@ -205,6 +206,7 @@ async function onSaveSlotSettings(e) {
marginMinutes: parseInt(form.marginMinutes.value, 10),
concertStart: form.concertStart.value,
concertEnd: form.concertEnd.value,
rehearsalConfirmThreshold: parseInt(form.rehearsalConfirmThreshold.value, 10),
});
statusEl.textContent = 'Horaires enregistrés.';
} catch (err) {
@ -267,6 +269,14 @@ async function onSaveSlotSettings(e) {
Comme la date d'un concert n'a pas d'heure enregistrée, ces horaires sont utilisés pour
pré-remplir les liens « ajouter à mon agenda » sur <code>/calendar.html</code> et <code>/concerts.html</code>.
</p>
<label>Seuil de confirmation d'une répétition (votes « accepter »)
<input type="number" name="rehearsalConfirmThreshold" min="1" max="50" required>
</label>
<p class="note">
Une répétition proposée reste une « suggestion » tant que ce nombre de votes « accepter »
n'est pas atteint ; elle passe ensuite en répétition confirmée (dans l'application et dans le
flux ICS).
</p>
<p class="note" id="slot-settings-status"></p>
<button type="submit">Enregistrer</button>
</form>

View file

@ -104,11 +104,17 @@ function concertLinksHtml(concert) {
`;
}
function rehearsalStatusLabel(r) {
if (r.status === 'confirmed') return 'Répétition confirmée';
const accepted = r.votes.filter((v) => v.vote === 'accept').length;
return `Suggestion de répétition (${accepted}/${r.confirmThreshold} votes)`;
}
function rehearsalInfoHtml(r) {
const linkArgs = { uid: `rehearsal-${r.id}`, title: 'Répétition Octane', startISO: r.startsAt, endISO: r.endsAt, location: r.location };
return `
<div class="modal-section">
<div class="modal-section-title">Répétition proposée</div>
<div class="modal-section-title">${escapeHtml(rehearsalStatusLabel(r))}</div>
<p class="note" style="margin:0.25rem 0 0.5rem">${r.location ? escapeHtml(r.location) + ' · ' : ''}${formatTime(r.startsAt)} ${formatTime(r.endsAt)} · Proposée par ${escapeHtml(r.proposedByName)}</p>
${rehearsalAcceptedByHtml(r)}
<div class="rehearsal-actions">
@ -364,9 +370,9 @@ function openModal(date, slot, visible, concert, rehearsal) {
currentModalDate = { date, slot };
const proposeBtn = document.getElementById('modal-propose-rehearsal-btn');
const availabilitySection = document.getElementById('modal-availability-section');
const rehearsalAccepted = rehearsal && rehearsal.votes.some((v) => v.vote === 'accept');
const rehearsalConfirmed = rehearsal && rehearsal.status === 'confirmed';
if (slot) {
availabilitySection.style.display = rehearsalAccepted ? 'none' : '';
availabilitySection.style.display = rehearsalConfirmed ? 'none' : '';
proposeBtn.style.display = '';
const alreadyProposed = state.rehearsals.some((r) => isoDate(new Date(r.startsAt)) === isoDate(date));
proposeBtn.disabled = alreadyProposed;
@ -499,6 +505,7 @@ function rehearsalRowTemplate(r) {
return `
<div class="rehearsal-row" data-rehearsal-id="${r.id}">
<div>
<span class="vote-badge ${r.status === 'confirmed' ? 'accept' : ''}">${escapeHtml(rehearsalStatusLabel(r))}</span>
<div class="card-title">${formatDatetime(r.startsAt)} ${formatTime(r.endsAt)}</div>
<div class="card-subtitle">${r.location ? `${escapeHtml(r.location)} · ` : ''}Proposée par ${escapeHtml(r.proposedByName)}</div>
${rehearsalAcceptedByHtml(r)}
@ -752,7 +759,22 @@ async function onRemoveMyFeed(feedId) {
await loadSlots();
await loadLastChecked();
await loadMyFeeds();
jumpToRehearsalFromUrl();
} catch (err) {
showError(err.message);
}
})();
// Supports the "Votez ici" link embedded in the ICS feed (calendar.html?rehearsalId=N):
// scroll straight to that rehearsal's row in the list and highlight it briefly.
function jumpToRehearsalFromUrl() {
const rehearsalId = new URLSearchParams(window.location.search).get('rehearsalId');
if (!rehearsalId) return;
const row = document.querySelector(`.rehearsal-row[data-rehearsal-id="${rehearsalId}"]`);
if (!row) return;
const details = row.closest('details');
if (details) details.open = true;
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
row.classList.add('highlight');
setTimeout(() => row.classList.remove('highlight'), 3000);
}