feat(mui): migrate PlanWork modals + toolbar onto MUI kit (planner grid kept bespoke)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-07 01:29:48 +02:00
parent cc4b14f862
commit ebda3ecf23
3 changed files with 324 additions and 277 deletions

View File

@@ -1,6 +1,8 @@
import { useState, useMemo, useCallback, useRef, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { motion, AnimatePresence } from "framer-motion";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { useAuth } from "../context/AuthContext";
import { useAlert } from "../context/AlertContext";
import {
@@ -13,6 +15,7 @@ import PlanGrid from "../components/PlanGrid";
import PlanCellModal from "../components/PlanCellModal";
import PlanCategoriesModal from "../components/PlanCategoriesModal";
import Forbidden from "../components/Forbidden";
import { Button, Alert } from "../ui";
import { projectListOptions, type Project } from "../lib/queries/projects";
import { planCategoriesOptions, type PlanCategory } from "../lib/queries/plan";
// plan.css is imported once globally in AdminApp.tsx — no per-page re-import.
@@ -542,120 +545,129 @@ export default function PlanWork() {
}
return (
<div className="plan-work-page">
<motion.h1
className="admin-page-title"
<Box className="plan-work-page">
<motion.div
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
>
Plán prací
</motion.h1>
<Typography variant="h4" sx={{ fontWeight: 700, mb: 2 }}>
Plán prací
</Typography>
</motion.div>
{!canEdit && (
<motion.div
className="plan-banner"
role="status"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25, delay: 0.06 }}
>
<span className="plan-banner-icon" aria-hidden>
i
</span>
<span>
<strong>Režim náhledu</strong> můžete pouze prohlížet plán. Úpravy
jsou dostupné oprávněným uživatelům.
</span>
<Box sx={{ mb: 2 }}>
<Alert severity="info">
<strong>Režim náhledu</strong> můžete pouze prohlížet plán.
Úpravy jsou dostupné oprávněným uživatelům.
</Alert>
</Box>
</motion.div>
)}
<motion.div
className="plan-toolbar"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25, delay: 0.12 }}
>
{/* Nav buttons grouped so they stay on one row when the toolbar
stacks on mobile (see .plan-toolbar-nav in plan.css). On desktop
the wrapper is an inline-flex with the same 12px gap, so the
layout is visually identical. */}
<div className="plan-toolbar-nav">
<button
type="button"
className="admin-btn admin-btn-secondary"
onClick={goToToday}
>
Dnes
</button>
<button
type="button"
className="admin-btn admin-btn-secondary"
onClick={goPrev}
aria-label="Předchozí"
>
</button>
<button
type="button"
className="admin-btn admin-btn-secondary"
onClick={goNext}
aria-label="Další"
>
</button>
</div>
<span className="plan-range-label-slot">
<AnimatePresence mode="popLayout" initial={false}>
<motion.span
className="plan-range-label"
key={`${isoDate(range.from)}-${view}`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
{formatRangeLabel(isoDate(range.from), isoDate(range.to), view)}
</motion.span>
</AnimatePresence>
</span>
<div
className="plan-view-toggle"
role="group"
aria-label="Měřítko zobrazení"
<Box
sx={{
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: 1.5,
mb: 2,
}}
>
<button
type="button"
className={
view === "week"
? "admin-btn admin-btn-primary"
: "admin-btn admin-btn-secondary"
}
onClick={() => setViewWithAnim("week")}
{/* Nav buttons grouped so they stay on one row when the toolbar
wraps on mobile. */}
<Box sx={{ display: "inline-flex", gap: 1 }}>
<Button variant="outlined" color="inherit" onClick={goToToday}>
Dnes
</Button>
<Button
variant="outlined"
color="inherit"
onClick={goPrev}
aria-label="Předchozí"
>
</Button>
<Button
variant="outlined"
color="inherit"
onClick={goNext}
aria-label="Další"
>
</Button>
</Box>
<Box
sx={{
flex: 1,
minWidth: 220,
display: "flex",
justifyContent: "center",
}}
>
Týden
</button>
<button
type="button"
className={
view === "month"
? "admin-btn admin-btn-primary"
: "admin-btn admin-btn-secondary"
}
onClick={() => setViewWithAnim("month")}
<AnimatePresence mode="popLayout" initial={false}>
<motion.div
key={`${isoDate(range.from)}-${view}`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<Typography
variant="h6"
sx={{ fontWeight: 600, whiteSpace: "nowrap" }}
>
{formatRangeLabel(
isoDate(range.from),
isoDate(range.to),
view,
)}
</Typography>
</motion.div>
</AnimatePresence>
</Box>
<Box
role="group"
aria-label="Měřítko zobrazení"
sx={{ display: "inline-flex", gap: 1 }}
>
Měsíc
</button>
</div>
{canEdit && (
<button
type="button"
className="admin-btn admin-btn-secondary plan-toolbar-cat"
onClick={() => setCatModalOpen(true)}
>
Správa kategorií
</button>
)}
<Button
variant={view === "week" ? "contained" : "outlined"}
color={view === "week" ? "primary" : "inherit"}
onClick={() => setViewWithAnim("week")}
>
Týden
</Button>
<Button
variant={view === "month" ? "contained" : "outlined"}
color={view === "month" ? "primary" : "inherit"}
onClick={() => setViewWithAnim("month")}
>
Měsíc
</Button>
</Box>
{canEdit && (
<Button
variant="outlined"
color="inherit"
onClick={() => setCatModalOpen(true)}
>
Správa kategorií
</Button>
)}
</Box>
</motion.div>
{gridLoading && (
@@ -718,6 +730,6 @@ export default function PlanWork() {
onClose={() => setCatModalOpen(false)}
categories={categories}
/>
</div>
</Box>
);
}