bmc_hub/static/js/case-slot-resize.js

84 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Resize case bookings through the existing versioned planner API. */
(() => {
'use strict';
const host = document.getElementById('casePlannerWeek');
if (!host) return;
let drag = null;
let saving = false;
const duration = button => (new Date(button.dataset.end) - new Date(button.dataset.start)) / 60000;
const endAt = (button, minutes) => new Date(new Date(button.dataset.start).getTime() + minutes * 60000);
function preview(button, minutes) {
button.closest('.case-planner-calendar-item').querySelector('strong').textContent =
`${new Date(button.dataset.start).toLocaleTimeString('da-DK', {hour:'2-digit', minute:'2-digit'})}–${endAt(button, minutes).toLocaleTimeString('da-DK', {hour:'2-digit', minute:'2-digit'})} · ${minutes} min`;
const card = button.closest('.v5-time-event');
if (card && button.dataset.pixelsPerStep) card.style.height = `${Math.max(24, card.offsetHeight + (minutes - Number(button.dataset.previewMinutes || duration(button))) * 1.6)}px`;
button.dataset.previewMinutes = String(minutes);
}
async function save(button, minutes) {
if (minutes === duration(button)) return;
saving = true;
button.disabled = true;
const payload = {version: Number(button.dataset.version), ends_at: endAt(button, minutes).toISOString()};
try {
for (;;) {
const response = await fetch(`/api/v1/planner/allocations/${Number(button.dataset.allocationId)}`, {
method: 'PATCH', credentials: 'include',
headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload),
});
const body = await response.json();
if (response.ok) break;
const message = typeof body.detail === 'string' ? body.detail : body.detail?.message;
if (response.status === 409 && body.detail?.conflict_status && !payload.confirm_conflicts &&
window.confirm(`${message}. Vil du gemme alligevel?`)) {
payload.confirm_conflicts = true;
continue;
}
throw new Error(message || 'Kunne ikke ændre varigheden');
}
window.showCaseFeedback('Aftalens varighed er gemt', 'success');
} catch (error) {
window.showCaseFeedback(error.message);
} finally {
await window.loadEmbeddedCasePlanner(true);
saving = false;
button.disabled = false;
}
}
host.addEventListener('click', event => {
if (event.target.closest('.case-slot-resize')) event.stopPropagation();
});
host.addEventListener('pointerdown', event => {
const button = event.target.closest('.case-slot-resize');
if (!button || saving || drag || (event.button !== 0 && event.pointerType !== 'touch')) return;
event.preventDefault();
event.stopPropagation();
drag = {button, y: event.clientY, original: duration(button), minutes: duration(button), pointer: event.pointerId};
button.setPointerCapture(event.pointerId);
button.classList.add('is-resizing');
});
host.addEventListener('pointermove', event => {
if (!drag || drag.pointer !== event.pointerId) return;
drag.minutes = Math.max(15, drag.original + Math.round((event.clientY - drag.y) / (Number(drag.button.dataset.pixelsPerStep) || 12)) * 15);
preview(drag.button, drag.minutes);
});
function finish(event, cancel) {
if (!drag || drag.pointer !== event.pointerId) return;
const current = drag;
drag = null;
current.button.classList.remove('is-resizing');
if (current.button.hasPointerCapture(event.pointerId)) current.button.releasePointerCapture(event.pointerId);
if (cancel) preview(current.button, current.original);
else void save(current.button, current.minutes);
}
host.addEventListener('pointerup', event => finish(event, false));
host.addEventListener('pointercancel', event => finish(event, true));
host.addEventListener('lostpointercapture', event => finish(event, true));
host.addEventListener('keydown', event => {
const button = event.target.closest('.case-slot-resize');
if (!button || saving || drag || !['ArrowUp', 'ArrowDown'].includes(event.key)) return;
event.preventDefault();
event.stopPropagation();
void save(button, Math.max(15, duration(button) + (event.key === 'ArrowDown' ? 15 : -15)));
});
})();