Files
app/src/admin/components/dashboard/DashAttendanceToday.tsx
BOHA 177d3f1902 fix(mui): stop dark/light oscillation (single data-theme owner) + animated ThemeToggle + white avatars
Removed MuiColorSchemeSync: it called MUI setMode which ALSO wrote data-theme, fighting ThemeContext (the other writer) → theme flip-flop loop. ThemeContext is now the sole owner; MUI's cssVariables CSS (scoped by [data-theme]) applies from the attribute, no JS bridge needed. New animated ThemeToggle kit button (sun/moon crossfade+rotate) replaces the static AppShell toggle. DashAttendanceToday avatars now solid-fill + white initials (matches StatCard/activity badges).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 07:39:14 +02:00

139 lines
3.9 KiB
TypeScript

import { Link as RouterLink } from "react-router-dom";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Avatar from "@mui/material/Avatar";
import { Card, Button, StatusChip, EmptyState } from "../../ui";
import { LEAVE_TYPE_LABELS, STATUS_LABELS } from "../../utils/dashboardHelpers";
interface AttendanceUser {
user_id: number | string;
name: string;
initials?: string;
status: string;
leave_type?: string;
arrived_at?: string;
}
interface AttendanceData {
users: AttendanceUser[];
}
interface DashAttendanceTodayProps {
attendance: AttendanceData | null;
}
// Maps the attendance status to the StatusChip / avatar tint color.
// Mirrors the legacy dash-status-* dot classes: in→success, away→warning,
// out→default, leave→error.
const STATUS_COLOR: Record<
string,
"default" | "success" | "warning" | "error"
> = {
in: "success",
away: "warning",
out: "default",
leave: "error",
};
export default function DashAttendanceToday({
attendance,
}: DashAttendanceTodayProps) {
if (!attendance) {
return null;
}
return (
<Card sx={{ display: "flex", flexDirection: "column" }}>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
mb: 2,
}}
>
<Typography variant="h6">Docházka dnes</Typography>
<Button
component={RouterLink}
to="/attendance/admin"
size="small"
sx={{ flexShrink: 0 }}
>
Detail &rarr;
</Button>
</Box>
{attendance.users.length === 0 ? (
<EmptyState title="Žádná docházka" />
) : (
<Box sx={{ display: "flex", flexDirection: "column" }}>
{attendance.users.map((u, i) => {
const color = STATUS_COLOR[u.status] ?? "default";
const isDefault = color === "default";
return (
<Box
key={`${u.user_id}-${i}`}
sx={{
display: "flex",
alignItems: "center",
gap: 1.5,
py: 1.25,
borderBottom: 1,
borderColor: "divider",
"&:last-of-type": { borderBottom: 0 },
}}
>
<Avatar
sx={{
width: 36,
height: 36,
fontSize: "0.8rem",
fontWeight: 700,
bgcolor: isDefault ? "grey.600" : `${color}.main`,
color: "common.white",
}}
>
{u.initials || "?"}
</Avatar>
<Typography
variant="body2"
sx={{ fontWeight: 500, flex: 1, minWidth: 0 }}
noWrap
>
{u.name}
</Typography>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 1,
flexShrink: 0,
}}
>
<StatusChip
color={color}
label={
u.status === "leave"
? LEAVE_TYPE_LABELS[u.leave_type || ""] || "Nepřítomen"
: STATUS_LABELS[u.status]
}
/>
{u.arrived_at && (
<Typography
variant="caption"
color="text.secondary"
sx={{ fontFamily: "'DM Mono', Menlo, monospace" }}
>
{u.arrived_at}
</Typography>
)}
</Box>
</Box>
);
})}
</Box>
)}
</Card>
);
}