125 lines
5.3 KiB
JavaScript
125 lines
5.3 KiB
JavaScript
/**
|
|
* Диалог «На какой срок…» для skip/replace/adjustment плана на день.
|
|
*/
|
|
(function (global) {
|
|
function escapeHtml(value) {
|
|
return String(value ?? "")
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """);
|
|
}
|
|
|
|
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);
|