IIP calculation corrected : GWM
This commit is contained in:
parent
028f728ebd
commit
7608e93334
219
app/controllers/ipiDemo.controller.js
Normal file
219
app/controllers/ipiDemo.controller.js
Normal file
@ -0,0 +1,219 @@
|
||||
const db = require("../models");
|
||||
|
||||
const ManufacturingIpi = db.ManufacturingIpi;
|
||||
const Isic2DigitIndices = db.Isic2DigitIndices;
|
||||
const Isic3DigitIndices = db.Isic3DigitIndices;
|
||||
const Isic4DigitIndices = db.Isic4DigitIndices;
|
||||
const CalculationLog = db.CalculationLog;
|
||||
|
||||
const formatNumber = (value) => {
|
||||
if (value === null || value === undefined || value === "") return null;
|
||||
const parsed = Number(value);
|
||||
if (Number.isNaN(parsed)) return null;
|
||||
return parsed.toFixed(2);
|
||||
};
|
||||
|
||||
exports.renderPage = async (req, res) => {
|
||||
const now = new Date();
|
||||
const year = Number(req.query.year) || now.getFullYear();
|
||||
const month = Number(req.query.month) || now.getMonth() + 1;
|
||||
|
||||
return res.render("ipi-demo", {
|
||||
title: "IPI Calculation Demo Dashboard",
|
||||
defaultYear: year,
|
||||
defaultMonth: month,
|
||||
});
|
||||
};
|
||||
|
||||
exports.getData = async (req, res) => {
|
||||
try {
|
||||
const year = Number(req.query.year);
|
||||
const month = Number(req.query.month);
|
||||
|
||||
if (!year || !month || month < 1 || month > 12) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Valid year and month are required",
|
||||
});
|
||||
}
|
||||
|
||||
const whereClause = { year, month };
|
||||
|
||||
const [manufacturing, isic2, isic3, isic4, itemCount, logs] = await Promise.all([
|
||||
ManufacturingIpi.findOne({ where: whereClause, raw: true }),
|
||||
Isic2DigitIndices.findAll({ where: whereClause, order: [["isic_2digit_code", "ASC"]], raw: true }),
|
||||
Isic3DigitIndices.findAll({ where: whereClause, order: [["isic_3digit_code", "ASC"]], raw: true }),
|
||||
Isic4DigitIndices.findAll({ where: whereClause, order: [["isic_4digit_code", "ASC"]], raw: true }),
|
||||
db.sequelize.query(
|
||||
`SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`,
|
||||
{ replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT }
|
||||
),
|
||||
CalculationLog.findAll({
|
||||
where: { reference_year: year, reference_month: month },
|
||||
order: [["started_at", "DESC"]],
|
||||
raw: true,
|
||||
}),
|
||||
db.sequelize.query(
|
||||
`SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`
|
||||
FROM base_year_production
|
||||
WHERE base_year = 2022
|
||||
ORDER BY product_id
|
||||
LIMIT 25`,
|
||||
{ type: db.Sequelize.QueryTypes.SELECT }
|
||||
),
|
||||
db.sequelize.query(
|
||||
`SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit
|
||||
FROM monthly_production
|
||||
WHERE year = ? AND month = ?
|
||||
ORDER BY product_id
|
||||
LIMIT 50`,
|
||||
{ replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT }
|
||||
),
|
||||
]);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
year,
|
||||
month,
|
||||
summary: {
|
||||
item_count: Number(itemCount[0]?.total || 0),
|
||||
isic4_count: isic4.length,
|
||||
isic3_count: isic3.length,
|
||||
isic2_count: isic2.length,
|
||||
},
|
||||
manufacturing: manufacturing
|
||||
? {
|
||||
...manufacturing,
|
||||
total_weight: formatNumber(manufacturing.total_weight),
|
||||
weighted_index_sum: formatNumber(manufacturing.weighted_index_sum),
|
||||
manufacturing_index: formatNumber(manufacturing.manufacturing_index),
|
||||
mom_change: formatNumber(manufacturing.mom_change),
|
||||
yoy_change: formatNumber(manufacturing.yoy_change),
|
||||
}
|
||||
: null,
|
||||
isic2: isic2.map((row) => ({
|
||||
...row,
|
||||
total_weight: formatNumber(row.total_weight),
|
||||
weighted_index_sum: formatNumber(row.weighted_index_sum),
|
||||
isic_2digit_index: formatNumber(row.isic_2digit_index),
|
||||
})),
|
||||
isic3: isic3.map((row) => ({
|
||||
...row,
|
||||
total_weight: formatNumber(row.total_weight),
|
||||
weighted_index_sum: formatNumber(row.weighted_index_sum),
|
||||
isic_3digit_index: formatNumber(row.isic_3digit_index),
|
||||
})),
|
||||
isic4: isic4.map((row) => ({
|
||||
...row,
|
||||
total_weight: formatNumber(row.total_weight),
|
||||
weighted_index_sum: formatNumber(row.weighted_index_sum),
|
||||
isic_4digit_index: formatNumber(row.isic_4digit_index),
|
||||
})),
|
||||
logs,
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to load IPI demo data",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.getTableData = async (req, res) => {
|
||||
try {
|
||||
const table = String(req.query.table || "");
|
||||
const year = Number(req.query.year);
|
||||
const month = Number(req.query.month);
|
||||
const baseYear = Number(req.query.baseYear) || 2022;
|
||||
const page = Math.max(1, Number(req.query.page) || 1);
|
||||
const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 50));
|
||||
const search = String(req.query.search || "").trim();
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const queryConfig = {
|
||||
monthly: {
|
||||
whereSql: `year = ? AND month = ?`,
|
||||
whereParams: [year, month],
|
||||
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ? OR month_name LIKE ?)` : "",
|
||||
searchParams: search ? [`%${search}%`, `%${search}%`, `%${search}%`] : [],
|
||||
countSql: `SELECT COUNT(*) AS total FROM monthly_production WHERE year = ? AND month = ?`,
|
||||
dataSql: `SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit
|
||||
FROM monthly_production
|
||||
WHERE year = ? AND month = ?`,
|
||||
orderSql: ` ORDER BY product_id`,
|
||||
},
|
||||
baseyear: {
|
||||
whereSql: `base_year = ?`,
|
||||
whereParams: [baseYear],
|
||||
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "",
|
||||
searchParams: search ? [`%${search}%`, `%${search}%`] : [],
|
||||
countSql: `SELECT COUNT(*) AS total FROM base_year_production WHERE base_year = ?`,
|
||||
dataSql: `SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`
|
||||
FROM base_year_production
|
||||
WHERE base_year = ?`,
|
||||
orderSql: ` ORDER BY product_id`,
|
||||
},
|
||||
items: {
|
||||
whereSql: `year = ? AND month = ?`,
|
||||
whereParams: [year, month],
|
||||
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "",
|
||||
searchParams: search ? [`%${search}%`, `%${search}%`] : [],
|
||||
countSql: `SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`,
|
||||
dataSql: `SELECT product_id, product_hs_code, current_production, base_year_avg_production, production_relative, item_index
|
||||
FROM item_level_indices
|
||||
WHERE year = ? AND month = ?`,
|
||||
orderSql: ` ORDER BY product_id`,
|
||||
},
|
||||
};
|
||||
|
||||
if (!queryConfig[table]) {
|
||||
return res.status(400).json({ success: false, message: "Invalid table parameter" });
|
||||
}
|
||||
|
||||
if ((table === "monthly" || table === "items") && (!year || !month || month < 1 || month > 12)) {
|
||||
return res.status(400).json({ success: false, message: "Valid year and month are required" });
|
||||
}
|
||||
|
||||
const cfg = queryConfig[table];
|
||||
const whereParams = [...cfg.whereParams, ...cfg.searchParams];
|
||||
|
||||
const [countResult] = await db.sequelize.query(
|
||||
cfg.countSql + (search ? cfg.searchSql.replace(" AND ", " AND ") : ""),
|
||||
{ replacements: whereParams, type: db.Sequelize.QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
const rows = await db.sequelize.query(
|
||||
cfg.dataSql + cfg.searchSql + cfg.orderSql + " LIMIT ? OFFSET ?",
|
||||
{ replacements: [...whereParams, pageSize, offset], type: db.Sequelize.QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
const formattedRows = rows.map((row) => {
|
||||
const copy = { ...row };
|
||||
Object.keys(copy).forEach((k) => {
|
||||
if (
|
||||
["production_quantity", "avg_by_production", "current_production", "base_year_avg_production", "production_relative", "item_index", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"].includes(k)
|
||||
) {
|
||||
copy[k] = formatNumber(copy[k]);
|
||||
}
|
||||
});
|
||||
return copy;
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
table,
|
||||
page,
|
||||
pageSize,
|
||||
total: Number(countResult.total || 0),
|
||||
rows: formattedRows,
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to load table data",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
308
app/views/ipi-demo.ejs
Normal file
308
app/views/ipi-demo.ejs
Normal file
@ -0,0 +1,308 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%= title %></title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 0; background: #f5f7fb; color: #1f2937; }
|
||||
.wrap { max-width: 1300px; margin: 20px auto; padding: 0 16px; }
|
||||
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px; margin-bottom: 14px; }
|
||||
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
||||
input, button { padding: 8px 10px; border: 1px solid #d1d5db; border-radius: 6px; }
|
||||
button { background: #1d4ed8; border-color: #1d4ed8; color: #fff; cursor: pointer; }
|
||||
button:hover { background: #1e40af; }
|
||||
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
th, td { border: 1px solid #e5e7eb; padding: 8px; text-align: left; }
|
||||
th { background: #f9fafb; }
|
||||
.muted { color: #6b7280; font-size: 13px; }
|
||||
.grid4 { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 10px; }
|
||||
.kpi { background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 8px; padding: 10px; }
|
||||
.kpi b { display: block; font-size: 18px; margin-top: 2px; }
|
||||
.err { color: #b91c1c; }
|
||||
.ok { color: #065f46; }
|
||||
.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
|
||||
.tabs button { background: #fff; color: #111827; border: 1px solid #d1d5db; }
|
||||
.tabs button.active { background: #1d4ed8; color: #fff; border-color: #1d4ed8; }
|
||||
.panel { display: none; }
|
||||
.panel.active { display: block; }
|
||||
.pager { display: flex; gap: 8px; align-items: center; margin: 10px 0; flex-wrap: wrap; }
|
||||
.pager input { width: 220px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="card">
|
||||
<h2 style="margin:0 0 8px 0;">IPI Calculation Demo Dashboard</h2>
|
||||
<div class="muted">Use Swagger to trigger calculation first, then view data here by year/month.</div>
|
||||
<div class="row" style="margin-top:12px;">
|
||||
<label>Year <input id="year" type="number" value="<%= defaultYear %>" min="2000" max="2100" /></label>
|
||||
<label>Month <input id="month" type="number" value="<%= defaultMonth %>" min="1" max="12" /></label>
|
||||
<button id="loadBtn">Load Data</button>
|
||||
<span id="status" class="muted"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="grid4">
|
||||
<div class="kpi">Item-level rows<b id="kpiItem">-</b></div>
|
||||
<div class="kpi">ISIC 4-digit rows<b id="kpi4">-</b></div>
|
||||
<div class="kpi">ISIC 3-digit rows<b id="kpi3">-</b></div>
|
||||
<div class="kpi">ISIC 2-digit rows<b id="kpi2">-</b></div>
|
||||
</div>
|
||||
<div style="margin-top:12px;">
|
||||
<table>
|
||||
<thead><tr><th>Manufacturing Index</th><th>MoM %</th><th>YoY %</th><th>Total Weight</th><th>Weighted Index Sum</th></tr></thead>
|
||||
<tbody id="manufacturingBody"><tr><td colspan="5" class="muted">No data loaded.</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="isic4">ISIC 4-digit</button>
|
||||
<button class="tab" data-tab="isic3">ISIC 3-digit</button>
|
||||
<button class="tab" data-tab="isic2">ISIC 2-digit</button>
|
||||
<button class="tab" data-tab="monthly">Monthly Production</button>
|
||||
<button class="tab" data-tab="baseyear">Base Year Production</button>
|
||||
<button class="tab" data-tab="items">Item Level Indices</button>
|
||||
<button class="tab" data-tab="logs">Calculation Logs</button>
|
||||
</div>
|
||||
|
||||
<div id="tableControls" class="pager" style="display:none;">
|
||||
<input id="tableSearch" type="text" placeholder="Search product_id / hs_code..." />
|
||||
<button id="searchBtn">Search</button>
|
||||
<button id="prevBtn">Prev</button>
|
||||
<button id="nextBtn">Next</button>
|
||||
<span id="pageInfo" class="muted"></span>
|
||||
</div>
|
||||
|
||||
<div id="panel-isic4" class="panel active"></div>
|
||||
<div id="panel-isic3" class="panel"></div>
|
||||
<div id="panel-isic2" class="panel"></div>
|
||||
<div id="panel-monthly" class="panel"></div>
|
||||
<div id="panel-baseyear" class="panel"></div>
|
||||
<div id="panel-items" class="panel"></div>
|
||||
<div id="panel-logs" class="panel"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const statusEl = document.getElementById("status");
|
||||
const pagedTabs = new Set(["monthly", "baseyear", "items"]);
|
||||
const tableState = {
|
||||
monthly: { page: 1, pageSize: 50, total: 0, search: "" },
|
||||
baseyear: { page: 1, pageSize: 50, total: 0, search: "" },
|
||||
items: { page: 1, pageSize: 50, total: 0, search: "" },
|
||||
};
|
||||
let activeTab = "isic4";
|
||||
|
||||
function tableHtml(columns, rows) {
|
||||
if (!rows || rows.length === 0) return '<div class="muted">No rows found.</div>';
|
||||
const head = `<thead><tr>${columns.map(c => `<th>${c.label}</th>`).join("")}</tr></thead>`;
|
||||
const bodyRows = rows.map(r => `<tr>${columns.map(c => `<td>${r[c.key] ?? ""}</td>`).join("")}</tr>`).join("");
|
||||
return `<table>${head}<tbody>${bodyRows}</tbody></table>`;
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
document.getElementById("kpiItem").textContent = data.summary.item_count;
|
||||
document.getElementById("kpi4").textContent = data.summary.isic4_count;
|
||||
document.getElementById("kpi3").textContent = data.summary.isic3_count;
|
||||
document.getElementById("kpi2").textContent = data.summary.isic2_count;
|
||||
|
||||
const m = data.manufacturing;
|
||||
const mBody = document.getElementById("manufacturingBody");
|
||||
if (!m) {
|
||||
mBody.innerHTML = `<tr><td colspan="5" class="muted">No manufacturing index for selected period.</td></tr>`;
|
||||
} else {
|
||||
mBody.innerHTML = `<tr>
|
||||
<td>${m.manufacturing_index ?? ""}</td>
|
||||
<td>${m.mom_change ?? ""}</td>
|
||||
<td>${m.yoy_change ?? ""}</td>
|
||||
<td>${m.total_weight ?? ""}</td>
|
||||
<td>${m.weighted_index_sum ?? ""}</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
document.getElementById("panel-isic4").innerHTML = tableHtml(
|
||||
[
|
||||
{ key: "isic_4digit_code", label: "ISIC 4 Code" },
|
||||
{ key: "isic_description", label: "Description" },
|
||||
{ key: "total_weight", label: "Total Weight" },
|
||||
{ key: "weighted_index_sum", label: "Weighted Index Sum" },
|
||||
{ key: "isic_4digit_index", label: "ISIC 4 Index" },
|
||||
],
|
||||
data.isic4
|
||||
);
|
||||
|
||||
document.getElementById("panel-isic3").innerHTML = tableHtml(
|
||||
[
|
||||
{ key: "isic_3digit_code", label: "ISIC 3 Code" },
|
||||
{ key: "isic_description", label: "Description" },
|
||||
{ key: "total_weight", label: "Total Weight" },
|
||||
{ key: "weighted_index_sum", label: "Weighted Index Sum" },
|
||||
{ key: "isic_3digit_index", label: "ISIC 3 Index" },
|
||||
],
|
||||
data.isic3
|
||||
);
|
||||
|
||||
document.getElementById("panel-isic2").innerHTML = tableHtml(
|
||||
[
|
||||
{ key: "isic_2digit_code", label: "ISIC 2 Code" },
|
||||
{ key: "isic_description", label: "Description" },
|
||||
{ key: "total_weight", label: "Total Weight" },
|
||||
{ key: "weighted_index_sum", label: "Weighted Index Sum" },
|
||||
{ key: "isic_2digit_index", label: "ISIC 2 Index" },
|
||||
],
|
||||
data.isic2
|
||||
);
|
||||
|
||||
document.getElementById("panel-monthly").innerHTML = '<div class="muted">Loading monthly production...</div>';
|
||||
document.getElementById("panel-baseyear").innerHTML = '<div class="muted">Loading base year production...</div>';
|
||||
document.getElementById("panel-items").innerHTML = '<div class="muted">Loading item level indices...</div>';
|
||||
|
||||
document.getElementById("panel-logs").innerHTML = tableHtml(
|
||||
[
|
||||
{ key: "calculation_type", label: "Type" },
|
||||
{ key: "status", label: "Status" },
|
||||
{ key: "records_processed", label: "Records" },
|
||||
{ key: "started_at", label: "Started At" },
|
||||
{ key: "completed_at", label: "Completed At" },
|
||||
{ key: "error_message", label: "Error" },
|
||||
],
|
||||
data.logs
|
||||
);
|
||||
|
||||
loadPagedTable("monthly");
|
||||
loadPagedTable("baseyear");
|
||||
loadPagedTable("items");
|
||||
refreshTableControls();
|
||||
}
|
||||
|
||||
function getColumnsForTable(tab) {
|
||||
if (tab === "monthly") {
|
||||
return [
|
||||
{ key: "product_id", label: "Product ID" },
|
||||
{ key: "product_hs_code", label: "HS Code" },
|
||||
{ key: "year", label: "Year" },
|
||||
{ key: "month", label: "Month" },
|
||||
{ key: "month_name", label: "Month Name" },
|
||||
{ key: "production_quantity", label: "Production Qty" },
|
||||
{ key: "unit", label: "Unit" },
|
||||
];
|
||||
}
|
||||
if (tab === "baseyear") {
|
||||
return [
|
||||
{ key: "product_id", label: "Product ID" },
|
||||
{ key: "product_hs_code", label: "HS Code" },
|
||||
{ key: "base_year", label: "Base Year" },
|
||||
{ key: "avg_by_production", label: "Avg Base Year Prod." },
|
||||
{ key: "jan", label: "Jan" }, { key: "feb", label: "Feb" }, { key: "mar", label: "Mar" },
|
||||
{ key: "apr", label: "Apr" }, { key: "may", label: "May" }, { key: "jun", label: "Jun" },
|
||||
{ key: "jul", label: "Jul" }, { key: "aug", label: "Aug" }, { key: "sep", label: "Sep" },
|
||||
{ key: "oct", label: "Oct" }, { key: "nov", label: "Nov" }, { key: "dec", label: "Dec" },
|
||||
];
|
||||
}
|
||||
return [
|
||||
{ key: "product_id", label: "Product ID" },
|
||||
{ key: "product_hs_code", label: "HS Code" },
|
||||
{ key: "current_production", label: "Current Production" },
|
||||
{ key: "base_year_avg_production", label: "Base Avg" },
|
||||
{ key: "production_relative", label: "Relative" },
|
||||
{ key: "item_index", label: "Item Index" },
|
||||
];
|
||||
}
|
||||
|
||||
async function loadPagedTable(tab) {
|
||||
const year = document.getElementById("year").value;
|
||||
const month = document.getElementById("month").value;
|
||||
const panel = document.getElementById(`panel-${tab}`);
|
||||
const state = tableState[tab];
|
||||
panel.innerHTML = '<div class="muted">Loading...</div>';
|
||||
try {
|
||||
const url = `/ipi-demo/table-data?table=${tab}&year=${encodeURIComponent(year)}&month=${encodeURIComponent(month)}&page=${state.page}&pageSize=${state.pageSize}&search=${encodeURIComponent(state.search)}`;
|
||||
const resp = await fetch(url);
|
||||
const data = await resp.json();
|
||||
if (!resp.ok || !data.success) throw new Error(data.message || "Load failed");
|
||||
state.total = data.total;
|
||||
panel.innerHTML = tableHtml(getColumnsForTable(tab), data.rows);
|
||||
if (activeTab === tab) refreshTableControls();
|
||||
} catch (err) {
|
||||
panel.innerHTML = `<div class="err">${err.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTableControls() {
|
||||
const controls = document.getElementById("tableControls");
|
||||
if (!pagedTabs.has(activeTab)) {
|
||||
controls.style.display = "none";
|
||||
return;
|
||||
}
|
||||
controls.style.display = "flex";
|
||||
const st = tableState[activeTab];
|
||||
document.getElementById("tableSearch").value = st.search;
|
||||
const totalPages = Math.max(1, Math.ceil(st.total / st.pageSize));
|
||||
document.getElementById("pageInfo").textContent = `Page ${st.page} / ${totalPages} (Total: ${st.total}, Page size: ${st.pageSize})`;
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
const year = document.getElementById("year").value;
|
||||
const month = document.getElementById("month").value;
|
||||
statusEl.textContent = "Loading...";
|
||||
statusEl.className = "muted";
|
||||
try {
|
||||
const resp = await fetch(`/ipi-demo/data?year=${encodeURIComponent(year)}&month=${encodeURIComponent(month)}`);
|
||||
const data = await resp.json();
|
||||
if (!resp.ok || !data.success) throw new Error(data.message || "Failed to load");
|
||||
render(data);
|
||||
statusEl.textContent = "Loaded successfully";
|
||||
statusEl.className = "ok";
|
||||
} catch (err) {
|
||||
statusEl.textContent = err.message;
|
||||
statusEl.className = "err";
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("loadBtn").addEventListener("click", loadData);
|
||||
document.querySelectorAll(".tab").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
|
||||
document.querySelectorAll(".panel").forEach((p) => p.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
document.getElementById(`panel-${btn.dataset.tab}`).classList.add("active");
|
||||
activeTab = btn.dataset.tab;
|
||||
refreshTableControls();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("searchBtn").addEventListener("click", () => {
|
||||
if (!pagedTabs.has(activeTab)) return;
|
||||
tableState[activeTab].search = document.getElementById("tableSearch").value.trim();
|
||||
tableState[activeTab].page = 1;
|
||||
loadPagedTable(activeTab);
|
||||
});
|
||||
|
||||
document.getElementById("prevBtn").addEventListener("click", () => {
|
||||
if (!pagedTabs.has(activeTab)) return;
|
||||
const st = tableState[activeTab];
|
||||
if (st.page > 1) {
|
||||
st.page -= 1;
|
||||
loadPagedTable(activeTab);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("nextBtn").addEventListener("click", () => {
|
||||
if (!pagedTabs.has(activeTab)) return;
|
||||
const st = tableState[activeTab];
|
||||
const totalPages = Math.max(1, Math.ceil(st.total / st.pageSize));
|
||||
if (st.page < totalPages) {
|
||||
st.page += 1;
|
||||
loadPagedTable(activeTab);
|
||||
}
|
||||
});
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
server.js
10
server.js
@ -13,6 +13,7 @@ const verifySignature = require("./app/middleware/app.middleware");
|
||||
const authController = require("./app/controllers/auth.controller");
|
||||
const establishmentController = require("./app/controllers/establishment.controller");
|
||||
const AutomatedSchedulerService = require('./app/services/scheduler.service');
|
||||
const ipiDemoController = require("./app/controllers/ipiDemo.controller");
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
@ -299,6 +300,15 @@ app.get("/api/test", (req, res) => {
|
||||
const deploymentController = require("./app/controllers/deployment.controller");
|
||||
app.post("/deploy", deploymentController.deployment);
|
||||
|
||||
/**
|
||||
* =========================
|
||||
* IPI DEMO UI
|
||||
* =========================
|
||||
*/
|
||||
app.get("/ipi-demo", ipiDemoController.renderPage);
|
||||
app.get("/ipi-demo/data", ipiDemoController.getData);
|
||||
app.get("/ipi-demo/table-data", ipiDemoController.getTableData);
|
||||
|
||||
|
||||
/**
|
||||
* =========================
|
||||
|
||||
Loading…
Reference in New Issue
Block a user