| | 1 | 1 | | import React, { useState, useEffect } from 'react'; |
| | 1 | 2 | | import { MaintenanceProvider, useMaintenance, useMaintenanceDispatch } from './context/MaintenanceContext.js'; |
| | 1 | 3 | | import { useMachineStats } from './context/useHooks.js'; |
| | 1 | 4 | | import WelcomeMessage, { Header } from './components/Header.js'; |
| | 1 | 5 | | import { MachineList } from './components/MachineList.js'; |
| | 1 | 6 | | import { MaintenanceForm } from './components/MaintenanceForm.js'; |
| | 1 | 7 | | import { AlertsPanel } from './components/AlertsPanel.js'; |
| | 1 | 8 | | import LoginPage from './components/LoginPage.js'; |
| | 1 | 9 | | import { InventoryPanel } from './components/InventoryPanel.js'; |
| | 1 | 10 | | import { MaintenanceHistory } from './components/MaintenanceHistory.js'; |
| | 1 | 11 | | import { AnalyticsPanel } from './components/AnalyticsPanel.js'; |
| | 1 | 12 | | |
| | 15 | 13 | | function Dashboard() { |
| | 15 | 14 | | const { machines = [], alerts = [], maintenanceLogs = [] } = useMaintenance(); |
| | 15 | 15 | | const dispatch = useMaintenanceDispatch(); |
| | 15 | 16 | | const stats = useMachineStats(); |
| | 15 | 17 | | const [isSyncing, setIsSyncing] = useState(false); |
| | 15 | 18 | | |
| | 15 | 19 | | const syncWithBackend = async () => { |
| | 5 | 20 | | setIsSyncing(true); |
| | 5 | 21 | | try { |
| | 5 | 22 | | const [mRes, iRes, aRes, maintRes] = await Promise.all([ |
| | 5 | 23 | | fetch('http://127.0.0.1:8080/api/machines'), |
| | 5 | 24 | | fetch('http://127.0.0.1:8080/api/inventory'), |
| | 5 | 25 | | fetch('http://127.0.0.1:8080/api/alerts'), |
| | 5 | 26 | | fetch('http://127.0.0.1:8080/api/maintenance') |
| | 5 | 27 | | ]); |
| | 2 | 28 | | |
| | 2 | 29 | | const machinesData = await mRes.json(); |
| | 2 | 30 | | const inventoryData = await iRes.json(); |
| | 2 | 31 | | const alertsData = await aRes.json(); |
| | 2 | 32 | | const maintData = await maintRes.json(); |
| | 2 | 33 | | |
| | 5 | 34 | | dispatch({ type: 'SET_MACHINES', payload: machinesData || [] }); |
| | 5 | 35 | | dispatch({ type: 'SET_INVENTORY', payload: inventoryData || [] }); |
| | 5 | 36 | | dispatch({ type: 'SET_ALERTS', payload: alertsData || [] }); |
| | 5 | 37 | | dispatch({ type: 'SET_MAINTENANCE_LOGS', payload: maintData || [] }); |
| | 5 | 38 | | |
| | 5 | 39 | | console.log("System data synchronized with C backend."); |
| | 5 | 40 | | } catch (error) { |
| | 3 | 41 | | console.error("Backend connection failed:", error); |
| | 5 | 42 | | } finally { |
| | 5 | 43 | | setIsSyncing(false); |
| | 5 | 44 | | } |
| | 5 | 45 | | }; |
| | 15 | 46 | | |
| | 15 | 47 | | useEffect(() => { |
| | 5 | 48 | | syncWithBackend(); |
| | 5 | 49 | | const interval = setInterval(syncWithBackend, 30000); |
| | 5 | 50 | | return () => clearInterval(interval); |
| | 15 | 51 | | }, [dispatch]); |
| | 15 | 52 | | |
| | 15 | 53 | | return ( |
| | 15 | 54 | | <div style={{ padding: '20px', maxWidth: '1440px', margin: '0 auto', backgroundColor: '#f4f7f6', minHeight: '100 |
| | 15 | 55 | | <Header title="Smart Maintenance Control" /> |
| | 15 | 56 | | <WelcomeMessage /> |
| | 15 | 57 | | |
| | 15 | 58 | | {localStorage.getItem('user_role') === 'admin' && ( |
| | 3 | 59 | | <div style={{ display: 'flex', gap: '10px', marginBottom: '15px' }}> |
| | 3 | 60 | | <button |
| | 3 | 61 | | onClick={() => window.open('http://127.0.0.1:8080/api/reports/maintenance')} |
| | 3 | 62 | | style={{ backgroundColor: '#2c3e50', color: 'white', border: 'none', padding: '8px 12px', border |
| | 3 | 63 | | > |
| | 3 | 64 | | 📊 MAINTENANCE (CSV) |
| | 3 | 65 | | </button> |
| | 3 | 66 | | <button |
| | 3 | 67 | | onClick={() => window.open('http://127.0.0.1:8080/api/reports/maintenance/xml')} |
| | 3 | 68 | | style={{ backgroundColor: '#34495e', color: 'white', border: 'none', padding: '8px 12px', border |
| | 3 | 69 | | > |
| | 3 | 70 | | 📂 MAINTENANCE (XML) |
| | 3 | 71 | | </button> |
| | 3 | 72 | | <button |
| | 3 | 73 | | onClick={() => window.open('http://127.0.0.1:8080/api/reports/inventory')} |
| | 3 | 74 | | style={{ backgroundColor: '#2c3e50', color: 'white', border: 'none', padding: '8px 12px', border |
| | 3 | 75 | | > |
| | 3 | 76 | | 📦 INVENTORY (CSV) |
| | 3 | 77 | | </button> |
| | 3 | 78 | | <button |
| | 3 | 79 | | onClick={() => window.open('http://127.0.0.1:8080/api/reports/inventory/xml')} |
| | 3 | 80 | | style={{ backgroundColor: '#34495e', color: 'white', border: 'none', padding: '8px 12px', border |
| | 3 | 81 | | > |
| | 3 | 82 | | 📄 INVENTORY (XML) |
| | 3 | 83 | | </button> |
| | 3 | 84 | | <button |
| | 3 | 85 | | onClick={() => window.print()} |
| | 3 | 86 | | style={{ backgroundColor: '#e67e22', color: 'white', border: 'none', padding: '8px 12px', border |
| | 3 | 87 | | > |
| | 3 | 88 | | 🖨️ PRINT PDF REPORT |
| | 3 | 89 | | </button> |
| | 3 | 90 | | </div> |
| | 15 | 91 | | )} |
| | 15 | 92 | | |
| | 15 | 93 | | <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '15px', margin: '20px 0' }}> |
| | 15 | 94 | | <StatTile label="Total Assets" value={stats?.total || 0} color="#3498db" /> |
| | 15 | 95 | | <StatTile label="Healthy" value={stats?.operational || 0} color="#2ecc71" /> |
| | 15 | 96 | | <StatTile label="Critical Issues" value={alerts.filter(a => a.severity === 'CRITICAL').length} color="#e |
| | 15 | 97 | | <StatTile label="Recent Logs" value={maintenanceLogs.length} color="#9b59b6" /> |
| | 15 | 98 | | </div> |
| | 15 | 99 | | |
| | 15 | 100 | | <AnalyticsPanel /> |
| | 15 | 101 | | |
| | 15 | 102 | | <div style={{ display: 'grid', gridTemplateColumns: '320px 1fr 340px', gap: '20px', marginTop: '20px' }}> |
| | 15 | 103 | | <aside style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}> |
| | 15 | 104 | | <div style={{ backgroundColor: 'white', padding: '15px', borderRadius: '12px', boxShadow: '0 4px 6px |
| | 15 | 105 | | <InventoryPanel /> |
| | 15 | 106 | | </div> |
| | 15 | 107 | | </aside> |
| | 15 | 108 | | |
| | 15 | 109 | | <main style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}> |
| | 15 | 110 | | <div style={{ backgroundColor: 'white', padding: '25px', borderRadius: '12px', boxShadow: '0 4px 20p |
| | 15 | 111 | | <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBott |
| | 15 | 112 | | <h2 style={{ margin: 0 }}>Asset Telemetry</h2> |
| | 15 | 113 | | {isSyncing && <span style={{ fontSize: '0.8em', color: '#3498db', fontWeight: 'bold' }}>📡 S |
| | 15 | 114 | | </div> |
| | 15 | 115 | | <MachineList /> |
| | 15 | 116 | | </div> |
| | 15 | 117 | | <MaintenanceHistory /> |
| | 15 | 118 | | </main> |
| | 15 | 119 | | |
| | 15 | 120 | | <aside style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}> |
| | 15 | 121 | | {localStorage.getItem('user_role') !== 'operator' && <MaintenanceForm />} |
| | 15 | 122 | | <div style={{ backgroundColor: 'white', padding: '15px', borderRadius: '12px', boxShadow: '0 4px 6px |
| | 15 | 123 | | <AlertsPanel /> |
| | 15 | 124 | | </div> |
| | 15 | 125 | | </aside> |
| | 15 | 126 | | </div> |
| | 15 | 127 | | </div> |
| | 15 | 128 | | ); |
| | 15 | 129 | | } |
| | 1 | 130 | | |
| | 60 | 131 | | function StatTile({ label, value, color }) { |
| | 60 | 132 | | return ( |
| | 60 | 133 | | <div style={{ backgroundColor: 'white', padding: '15px', borderRadius: '8px', borderTop: `4px solid ${color}`, b |
| | 60 | 134 | | <small style={{ color: '#7f8c8d', textTransform: 'uppercase', letterSpacing: '1px' }}>{label}</small> |
| | 60 | 135 | | <div style={{ fontSize: '1.5em', fontWeight: 'bold', color: '#2c3e50' }}>{value}</div> |
| | 60 | 136 | | </div> |
| | 60 | 137 | | ); |
| | 60 | 138 | | } |
| | 1 | 139 | | |
| | 12 | 140 | | function App() { |
| | 12 | 141 | | const [isAuthenticated, setIsAuthenticated] = useState(false); |
| | 12 | 142 | | |
| | 12 | 143 | | useEffect(() => { |
| | 6 | 144 | | const token = localStorage.getItem('auth_token'); |
| | 6 | 145 | | if (token) { |
| | 5 | 146 | | setIsAuthenticated(true); |
| | 5 | 147 | | } |
| | 12 | 148 | | }, []); |
| | 12 | 149 | | |
| | 12 | 150 | | const handleLogin = () => { |
| | 0 | 151 | | setIsAuthenticated(true); |
| | 0 | 152 | | }; |
| | 12 | 153 | | |
| | 12 | 154 | | const handleLogout = () => { |
| | 1 | 155 | | localStorage.clear(); |
| | 1 | 156 | | setIsAuthenticated(false); |
| | 1 | 157 | | }; |
| | 12 | 158 | | |
| | 12 | 159 | | return ( |
| | 12 | 160 | | <MaintenanceProvider> |
| | 12 | 161 | | {!isAuthenticated ? ( |
| | 7 | 162 | | <LoginPage onLogin={handleLogin} /> |
| | 5 | 163 | | ) : ( |
| | 5 | 164 | | <div className="App"> |
| | 5 | 165 | | <div style={{ textAlign: 'right', padding: '10px', backgroundColor: '#2c3e50' }}> |
| | 5 | 166 | | <button |
| | 5 | 167 | | onClick={handleLogout} |
| | 5 | 168 | | style={{ backgroundColor: '#e74c3c', color: 'white', border: 'none', padding: '8px 15px', bo |
| | 5 | 169 | | > |
| | 5 | 170 | | SİSTEMDEN ÇIKIŞ YAP 🔓 |
| | 5 | 171 | | </button> |
| | 5 | 172 | | </div> |
| | 5 | 173 | | <Dashboard /> |
| | 5 | 174 | | </div> |
| | 12 | 175 | | )} |
| | 12 | 176 | | </MaintenanceProvider> |
| | 12 | 177 | | ); |
| | 12 | 178 | | } |
| | 1 | 179 | | |
| | 1 | 180 | | export default App; |