Refactor templates to externalize JavaScript: Moved inline scripts to separate JS files and added JSON data attributes for better maintainability and performance. Updated consumption, costs, equipment, maintenance, production, reporting, and simulations templates accordingly.
This commit is contained in:
@@ -149,200 +149,9 @@ block content %}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const scenarios = {{ scenarios | tojson | safe }};
|
||||
const capexByScenario = {{ capex_by_scenario | tojson | safe }};
|
||||
const opexByScenario = {{ opex_by_scenario | tojson | safe }};
|
||||
|
||||
const filterSelect = document.getElementById("costs-scenario-filter");
|
||||
const costsEmptyState = document.getElementById("costs-empty");
|
||||
const costsDataWrapper = document.getElementById("costs-data");
|
||||
const capexTableBody = document.getElementById("capex-table-body");
|
||||
const opexTableBody = document.getElementById("opex-table-body");
|
||||
const capexEmpty = document.getElementById("capex-empty");
|
||||
const opexEmpty = document.getElementById("opex-empty");
|
||||
const capexTotal = document.getElementById("capex-total");
|
||||
const opexTotal = document.getElementById("opex-total");
|
||||
const capexForm = document.getElementById("capex-form");
|
||||
const opexForm = document.getElementById("opex-form");
|
||||
const capexFeedback = document.getElementById("capex-feedback");
|
||||
const opexFeedback = document.getElementById("opex-feedback");
|
||||
const capexFormScenario = document.getElementById("capex-form-scenario");
|
||||
const opexFormScenario = document.getElementById("opex-form-scenario");
|
||||
|
||||
function showFeedback(element, message, type = "success") {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
element.textContent = message;
|
||||
element.classList.remove("hidden", "success", "error");
|
||||
element.classList.add(type);
|
||||
}
|
||||
|
||||
function hideFeedback(element) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
element.classList.add("hidden");
|
||||
element.textContent = "";
|
||||
}
|
||||
|
||||
function formatAmount(value) {
|
||||
return Number(value).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
}
|
||||
|
||||
function sumAmount(records) {
|
||||
return records.reduce((total, record) => total + Number(record.amount || 0), 0);
|
||||
}
|
||||
|
||||
function renderCostTables(scenarioId) {
|
||||
const capexRecords = capexByScenario[String(scenarioId)] || [];
|
||||
const opexRecords = opexByScenario[String(scenarioId)] || [];
|
||||
|
||||
capexTableBody.innerHTML = "";
|
||||
opexTableBody.innerHTML = "";
|
||||
|
||||
if (!capexRecords.length) {
|
||||
capexEmpty.classList.remove("hidden");
|
||||
} else {
|
||||
capexEmpty.classList.add("hidden");
|
||||
capexRecords.forEach((record) => {
|
||||
const row = document.createElement("tr");
|
||||
row.innerHTML = `
|
||||
<td>${formatAmount(record.amount)}</td>
|
||||
<td>${record.description || "—"}</td>
|
||||
`;
|
||||
capexTableBody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
if (!opexRecords.length) {
|
||||
opexEmpty.classList.remove("hidden");
|
||||
} else {
|
||||
opexEmpty.classList.add("hidden");
|
||||
opexRecords.forEach((record) => {
|
||||
const row = document.createElement("tr");
|
||||
row.innerHTML = `
|
||||
<td>${formatAmount(record.amount)}</td>
|
||||
<td>${record.description || "—"}</td>
|
||||
`;
|
||||
opexTableBody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
capexTotal.textContent = formatAmount(sumAmount(capexRecords));
|
||||
opexTotal.textContent = formatAmount(sumAmount(opexRecords));
|
||||
}
|
||||
|
||||
function toggleCostView(show) {
|
||||
if (show) {
|
||||
costsEmptyState.classList.add("hidden");
|
||||
costsDataWrapper.classList.remove("hidden");
|
||||
} else {
|
||||
costsEmptyState.classList.remove("hidden");
|
||||
costsDataWrapper.classList.add("hidden");
|
||||
capexTableBody.innerHTML = "";
|
||||
opexTableBody.innerHTML = "";
|
||||
capexTotal.textContent = "—";
|
||||
opexTotal.textContent = "—";
|
||||
capexEmpty.classList.add("hidden");
|
||||
opexEmpty.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function syncFormSelections(value) {
|
||||
if (capexFormScenario) {
|
||||
capexFormScenario.value = value || "";
|
||||
}
|
||||
if (opexFormScenario) {
|
||||
opexFormScenario.value = value || "";
|
||||
}
|
||||
}
|
||||
|
||||
if (filterSelect) {
|
||||
filterSelect.addEventListener("change", (event) => {
|
||||
const value = event.target.value;
|
||||
if (!value) {
|
||||
toggleCostView(false);
|
||||
syncFormSelections("");
|
||||
return;
|
||||
}
|
||||
toggleCostView(true);
|
||||
renderCostTables(value);
|
||||
syncFormSelections(value);
|
||||
});
|
||||
}
|
||||
|
||||
async function submitCostEntry(event, targetUrl, storageMap, feedbackEl) {
|
||||
event.preventDefault();
|
||||
hideFeedback(feedbackEl);
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
const scenarioId = formData.get("scenario_id");
|
||||
const payload = {
|
||||
scenario_id: scenarioId ? Number(scenarioId) : null,
|
||||
amount: Number(formData.get("amount")),
|
||||
description: formData.get("description") || null,
|
||||
};
|
||||
|
||||
if (!payload.scenario_id) {
|
||||
showFeedback(feedbackEl, "Select a scenario before submitting.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(targetUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorDetail = await response.json().catch(() => ({}));
|
||||
throw new Error(errorDetail.detail || "Unable to save cost entry.");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const mapKey = String(result.scenario_id);
|
||||
|
||||
if (!Array.isArray(storageMap[mapKey])) {
|
||||
storageMap[mapKey] = [];
|
||||
}
|
||||
|
||||
storageMap[mapKey].push(result);
|
||||
|
||||
event.target.reset();
|
||||
showFeedback(feedbackEl, "Entry saved successfully.", "success");
|
||||
|
||||
if (filterSelect && filterSelect.value === mapKey) {
|
||||
renderCostTables(mapKey);
|
||||
}
|
||||
} catch (error) {
|
||||
showFeedback(feedbackEl, error.message || "An unexpected error occurred.", "error");
|
||||
}
|
||||
}
|
||||
|
||||
if (capexForm) {
|
||||
capexForm.addEventListener("submit", (event) =>
|
||||
submitCostEntry(event, "/api/costs/capex", capexByScenario, capexFeedback)
|
||||
);
|
||||
}
|
||||
|
||||
if (opexForm) {
|
||||
opexForm.addEventListener("submit", (event) =>
|
||||
submitCostEntry(event, "/api/costs/opex", opexByScenario, opexFeedback)
|
||||
);
|
||||
}
|
||||
|
||||
if (filterSelect && filterSelect.value) {
|
||||
toggleCostView(true);
|
||||
renderCostTables(filterSelect.value);
|
||||
syncFormSelections(filterSelect.value);
|
||||
}
|
||||
{% endblock %} {% block scripts %} {{ super() }}
|
||||
<script id="costs-payload" type="application/json">
|
||||
{{ {"capex": capex_by_scenario, "opex": opex_by_scenario} | tojson }}
|
||||
</script>
|
||||
<script src="/static/js/costs.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user