c803efe059
- ProposalDeck: outer div h-screen overflow-hidden, content wrapper h-full - Rimosso il div interno ridondante min-h-screen - useEffect blocca body overflow mentre la deck è aperta - Tutti i 20 componenti slide: min-h-screen → h-full Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import type { ProposalContent } from "@/lib/proposal/schema";
|
||
|
||
type Props = { timeline: ProposalContent["timeline"] };
|
||
|
||
const COLOR_MAP = {
|
||
cyan: { bg: "bg-cyan-400", text: "text-cyan-900" },
|
||
purple: { bg: "bg-violet-400", text: "text-violet-900" },
|
||
green: { bg: "bg-emerald-400",text: "text-emerald-900" },
|
||
};
|
||
|
||
export function TimelineSection({ timeline }: Props) {
|
||
const weeks = Array.from({ length: timeline.totalWeeks }, (_, i) => i + 1);
|
||
|
||
return (
|
||
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||
<div>
|
||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Timeline</p>
|
||
<h2 className="text-5xl font-light text-foreground mt-4">
|
||
{timeline.totalWeeks} settimane di <span className="text-primary">progressione.</span>
|
||
</h2>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
{/* Header settimane */}
|
||
<div className="grid gap-2" style={{ gridTemplateColumns: `140px repeat(${timeline.totalWeeks}, 1fr)` }}>
|
||
<div />
|
||
{weeks.map((w) => (
|
||
<div key={w} className="text-center text-xs font-mono text-muted-foreground">W{w}</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Righe fase */}
|
||
{timeline.phases.map((phase, i) => {
|
||
const colors = COLOR_MAP[phase.color];
|
||
return (
|
||
<div
|
||
key={i}
|
||
className="grid gap-2 items-center"
|
||
style={{ gridTemplateColumns: `140px repeat(${timeline.totalWeeks}, 1fr)` }}
|
||
>
|
||
<div className="text-xs text-muted-foreground font-mono truncate pr-2">
|
||
{i === 0 ? "OPZIONE A" : i === 1 ? "OPZIONE B" : "OPZIONE C"}
|
||
<br />
|
||
<span className="text-[10px] text-muted-foreground/60">
|
||
{phase.endWeek - phase.startWeek + 1} settimane
|
||
</span>
|
||
</div>
|
||
{weeks.map((w) => {
|
||
const active = w >= phase.startWeek && w <= phase.endWeek;
|
||
const isFirst = w === phase.startWeek;
|
||
const isLast = w === phase.endWeek;
|
||
return (
|
||
<div
|
||
key={w}
|
||
className={`h-10 ${active ? `${colors.bg} ${isFirst ? "rounded-l-md" : ""} ${isLast ? "rounded-r-md" : ""}` : ""} flex items-center ${isFirst ? "justify-start pl-3" : ""}`}
|
||
>
|
||
{isFirst && (
|
||
<span className={`text-xs font-medium ${colors.text} whitespace-nowrap`}>
|
||
{phase.label}
|
||
</span>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Legenda */}
|
||
<div className="flex items-center gap-6">
|
||
{timeline.phases.map((phase, i) => {
|
||
const colors = COLOR_MAP[phase.color];
|
||
return (
|
||
<div key={i} className="flex items-center gap-2">
|
||
<div className={`w-3 h-3 rounded-sm ${colors.bg}`} />
|
||
<span className="text-xs text-muted-foreground">{phase.label}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
<p className="text-xs font-mono text-muted-foreground">
|
||
› {timeline.disclaimer}
|
||
</p>
|
||
</div>
|
||
);
|
||
}
|