1343 lines
47 KiB
HTML
1343 lines
47 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>CRI — MIS Dashboard</title>
|
||
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
|
||
|
||
<style>
|
||
/* ============ TOKENS ============ */
|
||
:root{
|
||
|
||
|
||
--bg:#0a1018;
|
||
--panel:#121b28;
|
||
--panel-2:#182333;
|
||
--border:#263547;
|
||
--steel:#3d4d61;
|
||
--text:#e7ecf2;
|
||
--text-dim:#8a97a8;
|
||
--text-faint:#5b6b80;
|
||
|
||
|
||
|
||
--bg:#0a1018;
|
||
--panel:#121b28;
|
||
--panel-2:#182333;
|
||
--border:#263547;
|
||
--steel:#3d4d61;
|
||
--text:#e7ecf2;
|
||
--text-dim:#8a97a8;
|
||
--text-faint:#5b6b80;
|
||
--amber:#10b981;
|
||
--amber-dim:#3a2f1c;
|
||
--green:#35c97a;
|
||
--green-dim:#173226;
|
||
--red:#e8604a;
|
||
--red-dim:#3a1e1a;
|
||
--blue:#4fa3e3;
|
||
--blue-dim:#152736;
|
||
|
||
--display:'Rajdhani',sans-serif;
|
||
--body:'Inter',sans-serif;
|
||
--mono:'JetBrains Mono',monospace;
|
||
|
||
--radius:10px;
|
||
--sidebar-w:252px;
|
||
}
|
||
|
||
body.light-theme{
|
||
--bg:#f5f7fa;
|
||
--panel:#ffffff;
|
||
--panel-2:#f1f5f9;
|
||
--border:#dbe3ec;
|
||
--steel:#94a3b8;
|
||
|
||
--text:#1e293b;
|
||
--text-dim:#475569;
|
||
--text-faint:#64748b;
|
||
|
||
--amber:#f59e0b;
|
||
--amber-dim:#fef3c7;
|
||
|
||
--green:#16a34a;
|
||
--green-dim:#dcfce7;
|
||
|
||
--red:#dc2626;
|
||
--red-dim:#fee2e2;
|
||
|
||
--blue:#2563eb;
|
||
--blue-dim:#dbeafe;
|
||
}
|
||
|
||
.theme-btn{
|
||
width:40px;
|
||
height:40px;
|
||
border:none;
|
||
border-radius:8px;
|
||
cursor:pointer;
|
||
background:var(--panel);
|
||
color:var(--text);
|
||
border:1px solid var(--border);
|
||
transition:.3s;
|
||
}
|
||
|
||
.theme-btn:hover{
|
||
background:var(--panel-2);
|
||
}
|
||
|
||
*{box-sizing:border-box;}
|
||
|
||
body{
|
||
margin:0;
|
||
padding:0;
|
||
background:var(--bg);
|
||
color:var(--text);
|
||
font-family:var(--body);
|
||
min-height:100vh;
|
||
}
|
||
|
||
::selection{background:var(--amber);color:#0a1018;}
|
||
|
||
/*Maintaenance Sub tabs*/
|
||
|
||
/* ============ SUBTABS (used inside Maintenance) ============ */
|
||
.subtabs{
|
||
display:flex;gap:8px;margin-bottom:20px;
|
||
border-bottom:1px solid var(--border);
|
||
}
|
||
.subtab-btn{
|
||
font-family:var(--mono);font-size:12px;letter-spacing:.3px;
|
||
background:none;border:none;color:var(--text-faint);
|
||
padding:10px 16px;cursor:pointer;position:relative;
|
||
display:flex;align-items:center;gap:8px;
|
||
border-bottom:2px solid transparent;margin-bottom:-1px;
|
||
transition:color .18s ease;
|
||
}
|
||
.subtab-btn i{font-size:12px;}
|
||
.subtab-btn:hover{color:var(--text-dim);}
|
||
.subtab-btn.active{color:var(--amber);border-bottom-color:var(--amber);}
|
||
.subtab-page{display:none;animation:fadein .3s ease;}
|
||
.subtab-page.active{display:block;}
|
||
|
||
/* Filter */
|
||
|
||
.filter-panel{
|
||
display:flex;
|
||
flex-wrap:wrap;
|
||
gap:15px;
|
||
background:#fff;
|
||
padding:20px;
|
||
border-radius:10px;
|
||
margin-bottom:20px;
|
||
box-shadow:0 2px 8px rgba(0,0,0,.08);
|
||
}
|
||
|
||
.filter-group{
|
||
display:flex;
|
||
flex-direction:column;
|
||
min-width:200px;
|
||
}
|
||
|
||
.filter-group label{
|
||
font-size:13px;
|
||
font-weight:600;
|
||
margin-bottom:6px;
|
||
}
|
||
|
||
.filter-group input,
|
||
.filter-group select{
|
||
height:42px;
|
||
border:1px solid #ccc;
|
||
border-radius:6px;
|
||
padding:0 12px;
|
||
}
|
||
|
||
.btn-primary{
|
||
margin-top:24px;
|
||
height:42px;
|
||
padding:0 25px;
|
||
border:none;
|
||
border-radius:6px;
|
||
background:#0d6efd;
|
||
color:#fff;
|
||
cursor:pointer;
|
||
}
|
||
|
||
/* new break down */
|
||
|
||
.modal{
|
||
display:none;
|
||
position:fixed;
|
||
left:0;
|
||
top:0;
|
||
width:100%;
|
||
height:100%;
|
||
background:rgba(0,0,0,.45);
|
||
z-index:9999;
|
||
}
|
||
|
||
.modal-content{
|
||
width:850px;
|
||
max-width:95%;
|
||
background:#fff;
|
||
border-radius:10px;
|
||
margin:50px auto;
|
||
overflow:hidden;
|
||
}
|
||
|
||
.modal-header{
|
||
display:flex;
|
||
justify-content:space-between;
|
||
align-items:center;
|
||
padding:18px 25px;
|
||
border-bottom:1px solid #eee;
|
||
}
|
||
|
||
.modal-body{
|
||
padding:25px;
|
||
}
|
||
|
||
.close{
|
||
cursor:pointer;
|
||
font-size:28px;
|
||
}
|
||
|
||
.form-grid{
|
||
display:grid;
|
||
grid-template-columns:repeat(2,1fr);
|
||
gap:20px;
|
||
}
|
||
|
||
.form-grid input,
|
||
.form-grid select,
|
||
textarea{
|
||
width:100%;
|
||
padding:10px;
|
||
border:1px solid #ccc;
|
||
border-radius:6px;
|
||
}
|
||
|
||
.btn-secondary{
|
||
padding:10px 18px;
|
||
border:none;
|
||
background:#999;
|
||
color:#fff;
|
||
border-radius:6px;
|
||
cursor:pointer;
|
||
}
|
||
|
||
/* ============ SIDEBAR ============ */
|
||
|
||
.sidebar{
|
||
position:fixed;
|
||
left:0;
|
||
top:0;
|
||
width:var(--sidebar-w);
|
||
height:100vh;
|
||
|
||
background:var(--panel);
|
||
|
||
border-right:1px solid var(--border);
|
||
display:flex;
|
||
flex-direction:column;
|
||
z-index:10;
|
||
}
|
||
|
||
.logo{
|
||
padding:26px 25px 22px;
|
||
border-bottom:1px solid var(--border);
|
||
position:relative;
|
||
}
|
||
.logo::after{
|
||
content:"";position:absolute;left:0;bottom:-1px;width:64px;height:1px;
|
||
background:var(--amber);
|
||
}
|
||
.logo .mark{
|
||
display:flex;align-items:center;gap:10px;
|
||
font-family:var(--display);font-weight:700;font-size:23px;letter-spacing:0.5px;
|
||
color:var(--text);
|
||
}
|
||
.logo .mark i{
|
||
color:var(--amber);font-size:19px;
|
||
}
|
||
.logo small{
|
||
display:block;color:var(--text-faint);font-size:10.5px;margin-top:6px;
|
||
letter-spacing:2.5px;font-weight:600;text-transform:uppercase;
|
||
font-family:var(--mono);
|
||
}
|
||
|
||
.menu{margin-top:14px;flex:1;}
|
||
|
||
.menu a{
|
||
display:flex;align-items:center;gap:14px;
|
||
color:var(--text-dim);text-decoration:none;
|
||
padding:14px 25px;font-size:14.5px;font-weight:500;
|
||
border-left:3px solid transparent;
|
||
cursor:pointer;position:relative;
|
||
transition:background .18s ease, color .18s ease;
|
||
}
|
||
.menu a i{width:18px;text-align:center;font-size:15px;color:var(--text-faint);transition:color .18s ease;}
|
||
.menu a:hover{background:rgba(255,255,255,.03);color:var(--text);}
|
||
.menu a:hover i{color:var(--text-dim);}
|
||
.menu a.active{
|
||
background:linear-gradient(90deg, rgba(242,169,59,.09), transparent);
|
||
color:var(--text);border-left:3px solid var(--amber);
|
||
}
|
||
.menu a.active i{color:var(--amber);}
|
||
|
||
.sidebar-foot{
|
||
padding:18px 25px 22px;border-top:1px solid var(--border);
|
||
font-family:var(--mono);font-size:11px;color:var(--text-faint);
|
||
display:flex;justify-content:space-between;
|
||
}
|
||
.sidebar-foot .dot{color:var(--green);}
|
||
|
||
/* ============ MAIN ============ */
|
||
.main{margin-left:var(--sidebar-w);}
|
||
|
||
.header{
|
||
height:72px;background:rgba(18,27,40,.7);backdrop-filter:blur(10px);
|
||
display:flex;justify-content:space-between;align-items:center;
|
||
padding:0 32px;border-bottom:1px solid var(--border);
|
||
position:sticky;top:0;z-index:5;
|
||
}
|
||
.header .title-block h2{
|
||
font-family:var(--display);font-weight:600;font-size:20px;color:var(--text);letter-spacing:.3px;
|
||
}
|
||
.header .title-block span{
|
||
font-size:11.5px;color:var(--text-faint);font-family:var(--mono);letter-spacing:1px;
|
||
}
|
||
.header-right{display:flex;align-items:center;gap:22px;}
|
||
.shift-badge{
|
||
font-family:var(--mono);font-size:11.5px;background:var(--panel-2);
|
||
border:1px solid var(--border);color:var(--amber);
|
||
padding:6px 12px;border-radius:20px;letter-spacing:.5px;
|
||
}
|
||
.clock{font-family:var(--mono);font-size:13px;color:var(--text-dim);}
|
||
.user{
|
||
display:flex;align-items:center;gap:10px;font-weight:600;font-size:14px;
|
||
padding-left:20px;border-left:1px solid var(--border);
|
||
}
|
||
.user .avatar{
|
||
width:34px;height:34px;border-radius:50%;
|
||
background:linear-gradient(135deg,var(--amber),#c97f1f);
|
||
display:flex;align-items:center;justify-content:center;
|
||
color:#0a1018;font-family:var(--display);font-weight:700;
|
||
}
|
||
|
||
.content{padding:30px 32px 60px;max-width:1400px;}
|
||
|
||
.page{display:none;animation:fadein .35s ease;}
|
||
.page.active{display:block;}
|
||
@keyframes fadein{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
|
||
|
||
.section-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:20px;}
|
||
.section-head h2{font-family:var(--display);font-weight:600;font-size:24px;letter-spacing:.3px;}
|
||
.section-head p{color:var(--text-faint);font-size:13px;margin-top:4px;}
|
||
.eyebrow{
|
||
font-family:var(--mono);font-size:11px;color:var(--amber);letter-spacing:2px;
|
||
text-transform:uppercase;margin-bottom:6px;
|
||
}
|
||
|
||
/* ============ GAUGE CLUSTER (signature element) ============ */
|
||
.gauge-row{
|
||
display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-bottom:24px;
|
||
}
|
||
.gauge-card{
|
||
background:linear-gradient(180deg,var(--panel),var(--panel-2));
|
||
border:1px solid var(--border);border-radius:var(--radius);
|
||
padding:20px 18px 16px;
|
||
display:flex;flex-direction:column;align-items:center;
|
||
position:relative;overflow:hidden;
|
||
}
|
||
.gauge-card::before{
|
||
content:"";position:absolute;top:0;left:0;right:0;height:2px;
|
||
background:linear-gradient(90deg,transparent,var(--gauge-color,var(--amber)),transparent);
|
||
}
|
||
.gauge-card .g-label{
|
||
font-family:var(--mono);font-size:11px;letter-spacing:1.5px;text-transform:uppercase;
|
||
color:var(--text-faint);margin-bottom:6px;
|
||
}
|
||
.gauge-card svg{width:150px;height:96px;}
|
||
.gauge-card .g-value{
|
||
font-family:var(--display);font-weight:700;font-size:30px;margin-top:-6px;
|
||
color:var(--text);
|
||
}
|
||
.gauge-card .g-value span{font-size:16px;color:var(--text-faint);font-weight:600;}
|
||
.gauge-card .g-sub{font-size:11.5px;color:var(--text-faint);margin-top:2px;}
|
||
|
||
/* ============ CARDS ============ */
|
||
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-bottom:24px;}
|
||
.card{
|
||
background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);
|
||
padding:22px 22px;position:relative;
|
||
}
|
||
.card h4{
|
||
font-family:var(--mono);color:var(--text-faint);font-size:11px;
|
||
letter-spacing:1.5px;text-transform:uppercase;font-weight:500;
|
||
}
|
||
.card h1{
|
||
font-family:var(--display);font-weight:700;color:var(--text);margin-top:12px;font-size:32px;
|
||
}
|
||
.card .trend{font-size:12px;margin-top:8px;font-family:var(--mono);}
|
||
.trend.up{color:var(--green);}
|
||
.trend.down{color:var(--red);}
|
||
|
||
/* ============ PANEL / CHART ============ */
|
||
.panel{
|
||
background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);
|
||
padding:22px 24px;margin-top:22px;
|
||
}
|
||
.panel-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;}
|
||
.panel-head h3{font-family:var(--display);font-weight:600;font-size:16.5px;letter-spacing:.3px;}
|
||
.panel-head .tag{
|
||
font-family:var(--mono);font-size:10.5px;color:var(--text-faint);
|
||
border:1px solid var(--border);padding:3px 9px;border-radius:20px;
|
||
}
|
||
.chart-box{height:280px;position:relative;}
|
||
|
||
/* ============ MACHINE STATUS STRIP ============ */
|
||
.machine-strip{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px;margin-top:22px;}
|
||
.mchip{
|
||
background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);
|
||
padding:14px 16px;display:flex;flex-direction:column;gap:8px;
|
||
}
|
||
.mchip .mtop{display:flex;justify-content:space-between;align-items:center;}
|
||
.mchip .mname{font-family:var(--display);font-weight:600;font-size:14.5px;}
|
||
.mchip .pulse{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 0 rgba(53,201,122,.6);animation:pulse 1.8s infinite;}
|
||
.mchip.idle .pulse{background:var(--text-faint);animation:none;box-shadow:none;}
|
||
.mchip .mval{font-family:var(--mono);font-size:20px;font-weight:700;}
|
||
.mchip .mmeta{font-size:11px;color:var(--text-faint);font-family:var(--mono);}
|
||
@keyframes pulse{
|
||
0%{box-shadow:0 0 0 0 rgba(53,201,122,.55);}
|
||
70%{box-shadow:0 0 0 7px rgba(53,201,122,0);}
|
||
100%{box-shadow:0 0 0 0 rgba(53,201,122,0);}
|
||
}
|
||
|
||
/* ============ TABLE ============ */
|
||
table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;margin-top:18px;}
|
||
table thead th{
|
||
background:var(--panel-2);color:var(--text-dim);text-align:left;
|
||
padding:13px 18px;font-family:var(--mono);font-size:11px;letter-spacing:1px;
|
||
text-transform:uppercase;font-weight:500;border-bottom:1px solid var(--border);
|
||
}
|
||
table td{padding:14px 18px;border-bottom:1px solid var(--border);font-size:13.5px;color:var(--text);}
|
||
table tr:last-child td{border-bottom:none;}
|
||
table tbody tr{transition:background .15s ease;}
|
||
table tbody tr:hover{background:rgba(255,255,255,.02);}
|
||
|
||
.status-pill{
|
||
display:inline-flex;align-items:center;gap:6px;
|
||
font-size:11.5px;font-weight:600;padding:5px 11px;border-radius:20px;
|
||
font-family:var(--mono);letter-spacing:.3px;
|
||
}
|
||
.status-pill.running{background:var(--green-dim);color:var(--green);}
|
||
.status-pill.idle{background:var(--amber-dim);color:var(--amber);}
|
||
.status-pill.down{background:var(--red-dim);color:var(--red);}
|
||
.status-pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;}
|
||
|
||
/* placeholder empty state */
|
||
.empty-state{
|
||
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
||
padding:70px 20px;color:var(--text-faint);
|
||
}
|
||
.empty-state i{font-size:34px;margin-bottom:14px;color:var(--steel);}
|
||
.empty-state h3{font-family:var(--display);color:var(--text-dim);font-size:17px;margin-bottom:6px;}
|
||
.empty-state p{font-size:13px;max-width:340px;text-align:center;}
|
||
|
||
@media(max-width:1100px){
|
||
.gauge-row,.cards{grid-template-columns:repeat(2,1fr);}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ============ SIDEBAR ============ -->
|
||
<div class="sidebar">
|
||
<div class="logo">
|
||
<div class="mark"><i class="fa-solid fa-gauge-high"></i> CRI PUMPS</div>
|
||
<small>Digital Manufacturing</small>
|
||
</div>
|
||
|
||
<div class="menu">
|
||
<a class="active" onclick="showPage('dashboard',this)"><i class="fa-solid fa-chart-line"></i>Dashboard</a>
|
||
<a onclick="showPage('production',this)"><i class="fa-solid fa-industry"></i>Production</a>
|
||
<a onclick="showPage('oee',this)"><i class="fa-solid fa-gauge-high"></i>OEE</a>
|
||
<a onclick="showPage('quality',this)"><i class="fa-solid fa-circle-check"></i>Quality</a>
|
||
<a onclick="showPage('maintenance',this)"><i class="fa-solid fa-wrench"></i>Maintenance</a>
|
||
<a onclick="showPage('reports',this)"><i class="fa-solid fa-file-lines"></i>Reports</a>
|
||
<a onclick="showPage('users',this)"><i class="fa-solid fa-users"></i>Users</a>
|
||
<a href="/"><i class="fa-solid fa-right-from-bracket"></i>Logout</a>
|
||
</div>
|
||
|
||
<div class="sidebar-foot">
|
||
<span><i class="fa-solid fa-circle dot" style="font-size:7px;"></i> System Online</span>
|
||
<span id="footclock">--:--:--</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ MAIN ============ -->
|
||
<div class="main">
|
||
|
||
<div class="header">
|
||
<div class="title-block">
|
||
<h2>Digital Manufacturing — MIS</h2>
|
||
<span id="pageTag">DASHBOARD OVERVIEW</span>
|
||
</div>
|
||
<div class="header-right">
|
||
<button id="themeToggle" class="theme-btn">
|
||
<i class="fa-solid fa-moon"></i>
|
||
</button>
|
||
<div class="shift-badge"><i class="fa-solid fa-clock"></i> Shift A · 06:00–14:00</div>
|
||
<div class="clock" id="headerClock">--:--:--</div>
|
||
<div class="user">
|
||
<div class="avatar">A</div>
|
||
Admin
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content">
|
||
|
||
<!-- ============ DASHBOARD ============ -->
|
||
<div id="dashboard" class="page active">
|
||
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Live Overview</div>
|
||
<h2>Plant Performance</h2>
|
||
</div>
|
||
<div class="tag" style="font-family:var(--mono);font-size:11px;color:var(--text-faint);border:1px solid var(--border);padding:5px 12px;border-radius:20px;">Updated <span id="lastUpdate">just now</span></div>
|
||
</div>
|
||
|
||
<div class="gauge-row" id="gaugeRow"></div>
|
||
|
||
<div class="panel">
|
||
<div class="panel-head">
|
||
<h3>OEE Trend — Last 7 Days</h3>
|
||
<div class="tag">% daily average</div>
|
||
</div>
|
||
<div class="chart-box"><canvas id="oeeTrendChart"></canvas></div>
|
||
</div>
|
||
|
||
<div class="section-head" style="margin-top:30px;">
|
||
<div>
|
||
<div class="eyebrow">Floor Status</div>
|
||
<h2>Machine Line</h2>
|
||
</div>
|
||
</div>
|
||
<div class="machine-strip" id="machineStrip"></div>
|
||
|
||
</div>
|
||
|
||
<!-- ============ PRODUCTION ============ -->
|
||
<div id="production" class="page">
|
||
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Production</div>
|
||
<h2>Production Details</h2>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cards">
|
||
<div class="card">
|
||
<h4>Total Plants</h4>
|
||
<h1>{{ total_plants }}</h1>
|
||
<div class="trend up"><i class="fa-solid fa-arrow-up"></i> Active sites</div>
|
||
</div>
|
||
<div class="card">
|
||
<h4>Total Serial Invoices</h4>
|
||
<h1>{{ total_serial }}</h1>
|
||
<div class="trend up"><i class="fa-solid fa-arrow-up"></i> Processed today</div>
|
||
</div>
|
||
<div class="card">
|
||
<h4>Completed Serial Invoices</h4>
|
||
<h1>{{ scanned_serial }}</h1>
|
||
<div class="trend up"><i class="fa-solid fa-arrow-up"></i> Completed today</div>
|
||
</div>
|
||
<div class="card">
|
||
<h4>Units Produced</h4>
|
||
<h1>2,650</h1>
|
||
<div class="trend up"><i class="fa-solid fa-arrow-up"></i> 4.2% vs yesterday</div>
|
||
</div>
|
||
<div class="card">
|
||
<h4>Downtime</h4>
|
||
<h1>18<span style="font-size:16px;color:var(--text-faint);"> min</span></h1>
|
||
<div class="trend down"><i class="fa-solid fa-arrow-down"></i> Within target</div>
|
||
</div>
|
||
</div>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr><th>Machine</th><th>Shift</th><th>Production</th><th>Status</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Machine 01</td><td>A</td><td>1,450 units</td><td><span class="status-pill running">Running</span></td></tr>
|
||
<tr><td>Machine 02</td><td>A</td><td>1,200 units</td><td><span class="status-pill running">Running</span></td></tr>
|
||
<tr><td>Machine 03</td><td>A</td><td>0 units</td><td><span class="status-pill down">Down</span></td></tr>
|
||
<tr><td>Machine 04</td><td>A</td><td>860 units</td><td><span class="status-pill idle">Idle</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
</div>
|
||
|
||
<!-- ============ OEE ============ -->
|
||
<div id="oee" class="page">
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Efficiency</div>
|
||
<h2>OEE Dashboard</h2>
|
||
</div>
|
||
</div>
|
||
<div class="gauge-row" id="gaugeRowOee"></div>
|
||
<div class="panel">
|
||
<div class="panel-head"><h3>Availability · Performance · Quality — 7 Day</h3><div class="tag">%</div></div>
|
||
<div class="chart-box"><canvas id="apqChart"></canvas></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ QUALITY ============ -->
|
||
<div id="quality" class="page">
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Quality</div>
|
||
<h2>Quality Dashboard</h2>
|
||
</div>
|
||
</div>
|
||
<div class="cards">
|
||
<div class="card"><h4>First Pass Yield</h4><h1>98.4%</h1><div class="trend up"><i class="fa-solid fa-arrow-up"></i> 0.6% vs last week</div></div>
|
||
<div class="card"><h4>Rejected Units</h4><h1>36</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> 12 fewer than yesterday</div></div>
|
||
<div class="card"><h4>Rework</h4><h1>14</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> Stable</div></div>
|
||
<div class="card"><h4>Customer Returns</h4><h1>2</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> Low</div></div>
|
||
</div>
|
||
<div class="panel">
|
||
<div class="panel-head"><h3>Defect Rate — Last 7 Days</h3><div class="tag">ppm</div></div>
|
||
<div class="chart-box"><canvas id="qualityChart"></canvas></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="maintenance" class="page">
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Asset Management</div>
|
||
<h2>Maintenance</h2>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="subtabs">
|
||
<button class="subtab-btn active"
|
||
onclick="showSubtab('breakdownLog',this)">
|
||
<i class="fa-solid fa-screwdriver-wrench"></i>
|
||
Breakdown Log
|
||
</button>
|
||
|
||
<button class="subtab-btn"
|
||
onclick="showSubtab('spareParts',this)">
|
||
<i class="fa-solid fa-boxes-stacked"></i>
|
||
Spare Parts
|
||
</button>
|
||
|
||
<button class="subtab-btn"
|
||
onclick="showSubtab('machineHealth',this)">
|
||
<i class="fa-solid fa-heart-pulse"></i>
|
||
Machine Health
|
||
</button>
|
||
|
||
<button class="subtab-btn"
|
||
onclick="showSubtab('maintenanceHistory',this)">
|
||
<i class="fa-solid fa-clock-rotate-left"></i>
|
||
Maintenance History
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Sub-tab: PM Calendar -->
|
||
<div id="breakdownLog" class="subtab-page active">
|
||
<div style="display:flex;justify-content:flex-end;margin:20px 0;">
|
||
<button class="btn-primary" onclick="openBreakdownModal()">
|
||
<i class="fa-solid fa-plus"></i>
|
||
New Breakdown
|
||
</button>
|
||
</div>
|
||
<div class="cards">
|
||
<div class="card"><h4>Scheduled This Week</h4><h1>4</h1><div class="trend" style="color:var(--text-faint);">Across all lines</div></div>
|
||
<div class="card"><h4>Overdue</h4><h1 style="color:var(--red);">1</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> Needs attention</div></div>
|
||
<div class="card"><h4>Completed This Month</h4><h1>11</h1><div class="trend up"><i class="fa-solid fa-arrow-up"></i> On schedule</div></div>
|
||
<div class="card"><h4>Avg. Completion Time</h4><h1>1h 40<span style="font-size:16px;color:var(--text-faint);">m</span></h1><div class="trend" style="color:var(--text-faint);">Per service</div></div>
|
||
</div>
|
||
|
||
<!-- Breakdown Modal -->
|
||
<div id="breakdownModal" class="modal">
|
||
<div class="modal-content">
|
||
|
||
<div class="modal-header">
|
||
<h3>New Breakdown</h3>
|
||
|
||
<span class="close" onclick="closeBreakdownModal()">
|
||
×
|
||
</span>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
|
||
<form id="breakdownForm">
|
||
|
||
<div class="form-grid">
|
||
|
||
<div>
|
||
<label>Plant</label>
|
||
<select id="plant_id" name="plant_id">
|
||
<option value="">Select Plant</option>
|
||
{% for plant in plants %}
|
||
<option value="{{ plant.id }}">
|
||
{{ plant.name }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label>Line</label>
|
||
<select id="line_id" name="line_id">
|
||
<option value="">Select Line</option>
|
||
{% for line in lines %}
|
||
<option value="{{ line.id }}">
|
||
{{ line.name }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label>Machine</label>
|
||
<input
|
||
type="text"
|
||
id="machine_name"
|
||
name="machine_name"
|
||
placeholder="Machine Name">
|
||
</div>
|
||
|
||
<div>
|
||
<label>Breakdown Date</label>
|
||
<input
|
||
type="date"
|
||
id="breakdown_date"
|
||
name="breakdown_date">
|
||
</div>
|
||
|
||
<div>
|
||
<label>Breakdown Time</label>
|
||
<input
|
||
type="time"
|
||
id="breakdown_time"
|
||
name="breakdown_time">
|
||
</div>
|
||
|
||
<div>
|
||
<label>Down Time</label>
|
||
<input
|
||
type="time"
|
||
id="down_time"
|
||
name="down_time">
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div style="margin-top:20px;">
|
||
|
||
<label>Reason</label>
|
||
|
||
<textarea
|
||
id="reason"
|
||
name="reason"
|
||
rows="4"
|
||
placeholder="Enter breakdown reason"></textarea>
|
||
|
||
</div>
|
||
|
||
<div style="text-align:right;margin-top:20px;">
|
||
|
||
<button
|
||
type="button"
|
||
class="btn-secondary"
|
||
onclick="closeBreakdownModal()">
|
||
|
||
Cancel
|
||
|
||
</button>
|
||
|
||
<button
|
||
type="button"
|
||
class="btn-primary"
|
||
onclick="saveBreakdown()">
|
||
|
||
Save Breakdown
|
||
|
||
</button>
|
||
|
||
</div>
|
||
|
||
</form>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Plant</th>
|
||
<th>Line</th>
|
||
<th>Machine</th>
|
||
<th>Breakdown Date</th>
|
||
<th>Breakdown Time</th>
|
||
<th>Downtime</th>
|
||
<th>Reason</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
|
||
{% for row in breakdowns %}
|
||
|
||
<tr>
|
||
<td>{{ row.plant.name }}</td>
|
||
<td>{{ row.line.name }}</td>
|
||
<td>{{ row.machine_name }}</td>
|
||
<td>{{ row.breakdown_at|date:"d M Y" }}</td>
|
||
<td>{{ row.breakdown_time }}</td>
|
||
<td>{{ row.down_time }}</td>
|
||
<td>{{ row.reason }}</td>
|
||
</tr>
|
||
|
||
{% empty %}
|
||
|
||
<tr>
|
||
<td colspan="7" style="text-align:center;">
|
||
No breakdowns found
|
||
</td>
|
||
</tr>
|
||
|
||
{% endfor %}
|
||
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Sub-tab: Spare Parts -->
|
||
<div id="spareParts" class="subtab-page">
|
||
<div class="cards">
|
||
<div class="card"><h4>Total SKUs Tracked</h4><h1>28</h1><div class="trend" style="color:var(--text-faint);">Critical spares</div></div>
|
||
<div class="card"><h4>Below Reorder Level</h4><h1 style="color:var(--amber);">3</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> Reorder soon</div></div>
|
||
<div class="card"><h4>Out of Stock</h4><h1 style="color:var(--red);">1</h1><div class="trend down"><i class="fa-solid fa-arrow-down"></i> Urgent</div></div>
|
||
<div class="card"><h4>Stock Value</h4><h1 style="font-size:26px;">₹4.6L</h1><div class="trend" style="color:var(--text-faint);">Current inventory</div></div>
|
||
</div>
|
||
|
||
<table>
|
||
<thead><tr><th>Part</th><th>Compatible Machine</th><th>In Stock</th><th>Reorder Level</th><th>Status</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Bearing 6205-ZZ</td><td>Machine 03</td><td>0</td><td>4</td><td><span class="status-pill down">Out of Stock</span></td></tr>
|
||
<tr><td>Drive Belt A-42</td><td>Machine 05</td><td>2</td><td>5</td><td><span class="status-pill idle">Low</span></td></tr>
|
||
<tr><td>Hydraulic Filter HF-12</td><td>Machine 02</td><td>3</td><td>6</td><td><span class="status-pill idle">Low</span></td></tr>
|
||
<tr><td>Motor Coupling MC-8</td><td>Machine 01</td><td>1</td><td>3</td><td><span class="status-pill idle">Low</span></td></tr>
|
||
<tr><td>Proximity Sensor PS-30</td><td>Machine 04</td><td>9</td><td>4</td><td><span class="status-pill running">Sufficient</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Sub-tab: Machine Health -->
|
||
<div id="machineHealth" class="subtab-page">
|
||
<div class="panel" style="margin-top:0;">
|
||
<div class="panel-head">
|
||
<h3>Composite Health Score</h3>
|
||
<div class="tag">vibration · temperature · runtime</div>
|
||
</div>
|
||
<div class="gauge-row" id="gaugeRowHealth"></div>
|
||
</div>
|
||
|
||
<table>
|
||
<thead><tr><th>Machine</th><th>Vibration</th><th>Temperature</th><th>Runtime Hrs (30d)</th><th>Health</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Machine 01</td><td>Normal</td><td>52°C</td><td>612</td><td><span class="status-pill running">Good</span></td></tr>
|
||
<tr><td>Machine 02</td><td>Normal</td><td>55°C</td><td>598</td><td><span class="status-pill running">Good</span></td></tr>
|
||
<tr><td>Machine 03</td><td>High</td><td>71°C</td><td>640</td><td><span class="status-pill down">Critical</span></td></tr>
|
||
<tr><td>Machine 04</td><td>Slightly elevated</td><td>61°C</td><td>580</td><td><span class="status-pill idle">Watch</span></td></tr>
|
||
<tr><td>Machine 05</td><td>Normal</td><td>58°C</td><td>605</td><td><span class="status-pill idle">Watch</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Sub-tab: Maintenance History -->
|
||
<div id="maintenanceHistory" class="subtab-page">
|
||
|
||
<div class="cards">
|
||
|
||
<div class="card">
|
||
<h4>Total Repairs</h4>
|
||
<h1>86</h1>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h4>Total Downtime</h4>
|
||
<h1>74 Hrs</h1>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h4>Most Failed Machine</h4>
|
||
<h1 style="font-size:24px;">Machine 03</h1>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h4>MTTR</h4>
|
||
<h1>48 Min</h1>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<table>
|
||
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Machine</th>
|
||
<th>Issue</th>
|
||
<th>Downtime</th>
|
||
<th>Technician</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
|
||
<tr>
|
||
<td>12 Jul 2026</td>
|
||
<td>Machine 01</td>
|
||
<td>Bearing Replacement</td>
|
||
<td>50 Min</td>
|
||
<td>Rajesh</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td>11 Jul 2026</td>
|
||
<td>Machine 04</td>
|
||
<td>Motor Overheating</td>
|
||
<td>1 Hr 20 Min</td>
|
||
<td>Kumar</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td>10 Jul 2026</td>
|
||
<td>Machine 02</td>
|
||
<td>PLC Fault</td>
|
||
<td>25 Min</td>
|
||
<td>Arun</td>
|
||
</tr>
|
||
|
||
</tbody>
|
||
|
||
</table>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ REPORTS ============ -->
|
||
<div id="reports" class="page">
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Reports</div>
|
||
<h2>Report Center</h2>
|
||
</div>
|
||
</div>
|
||
<div class="panel">
|
||
<div class="empty-state">
|
||
<i class="fa-solid fa-file-lines"></i>
|
||
<h3>No reports generated yet</h3>
|
||
<p>Daily, weekly, and shift-wise production reports will appear here once configured.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ USERS ============ -->
|
||
<div id="users" class="page">
|
||
<div class="section-head">
|
||
<div>
|
||
<div class="eyebrow">Access</div>
|
||
<h2>User Management</h2>
|
||
</div>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Name</th><th>Role</th><th>Status</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Admin</td><td>Administrator</td><td><span class="status-pill running">Active</span></td></tr>
|
||
<tr><td>Supervisor</td><td>Production</td><td><span class="status-pill running">Active</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* ============ PAGE NAV ============ */
|
||
const pageTags = {
|
||
dashboard:'DASHBOARD OVERVIEW', production:'PRODUCTION', oee:'OEE ANALYSIS',
|
||
quality:'QUALITY CONTROL', maintenance:'MAINTENANCE', reports:'REPORTS', users:'USER MANAGEMENT'
|
||
};
|
||
|
||
function showSubtab(id, el){
|
||
const container = el.closest('.page');
|
||
container.querySelectorAll('.subtab-page').forEach(p=>p.classList.remove('active'));
|
||
container.querySelector('#'+id).classList.add('active');
|
||
container.querySelectorAll('.subtab-btn').forEach(b=>b.classList.remove('active'));
|
||
el.classList.add('active');
|
||
}
|
||
|
||
function showPage(id, el){
|
||
document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));
|
||
document.getElementById(id).classList.add('active');
|
||
document.querySelectorAll('.menu a').forEach(a=>a.classList.remove('active'));
|
||
el.classList.add('active');
|
||
document.getElementById('pageTag').textContent = pageTags[id] || '';
|
||
}
|
||
|
||
/* ============ CLOCK ============ */
|
||
function tickClock(){
|
||
const now = new Date();
|
||
const str = now.toLocaleTimeString('en-IN', {hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
||
document.getElementById('headerClock').textContent = str;
|
||
document.getElementById('footclock').textContent = str;
|
||
}
|
||
tickClock();
|
||
setInterval(tickClock, 1000);
|
||
|
||
/* ============ GAUGE BUILDER (signature element) ============ */
|
||
function buildGauge(percent, colorVar){
|
||
// semi-circular instrument gauge, 180deg sweep, tick marks + needle
|
||
const cx=75, cy=88, r=62;
|
||
const startAngle = 180, endAngle = 0; // sweep left to right over top
|
||
const toRad = d => d * Math.PI/180;
|
||
const pt = (ang,radius=r) => [cx + radius*Math.cos(toRad(ang)), cy - radius*Math.sin(toRad(ang))];
|
||
|
||
// arc path helper
|
||
function arcPath(a1,a2,radius){
|
||
const [x1,y1]=pt(a1,radius), [x2,y2]=pt(a2,radius);
|
||
const large = (a1-a2)>180 ? 1:0;
|
||
return `M ${x1} ${y1} A ${radius} ${radius} 0 ${large} 1 ${x2} ${y2}`;
|
||
}
|
||
|
||
// ticks
|
||
let ticks='';
|
||
for(let i=0;i<=10;i++){
|
||
const ang = 180 - i*18;
|
||
const p1 = pt(ang, r+2), p2 = pt(ang, i%5===0? r-9 : r-5);
|
||
ticks += `<line x1="${p1[0]}" y1="${p1[1]}" x2="${p2[0]}" y2="${p2[1]}" stroke="var(--steel)" stroke-width="${i%5===0?2:1}" stroke-linecap="round"/>`;
|
||
}
|
||
|
||
const valueAngle = 180 - (percent/100)*180;
|
||
const [nx,ny] = pt(valueAngle, r-14);
|
||
|
||
const trackPath = arcPath(180,0,r);
|
||
const fillPath = arcPath(180, valueAngle, r);
|
||
|
||
return `
|
||
<svg viewBox="0 0 150 96" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="${trackPath}" fill="none" stroke="var(--panel-2)" stroke-width="9" stroke-linecap="round"/>
|
||
<path d="${fillPath}" fill="none" stroke="${colorVar}" stroke-width="9" stroke-linecap="round"/>
|
||
${ticks}
|
||
<circle cx="${cx}" cy="${cy}" r="4" fill="var(--text-dim)"/>
|
||
<line x1="${cx}" y1="${cy}" x2="${nx}" y2="${ny}" stroke="${colorVar}" stroke-width="2.5" stroke-linecap="round"/>
|
||
</svg>`;
|
||
}
|
||
|
||
function gaugeColor(pct){
|
||
if(pct>=90) return 'var(--green)';
|
||
if(pct>=75) return 'var(--amber)';
|
||
return 'var(--red)';
|
||
}
|
||
|
||
function renderGaugeRow(targetId, metrics){
|
||
const row = document.getElementById(targetId);
|
||
if(!row) return;
|
||
row.innerHTML = metrics.map(m=>{
|
||
const color = gaugeColor(m.value);
|
||
return `
|
||
<div class="gauge-card" style="--gauge-color:${color}">
|
||
<div class="g-label">${m.label}</div>
|
||
${buildGauge(m.value, color)}
|
||
<div class="g-value">${m.value}<span>%</span></div>
|
||
<div class="g-sub">${m.sub}</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
const oeeMetrics = [
|
||
{label:'Availability', value:96, sub:'Target 92%'},
|
||
{label:'Performance', value:93, sub:'Target 90%'},
|
||
{label:'Quality', value:99, sub:'Target 97%'},
|
||
{label:'Overall OEE', value:88, sub:'Target 85%'}
|
||
];
|
||
renderGaugeRow('gaugeRow', oeeMetrics);
|
||
renderGaugeRow('gaugeRowOee', oeeMetrics);
|
||
|
||
/* ============ MACHINE STRIP ============ */
|
||
const machines = [
|
||
{name:'Machine 01', val:'1,450', unit:'units/shift', status:'running'},
|
||
{name:'Machine 02', val:'1,200', unit:'units/shift', status:'running'},
|
||
{name:'Machine 03', val:'0', unit:'stopped 14m', status:'down'},
|
||
{name:'Machine 04', val:'860', unit:'units/shift', status:'idle'},
|
||
{name:'Machine 05', val:'1,320', unit:'units/shift', status:'running'},
|
||
{name:'Machine 06', val:'1,510', unit:'units/shift', status:'running'},
|
||
];
|
||
document.getElementById('machineStrip').innerHTML = machines.map(m=>`
|
||
<div class="mchip ${m.status==='running'?'':'idle'}">
|
||
<div class="mtop"><span class="mname">${m.name}</span><span class="pulse" style="${m.status==='down'?'background:var(--red);':''}"></span></div>
|
||
<div class="mval">${m.val}</div>
|
||
<div class="mmeta">${m.unit}</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
/* ============ CHARTS ============ */
|
||
Chart.defaults.color = '#8a97a8';
|
||
Chart.defaults.font.family = "'Inter',sans-serif";
|
||
|
||
const days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
|
||
|
||
new Chart(document.getElementById('oeeTrendChart'), {
|
||
type:'line',
|
||
data:{
|
||
labels:days,
|
||
datasets:[{
|
||
label:'OEE %',
|
||
data:[84,86,85,88,87,89,88],
|
||
borderColor:'#f2a93b',
|
||
backgroundColor:'rgba(242,169,59,0.12)',
|
||
fill:true, tension:.35, pointRadius:3, pointBackgroundColor:'#f2a93b', borderWidth:2.5
|
||
}]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
plugins:{legend:{display:false}},
|
||
scales:{
|
||
y:{min:70,max:100,grid:{color:'#1e2a3a'},ticks:{callback:v=>v+'%'}},
|
||
x:{grid:{display:false}}
|
||
}
|
||
}
|
||
});
|
||
|
||
new Chart(document.getElementById('apqChart'), {
|
||
type:'line',
|
||
data:{
|
||
labels:days,
|
||
datasets:[
|
||
{label:'Availability',data:[94,95,96,95,97,96,96],borderColor:'#4fa3e3',backgroundColor:'transparent',tension:.35,pointRadius:2,borderWidth:2},
|
||
{label:'Performance',data:[90,91,92,93,92,94,93],borderColor:'#f2a93b',backgroundColor:'transparent',tension:.35,pointRadius:2,borderWidth:2},
|
||
{label:'Quality',data:[98,98,99,99,98,99,99],borderColor:'#35c97a',backgroundColor:'transparent',tension:.35,pointRadius:2,borderWidth:2}
|
||
]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
plugins:{legend:{position:'top',labels:{boxWidth:10,padding:16}}},
|
||
scales:{
|
||
y:{min:80,max:100,grid:{color:'#1e2a3a'},ticks:{callback:v=>v+'%'}},
|
||
x:{grid:{display:false}}
|
||
}
|
||
}
|
||
});
|
||
|
||
new Chart(document.getElementById('qualityChart'), {
|
||
type:'bar',
|
||
data:{
|
||
labels:days,
|
||
datasets:[{
|
||
label:'Defect rate (ppm)',
|
||
data:[420,380,410,350,300,290,260],
|
||
backgroundColor:'#e8604a',
|
||
borderRadius:4,
|
||
maxBarThickness:34
|
||
}]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
plugins:{legend:{display:false}},
|
||
scales:{
|
||
y:{grid:{color:'#1e2a3a'}},
|
||
x:{grid:{display:false}}
|
||
}
|
||
}
|
||
});
|
||
|
||
const themeBtn = document.getElementById("themeToggle");
|
||
|
||
function updateChartTheme(){
|
||
|
||
if (typeof Chart === "undefined") {
|
||
return;
|
||
}
|
||
|
||
if(document.body.classList.contains("light-theme")){
|
||
Chart.defaults.color = "#475569";
|
||
}else{
|
||
Chart.defaults.color = "#8a97a8";
|
||
}
|
||
|
||
}
|
||
|
||
themeBtn.addEventListener("click", () => {
|
||
|
||
document.body.classList.toggle("light-theme");
|
||
|
||
updateChartTheme();
|
||
|
||
if(document.body.classList.contains("light-theme")){
|
||
themeBtn.innerHTML = '<i class="fa-solid fa-sun"></i>';
|
||
}else{
|
||
themeBtn.innerHTML = '<i class="fa-solid fa-moon"></i>';
|
||
}
|
||
|
||
});
|
||
|
||
function openBreakdownModal(){
|
||
document.getElementById("breakdownModal").style.display="block";
|
||
}
|
||
|
||
function closeBreakdownModal(){
|
||
document.getElementById("breakdownModal").style.display="none";
|
||
}
|
||
|
||
window.onclick=function(event){
|
||
|
||
const modal=document.getElementById("breakdownModal");
|
||
|
||
if(event.target===modal){
|
||
modal.style.display="none";
|
||
}
|
||
|
||
}
|
||
|
||
function saveBreakdown() {
|
||
|
||
const data = {
|
||
plant_id: document.getElementById("plant_id").value,
|
||
line_id: document.getElementById("line_id").value,
|
||
machine_name: document.getElementById("machine_name").value,
|
||
breakdown_date: document.getElementById("breakdown_date").value,
|
||
breakdown_time: document.getElementById("breakdown_time").value,
|
||
down_time: document.getElementById("down_time").value,
|
||
reason: document.getElementById("reason").value
|
||
};
|
||
|
||
fetch("/save-breakdown/", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRFToken": getCookie("csrftoken")
|
||
},
|
||
body: JSON.stringify(data)
|
||
})
|
||
.then(response => response.json())
|
||
.then(result => {
|
||
if(result.status === "success"){
|
||
alert("Breakdown saved successfully");
|
||
closeBreakdownModal();
|
||
location.reload();
|
||
}else{
|
||
alert(result.message);
|
||
}
|
||
});
|
||
}
|
||
|
||
function getCookie(name) {
|
||
let cookieValue = null;
|
||
|
||
if (document.cookie && document.cookie !== '') {
|
||
const cookies = document.cookie.split(';');
|
||
|
||
for (let i = 0; i < cookies.length; i++) {
|
||
const cookie = cookies[i].trim();
|
||
|
||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return cookieValue;
|
||
}
|
||
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
|
||
const plantDropdown = document.getElementById("plant_id");
|
||
const lineDropdown = document.getElementById("line_id");
|
||
|
||
plantDropdown.addEventListener("change", function () {
|
||
|
||
const plantId = this.value;
|
||
|
||
lineDropdown.innerHTML =
|
||
'<option value="">Loading...</option>';
|
||
|
||
if (plantId === "") {
|
||
|
||
lineDropdown.innerHTML =
|
||
'<option value="">Select Line</option>';
|
||
|
||
return;
|
||
}
|
||
|
||
fetch("/get-lines/" + plantId + "/")
|
||
|
||
.then(function(response){
|
||
|
||
if(!response.ok){
|
||
throw new Error("Network Error");
|
||
}
|
||
|
||
return response.json();
|
||
|
||
})
|
||
|
||
.then(function(data){
|
||
|
||
lineDropdown.innerHTML =
|
||
'<option value="">Select Line</option>';
|
||
|
||
data.lines.forEach(function(line){
|
||
|
||
const option = document.createElement("option");
|
||
|
||
option.value = line.id;
|
||
option.text = line.name;
|
||
|
||
lineDropdown.appendChild(option);
|
||
|
||
});
|
||
|
||
})
|
||
|
||
.catch(function(error){
|
||
|
||
console.log(error);
|
||
|
||
lineDropdown.innerHTML =
|
||
'<option value="">Unable to load lines</option>';
|
||
|
||
});
|
||
|
||
});
|
||
|
||
});
|
||
|
||
</script>
|
||
|
||
|
||
|
||
</body>
|
||
</html> |