feat(plan): resolveCell/resolveGrid return arrays; dashboard shows up to 3
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import Typography from "@mui/material/Typography";
|
||||
import { Card } from "../../ui";
|
||||
import { formatDate } from "../../utils/formatters";
|
||||
|
||||
/** The logged-in user's resolved work plan for today (see GET /api/admin/dashboard). */
|
||||
/** One resolved plan record for today (see GET /api/admin/dashboard). */
|
||||
export interface TodayPlan {
|
||||
shift_date: string;
|
||||
category: string;
|
||||
@@ -23,15 +23,64 @@ function projectLabel(p: TodayPlan): string {
|
||||
return parts.length > 0 ? parts.join(" — ") : "Bez projektu";
|
||||
}
|
||||
|
||||
/**
|
||||
* "Vaše dnešní zařazení" — shows the logged-in user's work-plan entry for today
|
||||
* (category + project/site + note, with a range badge when the day is part of a
|
||||
* multi-day range). `plan === null` = the user has plan access but nothing is
|
||||
* scheduled today (muted empty state). Rendered near the clock-in on the
|
||||
* dashboard; the caller gates it on the plan permission.
|
||||
*/
|
||||
export default function DashTodayPlan({ plan }: { plan: TodayPlan | null }) {
|
||||
const color = plan?.category_color || "var(--mui-palette-primary-main)";
|
||||
function PlanRow({ plan }: { plan: TodayPlan }) {
|
||||
const color = plan.category_color || "var(--mui-palette-primary-main)";
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
||||
<Box
|
||||
sx={{ display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 0.75,
|
||||
px: 1.25,
|
||||
py: 0.5,
|
||||
borderRadius: 999,
|
||||
border: 1,
|
||||
borderColor: "divider",
|
||||
bgcolor: `color-mix(in srgb, ${color} 12%, transparent)`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: "50%",
|
||||
bgcolor: color,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{ fontWeight: 700, fontSize: "0.8rem" }}
|
||||
>
|
||||
{plan.category_label}
|
||||
</Typography>
|
||||
</Box>
|
||||
{plan.rangeFrom && plan.rangeTo && plan.rangeFrom !== plan.rangeTo && (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
součást rozsahu {formatDate(plan.rangeFrom)} –{" "}
|
||||
{formatDate(plan.rangeTo)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<Typography variant="h6" sx={{ lineHeight: 1.25 }}>
|
||||
{projectLabel(plan)}
|
||||
</Typography>
|
||||
{plan.note && (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{plan.note}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
/** "Vaše dnešní zařazení" — the logged-in user's resolved plan record(s) for
|
||||
* today (up to 3). Empty array = nothing scheduled. */
|
||||
export default function DashTodayPlan({ plans }: { plans: TodayPlan[] }) {
|
||||
return (
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<Typography
|
||||
@@ -40,65 +89,20 @@ export default function DashTodayPlan({ plan }: { plan: TodayPlan | null }) {
|
||||
>
|
||||
Vaše dnešní zařazení
|
||||
</Typography>
|
||||
|
||||
{plan ? (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 1,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{plans.length > 0 ? (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||
{plans.map((p, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
sx={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 0.75,
|
||||
px: 1.25,
|
||||
py: 0.5,
|
||||
borderRadius: 999,
|
||||
border: 1,
|
||||
pt: i === 0 ? 0 : 2,
|
||||
borderTop: i === 0 ? 0 : 1,
|
||||
borderColor: "divider",
|
||||
bgcolor: `color-mix(in srgb, ${color} 12%, transparent)`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: "50%",
|
||||
bgcolor: color,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{ fontWeight: 700, fontSize: "0.8rem" }}
|
||||
>
|
||||
{plan.category_label}
|
||||
</Typography>
|
||||
<PlanRow plan={p} />
|
||||
</Box>
|
||||
{plan.rangeFrom &&
|
||||
plan.rangeTo &&
|
||||
plan.rangeFrom !== plan.rangeTo && (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
součást rozsahu {formatDate(plan.rangeFrom)} –{" "}
|
||||
{formatDate(plan.rangeTo)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Typography variant="h6" sx={{ lineHeight: 1.25 }}>
|
||||
{projectLabel(plan)}
|
||||
</Typography>
|
||||
|
||||
{plan.note && (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{plan.note}
|
||||
</Typography>
|
||||
)}
|
||||
))}
|
||||
</Box>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
|
||||
Reference in New Issue
Block a user