Files
tickte-system/src/components/WorksheetStats.jsx
2025-12-30 20:29:59 +01:00

313 lines
11 KiB
JavaScript

import { FaClock, FaUsers, 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
}, {})
// 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`
}
return (
<div className="worksheet-stats" style={{
display: 'flex',
flexDirection: 'column',
gap: '16px',
height: '100%'
}}>
{/* Gesamtübersicht */}
<div style={{
background: 'rgba(45, 55, 72, 0.5)',
borderRadius: '12px',
padding: '16px',
border: '1px solid rgba(16, 185, 129, 0.2)',
flex: '0 0 auto'
}}>
<h6 style={{
color: 'var(--dark-text)',
marginBottom: '12px',
fontSize: '14px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
<FaChartLine size={16} style={{ color: '#10b981' }} />
Gesamtübersicht
</h6>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px',
background: 'rgba(26, 32, 44, 0.4)',
borderRadius: '6px'
}}>
<span style={{ color: 'rgba(226, 232, 240, 0.8)', fontSize: '12px' }}>Worksheets:</span>
<strong style={{ color: 'var(--dark-text)', fontSize: '16px' }}>{worksheets.length}</strong>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px',
background: 'rgba(26, 32, 44, 0.4)',
borderRadius: '6px'
}}>
<span style={{ color: 'rgba(226, 232, 240, 0.8)', fontSize: '12px' }}>Arbeitszeit:</span>
<strong style={{ color: '#10b981', fontSize: '16px' }}>{formatTime(totalMinutes)}</strong>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px',
background: 'rgba(26, 32, 44, 0.4)',
borderRadius: '6px'
}}>
<span style={{ color: 'rgba(226, 232, 240, 0.8)', fontSize: '12px' }}>Kommentare:</span>
<strong style={{ color: 'var(--dark-text)', fontSize: '16px' }}>{worksheets.filter(ws => ws.isComment).length}</strong>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px',
background: 'rgba(26, 32, 44, 0.4)',
borderRadius: '6px'
}}>
<span style={{ color: 'rgba(226, 232, 240, 0.8)', fontSize: '12px' }}>Ø pro WS:</span>
<strong style={{ color: 'var(--dark-text)', fontSize: '16px' }}>
{formatTime(Math.round(totalMinutes / (worksheets.filter(ws => !ws.isComment).length || 1)))}
</strong>
</div>
</div>
</div>
{/* Mitarbeiter Kombiniertes Diagramm */}
<div style={{
background: 'rgba(45, 55, 72, 0.5)',
borderRadius: '12px',
padding: '16px',
border: '1px solid rgba(16, 185, 129, 0.2)',
flex: '1 1 auto',
minHeight: '200px'
}}>
<h6 style={{
color: 'var(--dark-text)',
marginBottom: '16px',
fontSize: '14px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
<FaUsers size={16} style={{ color: '#10b981' }} />
Mitarbeiter-Statistiken
</h6>
{Object.keys(byEmployee).length === 0 ? (
<div style={{ color: '#a0aec0', textAlign: 'center', padding: '20px' }}>
Keine Mitarbeiter-Daten verfügbar
</div>
) : (
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{(() => {
const employeeArray = Object.values(byEmployee).sort((a, b) => b.time - a.time)
const maxTime = Math.max(...employeeArray.map(e => e.time), 1)
return employeeArray.map((emp, idx) => {
const percentage = maxTime > 0 ? (emp.time / maxTime) * 100 : 0
return (
<div key={idx}>
<div style={{
display: 'flex',
alignItems: 'center',
marginBottom: '6px',
fontSize: '11px'
}}>
<span style={{
color: 'var(--dark-text)',
fontWeight: '500',
display: 'flex',
alignItems: 'center',
gap: '6px',
minWidth: '120px'
}}>
{emp.short && (
<span style={{
background: 'rgba(16, 185, 129, 0.2)',
color: '#10b981',
padding: '2px 6px',
borderRadius: '3px',
fontSize: '9px',
fontWeight: 'bold'
}}>{emp.short}</span>
)}
<span style={{ maxWidth: '100px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{emp.name}
</span>
</span>
</div>
<div style={{
width: '100%',
height: '28px',
background: 'rgba(26, 32, 44, 0.6)',
borderRadius: '6px',
overflow: 'hidden',
position: 'relative',
display: 'flex',
alignItems: 'center'
}}>
{/* WS Anzahl am Anfang */}
<div style={{
position: 'absolute',
left: '8px',
zIndex: 2,
color: 'white',
fontSize: '11px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: '4px'
}}>
<span>WS</span>
<span style={{
background: 'rgba(255, 255, 255, 0.3)',
padding: '2px 6px',
borderRadius: '4px'
}}>{emp.count}</span>
</div>
{/* Balken mit Zeit */}
<div style={{
width: `${percentage}%`,
height: '100%',
background: 'linear-gradient(90deg, #10b981 0%, #059669 100%)',
borderRadius: '6px',
transition: 'width 0.5s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
paddingRight: '8px',
paddingLeft: '60px',
boxShadow: '0 2px 8px rgba(16, 185, 129, 0.3)',
position: 'relative'
}}>
{/* Zeit am Ende des Balkens */}
<span style={{
color: 'white',
fontSize: '11px',
fontWeight: 'bold',
whiteSpace: 'nowrap'
}}>
{formatTime(emp.time)}
</span>
</div>
{/* Zeit außerhalb des Balkens (falls Balken zu kurz) */}
{percentage < 30 && (
<div style={{
position: 'absolute',
right: '8px',
zIndex: 2,
color: '#10b981',
fontSize: '11px',
fontWeight: 'bold'
}}>
{formatTime(emp.time)}
</div>
)}
</div>
</div>
)
})
})()}
</div>
)}
</div>
{/* Service Type Verteilung */}
<div style={{
background: 'rgba(45, 55, 72, 0.5)',
borderRadius: '12px',
padding: '16px',
border: '1px solid rgba(16, 185, 129, 0.2)',
flex: '0 0 auto'
}}>
<h6 style={{
color: 'var(--dark-text)',
marginBottom: '12px',
fontSize: '14px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
<FaClock size={16} style={{ color: '#10b981' }} />
Service Types
</h6>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{Object.entries(byServiceType).map(([type, count]) => (
<div key={type} style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px',
background: 'rgba(26, 32, 44, 0.4)',
borderRadius: '6px'
}}>
<span style={{
color: 'var(--dark-text)',
fontSize: '12px',
fontWeight: '500'
}}>{type}</span>
<strong style={{
color: '#10b981',
fontSize: '14px'
}}>{count}</strong>
</div>
))}
</div>
</div>
</div>
)
}