< Summary - Frontend Coverage Report (Windows)

Information
Class: src\components\MachineList.js
Assembly: Default
File(s): src\components\MachineList.js
Line coverage
99%
Covered lines: 145
Uncovered lines: 1
Coverable lines: 146
Total lines: 146
Line coverage: 99.3%
Branch coverage
79%
Covered branches: 23
Total branches: 29
Branch coverage: 79.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

src\components\MachineList.js

#LineLine coverage
 11import React, { useState } from 'react';
 12import { useMaintenance, useMaintenanceDispatch } from '../context/MaintenanceContext.js';
 13import { useLiveSearch } from '../context/useHooks.js';
 14
 15export function MachineList() {
 246    const { machines } = useMaintenance();
 247    const dispatch = useMaintenanceDispatch();
 248    const [filterStatus, setFilterStatus] = useState('All');
 249
 2410    // Point 5: Using Custom Hook for search logic
 2411    // Point 4: Inside useLiveSearch, useMemo is handling expensive filtering
 2412    const { query, setQuery, filteredItems } = useLiveSearch(machines || [], 'name');
 2413
 2414    const finalItems = filterStatus === 'All'
 2315        ? filteredItems
 116        : filteredItems.filter(m => m.status === filterStatus);
 2417
 2418    return (
 2419        <div className="machine-list">
 2420            <h2 style={{ marginBottom: '15px' }}>Assets Inventory</h2>
 2421            <div style={{ marginBottom: '20px' }}>
 2422                <input
 2423                    type="text"
 2424                    placeholder="Search assets..."
 2425                    value={query}
 2426                    onChange={(e) => setQuery(e.target.value)}
 2427                    style={{ width: '100%', padding: '10px', borderRadius: '4px', border: '1px solid #ddd', marginBottom
 2428                />
 2429                <div style={{ display: 'flex', gap: '5px' }}>
 2430                    {['All', 'Operational', 'Warning', 'Maintenance'].map(s => (
 9631                        <button
 9632                            key={s}
 9633                            onClick={() => setFilterStatus(s)}
 9634                            style={{
 9635                                padding: '5px 10px',
 9636                                backgroundColor: filterStatus === s ? '#3498db' : '#eee',
 9637                                color: filterStatus === s ? 'white' : 'black',
 9638                                border: 'none',
 9639                                borderRadius: '4px',
 9640                                cursor: 'pointer'
 9641                            }}
 9642                        >
 9643                            {s}
 9644                        </button>
 2445                    ))}
 2446                </div>
 2447            </div>
 2448
 2449            <div style={{ maxHeight: '600px', overflowY: 'auto' }}>
 2450                {finalItems.map((machine) => (
 1051                    <MachineCard key={machine.id} machine={machine} />
 2452                ))}
 2453            </div>
 2454        </div>
 2455    );
 2456}
 157
 1458function MachineCard({ machine }) {
 1459    const dispatch = useMaintenanceDispatch();
 1460    const [showSensors, setShowSensors] = useState(false);
 1461    const [sensors, setSensors] = useState([]);
 1462    const [loading, setLoading] = useState(false);
 1463
 1464    const toggleSensors = async () => {
 265        if (!showSensors) {
 266            setLoading(true);
 267            try {
 268                const res = await fetch(`http://localhost:8080/api/sensors?id=${machine.id}`);
 169                const data = await res.json();
 170                setSensors(data);
 171            } catch (err) {
 172                console.error("Sensor fetch error:", err);
 273            } finally {
 274                setLoading(false);
 275            }
 276        }
 277        setShowSensors(!showSensors);
 278    };
 1479
 1480    return (
 1481        <div style={{
 1482            padding: '15px',
 1483            marginBottom: '15px',
 1484            backgroundColor: '#f8f9fa',
 1485            borderRadius: '10px',
 1486            borderLeft: `6px solid ${machine.status === 'operational' ? '#2ecc71' : machine.status === 'warning' ? '#f1c
 1487            boxShadow: '0 2px 4px rgba(0,0,0,0.05)'
 1488        }}>
 1489            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
 1490                <div>
 1491                    <h4 style={{ margin: 0, fontSize: '1.1rem' }}>{machine.name}</h4>
 1492                    <div style={{ fontSize: '0.8rem', color: '#7f8c8d' }}>📍 {machine.location} | ID: {machine.id}</div>
 1493                </div>
 1494                <div style={{ textAlign: 'right' }}>
 1495                    <span style={{
 1496                        padding: '4px 8px',
 1497                        borderRadius: '4px',
 1498                        backgroundColor: machine.status === 'operational' ? '#e1f9e1' : '#fff4e5',
 1499                        color: machine.status === 'operational' ? '#1e7e34' : '#d39e00',
 14100                        fontSize: '0.75rem',
 14101                        fontWeight: 'bold',
 14102                        textTransform: 'uppercase'
 14103                    }}>
 14104                        {machine.status}
 14105                    </span>
 14106                    <div style={{ marginTop: '10px' }}>
 14107                        <button
 14108                            onClick={toggleSensors}
 14109                            style={{ fontSize: '0.75rem', marginRight: '10px', border: 'none', background: 'none', color
 14110                        >
 14111                            {showSensors ? 'CLOSE TELEMETRY' : 'VIEW SENSORS'}
 14112                        </button>
 14113                        {localStorage.getItem('user_role') === 'admin' && (
 1114                            <button
 1115                                onClick={() => dispatch({ type: 'REMOVE_MACHINE', payload: machine.id })}
 1116                                style={{ color: '#e74c3c', border: 'none', background: 'none', cursor: 'pointer', fontSi
 1117                            >
 1118                                Deactivate
 1119                            </button>
 14120                        )}
 14121                    </div>
 14122                </div>
 14123            </div>
 14124
 14125            {showSensors && (
 2126                <div style={{ marginTop: '15px', padding: '10px', backgroundColor: '#fff', borderRadius: '6px', border: 
 2127                    <h5 style={{ margin: '0 0 10px 0', fontSize: '0.8rem', color: '#666' }}>LIVE TELEMETRY</h5>
 2128                    {loading ? (
 0129                        <p style={{ fontSize: '0.8rem' }}>Loading sensors...</p>
 2130                    ) : sensors.length > 0 ? (
 1131                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
 1132                            {sensors.map(s => (
 1133                                <div key={s.sensor_id} style={{ padding: '8px', border: '1px solid #f0f0f0', borderRadiu
 1134                                    <div style={{ fontSize: '0.7rem', color: '#95a5a6' }}>{s.type.toUpperCase()}</div>
 1135                                    <div style={{ fontSize: '1rem', fontWeight: 'bold' }}>{s.value} <span style={{ fontS
 1136                                </div>
 1137                            ))}
 1138                        </div>
 1139                    ) : (
 1140                        <p style={{ fontSize: '0.8rem', color: '#999' }}>No active sensors for this unit.</p>
 2141                    )}
 2142                </div>
 14143            )}
 14144        </div>
 14145    );
 14146}