feat(mui): migrate Attendance Admin (Správa docházky) onto MUI kit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-07 00:57:26 +02:00
parent 3eb364fba5
commit c690bd9a24
4 changed files with 914 additions and 785 deletions

View File

@@ -1,5 +1,6 @@
import FormModal from "./FormModal";
import AdminDatePicker from "./AdminDatePicker";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { Modal, MonthField, TimeField, Field, CheckboxField } from "../ui";
interface BulkAttendanceForm {
month: string;
@@ -39,127 +40,136 @@ export default function BulkAttendanceModal({
toggleAllUsers,
}: BulkAttendanceModalProps) {
return (
<FormModal
<Modal
isOpen={isOpen}
onClose={onClose}
title="Vyplnit docházku za měsíc"
size="lg"
maxWidth="lg"
loading={submitting}
onSubmit={onSubmit}
submitLabel="Vyplnit měsíc"
submitText="Vyplnit měsíc"
submitDisabled={form.user_ids.length === 0}
>
<p
style={{
color: "var(--text-secondary)",
marginTop: "0.25rem",
marginBottom: "1rem",
fontSize: "0.875rem",
}}
<Typography
variant="body2"
color="text.secondary"
sx={{ mt: 0.25, mb: 2 }}
>
Vytvoří záznamy pro všechny pracovní dny. Svátky se automaticky označí.
Existující záznamy se přeskočí.
</p>
</Typography>
<div className="admin-form">
<div className="admin-form-group">
<label className="admin-form-label">Měsíc</label>
<AdminDatePicker
mode="month"
value={form.month}
onChange={(val) => setForm({ ...form, month: val })}
/>
</div>
<Field label="Měsíc">
<MonthField
value={form.month}
onChange={(val) => setForm({ ...form, month: val })}
/>
</Field>
<div className="admin-form-group">
<label className="admin-form-label">
<Box sx={{ mb: 2 }}>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 1.5,
mb: 0.5,
}}
>
<Typography
component="span"
variant="body2"
sx={{ fontWeight: 600, color: "text.secondary" }}
>
Zaměstnanci
<button
type="button"
onClick={toggleAllUsers}
style={{
marginLeft: "0.75rem",
background: "none",
border: "none",
color: "var(--accent-color)",
cursor: "pointer",
fontSize: "0.8125rem",
fontWeight: 500,
padding: 0,
}}
>
{form.user_ids.length === users.length
? "Odznačit vše"
: "Vybrat vše"}
</button>
</label>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0.375rem",
maxHeight: "200px",
overflowY: "auto",
padding: "0.75rem",
background: "var(--bg-tertiary)",
borderRadius: "var(--border-radius-sm)",
border: "1px solid var(--border-color)",
</Typography>
<Typography
component="button"
type="button"
onClick={toggleAllUsers}
variant="caption"
sx={{
background: "none",
border: "none",
p: 0,
cursor: "pointer",
fontWeight: 500,
color: "primary.main",
}}
>
{users.map((user) => (
<label key={user.id} className="admin-form-checkbox">
<input
type="checkbox"
checked={form.user_ids.includes(String(user.id))}
onChange={() => toggleUser(user.id)}
/>
<span>{user.name}</span>
</label>
))}
</div>
<small className="admin-form-hint">
Vybráno: {form.user_ids.length} z {users.length}
</small>
</div>
{form.user_ids.length === users.length
? "Odznačit vše"
: "Vybrat vše"}
</Typography>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: 0.25,
maxHeight: 200,
overflowY: "auto",
p: 1.5,
bgcolor: "action.hover",
borderRadius: 2,
border: 1,
borderColor: "divider",
}}
>
{users.map((user) => (
<CheckboxField
key={user.id}
label={user.name}
checked={form.user_ids.includes(String(user.id))}
onChange={() => toggleUser(user.id)}
/>
))}
</Box>
<Typography variant="caption" color="text.secondary">
Vybráno: {form.user_ids.length} z {users.length}
</Typography>
</Box>
<div className="admin-form-row">
<div className="admin-form-group">
<label className="admin-form-label">Příchod</label>
<AdminDatePicker
mode="time"
value={form.arrival_time}
onChange={(val) => setForm({ ...form, arrival_time: val })}
/>
</div>
<div className="admin-form-group">
<label className="admin-form-label">Odchod</label>
<AdminDatePicker
mode="time"
value={form.departure_time}
onChange={(val) => setForm({ ...form, departure_time: val })}
/>
</div>
</div>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<Field label="Příchod">
<TimeField
value={form.arrival_time}
onChange={(val) => setForm({ ...form, arrival_time: val })}
/>
</Field>
<Field label="Odchod">
<TimeField
value={form.departure_time}
onChange={(val) => setForm({ ...form, departure_time: val })}
/>
</Field>
</Box>
<div className="admin-form-row">
<div className="admin-form-group">
<label className="admin-form-label">Začátek pauzy</label>
<AdminDatePicker
mode="time"
value={form.break_start_time}
onChange={(val) => setForm({ ...form, break_start_time: val })}
/>
</div>
<div className="admin-form-group">
<label className="admin-form-label">Konec pauzy</label>
<AdminDatePicker
mode="time"
value={form.break_end_time}
onChange={(val) => setForm({ ...form, break_end_time: val })}
/>
</div>
</div>
</div>
</FormModal>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<Field label="Začátek pauzy">
<TimeField
value={form.break_start_time}
onChange={(val) => setForm({ ...form, break_start_time: val })}
/>
</Field>
<Field label="Konec pauzy">
<TimeField
value={form.break_end_time}
onChange={(val) => setForm({ ...form, break_end_time: val })}
/>
</Field>
</Box>
</Modal>
);
}