초기화
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user