Files
Control_Panel/server/frontend/js/dashboard.js
T
2026-07-15 18:37:19 +09:00

137 lines
6.2 KiB
JavaScript

// Dashboard Business logic and data visualization for ChemiFactory MES
document.addEventListener("DOMContentLoaded", () => {
fetchAPIStatus();
loadDashboardData();
});
const BACKEND_URL = `${window.location.protocol}//${window.location.host}`;
// API 연결 상태 확인
async function fetchAPIStatus() {
const badge = document.getElementById("api-status-badge");
try {
const response = await fetch(`${BACKEND_URL}/api/status`);
if (response.ok) {
badge.textContent = "시스템 정상";
badge.className = "badge badge-online";
} else {
throw new Error("HTTP Status Error");
}
} catch (e) {
badge.textContent = "연결 실패";
badge.className = "badge badge-offline";
}
}
// 대시보드 데이터 바인딩
async function loadDashboardData() {
try {
// 1. 고객사 개수 로드 (API: /customers/)
const custRes = await fetch(`${BACKEND_URL}/customers/`);
if (custRes.ok) {
const list = await custRes.json();
document.getElementById("customer-count").textContent = `${list.length} 개사`;
}
// 2. 재고 정보 로드 (API: /inventory/)
const invRes = await fetch(`${BACKEND_URL}/inventory/`);
if (invRes.ok) {
const list = await invRes.json();
// 품절되거나 소진 임계에 도달한 재고 경보 카운트
const lowStockCount = list.filter(item => item.is_depleted === 1 || item.stock <= 10).length;
document.getElementById("low-inventory-count").textContent = `${lowStockCount} 건`;
}
// 3. 설비 리스트 및 작동 요약 로드 (⚠️ 제외 대상에 따른 대시보드 미니 가동기기 출력 유지)
const equipRes = await fetch(`${BACKEND_URL}/equipment/`);
let totalEquip = 0;
let activeEquip = 0;
if (equipRes.ok) {
const list = await equipRes.json();
totalEquip = list.length;
// 미니 HMI 대시보드 리스트 생성
const hmiContainer = document.getElementById("equipment-mini-list");
if (list.length === 0) {
hmiContainer.innerHTML = `<div style="text-align: center; color: var(--text-muted); padding: 30px 0;">등록된 설비가 없습니다.</div>`;
} else {
hmiContainer.innerHTML = ""; // 초기화
list.forEach(eq => {
const isOnline = eq.status === "available" || eq.is_online; // 활성화 체크 필드
if (isOnline) activeEquip++;
const dotClass = isOnline ? "dot-green" : "dot-red";
const statusText = isOnline ? "가동준비" : "점검필요";
const miniItem = document.createElement("div");
miniItem.className = "hmi-mini-card";
miniItem.innerHTML = `
<div>
<strong style="display:block; font-size:15px;">${eq.name || eq.id}</strong>
<span style="font-size:12px; color:var(--text-secondary);">${eq.status || "상태미필"}</span>
</div>
<div style="display:flex; align-items:center; gap:8px;">
<span style="font-size:13px; color:var(--text-secondary);">${statusText}</span>
<span class="indicator-dot ${dotClass}"></span>
</div>
`;
hmiContainer.appendChild(miniItem);
});
}
document.getElementById("active-equip-count").textContent = `${activeEquip} / ${totalEquip}`;
}
// 4. 생산 지시 리스트 및 실적 요약 (API: /plans/)
const planRes = await fetch(`${BACKEND_URL}/plans/`);
if (planRes.ok) {
const list = await planRes.json();
const planTable = document.getElementById("plan-list");
// 실적 합계 계산
let totalActualQty = 0;
let totalTargetQty = 0;
if (list.length === 0) {
planTable.innerHTML = `<tr><td colspan="5" style="text-align: center; color: var(--text-muted); padding: 30px 0;">등록된 생산 지시가 없습니다.</td></tr>`;
} else {
planTable.innerHTML = ""; // 초기화
// 최신 계획 5개만 대시보드에 표기
const recentPlans = list.slice(0, 5);
recentPlans.forEach(plan => {
// actual_quantity 속성이 없는 경우 baseline 0kg으로 방어
const actual = plan.actual_quantity || 0;
const target = plan.requested_quantity_kg || 0;
totalActualQty += actual;
totalTargetQty += target;
const row = document.createElement("tr");
row.innerHTML = `
<td><strong>Plan #${plan.plan_id}</strong></td>
<td>${plan.plan_name || "-"}</td>
<td>${target.toLocaleString()} kg</td>
<td>${actual.toLocaleString()} kg</td>
<td>
<span class="badge ${plan.status === 'Completed' ? 'badge-online' : 'badge-offline'}">
${plan.status === 'Completed' ? '완료' : '예정'}
</span>
</td>
`;
planTable.appendChild(row);
});
}
// 오늘의 생산량 카드 바인딩
document.getElementById("today-prod").textContent = `${totalActualQty.toLocaleString()} kg`;
const progress = totalTargetQty > 0 ? Math.round((totalActualQty / totalTargetQty) * 100) : 0;
const statCard = document.querySelector(".stat-card:nth-child(2) .stat-desc");
if (statCard) {
statCard.textContent = `전체 목표 대비 진행도 ${progress}%`;
}
}
} catch (e) {
console.error("Failed to load dashboard statistics:", e);
}
}