import { WorkloadUser, LoadLevel } from '../services/workload' interface WorkloadHeatmapProps { users: WorkloadUser[] weekStart: string weekEnd: string onUserClick: (userId: string, userName: string) => void } // Color mapping for load levels const loadLevelColors: Record = { normal: '#4caf50', warning: '#ff9800', overloaded: '#f44336', unavailable: '#9e9e9e', } const loadLevelLabels: Record = { normal: 'Normal', warning: 'Warning', overloaded: 'Overloaded', unavailable: 'Unavailable', } export function WorkloadHeatmap({ users, weekStart, weekEnd, onUserClick }: WorkloadHeatmapProps) { const formatDate = (dateStr: string) => { const date = new Date(dateStr) return date.toLocaleDateString('zh-TW', { month: 'short', day: 'numeric' }) } // Group users by department const groupedByDepartment = users.reduce((acc, user) => { const dept = user.department_name || 'No Department' if (!acc[dept]) { acc[dept] = [] } acc[dept].push(user) return acc }, {} as Record) const departments = Object.keys(groupedByDepartment).sort() if (users.length === 0) { return (

No workload data available for this week.

) } return (
{formatDate(weekStart)} - {formatDate(weekEnd)}
{(Object.keys(loadLevelColors) as LoadLevel[]).map((level) => (
{loadLevelLabels[level]}
))}
{departments.map((dept) => groupedByDepartment[dept].map((user, index) => ( onUserClick(user.user_id, user.user_name)} > )) )}
Team Member Department Allocated Capacity Load Status
{user.user_name} {user.department_name || '-'} {user.allocated_hours}h {user.capacity_hours}h
{user.load_percentage}%
{loadLevelLabels[user.load_level]}
) } const styles: { [key: string]: React.CSSProperties } = { container: { backgroundColor: 'white', borderRadius: '8px', boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)', overflow: 'hidden', }, header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #eee', }, weekRange: { fontSize: '14px', color: '#666', fontWeight: 500, }, legend: { display: 'flex', gap: '16px', }, legendItem: { display: 'flex', alignItems: 'center', gap: '6px', }, legendColor: { width: '12px', height: '12px', borderRadius: '3px', }, legendLabel: { fontSize: '12px', color: '#666', }, tableContainer: { overflowX: 'auto', }, table: { width: '100%', borderCollapse: 'collapse', }, th: { textAlign: 'left', padding: '12px 16px', fontSize: '12px', fontWeight: 600, color: '#666', textTransform: 'uppercase', letterSpacing: '0.5px', backgroundColor: '#f5f5f5', borderBottom: '1px solid #eee', }, tr: { cursor: 'pointer', transition: 'background-color 0.15s ease', }, td: { padding: '14px 16px', fontSize: '14px', borderBottom: '1px solid #eee', }, userName: { fontWeight: 500, color: '#333', }, department: { color: '#666', }, hours: { fontFamily: 'monospace', fontSize: '13px', }, loadBarContainer: { position: 'relative', width: '120px', height: '24px', backgroundColor: '#f0f0f0', borderRadius: '4px', overflow: 'hidden', }, loadBar: { position: 'absolute', top: 0, left: 0, height: '100%', borderRadius: '4px', transition: 'width 0.3s ease', }, loadPercentage: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', fontSize: '12px', fontWeight: 600, color: '#333', textShadow: '0 0 2px rgba(255, 255, 255, 0.8)', }, statusBadge: { display: 'inline-block', padding: '4px 10px', borderRadius: '4px', fontSize: '12px', fontWeight: 500, color: 'white', }, emptyState: { textAlign: 'center', padding: '48px', color: '#666', backgroundColor: 'white', borderRadius: '8px', boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)', }, } export default WorkloadHeatmap