feat(mui): consistent staggered page entrance across all 43 route pages
Adopt the PageEnter wrapper as every page's outermost render element and remove the ad-hoc per-page entrance motion.div wrappers. Every page now enters the same way — all top-level sections rise+fade in, staggered — so nothing appears instantly and the motion is identical app-wide. Presentation-only: no data/logic/hooks/invalidate/permissions touched. Embedded sub-tabs (CompanySettings, ReceivedInvoices), Login (auth shell) and the dev UiKit are intentionally excluded. Gates: tsc -b --noEmit=0, build=0, vitest 152/152. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,6 @@ import { useAlert } from "../context/AlertContext";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import Forbidden from "../components/Forbidden";
|
||||
import { useNavigate, useParams, Link as RouterLink } from "react-router-dom";
|
||||
import { motion } from "framer-motion";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
@@ -16,7 +15,7 @@ import {
|
||||
attendanceLocationOptions,
|
||||
type LocationRecord,
|
||||
} from "../lib/queries/attendance";
|
||||
import { Button, Card, LoadingState, PageHeader } from "../ui";
|
||||
import { Button, Card, LoadingState, PageEnter, PageHeader } from "../ui";
|
||||
|
||||
const BackIcon = (
|
||||
<svg
|
||||
@@ -187,79 +186,127 @@ export default function AttendanceLocation() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<PageEnter>
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 12 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
>
|
||||
<PageHeader
|
||||
title="Poloha záznamu"
|
||||
actions={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to={`/attendance/admin?month=${month}`}
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
startIcon={BackIcon}
|
||||
>
|
||||
Zpět na správu
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</motion.div>
|
||||
<PageHeader
|
||||
title="Poloha záznamu"
|
||||
actions={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to={`/attendance/admin?month=${month}`}
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
startIcon={BackIcon}
|
||||
>
|
||||
Zpět na správu
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Info + map card */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 12 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.25, delay: 0.06 }}
|
||||
>
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ mb: 2, fontWeight: 600 }}>
|
||||
{record.user_name} — {formatDate(record.shift_date)}
|
||||
</Typography>
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<Typography variant="subtitle2" sx={{ mb: 2, fontWeight: 600 }}>
|
||||
{record.user_name} — {formatDate(record.shift_date)}
|
||||
</Typography>
|
||||
|
||||
{/* Leaflet map — kept entirely as-is */}
|
||||
{hasAnyLocation && (
|
||||
<div ref={mapRef} className="attendance-location-map" />
|
||||
)}
|
||||
{/* Leaflet map — kept entirely as-is */}
|
||||
{hasAnyLocation && (
|
||||
<div ref={mapRef} className="attendance-location-map" />
|
||||
)}
|
||||
|
||||
{/* Location detail grid */}
|
||||
{/* Location detail grid */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
|
||||
gap: 2,
|
||||
mt: hasAnyLocation ? 2 : 0,
|
||||
}}
|
||||
>
|
||||
{/* Arrival */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
|
||||
gap: 2,
|
||||
mt: hasAnyLocation ? 2 : 0,
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
opacity: hasArrivalLocation ? 1 : 0.6,
|
||||
}}
|
||||
>
|
||||
{/* Arrival */}
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 600 }}>
|
||||
Příchod
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontFamily: "'DM Mono', Menlo, monospace", mb: 1 }}
|
||||
>
|
||||
{record.arrival_time
|
||||
? formatDatetimeLocal(record.arrival_time)
|
||||
: "—"}
|
||||
</Typography>
|
||||
{hasArrivalLocation ? (
|
||||
<>
|
||||
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
||||
{record.arrival_address || <em>Adresa nezjištěna</em>}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
fontFamily: "'DM Mono', Menlo, monospace",
|
||||
fontSize: "0.75rem",
|
||||
color: "text.secondary",
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
GPS: {record.arrival_lat}, {record.arrival_lng}
|
||||
{record.arrival_accuracy &&
|
||||
` (přesnost: ${Math.round(Number(record.arrival_accuracy))}m)`}
|
||||
</Typography>
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
size="small"
|
||||
>
|
||||
Otevřít v Google Maps
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<em>Poloha nebyla zaznamenána</em>
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Departure */}
|
||||
{(hasDepartureLocation || record.departure_time) && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
opacity: hasArrivalLocation ? 1 : 0.6,
|
||||
opacity: hasDepartureLocation ? 1 : 0.6,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 600 }}>
|
||||
Příchod
|
||||
Odchod
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontFamily: "'DM Mono', Menlo, monospace", mb: 1 }}
|
||||
>
|
||||
{record.arrival_time
|
||||
? formatDatetimeLocal(record.arrival_time)
|
||||
{record.departure_time
|
||||
? formatDatetimeLocal(record.departure_time)
|
||||
: "—"}
|
||||
</Typography>
|
||||
{hasArrivalLocation ? (
|
||||
{hasDepartureLocation ? (
|
||||
<>
|
||||
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
||||
{record.arrival_address || <em>Adresa nezjištěna</em>}
|
||||
{record.departure_address || <em>Adresa nezjištěna</em>}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
@@ -270,13 +317,13 @@ export default function AttendanceLocation() {
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
GPS: {record.arrival_lat}, {record.arrival_lng}
|
||||
{record.arrival_accuracy &&
|
||||
` (přesnost: ${Math.round(Number(record.arrival_accuracy))}m)`}
|
||||
GPS: {record.departure_lat}, {record.departure_lng}
|
||||
{record.departure_accuracy &&
|
||||
` (přesnost: ${Math.round(Number(record.departure_accuracy))}m)`}
|
||||
</Typography>
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
|
||||
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="outlined"
|
||||
@@ -292,69 +339,9 @@ export default function AttendanceLocation() {
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Departure */}
|
||||
{(hasDepartureLocation || record.departure_time) && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
border: "1px solid",
|
||||
borderColor: "divider",
|
||||
opacity: hasDepartureLocation ? 1 : 0.6,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 600 }}>
|
||||
Odchod
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontFamily: "'DM Mono', Menlo, monospace", mb: 1 }}
|
||||
>
|
||||
{record.departure_time
|
||||
? formatDatetimeLocal(record.departure_time)
|
||||
: "—"}
|
||||
</Typography>
|
||||
{hasDepartureLocation ? (
|
||||
<>
|
||||
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
||||
{record.departure_address || <em>Adresa nezjištěna</em>}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
fontFamily: "'DM Mono', Menlo, monospace",
|
||||
fontSize: "0.75rem",
|
||||
color: "text.secondary",
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
GPS: {record.departure_lat}, {record.departure_lng}
|
||||
{record.departure_accuracy &&
|
||||
` (přesnost: ${Math.round(Number(record.departure_accuracy))}m)`}
|
||||
</Typography>
|
||||
<Button
|
||||
component="a"
|
||||
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="outlined"
|
||||
color="inherit"
|
||||
size="small"
|
||||
>
|
||||
Otevřít v Google Maps
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<em>Poloha nebyla zaznamenána</em>
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Card>
|
||||
</PageEnter>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user