Files
site/apps/web/public/wesp/js/modules/daily-plan-skip-duration.js
T

125 lines
5.3 KiB
JavaScript

/**
* Диалог «На какой срок…» для skip/replace/adjustment плана на день.
*/
(function (global) {
function escapeHtml(value) {
return String(value ?? "")
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function weekEndIso(planDate) {
const parts = String(planDate || "").split("-");
const d = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
const jsDay = d.getDay();
const pyWeekday = jsDay === 0 ? 6 : jsDay - 1;
d.setDate(d.getDate() + (6 - pyWeekday));
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
function todayIso() {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}
function dismissOverlays(host, keepSelectors) {
host.querySelectorAll(".zt-k-hub-plan-overlay").forEach((el) => {
if (keepSelectors.some((sel) => el.matches(sel))) return;
el.remove();
});
}
/**
* @param {object} options
* @param {string} options.planDate YYYY-MM-DD
* @param {string} [options.title]
* @param {HTMLElement} [options.overlayHost] куда монтировать overlay
* @param {string[]} [options.keepOverlays] селекторы overlay, которые не закрывать
* @param {function} [options.onError]
* @param {function} [options.beforeAppend] (overlay) => void — dismiss other overlays
*/
function promptSkipDuration(options) {
const planDate = options.planDate || todayIso();
const title = options.title || "На какой срок исключить?";
const onError = options.onError || function () {};
const host = options.overlayHost || document.body;
const keepOverlays = options.keepOverlays || [];
return new Promise((resolve) => {
const weekEnd = weekEndIso(planDate);
const overlay = document.createElement("div");
overlay.className = "zt-k-hub-plan-overlay zt-k-hub-plan-skip-duration";
overlay.innerHTML =
'<div class="zt-k-hub-plan-subdialog zt-k-hub-plan-skip-duration__dialog" role="dialog" aria-modal="true">' +
`<p class="zt-k-hub-plan-subdialog__title zt-k-hub-plan-skip-duration__title">${escapeHtml(title)}</p>` +
'<div class="zt-k-hub-plan-skip-duration__options">' +
'<label class="zt-k-hub-plan-skip-duration__option">' +
'<input type="radio" name="skip-duration" value="today" checked> Только сегодня</label>' +
'<label class="zt-k-hub-plan-skip-duration__option">' +
`<input type="radio" name="skip-duration" value="week"> До конца недели (до ${escapeHtml(weekEnd)})</label>` +
'<label class="zt-k-hub-plan-skip-duration__option zt-k-hub-plan-skip-duration__option--date">' +
'<input type="radio" name="skip-duration" value="date"> До даты ' +
`<input type="date" class="form-control" data-skip-until-date min="${escapeHtml(planDate)}" value="${escapeHtml(planDate)}">` +
"</label></div>" +
'<div class="zt-k-hub-plan-subdialog__actions zt-k-hub-plan-skip-duration__actions">' +
'<button type="button" class="btn btn-secondary" data-skip-duration-cancel>Отмена</button>' +
'<button type="button" class="btn btn-primary" data-skip-duration-confirm>Применить</button>' +
"</div></div>";
if (typeof options.beforeAppend === "function") {
options.beforeAppend(overlay);
} else if (keepOverlays.length) {
dismissOverlays(host, keepOverlays);
}
overlay.setAttribute("data-daily-plan-overlay", "1");
host.appendChild(overlay);
const dateInput = overlay.querySelector("[data-skip-until-date]");
const finish = (value) => {
if (overlay.isConnected) overlay.remove();
resolve(value);
};
overlay.addEventListener("click", (event) => {
if (event.target === overlay) finish(null);
});
overlay.querySelector("[data-skip-duration-cancel]")?.addEventListener("click", () => finish(null));
overlay.querySelector("[data-skip-duration-confirm]")?.addEventListener("click", () => {
const selected = overlay.querySelector('input[name="skip-duration"]:checked');
const mode = selected?.value || "today";
if (mode === "week") {
finish({ duration: "week" });
return;
}
if (mode === "date") {
const untilDate = dateInput?.value || planDate;
if (untilDate < planDate) {
onError("Дата окончания не может быть раньше начала");
return;
}
finish({ duration: "date", untilDate });
return;
}
finish({ duration: "today" });
});
});
}
function skipDurationBody(planDate, extra, title, options) {
return promptSkipDuration({ planDate, title, ...options }).then((choice) => {
if (!choice) return null;
return { date: planDate, ...choice, ...(extra || {}) };
});
}
global.WespDailyPlanSkipDuration = {
weekEndIso,
todayIso,
promptSkipDuration,
skipDurationBody,
escapeHtml,
};
})(window);