137 lines
6.2 KiB
JavaScript
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);
|
|
}
|
|
}
|