diff --git a/static/js/consumption.js b/static/js/consumption.js
deleted file mode 100644
index 2866dd9..0000000
--- a/static/js/consumption.js
+++ /dev/null
@@ -1,205 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("consumption-data");
- let data = { scenarios: [], consumption: {}, unit_options: [] };
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- data = {
- scenarios: Array.isArray(parsed.scenarios) ? parsed.scenarios : [],
- consumption:
- parsed.consumption && typeof parsed.consumption === "object"
- ? parsed.consumption
- : {},
- unit_options: Array.isArray(parsed.unit_options)
- ? parsed.unit_options
- : [],
- };
- }
- } catch (error) {
- console.error("Unable to parse consumption data", error);
- }
- }
-
- const consumptionByScenario = data.consumption;
- const filterSelect = document.getElementById("consumption-scenario-filter");
- const tableWrapper = document.getElementById("consumption-table-wrapper");
- const tableBody = document.getElementById("consumption-table-body");
- const emptyState = document.getElementById("consumption-empty");
- const form = document.getElementById("consumption-form");
- const feedbackEl = document.getElementById("consumption-feedback");
- const unitSelect = document.getElementById("consumption-form-unit");
- const unitSymbolInput = document.getElementById(
- "consumption-form-unit-symbol"
- );
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- feedbackEl.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.classList.add("hidden");
- feedbackEl.textContent = "";
- };
-
- const formatAmount = (value) =>
- Number(value).toLocaleString(undefined, {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- });
-
- const formatMeasurement = (amount, symbol, name) => {
- if (symbol) {
- return `${formatAmount(amount)} ${symbol}`;
- }
- if (name) {
- return `${formatAmount(amount)} ${name}`;
- }
- return formatAmount(amount);
- };
-
- const renderConsumptionRows = (scenarioId) => {
- if (!tableBody || !tableWrapper || !emptyState) {
- return;
- }
-
- const key = String(scenarioId);
- const records = consumptionByScenario[key] || [];
-
- tableBody.innerHTML = "";
-
- if (!records.length) {
- emptyState.textContent = "No consumption records for this scenario yet.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- return;
- }
-
- emptyState.classList.add("hidden");
- tableWrapper.classList.remove("hidden");
-
- records.forEach((record) => {
- const row = document.createElement("tr");
- row.innerHTML = `
-
${formatMeasurement(
- record.amount,
- record.unit_symbol,
- record.unit_name
- )} |
- ${record.description || "—"} |
- `;
- tableBody.appendChild(row);
- });
- };
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (!value) {
- if (emptyState && tableWrapper && tableBody) {
- emptyState.textContent =
- "Choose a scenario to review its consumption records.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- tableBody.innerHTML = "";
- }
- return;
- }
- renderConsumptionRows(value);
- });
- }
-
- const submitConsumption = async (event) => {
- event.preventDefault();
- hideFeedback();
-
- if (!form) {
- return;
- }
-
- const formData = new FormData(form);
- const scenarioId = formData.get("scenario_id");
- const unitName = formData.get("unit_name");
- const unitSymbol = formData.get("unit_symbol");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- amount: Number(formData.get("amount")),
- description: formData.get("description") || null,
- unit_name: unitName ? String(unitName) : null,
- unit_symbol: unitSymbol ? String(unitSymbol) : null,
- };
-
- try {
- const response = await fetch("/api/consumption/", {
- 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 add consumption record."
- );
- }
-
- const result = await response.json();
- const mapKey = String(result.scenario_id);
-
- if (!Array.isArray(consumptionByScenario[mapKey])) {
- consumptionByScenario[mapKey] = [];
- }
- consumptionByScenario[mapKey].push(result);
-
- form.reset();
- syncUnitSelection();
- showFeedback("Consumption record saved.", "success");
-
- if (filterSelect && filterSelect.value === String(result.scenario_id)) {
- renderConsumptionRows(filterSelect.value);
- }
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- }
- };
-
- if (form) {
- form.addEventListener("submit", submitConsumption);
- }
-
- const syncUnitSelection = () => {
- if (!unitSelect || !unitSymbolInput) {
- return;
- }
- if (!unitSelect.value && unitSelect.options.length > 0) {
- const firstOption = Array.from(unitSelect.options).find(
- (option) => option.value
- );
- if (firstOption) {
- firstOption.selected = true;
- }
- }
- const selectedOption = unitSelect.options[unitSelect.selectedIndex];
- unitSymbolInput.value = selectedOption
- ? selectedOption.getAttribute("data-symbol") || ""
- : "";
- };
-
- if (unitSelect) {
- unitSelect.addEventListener("change", syncUnitSelection);
- syncUnitSelection();
- }
-
- if (filterSelect && filterSelect.value) {
- renderConsumptionRows(filterSelect.value);
- }
-});
diff --git a/static/js/costs.js b/static/js/costs.js
deleted file mode 100644
index d75139c..0000000
--- a/static/js/costs.js
+++ /dev/null
@@ -1,339 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("costs-payload");
- let capexByScenario = {};
- let opexByScenario = {};
- let currencyOptions = [];
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- if (parsed.capex && typeof parsed.capex === "object") {
- capexByScenario = parsed.capex;
- }
- if (parsed.opex && typeof parsed.opex === "object") {
- opexByScenario = parsed.opex;
- }
- if (Array.isArray(parsed.currency_options)) {
- currencyOptions = parsed.currency_options;
- }
- }
- } catch (error) {
- console.error("Unable to parse cost data", error);
- }
- }
-
- 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");
- const capexCurrencySelect = document.getElementById("capex-form-currency");
- const opexCurrencySelect = document.getElementById("opex-form-currency");
-
- // If no currency options were injected server-side, fetch from API
- const fetchCurrencyOptions = async () => {
- try {
- const resp = await fetch("/api/currencies/");
- if (!resp.ok) return;
- const list = await resp.json();
- if (Array.isArray(list) && list.length) {
- currencyOptions = list;
- populateCurrencySelects();
- }
- } catch (err) {
- console.warn("Unable to fetch currency options", err);
- }
- };
-
- const populateCurrencySelects = () => {
- const selectElements = [capexCurrencySelect, opexCurrencySelect].filter(Boolean);
- selectElements.forEach((sel) => {
- if (!sel) return;
- // Clear non-empty options except the empty placeholder
- const placeholder = sel.querySelector("option[value='']");
- sel.innerHTML = "";
- if (placeholder) sel.appendChild(placeholder);
- currencyOptions.forEach((opt) => {
- const option = document.createElement("option");
- option.value = opt.id;
- option.textContent = opt.name || opt.id;
- sel.appendChild(option);
- });
- });
- };
-
- // populate from injected options first, then fetch to refresh
- if (currencyOptions && currencyOptions.length) populateCurrencySelects();
- else fetchCurrencyOptions();
-
- const showFeedback = (element, message, type = "success") => {
- if (!element) {
- return;
- }
- element.textContent = message;
- element.classList.remove("hidden", "success", "error");
- element.classList.add(type);
- };
-
- const hideFeedback = (element) => {
- if (!element) {
- return;
- }
- element.classList.add("hidden");
- element.textContent = "";
- };
-
- const formatAmount = (value) =>
- Number(value).toLocaleString(undefined, {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- });
-
- const formatCurrencyAmount = (value, currencyCode) => {
- if (!currencyCode) {
- return formatAmount(value);
- }
- try {
- return new Intl.NumberFormat(undefined, {
- style: "currency",
- currency: currencyCode,
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- }).format(Number(value));
- } catch (error) {
- return `${currencyCode} ${formatAmount(value)}`;
- }
- };
-
- const sumAmount = (records) =>
- records.reduce((total, record) => total + Number(record.amount || 0), 0);
-
- const describeTotal = (records) => {
- if (!records || records.length === 0) {
- return "—";
- }
- const total = sumAmount(records);
- const currencyCodes = Array.from(
- new Set(
- records
- .map((record) => (record.currency_code || "").trim().toUpperCase())
- .filter(Boolean)
- )
- );
-
- if (currencyCodes.length === 1) {
- return formatCurrencyAmount(total, currencyCodes[0]);
- }
- return `${formatAmount(total)} (mixed)`;
- };
-
- const renderCostTables = (scenarioId) => {
- if (
- !capexTableBody ||
- !opexTableBody ||
- !capexEmpty ||
- !opexEmpty ||
- !capexTotal ||
- !opexTotal
- ) {
- return;
- }
-
- 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 = `
- ${formatCurrencyAmount(record.amount, record.currency_code)} |
- ${record.description || "—"} |
- `;
- 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 = `
- ${formatCurrencyAmount(record.amount, record.currency_code)} |
- ${record.description || "—"} |
- `;
- opexTableBody.appendChild(row);
- });
- }
-
- capexTotal.textContent = describeTotal(capexRecords);
- opexTotal.textContent = describeTotal(opexRecords);
- };
-
- const toggleCostView = (show) => {
- if (
- !costsEmptyState ||
- !costsDataWrapper ||
- !capexTableBody ||
- !opexTableBody
- ) {
- return;
- }
-
- if (show) {
- costsEmptyState.classList.add("hidden");
- costsDataWrapper.classList.remove("hidden");
- } else {
- costsEmptyState.classList.remove("hidden");
- costsDataWrapper.classList.add("hidden");
- capexTableBody.innerHTML = "";
- opexTableBody.innerHTML = "";
- if (capexTotal) {
- capexTotal.textContent = "—";
- }
- if (opexTotal) {
- opexTotal.textContent = "—";
- }
- if (capexEmpty) {
- capexEmpty.classList.add("hidden");
- }
- if (opexEmpty) {
- opexEmpty.classList.add("hidden");
- }
- }
- };
-
- const syncFormSelections = (value) => {
- if (capexFormScenario) {
- capexFormScenario.value = value || "";
- }
- if (opexFormScenario) {
- opexFormScenario.value = value || "";
- }
- };
-
- const ensureCurrencySelection = (selectElement) => {
- if (!selectElement || selectElement.value) {
- return;
- }
- const firstOption = selectElement.querySelector(
- "option[value]:not([value=''])"
- );
- if (firstOption && firstOption.value) {
- selectElement.value = firstOption.value;
- }
- };
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (!value) {
- toggleCostView(false);
- syncFormSelections("");
- return;
- }
- toggleCostView(true);
- renderCostTables(value);
- syncFormSelections(value);
- });
- }
-
- const submitCostEntry = async (event, targetUrl, storageMap, feedbackEl) => {
- event.preventDefault();
- hideFeedback(feedbackEl);
-
- const formData = new FormData(event.target);
- const scenarioId = formData.get("scenario_id");
- const currencyCode = formData.get("currency_code");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- amount: Number(formData.get("amount")),
- description: formData.get("description") || null,
- currency_code: currencyCode ? String(currencyCode).toUpperCase() : null,
- };
-
- if (!payload.scenario_id) {
- showFeedback(feedbackEl, "Select a scenario before submitting.", "error");
- return;
- }
-
- if (!payload.currency_code) {
- showFeedback(feedbackEl, "Choose a currency 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();
- ensureCurrencySelection(event.target.querySelector("select[name='currency_code']"));
- 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) {
- ensureCurrencySelection(capexCurrencySelect);
- capexForm.addEventListener("submit", (event) =>
- submitCostEntry(event, "/api/costs/capex", capexByScenario, capexFeedback)
- );
- }
-
- if (opexForm) {
- ensureCurrencySelection(opexCurrencySelect);
- opexForm.addEventListener("submit", (event) =>
- submitCostEntry(event, "/api/costs/opex", opexByScenario, opexFeedback)
- );
- }
-
- if (filterSelect && filterSelect.value) {
- toggleCostView(true);
- renderCostTables(filterSelect.value);
- syncFormSelections(filterSelect.value);
- }
-});
diff --git a/static/js/currencies.js b/static/js/currencies.js
deleted file mode 100644
index 86557f7..0000000
--- a/static/js/currencies.js
+++ /dev/null
@@ -1,537 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("currencies-data");
- const editorSection = document.getElementById("currencies-editor");
- const tableBody = document.getElementById("currencies-table-body");
- const tableEmptyState = document.getElementById("currencies-table-empty");
- const metrics = {
- total: document.getElementById("currency-metric-total"),
- active: document.getElementById("currency-metric-active"),
- inactive: document.getElementById("currency-metric-inactive"),
- };
-
- const form = document.getElementById("currency-form");
- const existingSelect = document.getElementById("currency-form-existing");
- const codeInput = document.getElementById("currency-form-code");
- const nameInput = document.getElementById("currency-form-name");
- const symbolInput = document.getElementById("currency-form-symbol");
- const statusSelect = document.getElementById("currency-form-status");
- const resetButton = document.getElementById("currency-form-reset");
- const feedbackElement = document.getElementById("currency-form-feedback");
-
- const saveButton = form ? form.querySelector("button[type='submit']") : null;
-
- const uppercaseCode = (value) =>
- (value || "").toString().trim().toUpperCase();
- const normalizeSymbol = (value) => {
- if (value === undefined || value === null) {
- return null;
- }
- const trimmed = String(value).trim();
- return trimmed ? trimmed : null;
- };
-
- const normalizeApiBase = (value) => {
- if (!value || typeof value !== "string") {
- return "/api/currencies";
- }
- return value.endsWith("/") ? value.slice(0, -1) : value;
- };
-
- let currencies = [];
- let apiBase = "/api/currencies";
- let defaultCurrencyCode = "USD";
-
- const buildCurrencyRecord = (record) => {
- if (!record || typeof record !== "object") {
- return null;
- }
- const code = uppercaseCode(record.code);
- return {
- id: record.id ?? null,
- code,
- name: record.name || "",
- symbol: record.symbol || "",
- is_active: Boolean(record.is_active),
- is_default: code === defaultCurrencyCode,
- };
- };
-
- const findCurrencyIndex = (code) => {
- return currencies.findIndex((item) => item.code === code);
- };
-
- const upsertCurrency = (record) => {
- const normalized = buildCurrencyRecord(record);
- if (!normalized) {
- return null;
- }
- const existingIndex = findCurrencyIndex(normalized.code);
- if (existingIndex >= 0) {
- currencies[existingIndex] = normalized;
- } else {
- currencies.push(normalized);
- }
- currencies.sort((a, b) => a.code.localeCompare(b.code));
- return normalized;
- };
-
- const replaceCurrencyList = (records) => {
- if (!Array.isArray(records)) {
- return;
- }
- currencies = records
- .map((record) => buildCurrencyRecord(record))
- .filter((record) => record !== null)
- .sort((a, b) => a.code.localeCompare(b.code));
- };
-
- const applyPayload = () => {
- if (!dataElement) {
- return;
- }
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- if (parsed.default_currency_code) {
- defaultCurrencyCode = uppercaseCode(parsed.default_currency_code);
- }
- if (parsed.currency_api_base) {
- apiBase = normalizeApiBase(parsed.currency_api_base);
- }
- if (Array.isArray(parsed.currencies)) {
- replaceCurrencyList(parsed.currencies);
- }
- }
- } catch (error) {
- console.error("Unable to parse currencies payload", error);
- }
- };
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackElement) {
- return;
- }
- feedbackElement.textContent = message;
- feedbackElement.classList.remove("hidden", "success", "error");
- feedbackElement.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackElement) {
- return;
- }
- feedbackElement.classList.add("hidden");
- feedbackElement.classList.remove("success", "error");
- feedbackElement.textContent = "";
- };
-
- const setButtonLoading = (button, isLoading) => {
- if (!button) {
- return;
- }
- button.disabled = isLoading;
- button.classList.toggle("is-loading", isLoading);
- };
-
- const updateMetrics = () => {
- const total = currencies.length;
- const active = currencies.filter((item) => item.is_active).length;
- const inactive = total - active;
- if (metrics.total) {
- metrics.total.textContent = String(total);
- }
- if (metrics.active) {
- metrics.active.textContent = String(active);
- }
- if (metrics.inactive) {
- metrics.inactive.textContent = String(inactive);
- }
- };
-
- const renderExistingOptions = (
- selectedCode = existingSelect ? existingSelect.value : ""
- ) => {
- if (!existingSelect) {
- return;
- }
- const placeholder = existingSelect.querySelector("option[value='']");
- const placeholderClone = placeholder ? placeholder.cloneNode(true) : null;
- existingSelect.innerHTML = "";
- if (placeholderClone) {
- existingSelect.appendChild(placeholderClone);
- }
- const fragment = document.createDocumentFragment();
- currencies.forEach((currency) => {
- const option = document.createElement("option");
- option.value = currency.code;
- option.textContent = currency.name
- ? `${currency.name} (${currency.code})`
- : currency.code;
- if (selectedCode === currency.code) {
- option.selected = true;
- }
- fragment.appendChild(option);
- });
- existingSelect.appendChild(fragment);
- if (
- selectedCode &&
- !currencies.some((item) => item.code === selectedCode)
- ) {
- existingSelect.value = "";
- }
- };
-
- const renderTable = () => {
- if (!tableBody) {
- return;
- }
- tableBody.innerHTML = "";
- if (!currencies.length) {
- if (tableEmptyState) {
- tableEmptyState.classList.remove("hidden");
- }
- return;
- }
- if (tableEmptyState) {
- tableEmptyState.classList.add("hidden");
- }
- const fragment = document.createDocumentFragment();
- currencies.forEach((currency) => {
- const row = document.createElement("tr");
-
- const codeCell = document.createElement("td");
- codeCell.textContent = currency.code;
- row.appendChild(codeCell);
-
- const nameCell = document.createElement("td");
- nameCell.textContent = currency.name || "—";
- row.appendChild(nameCell);
-
- const symbolCell = document.createElement("td");
- symbolCell.textContent = currency.symbol || "—";
- row.appendChild(symbolCell);
-
- const statusCell = document.createElement("td");
- statusCell.textContent = currency.is_active ? "Active" : "Inactive";
- if (currency.is_default) {
- statusCell.textContent += " (Default)";
- }
- row.appendChild(statusCell);
-
- const actionsCell = document.createElement("td");
- const editButton = document.createElement("button");
- editButton.type = "button";
- editButton.className = "btn";
- editButton.dataset.action = "edit";
- editButton.dataset.code = currency.code;
- editButton.textContent = "Edit";
- editButton.style.marginRight = "0.5rem";
-
- const toggleButton = document.createElement("button");
- toggleButton.type = "button";
- toggleButton.className = "btn";
- toggleButton.dataset.action = "toggle";
- toggleButton.dataset.code = currency.code;
- toggleButton.textContent = currency.is_active ? "Deactivate" : "Activate";
- if (currency.is_default && currency.is_active) {
- toggleButton.disabled = true;
- toggleButton.title = "The default currency must remain active.";
- }
-
- actionsCell.appendChild(editButton);
- actionsCell.appendChild(toggleButton);
-
- row.appendChild(actionsCell);
- fragment.appendChild(row);
- });
- tableBody.appendChild(fragment);
- };
-
- const refreshUI = (selectedCode) => {
- currencies.sort((a, b) => a.code.localeCompare(b.code));
- renderTable();
- renderExistingOptions(selectedCode);
- updateMetrics();
- };
-
- const findCurrency = (code) =>
- currencies.find((item) => item.code === code) || null;
-
- const setFormForCurrency = (currency) => {
- if (!form || !codeInput || !nameInput || !symbolInput || !statusSelect) {
- return;
- }
- if (!currency) {
- form.reset();
- if (existingSelect) {
- existingSelect.value = "";
- }
- codeInput.readOnly = false;
- codeInput.value = "";
- nameInput.value = "";
- symbolInput.value = "";
- statusSelect.disabled = false;
- statusSelect.value = "true";
- statusSelect.title = "";
- return;
- }
-
- if (existingSelect) {
- existingSelect.value = currency.code;
- }
- codeInput.readOnly = true;
- codeInput.value = currency.code;
- nameInput.value = currency.name || "";
- symbolInput.value = currency.symbol || "";
- statusSelect.value = currency.is_active ? "true" : "false";
- if (currency.is_default) {
- statusSelect.disabled = true;
- statusSelect.value = "true";
- statusSelect.title = "The default currency must remain active.";
- } else {
- statusSelect.disabled = false;
- statusSelect.title = "";
- }
- };
-
- const resetFormState = () => {
- setFormForCurrency(null);
- };
-
- const parseError = async (response, fallbackMessage) => {
- try {
- const detail = await response.json();
- if (detail && typeof detail === "object" && detail.detail) {
- return detail.detail;
- }
- } catch (error) {
- // ignore JSON parse errors
- }
- return fallbackMessage;
- };
-
- const fetchCurrenciesFromApi = async () => {
- const url = `${apiBase}/?include_inactive=true`;
- try {
- const response = await fetch(url);
- if (!response.ok) {
- return;
- }
- const list = await response.json();
- if (Array.isArray(list)) {
- replaceCurrencyList(list);
- refreshUI(existingSelect ? existingSelect.value : undefined);
- }
- } catch (error) {
- console.warn("Unable to refresh currency list", error);
- }
- };
-
- const handleSubmit = async (event) => {
- event.preventDefault();
- hideFeedback();
- if (!form || !codeInput || !nameInput || !statusSelect) {
- return;
- }
-
- const editingCode = existingSelect
- ? uppercaseCode(existingSelect.value)
- : "";
- const codeValue = uppercaseCode(codeInput.value);
- const nameValue = (nameInput.value || "").trim();
- const symbolValue = normalizeSymbol(symbolInput ? symbolInput.value : "");
- const isActive = statusSelect.value !== "false";
-
- if (!nameValue) {
- showFeedback("Provide a currency name.", "error");
- return;
- }
-
- if (!editingCode) {
- if (!codeValue || codeValue.length !== 3) {
- showFeedback("Provide a three-letter currency code.", "error");
- return;
- }
- }
-
- const payload = editingCode
- ? {
- name: nameValue,
- symbol: symbolValue,
- is_active: isActive,
- }
- : {
- code: codeValue,
- name: nameValue,
- symbol: symbolValue,
- is_active: isActive,
- };
-
- const targetCode = editingCode || codeValue;
- const url = editingCode
- ? `${apiBase}/${encodeURIComponent(editingCode)}`
- : `${apiBase}/`;
-
- setButtonLoading(saveButton, true);
- try {
- const response = await fetch(url, {
- method: editingCode ? "PUT" : "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(payload),
- });
-
- if (!response.ok) {
- const message = await parseError(
- response,
- editingCode
- ? "Unable to update the currency."
- : "Unable to create the currency."
- );
- throw new Error(message);
- }
-
- const result = await response.json();
- const updated = upsertCurrency(result);
- defaultCurrencyCode = uppercaseCode(defaultCurrencyCode);
- refreshUI(updated ? updated.code : targetCode);
-
- if (editingCode) {
- showFeedback("Currency updated successfully.");
- if (updated) {
- setFormForCurrency(updated);
- }
- } else {
- showFeedback("Currency created successfully.");
- resetFormState();
- }
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- } finally {
- setButtonLoading(saveButton, false);
- }
- };
-
- const handleToggle = async (code, button) => {
- const record = findCurrency(code);
- if (!record) {
- return;
- }
- hideFeedback();
- const nextState = !record.is_active;
- const url = `${apiBase}/${encodeURIComponent(code)}/activation`;
- setButtonLoading(button, true);
- try {
- const response = await fetch(url, {
- method: "PATCH",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ is_active: nextState }),
- });
-
- if (!response.ok) {
- const message = await parseError(
- response,
- nextState
- ? "Unable to activate the currency."
- : "Unable to deactivate the currency."
- );
- throw new Error(message);
- }
-
- const result = await response.json();
- const updated = upsertCurrency(result);
- refreshUI(updated ? updated.code : code);
- if (existingSelect && existingSelect.value === code && updated) {
- setFormForCurrency(updated);
- }
- const actionMessage = nextState
- ? `Currency ${code} activated.`
- : `Currency ${code} deactivated.`;
- showFeedback(actionMessage);
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- } finally {
- setButtonLoading(button, false);
- }
- };
-
- const handleTableClick = (event) => {
- const button = event.target.closest("button[data-action]");
- if (!button) {
- return;
- }
- const code = uppercaseCode(button.dataset.code);
- const action = button.dataset.action;
- if (!code || !action) {
- return;
- }
- if (action === "edit") {
- const currency = findCurrency(code);
- if (currency) {
- setFormForCurrency(currency);
- hideFeedback();
- if (nameInput) {
- nameInput.focus();
- }
- }
- } else if (action === "toggle") {
- handleToggle(code, button);
- }
- };
-
- applyPayload();
- if (editorSection && editorSection.dataset.defaultCode) {
- defaultCurrencyCode = uppercaseCode(editorSection.dataset.defaultCode);
- currencies = currencies.map((record) => {
- return record
- ? {
- ...record,
- is_default: record.code === defaultCurrencyCode,
- }
- : record;
- });
- }
- apiBase = normalizeApiBase(apiBase);
-
- refreshUI();
-
- if (form) {
- form.addEventListener("submit", handleSubmit);
- }
-
- if (existingSelect) {
- existingSelect.addEventListener("change", (event) => {
- const selectedCode = uppercaseCode(event.target.value);
- if (!selectedCode) {
- hideFeedback();
- resetFormState();
- return;
- }
- const currency = findCurrency(selectedCode);
- if (currency) {
- setFormForCurrency(currency);
- hideFeedback();
- }
- });
- }
-
- if (resetButton) {
- resetButton.addEventListener("click", (event) => {
- event.preventDefault();
- hideFeedback();
- resetFormState();
- });
- }
-
- if (codeInput) {
- codeInput.addEventListener("input", () => {
- const value = uppercaseCode(codeInput.value).slice(0, 3);
- codeInput.value = value;
- });
- }
-
- if (tableBody) {
- tableBody.addEventListener("click", handleTableClick);
- }
-
- fetchCurrenciesFromApi();
-});
diff --git a/static/js/dashboard.js b/static/js/dashboard.js
deleted file mode 100644
index 5cec954..0000000
--- a/static/js/dashboard.js
+++ /dev/null
@@ -1,289 +0,0 @@
-(() => {
- const dataElement = document.getElementById("dashboard-data");
- if (!dataElement) {
- return;
- }
-
- let state = {};
- try {
- state = JSON.parse(dataElement.textContent || "{}");
- } catch (error) {
- console.error("Failed to parse dashboard data", error);
- return;
- }
-
- const statusElement = document.getElementById("dashboard-status");
- const summaryContainer = document.getElementById("summary-metrics");
- const summaryEmpty = document.getElementById("summary-empty");
- const scenarioTableBody = document.querySelector("#scenario-table tbody");
- const scenarioEmpty = document.getElementById("scenario-table-empty");
- const overallMetricsList = document.getElementById("overall-metrics");
- const overallMetricsEmpty = document.getElementById("overall-metrics-empty");
- const recentList = document.getElementById("recent-simulations");
- const recentEmpty = document.getElementById("recent-simulations-empty");
- const maintenanceList = document.getElementById("upcoming-maintenance");
- const maintenanceEmpty = document.getElementById(
- "upcoming-maintenance-empty"
- );
- const refreshButton = document.getElementById("refresh-dashboard");
- const costChartCanvas = document.getElementById("cost-chart");
- const costChartEmpty = document.getElementById("cost-chart-empty");
- const activityChartCanvas = document.getElementById("activity-chart");
- const activityChartEmpty = document.getElementById("activity-chart-empty");
-
- let costChartInstance = null;
- let activityChartInstance = null;
-
- const setStatus = (message, variant = "success") => {
- if (!statusElement) {
- return;
- }
- if (!message) {
- statusElement.hidden = true;
- statusElement.textContent = "";
- statusElement.classList.remove("success", "error");
- return;
- }
- statusElement.textContent = message;
- statusElement.hidden = false;
- statusElement.classList.toggle("success", variant === "success");
- statusElement.classList.toggle("error", variant !== "success");
- };
-
- const renderSummaryMetrics = () => {
- if (!summaryContainer || !summaryEmpty) {
- return;
- }
- summaryContainer.innerHTML = "";
- const metrics = Array.isArray(state.summary_metrics)
- ? state.summary_metrics
- : [];
- metrics.forEach((metric) => {
- const card = document.createElement("article");
- card.className = "metric-card";
- card.innerHTML = `
- ${metric.label}
- ${metric.value}
- `;
- summaryContainer.appendChild(card);
- });
- summaryEmpty.hidden = metrics.length > 0;
- };
-
- const renderScenarioTable = () => {
- if (!scenarioTableBody || !scenarioEmpty) {
- return;
- }
- scenarioTableBody.innerHTML = "";
- const rows = Array.isArray(state.scenario_rows) ? state.scenario_rows : [];
- rows.forEach((row) => {
- const tr = document.createElement("tr");
- tr.innerHTML = `
- ${row.scenario_name} |
- ${row.parameter_display} |
- ${row.equipment_display} |
- ${row.capex_display} |
- ${row.opex_display} |
- ${row.production_display} |
- ${row.consumption_display} |
- ${row.maintenance_display} |
- ${row.iterations_display} |
- ${row.simulation_mean_display} |
- `;
- scenarioTableBody.appendChild(tr);
- });
- scenarioEmpty.hidden = rows.length > 0;
- };
-
- const renderOverallMetrics = () => {
- if (!overallMetricsList || !overallMetricsEmpty) {
- return;
- }
- overallMetricsList.innerHTML = "";
- const items = Array.isArray(state.overall_report_metrics)
- ? state.overall_report_metrics
- : [];
- items.forEach((item) => {
- const li = document.createElement("li");
- li.className = "metric-list-item";
- li.textContent = `${item.label}: ${item.value}`;
- overallMetricsList.appendChild(li);
- });
- overallMetricsEmpty.hidden = items.length > 0;
- };
-
- const renderRecentSimulations = () => {
- if (!recentList || !recentEmpty) {
- return;
- }
- recentList.innerHTML = "";
- const runs = Array.isArray(state.recent_simulations)
- ? state.recent_simulations
- : [];
- runs.forEach((run) => {
- const item = document.createElement("li");
- item.className = "metric-list-item";
- item.textContent = `${run.scenario_name} · ${run.iterations_display} iterations · ${run.mean_display}`;
- recentList.appendChild(item);
- });
- recentEmpty.hidden = runs.length > 0;
- };
-
- const renderMaintenanceReminders = () => {
- if (!maintenanceList || !maintenanceEmpty) {
- return;
- }
- maintenanceList.innerHTML = "";
- const items = Array.isArray(state.upcoming_maintenance)
- ? state.upcoming_maintenance
- : [];
- items.forEach((item) => {
- const li = document.createElement("li");
- li.innerHTML = `
- ${item.equipment_name} · ${item.scenario_name}
- ${item.date_display} · ${item.cost_display} · ${item.description}
- `;
- maintenanceList.appendChild(li);
- });
- maintenanceEmpty.hidden = items.length > 0;
- };
-
- const buildChartConfig = (dataset, overrides = {}) => ({
- type: dataset.type || "bar",
- data: {
- labels: dataset.labels || [],
- datasets: dataset.datasets || [],
- },
- options: Object.assign(
- {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: { position: "top" },
- tooltip: { enabled: true },
- },
- scales: {
- x: { stacked: dataset.stacked ?? false },
- y: { stacked: dataset.stacked ?? false, beginAtZero: true },
- },
- },
- overrides.options || {}
- ),
- });
-
- const renderCharts = () => {
- if (costChartInstance) {
- costChartInstance.destroy();
- }
- if (activityChartInstance) {
- activityChartInstance.destroy();
- }
-
- const costData = state.scenario_cost_chart || {};
- const activityData = state.scenario_activity_chart || {};
-
- if (costChartCanvas && state.cost_chart_has_data) {
- costChartInstance = new Chart(
- costChartCanvas,
- buildChartConfig(costData, {
- options: {
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- callback: (value) =>
- typeof value === "number"
- ? value.toLocaleString(undefined, {
- maximumFractionDigits: 0,
- })
- : value,
- },
- },
- },
- },
- })
- );
- if (costChartEmpty) {
- costChartEmpty.hidden = true;
- }
- costChartCanvas.classList.remove("hidden");
- } else if (costChartEmpty && costChartCanvas) {
- costChartEmpty.hidden = false;
- costChartCanvas.classList.add("hidden");
- }
-
- if (activityChartCanvas && state.activity_chart_has_data) {
- activityChartInstance = new Chart(
- activityChartCanvas,
- buildChartConfig(activityData, {
- options: {
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- callback: (value) =>
- typeof value === "number"
- ? value.toLocaleString(undefined, {
- maximumFractionDigits: 0,
- })
- : value,
- },
- },
- },
- },
- })
- );
- if (activityChartEmpty) {
- activityChartEmpty.hidden = true;
- }
- activityChartCanvas.classList.remove("hidden");
- } else if (activityChartEmpty && activityChartCanvas) {
- activityChartEmpty.hidden = false;
- activityChartCanvas.classList.add("hidden");
- }
- };
-
- const renderView = () => {
- renderSummaryMetrics();
- renderScenarioTable();
- renderOverallMetrics();
- renderRecentSimulations();
- renderMaintenanceReminders();
- renderCharts();
- };
-
- const refreshDashboard = async () => {
- setStatus("Refreshing dashboard…", "success");
- if (refreshButton) {
- refreshButton.classList.add("is-loading");
- }
-
- try {
- const response = await fetch("/ui/dashboard/data", {
- headers: { "X-Requested-With": "XMLHttpRequest" },
- });
-
- if (!response.ok) {
- throw new Error("Unable to refresh dashboard data.");
- }
-
- const payload = await response.json();
- state = payload || {};
- renderView();
- setStatus("Dashboard updated.", "success");
- } catch (error) {
- console.error(error);
- setStatus(error.message || "Failed to refresh dashboard.", "error");
- } finally {
- if (refreshButton) {
- refreshButton.classList.remove("is-loading");
- }
- }
- };
-
- renderView();
-
- if (refreshButton) {
- refreshButton.addEventListener("click", refreshDashboard);
- }
-})();
diff --git a/static/js/equipment.js b/static/js/equipment.js
deleted file mode 100644
index cf2c56d..0000000
--- a/static/js/equipment.js
+++ /dev/null
@@ -1,145 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("equipment-data");
- let equipmentByScenario = {};
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- if (parsed.equipment && typeof parsed.equipment === "object") {
- equipmentByScenario = parsed.equipment;
- }
- }
- } catch (error) {
- console.error("Unable to parse equipment data", error);
- }
- }
-
- const filterSelect = document.getElementById("equipment-scenario-filter");
- const tableWrapper = document.getElementById("equipment-table-wrapper");
- const tableBody = document.getElementById("equipment-table-body");
- const emptyState = document.getElementById("equipment-empty");
- const form = document.getElementById("equipment-form");
- const feedbackEl = document.getElementById("equipment-feedback");
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- feedbackEl.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.classList.add("hidden");
- feedbackEl.textContent = "";
- };
-
- const renderEquipmentRows = (scenarioId) => {
- if (!tableBody || !tableWrapper || !emptyState) {
- return;
- }
-
- const key = String(scenarioId);
- const records = equipmentByScenario[key] || [];
-
- tableBody.innerHTML = "";
-
- if (!records.length) {
- emptyState.textContent = "No equipment recorded for this scenario yet.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- return;
- }
-
- emptyState.classList.add("hidden");
- tableWrapper.classList.remove("hidden");
-
- records.forEach((record) => {
- const row = document.createElement("tr");
- row.innerHTML = `
- ${record.name || "—"} |
- ${record.description || "—"} |
- `;
- tableBody.appendChild(row);
- });
- };
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (!value) {
- if (emptyState && tableWrapper && tableBody) {
- emptyState.textContent =
- "Choose a scenario to review the equipment list.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- tableBody.innerHTML = "";
- }
- return;
- }
- renderEquipmentRows(value);
- });
- }
-
- const submitEquipment = async (event) => {
- event.preventDefault();
- hideFeedback();
-
- if (!form) {
- return;
- }
-
- const formData = new FormData(form);
- const scenarioId = formData.get("scenario_id");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- name: formData.get("name"),
- description: formData.get("description") || null,
- };
-
- try {
- const response = await fetch("/api/equipment/", {
- 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 add equipment record."
- );
- }
-
- const result = await response.json();
- const mapKey = String(result.scenario_id);
-
- if (!Array.isArray(equipmentByScenario[mapKey])) {
- equipmentByScenario[mapKey] = [];
- }
- equipmentByScenario[mapKey].push(result);
-
- form.reset();
- showFeedback("Equipment saved.", "success");
-
- if (filterSelect && filterSelect.value === String(result.scenario_id)) {
- renderEquipmentRows(filterSelect.value);
- }
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- }
- };
-
- if (form) {
- form.addEventListener("submit", submitEquipment);
- }
-
- if (filterSelect && filterSelect.value) {
- renderEquipmentRows(filterSelect.value);
- }
-});
diff --git a/static/js/maintenance.js b/static/js/maintenance.js
deleted file mode 100644
index 9ec5f4a..0000000
--- a/static/js/maintenance.js
+++ /dev/null
@@ -1,243 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("maintenance-data");
- let equipmentByScenario = {};
- let maintenanceByScenario = {};
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- if (parsed.equipment && typeof parsed.equipment === "object") {
- equipmentByScenario = parsed.equipment;
- }
- if (parsed.maintenance && typeof parsed.maintenance === "object") {
- maintenanceByScenario = parsed.maintenance;
- }
- }
- } catch (error) {
- console.error("Unable to parse maintenance data", error);
- }
- }
-
- const filterSelect = document.getElementById("maintenance-scenario-filter");
- const tableWrapper = document.getElementById("maintenance-table-wrapper");
- const tableBody = document.getElementById("maintenance-table-body");
- const emptyState = document.getElementById("maintenance-empty");
- const form = document.getElementById("maintenance-form");
- const feedbackEl = document.getElementById("maintenance-feedback");
- const formScenarioSelect = document.getElementById(
- "maintenance-form-scenario"
- );
- const equipmentSelect = document.getElementById("maintenance-form-equipment");
- const equipmentEmptyState = document.getElementById(
- "maintenance-equipment-empty"
- );
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- feedbackEl.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.classList.add("hidden");
- feedbackEl.textContent = "";
- };
-
- const formatCost = (value) =>
- Number(value).toLocaleString(undefined, {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- });
-
- const formatDate = (value) => {
- if (!value) {
- return "—";
- }
- const parsed = new Date(value);
- if (Number.isNaN(parsed.getTime())) {
- return value;
- }
- return parsed.toLocaleDateString();
- };
-
- const renderMaintenanceRows = (scenarioId) => {
- if (!tableBody || !tableWrapper || !emptyState) {
- return;
- }
-
- const key = String(scenarioId);
- const records = maintenanceByScenario[key] || [];
-
- tableBody.innerHTML = "";
-
- if (!records.length) {
- emptyState.textContent =
- "No maintenance entries recorded for this scenario yet.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- return;
- }
-
- emptyState.classList.add("hidden");
- tableWrapper.classList.remove("hidden");
-
- records.forEach((record) => {
- const row = document.createElement("tr");
- row.innerHTML = `
- ${formatDate(record.maintenance_date)} |
- ${record.equipment_name || "—"} |
- ${formatCost(record.cost)} |
- ${record.description || "—"} |
- `;
- tableBody.appendChild(row);
- });
- };
-
- const populateEquipmentOptions = (scenarioId) => {
- if (!equipmentSelect) {
- return;
- }
-
- equipmentSelect.innerHTML =
- '';
- equipmentSelect.disabled = true;
-
- if (equipmentEmptyState) {
- equipmentEmptyState.classList.add("hidden");
- }
-
- if (!scenarioId) {
- return;
- }
-
- const list = equipmentByScenario[String(scenarioId)] || [];
- if (!list.length) {
- if (equipmentEmptyState) {
- equipmentEmptyState.textContent =
- "Add equipment for this scenario before scheduling maintenance.";
- equipmentEmptyState.classList.remove("hidden");
- }
- return;
- }
-
- list.forEach((item) => {
- const option = document.createElement("option");
- option.value = item.id;
- option.textContent = item.name || `Equipment ${item.id}`;
- equipmentSelect.appendChild(option);
- });
-
- equipmentSelect.disabled = false;
- };
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (!value) {
- if (emptyState && tableWrapper && tableBody) {
- emptyState.textContent =
- "Choose a scenario to review upcoming or completed maintenance.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- tableBody.innerHTML = "";
- }
- return;
- }
- renderMaintenanceRows(value);
- });
- }
-
- if (formScenarioSelect) {
- formScenarioSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- populateEquipmentOptions(value);
- });
- }
-
- const submitMaintenance = async (event) => {
- event.preventDefault();
- hideFeedback();
-
- if (!form) {
- return;
- }
-
- const formData = new FormData(form);
- const scenarioId = formData.get("scenario_id");
- const equipmentId = formData.get("equipment_id");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- equipment_id: equipmentId ? Number(equipmentId) : null,
- maintenance_date: formData.get("maintenance_date"),
- cost: Number(formData.get("cost")),
- description: formData.get("description") || null,
- };
-
- if (!payload.scenario_id || !payload.equipment_id) {
- showFeedback(
- "Select a scenario and equipment before submitting.",
- "error"
- );
- return;
- }
-
- try {
- const response = await fetch("/api/maintenance/", {
- 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 add maintenance entry."
- );
- }
-
- const result = await response.json();
- const mapKey = String(result.scenario_id);
-
- if (!Array.isArray(maintenanceByScenario[mapKey])) {
- maintenanceByScenario[mapKey] = [];
- }
-
- const equipmentList = equipmentByScenario[mapKey] || [];
- const matchedEquipment = equipmentList.find(
- (item) => Number(item.id) === Number(result.equipment_id)
- );
- result.equipment_name = matchedEquipment ? matchedEquipment.name : "";
-
- maintenanceByScenario[mapKey].push(result);
-
- form.reset();
- populateEquipmentOptions(null);
- showFeedback("Maintenance entry saved.", "success");
-
- if (filterSelect && filterSelect.value === String(result.scenario_id)) {
- renderMaintenanceRows(filterSelect.value);
- }
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- }
- };
-
- if (form) {
- form.addEventListener("submit", submitMaintenance);
- }
-
- if (filterSelect && filterSelect.value) {
- renderMaintenanceRows(filterSelect.value);
- }
-
- if (formScenarioSelect && formScenarioSelect.value) {
- populateEquipmentOptions(formScenarioSelect.value);
- }
-});
diff --git a/static/js/parameters.js b/static/js/parameters.js
deleted file mode 100644
index b96d207..0000000
--- a/static/js/parameters.js
+++ /dev/null
@@ -1,124 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("parameters-data");
- let parametersByScenario = {};
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- parametersByScenario = parsed;
- }
- } catch (error) {
- console.error("Unable to parse parameter data", error);
- }
- }
-
- const form = document.getElementById("parameter-form");
- const scenarioSelect = /** @type {HTMLSelectElement | null} */ (
- document.getElementById("scenario_id")
- );
- const nameInput = /** @type {HTMLInputElement | null} */ (
- document.getElementById("name")
- );
- const valueInput = /** @type {HTMLInputElement | null} */ (
- document.getElementById("value")
- );
- const feedback = document.getElementById("parameter-feedback");
- const tableBody = document.getElementById("parameter-table-body");
-
- const setFeedback = (message, variant) => {
- if (!feedback) {
- return;
- }
- feedback.textContent = message;
- feedback.classList.remove("success", "error");
- if (variant) {
- feedback.classList.add(variant);
- }
- };
-
- const renderTable = (scenarioId) => {
- if (!tableBody) {
- return;
- }
- tableBody.innerHTML = "";
- const rows = parametersByScenario[String(scenarioId)] || [];
- if (!rows.length) {
- const emptyRow = document.createElement("tr");
- emptyRow.id = "parameter-empty-state";
- emptyRow.innerHTML =
- 'No parameters recorded for this scenario yet. | ';
- tableBody.appendChild(emptyRow);
- return;
- }
- rows.forEach((row) => {
- const tr = document.createElement("tr");
- tr.innerHTML = `
- ${row.name} |
- ${row.value} |
- ${row.distribution_type ?? "—"} |
- ${
- row.distribution_parameters
- ? JSON.stringify(row.distribution_parameters)
- : "—"
- } |
- `;
- tableBody.appendChild(tr);
- });
- };
-
- if (scenarioSelect) {
- renderTable(scenarioSelect.value);
- scenarioSelect.addEventListener("change", () =>
- renderTable(scenarioSelect.value)
- );
- }
-
- if (!form || !scenarioSelect || !nameInput || !valueInput) {
- return;
- }
-
- form.addEventListener("submit", async (event) => {
- event.preventDefault();
-
- const scenarioId = scenarioSelect.value;
- const payload = {
- scenario_id: Number(scenarioId),
- name: nameInput.value.trim(),
- value: Number(valueInput.value),
- };
-
- if (!payload.name) {
- setFeedback("Parameter name is required.", "error");
- return;
- }
-
- if (!Number.isFinite(payload.value)) {
- setFeedback("Enter a numeric value.", "error");
- return;
- }
-
- const response = await fetch("/api/parameters/", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(payload),
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- setFeedback(`Error saving parameter: ${errorText}`, "error");
- return;
- }
-
- const data = await response.json();
- const scenarioKey = String(scenarioId);
- parametersByScenario[scenarioKey] = parametersByScenario[scenarioKey] || [];
- parametersByScenario[scenarioKey].push(data);
-
- form.reset();
- scenarioSelect.value = scenarioKey;
- renderTable(scenarioKey);
- nameInput.focus();
- setFeedback("Parameter saved.", "success");
- });
-});
diff --git a/static/js/production.js b/static/js/production.js
deleted file mode 100644
index 9d19a41..0000000
--- a/static/js/production.js
+++ /dev/null
@@ -1,204 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("production-data");
- let data = { scenarios: [], production: {}, unit_options: [] };
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- data = {
- scenarios: Array.isArray(parsed.scenarios) ? parsed.scenarios : [],
- production:
- parsed.production && typeof parsed.production === "object"
- ? parsed.production
- : {},
- unit_options: Array.isArray(parsed.unit_options)
- ? parsed.unit_options
- : [],
- };
- }
- } catch (error) {
- console.error("Unable to parse production data", error);
- }
- }
-
- const productionByScenario = data.production;
- const filterSelect = document.getElementById("production-scenario-filter");
- const tableWrapper = document.getElementById("production-table-wrapper");
- const tableBody = document.getElementById("production-table-body");
- const emptyState = document.getElementById("production-empty");
- const form = document.getElementById("production-form");
- const feedbackEl = document.getElementById("production-feedback");
- const unitSelect = document.getElementById("production-form-unit");
- const unitSymbolInput = document.getElementById("production-form-unit-symbol");
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- feedbackEl.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.classList.add("hidden");
- feedbackEl.textContent = "";
- };
-
- const formatAmount = (value) =>
- Number(value).toLocaleString(undefined, {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- });
-
- const formatMeasurement = (amount, symbol, name) => {
- if (symbol) {
- return `${formatAmount(amount)} ${symbol}`;
- }
- if (name) {
- return `${formatAmount(amount)} ${name}`;
- }
- return formatAmount(amount);
- };
-
- const renderProductionRows = (scenarioId) => {
- if (!tableBody || !tableWrapper || !emptyState) {
- return;
- }
-
- const key = String(scenarioId);
- const records = productionByScenario[key] || [];
-
- tableBody.innerHTML = "";
-
- if (!records.length) {
- emptyState.textContent =
- "No production output recorded for this scenario yet.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- return;
- }
-
- emptyState.classList.add("hidden");
- tableWrapper.classList.remove("hidden");
-
- records.forEach((record) => {
- const row = document.createElement("tr");
- row.innerHTML = `
- ${formatMeasurement(
- record.amount,
- record.unit_symbol,
- record.unit_name
- )} |
- ${record.description || "—"} |
- `;
- tableBody.appendChild(row);
- });
- };
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (!value) {
- if (emptyState && tableWrapper && tableBody) {
- emptyState.textContent =
- "Choose a scenario to review its production output.";
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- tableBody.innerHTML = "";
- }
- return;
- }
- renderProductionRows(value);
- });
- }
-
- const submitProduction = async (event) => {
- event.preventDefault();
- hideFeedback();
-
- if (!form) {
- return;
- }
-
- const formData = new FormData(form);
- const scenarioId = formData.get("scenario_id");
- const unitName = formData.get("unit_name");
- const unitSymbol = formData.get("unit_symbol");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- amount: Number(formData.get("amount")),
- description: formData.get("description") || null,
- unit_name: unitName ? String(unitName) : null,
- unit_symbol: unitSymbol ? String(unitSymbol) : null,
- };
-
- try {
- const response = await fetch("/api/production/", {
- 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 add production output record."
- );
- }
-
- const result = await response.json();
- const mapKey = String(result.scenario_id);
-
- if (!Array.isArray(productionByScenario[mapKey])) {
- productionByScenario[mapKey] = [];
- }
- productionByScenario[mapKey].push(result);
-
- form.reset();
- syncUnitSelection();
- showFeedback("Production output saved.", "success");
-
- if (filterSelect && filterSelect.value === String(result.scenario_id)) {
- renderProductionRows(filterSelect.value);
- }
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- }
- };
-
- if (form) {
- form.addEventListener("submit", submitProduction);
- }
-
- const syncUnitSelection = () => {
- if (!unitSelect || !unitSymbolInput) {
- return;
- }
- if (!unitSelect.value && unitSelect.options.length > 0) {
- const firstOption = Array.from(unitSelect.options).find(
- (option) => option.value
- );
- if (firstOption) {
- firstOption.selected = true;
- }
- }
- const selectedOption = unitSelect.options[unitSelect.selectedIndex];
- unitSymbolInput.value = selectedOption
- ? selectedOption.getAttribute("data-symbol") || ""
- : "";
- };
-
- if (unitSelect) {
- unitSelect.addEventListener("change", syncUnitSelection);
- syncUnitSelection();
- }
-
- if (filterSelect && filterSelect.value) {
- renderProductionRows(filterSelect.value);
- }
-});
diff --git a/static/js/reporting.js b/static/js/reporting.js
deleted file mode 100644
index 3ca2f64..0000000
--- a/static/js/reporting.js
+++ /dev/null
@@ -1,149 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("reporting-data");
- let reportingSummaries = [];
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "[]");
- if (Array.isArray(parsed)) {
- reportingSummaries = parsed;
- }
- } catch (error) {
- console.error("Unable to parse reporting data", error);
- }
- }
-
- const REPORT_FIELDS = [
- { key: "iterations", label: "Iterations", decimals: 0 },
- { key: "mean", label: "Mean Result", decimals: 2 },
- { key: "variance", label: "Variance", decimals: 2 },
- { key: "std_dev", label: "Std. Dev", decimals: 2 },
- { key: "percentile_5", label: "Percentile 5", decimals: 2 },
- { key: "percentile_95", label: "Percentile 95", decimals: 2 },
- { key: "value_at_risk_95", label: "Value at Risk (95%)", decimals: 2 },
- {
- key: "expected_shortfall_95",
- label: "Expected Shortfall (95%)",
- decimals: 2,
- },
- ];
-
- const tableWrapper = document.getElementById("reporting-table-wrapper");
- const tableBody = document.getElementById("reporting-table-body");
- const emptyState = document.getElementById("reporting-empty");
- const refreshButton = document.getElementById("report-refresh");
- const feedbackEl = document.getElementById("report-feedback");
-
- const formatNumber = (value, decimals = 2) => {
- if (value === null || value === undefined || Number.isNaN(Number(value))) {
- return "—";
- }
- return Number(value).toLocaleString(undefined, {
- minimumFractionDigits: decimals,
- maximumFractionDigits: decimals,
- });
- };
-
- const showFeedback = (message, type = "success") => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- feedbackEl.classList.add(type);
- };
-
- const hideFeedback = () => {
- if (!feedbackEl) {
- return;
- }
- feedbackEl.classList.add("hidden");
- feedbackEl.textContent = "";
- };
-
- const renderReportingTable = (summaryData) => {
- if (!tableBody || !tableWrapper || !emptyState) {
- return;
- }
-
- tableBody.innerHTML = "";
-
- if (!summaryData.length) {
- emptyState.classList.remove("hidden");
- tableWrapper.classList.add("hidden");
- return;
- }
-
- emptyState.classList.add("hidden");
- tableWrapper.classList.remove("hidden");
-
- summaryData.forEach((entry) => {
- const row = document.createElement("tr");
- const scenarioCell = document.createElement("td");
- scenarioCell.textContent = entry.scenario_name;
- row.appendChild(scenarioCell);
-
- REPORT_FIELDS.forEach((field) => {
- const cell = document.createElement("td");
- const source = field.key === "iterations" ? entry : entry.summary || {};
- cell.textContent = formatNumber(source[field.key], field.decimals);
- row.appendChild(cell);
- });
-
- tableBody.appendChild(row);
- });
- };
-
- const refreshMetrics = async () => {
- hideFeedback();
- showFeedback("Refreshing metrics…", "success");
-
- try {
- const response = await fetch("/ui/reporting", {
- method: "GET",
- headers: { "X-Requested-With": "XMLHttpRequest" },
- });
-
- if (!response.ok) {
- throw new Error("Unable to refresh reporting data.");
- }
-
- const text = await response.text();
- const parser = new DOMParser();
- const doc = parser.parseFromString(text, "text/html");
- const newTable = doc.querySelector("#reporting-table-wrapper");
- const newFeedback = doc.querySelector("#report-feedback");
-
- if (!newTable) {
- throw new Error("Unexpected response while refreshing.");
- }
-
- const newEmptyState = doc.querySelector("#reporting-empty");
-
- if (emptyState && newEmptyState) {
- emptyState.className = newEmptyState.className;
- emptyState.textContent = newEmptyState.textContent;
- }
-
- if (tableWrapper) {
- tableWrapper.className = newTable.className;
- tableWrapper.innerHTML = newTable.innerHTML;
- }
-
- if (newFeedback && feedbackEl) {
- feedbackEl.className = newFeedback.className;
- feedbackEl.textContent = newFeedback.textContent;
- }
-
- showFeedback("Metrics refreshed.", "success");
- } catch (error) {
- showFeedback(error.message || "An unexpected error occurred.", "error");
- }
- };
-
- renderReportingTable(reportingSummaries);
-
- if (refreshButton) {
- refreshButton.addEventListener("click", refreshMetrics);
- }
-});
diff --git a/static/js/scenario-form.js b/static/js/scenario-form.js
deleted file mode 100644
index f27722a..0000000
--- a/static/js/scenario-form.js
+++ /dev/null
@@ -1,78 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const form = document.getElementById("scenario-form");
- if (!form) {
- return;
- }
-
- const nameInput = /** @type {HTMLInputElement | null} */ (
- document.getElementById("name")
- );
- const descriptionInput = /** @type {HTMLInputElement | null} */ (
- document.getElementById("description")
- );
- const table = document.getElementById("scenario-table");
- const tableBody = document.getElementById("scenario-table-body");
- const emptyState = document.getElementById("empty-state");
-
- form.addEventListener("submit", async (event) => {
- event.preventDefault();
-
- if (!nameInput || !descriptionInput) {
- return;
- }
-
- const payload = {
- name: nameInput.value.trim(),
- description: descriptionInput.value.trim() || null,
- };
-
- if (!payload.name) {
- return;
- }
-
- const response = await fetch("/api/scenarios/", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(payload),
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- console.error("Scenario creation failed", errorText);
- return;
- }
-
- const data = await response.json();
- const row = document.createElement("tr");
- row.dataset.scenarioId = String(data.id);
- row.innerHTML = `
- ${data.name} |
- ${data.description ?? "—"} |
- `;
-
- if (emptyState) {
- emptyState.remove();
- }
-
- if (table) {
- table.classList.remove("hidden");
- table.removeAttribute("aria-hidden");
- }
-
- if (tableBody) {
- tableBody.appendChild(row);
- }
-
- form.reset();
- nameInput.focus();
-
- const feedback = document.getElementById("feedback");
- if (feedback) {
- feedback.textContent = `Scenario "${data.name}" created successfully.`;
- feedback.classList.remove("hidden");
- setTimeout(() => {
- feedback.classList.add("hidden");
- }, 3000);
- }
- });
-});
diff --git a/static/js/settings.js b/static/js/settings.js
deleted file mode 100644
index 8d7d5c6..0000000
--- a/static/js/settings.js
+++ /dev/null
@@ -1,200 +0,0 @@
-(function () {
- const dataScript = document.getElementById("theme-settings-data");
- const form = document.getElementById("theme-settings-form");
- const feedbackEl = document.getElementById("theme-settings-feedback");
- const resetBtn = document.getElementById("theme-settings-reset");
- const panel = document.getElementById("theme-settings");
-
- if (!dataScript || !form || !feedbackEl || !panel) {
- return;
- }
-
- const apiUrl = panel.getAttribute("data-api");
- if (!apiUrl) {
- return;
- }
-
- const parsed = JSON.parse(dataScript.textContent || "{}");
- const currentValues = { ...(parsed.variables || {}) };
- const defaultValues = parsed.defaults || {};
- let envOverrides = { ...(parsed.envOverrides || {}) };
-
- const previewElements = new Map();
- const inputs = Array.from(form.querySelectorAll(".color-value-input"));
-
- inputs.forEach((input) => {
- const key = input.name;
- const field = input.closest(".color-form-field");
- const preview = field ? field.querySelector(".color-preview") : null;
- if (preview) {
- previewElements.set(input, preview);
- }
-
- if (Object.prototype.hasOwnProperty.call(envOverrides, key)) {
- const overrideValue = envOverrides[key];
- input.value = overrideValue;
- input.disabled = true;
- input.setAttribute("aria-disabled", "true");
- input.dataset.envOverride = "true";
- if (field) {
- field.classList.add("is-env-override");
- }
- if (preview) {
- preview.style.background = overrideValue;
- }
- return;
- }
-
- input.addEventListener("input", () => {
- const previewEl = previewElements.get(input);
- if (previewEl) {
- previewEl.style.background = input.value || defaultValues[key] || "";
- }
- });
- });
-
- function setFeedback(message, type) {
- feedbackEl.textContent = message;
- feedbackEl.classList.remove("hidden", "success", "error");
- if (type) {
- feedbackEl.classList.add(type);
- }
- }
-
- function clearFeedback() {
- feedbackEl.textContent = "";
- feedbackEl.classList.add("hidden");
- feedbackEl.classList.remove("success", "error");
- }
-
- function updateRootVariables(values) {
- if (!values) {
- return;
- }
- const root = document.documentElement;
- Object.entries(values).forEach(([key, value]) => {
- if (typeof key === "string" && typeof value === "string") {
- root.style.setProperty(key, value);
- }
- });
- }
-
- function resetTo(source) {
- inputs.forEach((input) => {
- const key = input.name;
- if (input.disabled) {
- const previewEl = previewElements.get(input);
- const fallback = envOverrides[key] || currentValues[key];
- if (previewEl && fallback) {
- previewEl.style.background = fallback;
- }
- return;
- }
- if (Object.prototype.hasOwnProperty.call(source, key)) {
- input.value = source[key];
- const previewEl = previewElements.get(input);
- if (previewEl) {
- previewEl.style.background = source[key];
- }
- }
- });
- }
-
- // Initialize previews to current values after page load.
- resetTo(currentValues);
-
- resetBtn?.addEventListener("click", () => {
- resetTo(defaultValues);
- clearFeedback();
- setFeedback("Reverted to default values. Submit to save.", "success");
- });
-
- form.addEventListener("submit", async (event) => {
- event.preventDefault();
- clearFeedback();
-
- const payload = {};
- inputs.forEach((input) => {
- if (input.disabled) {
- return;
- }
- payload[input.name] = input.value.trim();
- });
-
- try {
- const response = await fetch(apiUrl, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ variables: payload }),
- });
-
- if (!response.ok) {
- let detail = "Unable to save theme settings.";
- try {
- const errorData = await response.json();
- if (errorData?.detail) {
- detail = Array.isArray(errorData.detail)
- ? errorData.detail.map((item) => item.msg || item).join("; ")
- : errorData.detail;
- }
- } catch (parseError) {
- // Ignore JSON parse errors and use default detail message.
- }
- setFeedback(detail, "error");
- return;
- }
-
- const data = await response.json();
- const variables = data?.variables || {};
- const responseOverrides = data?.env_overrides || {};
-
- Object.assign(currentValues, variables);
- envOverrides = { ...responseOverrides };
-
- inputs.forEach((input) => {
- const key = input.name;
- const field = input.closest(".color-form-field");
- const previewEl = previewElements.get(input);
- const isOverride = Object.prototype.hasOwnProperty.call(
- envOverrides,
- key,
- );
-
- if (isOverride) {
- const overrideValue = envOverrides[key];
- input.value = overrideValue;
- if (!input.disabled) {
- input.disabled = true;
- input.setAttribute("aria-disabled", "true");
- }
- if (field) {
- field.classList.add("is-env-override");
- }
- if (previewEl) {
- previewEl.style.background = overrideValue;
- }
- } else if (input.disabled) {
- input.disabled = false;
- input.removeAttribute("aria-disabled");
- if (field) {
- field.classList.remove("is-env-override");
- }
- if (
- previewEl &&
- Object.prototype.hasOwnProperty.call(variables, key)
- ) {
- previewEl.style.background = variables[key];
- }
- }
- });
-
- updateRootVariables(variables);
- resetTo(variables);
- setFeedback("Theme colors updated successfully.", "success");
- } catch (error) {
- setFeedback("Network error: unable to save settings.", "error");
- }
- });
-})();
diff --git a/static/js/simulations.js b/static/js/simulations.js
deleted file mode 100644
index 9e9c8d6..0000000
--- a/static/js/simulations.js
+++ /dev/null
@@ -1,354 +0,0 @@
-document.addEventListener("DOMContentLoaded", () => {
- const dataElement = document.getElementById("simulations-data");
- let simulationScenarios = [];
- let initialRuns = [];
-
- if (dataElement) {
- try {
- const parsed = JSON.parse(dataElement.textContent || "{}");
- if (parsed && typeof parsed === "object") {
- if (Array.isArray(parsed.scenarios)) {
- simulationScenarios = parsed.scenarios;
- }
- if (Array.isArray(parsed.runs)) {
- initialRuns = parsed.runs;
- }
- }
- } catch (error) {
- console.error("Unable to parse simulations data", error);
- }
- }
-
- const SUMMARY_FIELDS = [
- { key: "count", label: "Iterations", decimals: 0 },
- { key: "mean", label: "Mean Result", decimals: 2 },
- { key: "median", label: "Median Result", decimals: 2 },
- { key: "min", label: "Minimum", decimals: 2 },
- { key: "max", label: "Maximum", decimals: 2 },
- { key: "variance", label: "Variance", decimals: 2 },
- { key: "std_dev", label: "Standard Deviation", decimals: 2 },
- { key: "percentile_5", label: "Percentile 5", decimals: 2 },
- { key: "percentile_95", label: "Percentile 95", decimals: 2 },
- { key: "value_at_risk_95", label: "Value at Risk (95%)", decimals: 2 },
- {
- key: "expected_shortfall_95",
- label: "Expected Shortfall (95%)",
- decimals: 2,
- },
- ];
- const SAMPLE_RESULT_LIMIT = 20;
-
- const filterSelect = document.getElementById("simulations-scenario-filter");
- const overviewWrapper = document.getElementById(
- "simulations-overview-wrapper"
- );
- const overviewBody = document.getElementById("simulations-overview-body");
- const overviewEmpty = document.getElementById("simulations-overview-empty");
- const emptyState = document.getElementById("simulations-empty");
- const summaryWrapper = document.getElementById("simulations-summary-wrapper");
- const summaryBody = document.getElementById("simulations-summary-body");
- const summaryEmpty = document.getElementById("simulations-summary-empty");
- const resultsWrapper = document.getElementById("simulations-results-wrapper");
- const resultsBody = document.getElementById("simulations-results-body");
- const resultsEmpty = document.getElementById("simulations-results-empty");
- const simulationForm = document.getElementById("simulation-run-form");
- const simulationFeedback = document.getElementById("simulation-feedback");
- const formScenarioSelect = document.getElementById(
- "simulation-form-scenario"
- );
-
- const simulationRunsMap = Object.create(null);
-
- const getScenarioName = (id) => {
- const match = simulationScenarios.find(
- (scenario) => String(scenario.id) === String(id)
- );
- return match ? match.name : `Scenario ${id}`;
- };
-
- const formatNumber = (value, decimals = 2) => {
- if (value === null || value === undefined || Number.isNaN(Number(value))) {
- return "—";
- }
- return Number(value).toLocaleString(undefined, {
- minimumFractionDigits: decimals,
- maximumFractionDigits: decimals,
- });
- };
-
- const showFeedback = (element, message, type = "success") => {
- if (!element) {
- return;
- }
- element.textContent = message;
- element.classList.remove("hidden", "success", "error");
- element.classList.add(type);
- };
-
- const hideFeedback = (element) => {
- if (!element) {
- return;
- }
- element.classList.add("hidden");
- element.textContent = "";
- };
-
- const initializeRunsMap = () => {
- simulationScenarios.forEach((scenario) => {
- const key = String(scenario.id);
- simulationRunsMap[key] = {
- scenario_id: scenario.id,
- scenario_name: scenario.name,
- iterations: 0,
- summary: null,
- sample_results: [],
- };
- });
-
- initialRuns.forEach((run) => {
- const key = String(run.scenario_id);
- simulationRunsMap[key] = {
- scenario_id: run.scenario_id,
- scenario_name: run.scenario_name || getScenarioName(key),
- iterations: run.iterations || 0,
- summary: run.summary || null,
- sample_results: Array.isArray(run.sample_results)
- ? run.sample_results
- : [],
- };
- });
- };
-
- const renderOverviewTable = () => {
- if (!overviewBody) {
- return;
- }
-
- overviewBody.innerHTML = "";
-
- if (!simulationScenarios.length) {
- if (overviewWrapper) {
- overviewWrapper.classList.add("hidden");
- }
- if (overviewEmpty) {
- overviewEmpty.classList.remove("hidden");
- }
- return;
- }
-
- if (overviewWrapper) {
- overviewWrapper.classList.remove("hidden");
- }
- if (overviewEmpty) {
- overviewEmpty.classList.add("hidden");
- }
-
- simulationScenarios.forEach((scenario) => {
- const key = String(scenario.id);
- const run = simulationRunsMap[key];
- const iterations = run && run.iterations ? run.iterations : 0;
- const meanValue =
- iterations && run && run.summary ? run.summary.mean : null;
-
- const row = document.createElement("tr");
- row.innerHTML = `
- ${scenario.name} |
- ${iterations || 0} |
- ${iterations ? formatNumber(meanValue) : "—"} |
- `;
- overviewBody.appendChild(row);
- });
- };
-
- const renderScenarioDetails = (scenarioId) => {
- if (!scenarioId) {
- if (emptyState) {
- emptyState.classList.remove("hidden");
- }
- if (summaryWrapper) {
- summaryWrapper.classList.add("hidden");
- }
- if (summaryEmpty) {
- summaryEmpty.classList.add("hidden");
- }
- if (resultsWrapper) {
- resultsWrapper.classList.add("hidden");
- }
- if (resultsEmpty) {
- resultsEmpty.classList.add("hidden");
- }
- return;
- }
-
- if (emptyState) {
- emptyState.classList.add("hidden");
- }
-
- const run = simulationRunsMap[String(scenarioId)];
- const summary = run ? run.summary : null;
- const samples = run ? run.sample_results || [] : [];
-
- if (!summary) {
- if (summaryWrapper) {
- summaryWrapper.classList.add("hidden");
- }
- if (summaryEmpty) {
- summaryEmpty.classList.remove("hidden");
- }
- } else {
- if (summaryWrapper) {
- summaryWrapper.classList.remove("hidden");
- }
- if (summaryEmpty) {
- summaryEmpty.classList.add("hidden");
- }
-
- if (summaryBody) {
- summaryBody.innerHTML = "";
- SUMMARY_FIELDS.forEach((field) => {
- const row = document.createElement("tr");
- row.innerHTML = `
- ${field.label} |
- ${formatNumber(summary[field.key], field.decimals)} |
- `;
- summaryBody.appendChild(row);
- });
- }
- }
-
- if (!samples.length) {
- if (resultsWrapper) {
- resultsWrapper.classList.add("hidden");
- }
- if (resultsEmpty) {
- resultsEmpty.classList.remove("hidden");
- }
- } else {
- if (resultsWrapper) {
- resultsWrapper.classList.remove("hidden");
- }
- if (resultsEmpty) {
- resultsEmpty.classList.add("hidden");
- }
-
- if (resultsBody) {
- resultsBody.innerHTML = "";
- samples.slice(0, SAMPLE_RESULT_LIMIT).forEach((item, index) => {
- const row = document.createElement("tr");
- row.innerHTML = `
- ${index + 1} |
- ${formatNumber(item)} |
- `;
- resultsBody.appendChild(row);
- });
- }
- }
- };
-
- const runSimulation = async (event) => {
- event.preventDefault();
- hideFeedback(simulationFeedback);
-
- if (!simulationForm) {
- return;
- }
-
- const formData = new FormData(simulationForm);
- const scenarioId = formData.get("scenario_id");
- const payload = {
- scenario_id: scenarioId ? Number(scenarioId) : null,
- iterations: Number(formData.get("iterations")),
- seed: formData.get("seed") ? Number(formData.get("seed")) : null,
- };
-
- if (!payload.scenario_id) {
- showFeedback(
- simulationFeedback,
- "Select a scenario before running a simulation.",
- "error"
- );
- return;
- }
-
- try {
- const response = await fetch("/api/simulations/", {
- 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 run simulation.");
- }
-
- const result = await response.json();
- const mapKey = String(result.scenario_id);
- const summary =
- result.summary && typeof result.summary === "object"
- ? result.summary
- : null;
- const iterations =
- summary && typeof summary.count === "number"
- ? summary.count
- : payload.iterations || 0;
-
- simulationRunsMap[mapKey] = {
- scenario_id: result.scenario_id,
- scenario_name: getScenarioName(mapKey),
- iterations,
- summary,
- sample_results: Array.isArray(result.sample_results)
- ? result.sample_results
- : [],
- };
-
- renderOverviewTable();
- renderScenarioDetails(mapKey);
-
- if (filterSelect) {
- filterSelect.value = mapKey;
- }
- if (formScenarioSelect) {
- formScenarioSelect.value = mapKey;
- }
-
- simulationForm.reset();
- showFeedback(simulationFeedback, "Simulation completed.", "success");
- } catch (error) {
- showFeedback(
- simulationFeedback,
- error.message || "An unexpected error occurred.",
- "error"
- );
- }
- };
-
- initializeRunsMap();
- renderOverviewTable();
-
- if (filterSelect) {
- filterSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- renderScenarioDetails(value);
- });
- }
-
- if (formScenarioSelect) {
- formScenarioSelect.addEventListener("change", (event) => {
- const value = event.target.value;
- if (filterSelect) {
- filterSelect.value = value;
- }
- renderScenarioDetails(value);
- });
- }
-
- if (simulationForm) {
- simulationForm.addEventListener("submit", runSimulation);
- }
-
- if (filterSelect && filterSelect.value) {
- renderScenarioDetails(filterSelect.value);
- }
-});