woms 3.0
This commit is contained in:
212
src/components/WorksheetStats.jsx
Normal file
212
src/components/WorksheetStats.jsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import { FaClock, FaUsers, FaHistory, FaChartLine } from 'react-icons/fa'
|
||||
|
||||
export default function WorksheetStats({ worksheets }) {
|
||||
if (!worksheets || worksheets.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Gesamtarbeitszeit
|
||||
const totalMinutes = worksheets
|
||||
.filter(ws => !ws.isComment)
|
||||
.reduce((sum, ws) => sum + (ws.totalTime || 0), 0)
|
||||
|
||||
// Nach Mitarbeiter gruppieren
|
||||
const byEmployee = worksheets.reduce((acc, ws) => {
|
||||
const empId = ws.employeeId
|
||||
if (!acc[empId]) {
|
||||
acc[empId] = {
|
||||
name: ws.employeeName,
|
||||
short: ws.employeeShort,
|
||||
time: 0,
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
if (!ws.isComment) {
|
||||
acc[empId].time += ws.totalTime || 0
|
||||
}
|
||||
acc[empId].count += 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
// Status-Historie
|
||||
const statusHistory = worksheets
|
||||
.filter(ws => ws.oldStatus && ws.newStatus && ws.oldStatus !== ws.newStatus)
|
||||
.map(ws => ({
|
||||
date: ws.startDate,
|
||||
time: ws.startTime,
|
||||
from: ws.oldStatus,
|
||||
to: ws.newStatus,
|
||||
employee: ws.employeeName
|
||||
}))
|
||||
|
||||
// Service Type Verteilung
|
||||
const byServiceType = worksheets.reduce((acc, ws) => {
|
||||
const type = ws.serviceType || 'Unknown'
|
||||
acc[type] = (acc[type] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
const formatTime = (minutes) => {
|
||||
if (!minutes || minutes === 0) return '0min'
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const mins = minutes % 60
|
||||
return hours > 0 ? `${hours}h ${mins}min` : `${mins}min`
|
||||
}
|
||||
|
||||
const formatTimeShort = (time) => {
|
||||
if (!time || time.length !== 4) return '-'
|
||||
return `${time.substring(0, 2)}:${time.substring(2, 4)}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="worksheet-stats mb-4">
|
||||
<div className="row g-4">
|
||||
{/* Gesamtübersicht */}
|
||||
<div className="col-lg-4 col-md-6">
|
||||
<div className="card h-100 border-0 shadow-sm" style={{
|
||||
background: 'linear-gradient(135deg, #2d3748 0%, #1a202c 100%)',
|
||||
color: 'white'
|
||||
}}>
|
||||
<div className="card-body p-4">
|
||||
<h6 className="card-title mb-3 d-flex align-items-center">
|
||||
<FaChartLine className="me-2" size={20} style={{ color: '#4ade80' }} />
|
||||
<strong>Gesamtübersicht</strong>
|
||||
</h6>
|
||||
<div className="mt-3">
|
||||
<div className="d-flex justify-content-between align-items-center mb-3 pb-2" style={{ borderBottom: '1px solid rgba(255,255,255,0.2)' }}>
|
||||
<span style={{ opacity: 0.9 }}>Worksheets:</span>
|
||||
<strong className="fs-5">{worksheets.length}</strong>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mb-3 pb-2" style={{ borderBottom: '1px solid rgba(255,255,255,0.2)' }}>
|
||||
<span style={{ opacity: 0.9 }}>Arbeitszeit:</span>
|
||||
<strong className="fs-5">{formatTime(totalMinutes)}</strong>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mb-3 pb-2" style={{ borderBottom: '1px solid rgba(255,255,255,0.2)' }}>
|
||||
<span style={{ opacity: 0.9 }}>Kommentare:</span>
|
||||
<strong className="fs-5">{worksheets.filter(ws => ws.isComment).length}</strong>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<span style={{ opacity: 0.9 }}>Ø pro Worksheet:</span>
|
||||
<strong className="fs-5">
|
||||
{formatTime(Math.round(totalMinutes / (worksheets.filter(ws => !ws.isComment).length || 1)))}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nach Mitarbeiter */}
|
||||
<div className="col-lg-4 col-md-6">
|
||||
<div className="card h-100 border-0 shadow-sm" style={{
|
||||
background: 'linear-gradient(135deg, #22c55e 0%, #10b981 100%)',
|
||||
color: 'white'
|
||||
}}>
|
||||
<div className="card-body p-4">
|
||||
<h6 className="card-title mb-3 d-flex align-items-center">
|
||||
<FaUsers className="me-2" size={20} />
|
||||
<strong>Nach Mitarbeiter</strong>
|
||||
</h6>
|
||||
<div className="mt-3">
|
||||
{Object.values(byEmployee).map((emp, idx) => (
|
||||
<div key={idx} className="mb-3 pb-3" style={{ borderBottom: idx < Object.values(byEmployee).length - 1 ? '1px solid rgba(255,255,255,0.2)' : 'none' }}>
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong className="d-block">{emp.name}</strong>
|
||||
{emp.short && (
|
||||
<span className="badge mt-1" style={{
|
||||
background: 'rgba(255,255,255,0.25)'
|
||||
}}>{emp.short}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-end">
|
||||
<div className="fs-5 fw-bold">{formatTime(emp.time)}</div>
|
||||
<small style={{ opacity: 0.8 }}>{emp.count} WS</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Service Type Verteilung */}
|
||||
<div className="col-lg-4 col-md-6">
|
||||
<div className="card h-100 border-0 shadow-sm" style={{
|
||||
background: 'linear-gradient(135deg, #4a5568 0%, #2d3748 100%)',
|
||||
color: 'white'
|
||||
}}>
|
||||
<div className="card-body p-4">
|
||||
<h6 className="card-title mb-3 d-flex align-items-center">
|
||||
<FaClock className="me-2" size={20} style={{ color: '#4ade80' }} />
|
||||
<strong>Service Types</strong>
|
||||
</h6>
|
||||
<div className="mt-3">
|
||||
{Object.entries(byServiceType).map(([type, count], idx) => (
|
||||
<div key={type} className="d-flex justify-content-between align-items-center mb-3 pb-3" style={{ borderBottom: idx < Object.entries(byServiceType).length - 1 ? '1px solid rgba(255,255,255,0.2)' : 'none' }}>
|
||||
<span className="badge px-3 py-2" style={{
|
||||
background: 'rgba(255,255,255,0.25)',
|
||||
fontSize: '0.9rem'
|
||||
}}>{type}</span>
|
||||
<strong className="fs-5">{count}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status-Historie */}
|
||||
{statusHistory.length > 0 && (
|
||||
<div className="card border-0 shadow-sm mt-3" style={{
|
||||
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)'
|
||||
}}>
|
||||
<div className="card-body p-4">
|
||||
<h6 className="card-title text-white mb-3 d-flex align-items-center">
|
||||
<FaHistory className="me-2" size={20} />
|
||||
<strong>Status-Historie</strong>
|
||||
</h6>
|
||||
<div className="table-responsive mt-3">
|
||||
<table className="table table-sm" style={{ borderColor: 'rgba(255,255,255,0.2)' }}>
|
||||
<thead>
|
||||
<tr style={{ color: 'white', borderColor: 'rgba(255,255,255,0.2)' }}>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}>Datum</th>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}>Zeit</th>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}>Von</th>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}></th>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}>Nach</th>
|
||||
<th style={{ borderColor: 'rgba(255,255,255,0.2)' }}>Mitarbeiter</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statusHistory.reverse().map((change, idx) => (
|
||||
<tr key={idx} style={{ color: 'white', borderColor: 'rgba(255,255,255,0.2)' }}>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>{change.date}</td>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>{formatTimeShort(change.time)}</td>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>
|
||||
<span className="badge" style={{
|
||||
background: 'rgba(255,255,255,0.25)'
|
||||
}}>{change.from}</span>
|
||||
</td>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>→</td>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>
|
||||
<span className="badge" style={{
|
||||
background: 'rgba(255,255,255,0.4)',
|
||||
fontWeight: 'bold'
|
||||
}}>{change.to}</span>
|
||||
</td>
|
||||
<td style={{ borderColor: 'rgba(255,255,255,0.2)' }}>{change.employee}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user