This commit is contained in:
Basilosaurusrex
2025-12-29 22:28:43 +01:00
parent 7fb446c53a
commit 0e19df6895
73 changed files with 7907 additions and 32290 deletions

View 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>
)
}