feat: add monthly history controls for organization view
This commit is contained in:
@@ -11,6 +11,9 @@ const currentViewTitle = document.getElementById("current-view-title");
|
||||
const globalDateControls = document.getElementById("global-date-controls");
|
||||
const globalStartDateInput = document.getElementById("global-start-date");
|
||||
const globalEndDateInput = document.getElementById("global-end-date");
|
||||
const organizationHistoryControls = document.getElementById("organization-history-controls");
|
||||
const organizationMonthSelect = document.getElementById("organization-month-select");
|
||||
const organizationCompareBtn = document.getElementById("organization-compare-btn");
|
||||
const navButtons = Array.from(document.querySelectorAll(".header-center [data-view]"));
|
||||
const organizationFrame = document.getElementById("organization-frame");
|
||||
const organizationStage = document.getElementById("organization-stage");
|
||||
@@ -155,6 +158,89 @@ const globalDateState = {
|
||||
endDate: "2026-01-31",
|
||||
};
|
||||
|
||||
const organizationHistoryState = {
|
||||
selectedMonth: "",
|
||||
currentMonth: "",
|
||||
};
|
||||
|
||||
function padDatePart(value) {
|
||||
return String(value).padStart(2, "0");
|
||||
}
|
||||
|
||||
function getCurrentMonthValue() {
|
||||
const now = new Date();
|
||||
return `${now.getFullYear()}-${padDatePart(now.getMonth() + 1)}`;
|
||||
}
|
||||
|
||||
function getMonthLabel(monthValue) {
|
||||
const [, month] = String(monthValue || "").split("-");
|
||||
if (!month) return "";
|
||||
const monthNumber = Number(month);
|
||||
if (!Number.isInteger(monthNumber) || monthNumber <= 0) return "";
|
||||
return `${monthNumber}월`;
|
||||
}
|
||||
|
||||
function getMonthEndDate(monthValue) {
|
||||
const [yearText, monthText] = String(monthValue || "").split("-");
|
||||
const year = Number(yearText);
|
||||
const month = Number(monthText);
|
||||
if (!Number.isInteger(year) || !Number.isInteger(month) || month <= 0) return "";
|
||||
const lastDay = new Date(year, month, 0);
|
||||
return `${lastDay.getFullYear()}-${padDatePart(lastDay.getMonth() + 1)}-${padDatePart(lastDay.getDate())}`;
|
||||
}
|
||||
|
||||
function getTodayDate() {
|
||||
const now = new Date();
|
||||
return `${now.getFullYear()}-${padDatePart(now.getMonth() + 1)}-${padDatePart(now.getDate())}`;
|
||||
}
|
||||
|
||||
function syncOrganizationHistoryControls() {
|
||||
if (!organizationHistoryControls) return;
|
||||
const visible = currentView === "organization";
|
||||
organizationHistoryControls.classList.toggle("hidden", !visible);
|
||||
if (organizationCompareBtn) {
|
||||
const isCurrentMonth = !organizationHistoryState.selectedMonth || organizationHistoryState.selectedMonth === organizationHistoryState.currentMonth;
|
||||
organizationCompareBtn.classList.toggle("hidden", !visible || isCurrentMonth);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeOrganizationMonthOptions() {
|
||||
if (!organizationMonthSelect) return;
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const monthCount = now.getMonth() + 1;
|
||||
organizationMonthSelect.innerHTML = "";
|
||||
for (let month = monthCount; month >= 1; month -= 1) {
|
||||
const value = `${year}-${padDatePart(month)}`;
|
||||
const option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = month === monthCount ? `${month}월 (최신)` : `${month}월`;
|
||||
organizationMonthSelect.append(option);
|
||||
}
|
||||
organizationHistoryState.currentMonth = `${year}-${padDatePart(monthCount)}`;
|
||||
organizationHistoryState.selectedMonth = organizationHistoryState.currentMonth;
|
||||
organizationMonthSelect.value = organizationHistoryState.selectedMonth;
|
||||
syncOrganizationHistoryControls();
|
||||
}
|
||||
|
||||
function postOrganizationHistoryState() {
|
||||
if (!organizationFrame?.contentWindow) return;
|
||||
const selectedMonth = organizationHistoryState.selectedMonth || organizationHistoryState.currentMonth;
|
||||
const currentMonth = organizationHistoryState.currentMonth || getCurrentMonthValue();
|
||||
const isHistorical = Boolean(selectedMonth) && selectedMonth !== currentMonth;
|
||||
organizationFrame.contentWindow.postMessage(
|
||||
{
|
||||
source: "total-control",
|
||||
type: "organization-history-view",
|
||||
month: selectedMonth,
|
||||
asOfDate: isHistorical ? getMonthEndDate(selectedMonth) : "",
|
||||
historical: isHistorical,
|
||||
},
|
||||
window.location.origin,
|
||||
);
|
||||
syncOrganizationHistoryControls();
|
||||
}
|
||||
|
||||
function getGlobalAsOfDate() {
|
||||
return globalDateState.endDate || "";
|
||||
}
|
||||
@@ -188,7 +274,6 @@ function shouldShowGlobalDateControls() {
|
||||
return currentView === "ledger"
|
||||
|| currentView === "project"
|
||||
|| currentView === "team"
|
||||
|| currentView === "organization"
|
||||
|| currentView === "seatmap-admin"
|
||||
|| currentView === "seatmap-readonly";
|
||||
}
|
||||
@@ -196,6 +281,7 @@ function shouldShowGlobalDateControls() {
|
||||
function syncGlobalDateControlVisibility() {
|
||||
if (!globalDateControls) return;
|
||||
globalDateControls.classList.toggle("hidden", !shouldShowGlobalDateControls());
|
||||
syncOrganizationHistoryControls();
|
||||
}
|
||||
|
||||
function syncGlobalDateControlInputs() {
|
||||
@@ -244,7 +330,6 @@ async function ensureGlobalDateRangeLoaded() {
|
||||
globalDateState.endDate = ends.length ? String(ends[ends.length - 1]).slice(0, 10) : "";
|
||||
globalDateState.loaded = true;
|
||||
syncGlobalDateControlInputs();
|
||||
postGlobalDateRangeToFrame(organizationFrame);
|
||||
postGlobalDateRangeToFrame(projectFrame);
|
||||
postGlobalDateRangeToFrame(teamFrame);
|
||||
} catch (error) {
|
||||
@@ -1446,6 +1531,8 @@ function setActiveView(view) {
|
||||
if (isOrganization && previousView !== "organization" && organizationFrame) {
|
||||
const frameSrc = organizationFrame.dataset.src || organizationFrame.src;
|
||||
organizationFrame.src = resolveAppUrl(frameSrc);
|
||||
} else if (isOrganization) {
|
||||
postOrganizationHistoryState();
|
||||
}
|
||||
if (isProject && previousView !== "project" && projectFrame) {
|
||||
const frameSrc = projectFrame.dataset.src || projectFrame.src;
|
||||
@@ -1547,7 +1634,6 @@ if (logoutBtn) {
|
||||
if (globalStartDateInput) {
|
||||
globalStartDateInput.addEventListener("change", () => {
|
||||
globalDateState.startDate = globalStartDateInput.value || "";
|
||||
postGlobalDateRangeToFrame(organizationFrame);
|
||||
postGlobalDateRangeToFrame(projectFrame);
|
||||
postGlobalDateRangeToFrame(teamFrame);
|
||||
});
|
||||
@@ -1556,7 +1642,6 @@ if (globalStartDateInput) {
|
||||
if (globalEndDateInput) {
|
||||
globalEndDateInput.addEventListener("change", () => {
|
||||
globalDateState.endDate = globalEndDateInput.value || "";
|
||||
postGlobalDateRangeToFrame(organizationFrame);
|
||||
postGlobalDateRangeToFrame(projectFrame);
|
||||
postGlobalDateRangeToFrame(teamFrame);
|
||||
if (currentView === "seatmap-admin" || currentView === "seatmap-readonly") {
|
||||
@@ -1567,7 +1652,7 @@ if (globalEndDateInput) {
|
||||
}
|
||||
|
||||
organizationFrame?.addEventListener("load", () => {
|
||||
postGlobalDateRangeToFrame(organizationFrame);
|
||||
postOrganizationHistoryState();
|
||||
});
|
||||
|
||||
projectFrame?.addEventListener("load", () => {
|
||||
@@ -1591,6 +1676,32 @@ navButtons.forEach((button) => {
|
||||
});
|
||||
});
|
||||
|
||||
if (organizationMonthSelect) {
|
||||
initializeOrganizationMonthOptions();
|
||||
organizationMonthSelect.addEventListener("change", () => {
|
||||
organizationHistoryState.selectedMonth = organizationMonthSelect.value || organizationHistoryState.currentMonth;
|
||||
postOrganizationHistoryState();
|
||||
});
|
||||
}
|
||||
|
||||
if (organizationCompareBtn) {
|
||||
organizationCompareBtn.addEventListener("click", () => {
|
||||
if (!organizationFrame?.contentWindow) return;
|
||||
const fromDate = getMonthEndDate(organizationHistoryState.selectedMonth);
|
||||
const toDate = getTodayDate();
|
||||
if (!fromDate) return;
|
||||
organizationFrame.contentWindow.postMessage(
|
||||
{
|
||||
source: "total-control",
|
||||
type: "open-history-compare",
|
||||
fromDate,
|
||||
toDate,
|
||||
},
|
||||
window.location.origin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Object.values(seatMapDom).forEach((dom) => {
|
||||
dom.officeTabs?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-seatmap-office]");
|
||||
|
||||
Reference in New Issue
Block a user