Refactor data loading and query projections
This commit is contained in:
+97
-73
@@ -1048,47 +1048,27 @@
|
||||
<div class="pc-note" id="processFlowModalNote"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script id="processCostPageState" type="application/json">
|
||||
{{ {
|
||||
"projects": process_cost_projects or [],
|
||||
"selectedCode": process_cost_selected_code,
|
||||
"selectedProject": process_cost_selected_project or {},
|
||||
"relatedCodes": process_cost_related_codes or [],
|
||||
"activeRelatedCodes": process_cost_active_related_codes or [],
|
||||
"quickLinkCodes": process_cost_quick_link_codes or [],
|
||||
"source": process_cost_source,
|
||||
"selectedStartYear": process_cost_selected_start_year,
|
||||
"selectedEndYear": process_cost_selected_end_year,
|
||||
"includeRelatedEnabled": process_cost_include_related,
|
||||
"monthlyRows": (process_cost_detail.monthly_rows or []),
|
||||
} | tojson }}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
(() => {
|
||||
const stateNode = document.getElementById("processCostPageState");
|
||||
const pageState = stateNode ? JSON.parse(stateNode.textContent || "{}") : {};
|
||||
const processCostProjects = Array.isArray(pageState.projects) ? pageState.projects : [];
|
||||
const bootstrapQuery = new URLSearchParams(window.location.search);
|
||||
const bootstrapDataUrl = `/process-cost/bootstrap-data?${bootstrapQuery.toString()}`;
|
||||
let pageState = {};
|
||||
let processCostProjects = [];
|
||||
const escapeHtml = (value) => String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
const processCostProjectMap = new Map(
|
||||
processCostProjects
|
||||
.filter((item) => item && item.support_dept_code)
|
||||
.map((item) => [item.support_dept_code, item])
|
||||
);
|
||||
const selectedCode = typeof pageState.selectedCode === "string" ? pageState.selectedCode : "";
|
||||
const selectedProject = pageState.selectedProject && typeof pageState.selectedProject === "object"
|
||||
? pageState.selectedProject
|
||||
: {};
|
||||
const initialRelatedCodes = Array.isArray(pageState.relatedCodes) ? pageState.relatedCodes : [];
|
||||
const initialActiveRelatedCodes = Array.isArray(pageState.activeRelatedCodes) ? pageState.activeRelatedCodes : [];
|
||||
const initialQuickLinkCodes = Array.isArray(pageState.quickLinkCodes) ? pageState.quickLinkCodes : [];
|
||||
let processCostProjectMap = new Map();
|
||||
let selectedCode = "";
|
||||
let selectedProject = {};
|
||||
let initialRelatedCodes = [];
|
||||
let initialActiveRelatedCodes = [];
|
||||
let initialQuickLinkCodes = [];
|
||||
const searchInput = document.getElementById("projectSearchInput");
|
||||
const list = document.getElementById("projectList");
|
||||
let projectListEmpty = document.getElementById("projectListEmpty");
|
||||
@@ -1101,12 +1081,48 @@
|
||||
const processModalNote = document.getElementById("processFlowModalNote");
|
||||
const processModalClose = document.getElementById("processFlowModalClose");
|
||||
const processButtons = document.querySelectorAll("[data-process-modal]");
|
||||
const selectedStartYear = pageState.selectedStartYear;
|
||||
const selectedEndYear = pageState.selectedEndYear;
|
||||
const selectedSource = typeof pageState.source === "string" ? pageState.source : "hanmac";
|
||||
const includeRelatedEnabled = Boolean(pageState.includeRelatedEnabled);
|
||||
let selectedStartYear = pageState.selectedStartYear;
|
||||
let selectedEndYear = pageState.selectedEndYear;
|
||||
let selectedSource = typeof pageState.source === "string" ? pageState.source : "hanmac";
|
||||
let includeRelatedEnabled = Boolean(pageState.includeRelatedEnabled);
|
||||
let bookmarkedCodes = Array.isArray(initialQuickLinkCodes) ? [...initialQuickLinkCodes] : [];
|
||||
let activeRelatedCodes = Array.isArray(initialActiveRelatedCodes) ? [...initialActiveRelatedCodes] : [];
|
||||
let monthlyRows = [];
|
||||
|
||||
async function loadProcessCostBootstrapData() {
|
||||
const response = await fetch(bootstrapDataUrl, {
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || payload?.error) {
|
||||
throw new Error(payload?.error || `HTTP ${response.status}`);
|
||||
}
|
||||
pageState = payload || {};
|
||||
processCostProjects = Array.isArray(pageState.projects) ? pageState.projects : [];
|
||||
processCostProjectMap = new Map(
|
||||
processCostProjects
|
||||
.filter((item) => item && item.support_dept_code)
|
||||
.map((item) => [item.support_dept_code, item])
|
||||
);
|
||||
selectedCode = typeof pageState.selectedCode === "string" ? pageState.selectedCode : "";
|
||||
selectedProject = pageState.selectedProject && typeof pageState.selectedProject === "object"
|
||||
? pageState.selectedProject
|
||||
: {};
|
||||
initialRelatedCodes = Array.isArray(pageState.relatedCodes) ? pageState.relatedCodes : [];
|
||||
initialActiveRelatedCodes = Array.isArray(pageState.activeRelatedCodes) ? pageState.activeRelatedCodes : [];
|
||||
initialQuickLinkCodes = Array.isArray(pageState.quickLinkCodes) ? pageState.quickLinkCodes : [];
|
||||
selectedStartYear = pageState.selectedStartYear;
|
||||
selectedEndYear = pageState.selectedEndYear;
|
||||
selectedSource = typeof pageState.source === "string" ? pageState.source : "hanmac";
|
||||
includeRelatedEnabled = Boolean(pageState.includeRelatedEnabled);
|
||||
bookmarkedCodes = [...initialQuickLinkCodes];
|
||||
activeRelatedCodes = [...initialActiveRelatedCodes];
|
||||
monthlyRows = Array.isArray(pageState.monthlyRows) ? pageState.monthlyRows : [];
|
||||
}
|
||||
|
||||
function buildProcessCostUrl(code, options = {}) {
|
||||
const params = new URLSearchParams();
|
||||
@@ -1342,11 +1358,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
renderQuickLinks();
|
||||
syncBookmarkButton();
|
||||
syncSelectedProjectCard();
|
||||
filterProjectList();
|
||||
|
||||
if (selectedProjectBookmark) {
|
||||
selectedProjectBookmark.addEventListener("click", async () => {
|
||||
const code = selectedProjectBookmark.dataset.bookmarkCode || "";
|
||||
@@ -1491,8 +1502,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
renderRelatedChips();
|
||||
|
||||
const startYearSelect = document.getElementById("processCostStartYear");
|
||||
const endYearSelect = document.getElementById("processCostEndYear");
|
||||
const syncYearRangeOptions = () => {
|
||||
@@ -1530,43 +1539,58 @@
|
||||
syncYearRangeOptions();
|
||||
}
|
||||
|
||||
const monthlyRows = Array.isArray(pageState.monthlyRows) ? pageState.monthlyRows : [];
|
||||
const svg = document.getElementById("processMonthlyChart");
|
||||
if (!svg || !Array.isArray(monthlyRows) || !monthlyRows.length) {
|
||||
return;
|
||||
}
|
||||
const renderMonthlyChart = () => {
|
||||
if (!svg || !Array.isArray(monthlyRows) || !monthlyRows.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const width = 540;
|
||||
const height = 220;
|
||||
const pad = { top: 16, right: 14, bottom: 36, left: 42 };
|
||||
const plotW = width - pad.left - pad.right;
|
||||
const plotH = height - pad.top - pad.bottom;
|
||||
const maxVal = Math.max(
|
||||
...monthlyRows.map((row) => Number(row.revenue_amount || 0)),
|
||||
...monthlyRows.map((row) => Number(row.expense_amount || 0)),
|
||||
1
|
||||
);
|
||||
const stepX = monthlyRows.length > 1 ? plotW / (monthlyRows.length - 1) : 0;
|
||||
const y = (v) => pad.top + (1 - Math.min(v / maxVal, 1)) * plotH;
|
||||
const x = (i) => pad.left + stepX * i;
|
||||
const width = 540;
|
||||
const height = 220;
|
||||
const pad = { top: 16, right: 14, bottom: 36, left: 42 };
|
||||
const plotW = width - pad.left - pad.right;
|
||||
const plotH = height - pad.top - pad.bottom;
|
||||
const maxVal = Math.max(
|
||||
...monthlyRows.map((row) => Number(row.revenue_amount || 0)),
|
||||
...monthlyRows.map((row) => Number(row.expense_amount || 0)),
|
||||
1
|
||||
);
|
||||
const stepX = monthlyRows.length > 1 ? plotW / (monthlyRows.length - 1) : 0;
|
||||
const y = (v) => pad.top + (1 - Math.min(v / maxVal, 1)) * plotH;
|
||||
const x = (i) => pad.left + stepX * i;
|
||||
|
||||
const revenuePoints = monthlyRows.map((row, i) => `${x(i)},${y(Number(row.revenue_amount || 0))}`).join(" ");
|
||||
const expensePoints = monthlyRows.map((row, i) => `${x(i)},${y(Number(row.expense_amount || 0))}`).join(" ");
|
||||
const revenuePoints = monthlyRows.map((row, i) => `${x(i)},${y(Number(row.revenue_amount || 0))}`).join(" ");
|
||||
const expensePoints = monthlyRows.map((row, i) => `${x(i)},${y(Number(row.expense_amount || 0))}`).join(" ");
|
||||
|
||||
let labels = "";
|
||||
monthlyRows.forEach((row, i) => {
|
||||
labels += `<text x="${x(i)}" y="${height - 12}" text-anchor="middle" fill="#677182" font-size="10">${escapeHtml((row.month_label || "").slice(2))}</text>`;
|
||||
});
|
||||
let labels = "";
|
||||
monthlyRows.forEach((row, i) => {
|
||||
labels += `<text x="${x(i)}" y="${height - 12}" text-anchor="middle" fill="#677182" font-size="10">${escapeHtml((row.month_label || "").slice(2))}</text>`;
|
||||
});
|
||||
|
||||
svg.innerHTML = `
|
||||
<line x1="${pad.left}" y1="${pad.top}" x2="${pad.left}" y2="${height - pad.bottom}" stroke="#d9dde3" />
|
||||
<line x1="${pad.left}" y1="${height - pad.bottom}" x2="${width - pad.right}" y2="${height - pad.bottom}" stroke="#d9dde3" />
|
||||
<polyline points="${revenuePoints}" fill="none" stroke="#2c5f96" stroke-width="2.3" />
|
||||
<polyline points="${expensePoints}" fill="none" stroke="#d9822b" stroke-width="2.3" />
|
||||
${labels}
|
||||
<text x="${pad.left}" y="${pad.top - 3}" fill="#2c5f96" font-size="11">수익</text>
|
||||
<text x="${pad.left + 36}" y="${pad.top - 3}" fill="#d9822b" font-size="11">비용</text>
|
||||
`;
|
||||
svg.innerHTML = `
|
||||
<line x1="${pad.left}" y1="${pad.top}" x2="${pad.left}" y2="${height - pad.bottom}" stroke="#d9dde3" />
|
||||
<line x1="${pad.left}" y1="${height - pad.bottom}" x2="${width - pad.right}" y2="${height - pad.bottom}" stroke="#d9dde3" />
|
||||
<polyline points="${revenuePoints}" fill="none" stroke="#2c5f96" stroke-width="2.3" />
|
||||
<polyline points="${expensePoints}" fill="none" stroke="#d9822b" stroke-width="2.3" />
|
||||
${labels}
|
||||
<text x="${pad.left}" y="${pad.top - 3}" fill="#2c5f96" font-size="11">수익</text>
|
||||
<text x="${pad.left + 36}" y="${pad.top - 3}" fill="#d9822b" font-size="11">비용</text>
|
||||
`;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await loadProcessCostBootstrapData();
|
||||
} catch (error) {
|
||||
console.error("프로세스 원가 부트스트랩 데이터 조회 에러", error);
|
||||
}
|
||||
renderQuickLinks();
|
||||
syncBookmarkButton();
|
||||
syncSelectedProjectCard();
|
||||
filterProjectList();
|
||||
renderRelatedChips();
|
||||
renderMonthlyChart();
|
||||
})();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user