fix(preventivo): slide deck 100vh — nessuno scroll di pagina
- 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>
This commit is contained in:
@@ -129,27 +129,31 @@ export function ProposalDeck({ proposal, onAccept, onReject }: Props) {
|
|||||||
return () => window.removeEventListener("keydown", handler);
|
return () => window.removeEventListener("keydown", handler);
|
||||||
}, [next, prev]);
|
}, [next, prev]);
|
||||||
|
|
||||||
|
// Blocca lo scroll del body mentre la deck è visibile
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
return () => { document.body.style.overflow = ""; };
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-screen bg-white font-sans select-none">
|
<div className="relative h-screen overflow-hidden bg-white font-sans select-none">
|
||||||
{/* Slide corrente */}
|
{/* Topbar */}
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="fixed top-0 left-0 right-0 z-20 flex items-center justify-between px-8 py-4 bg-white/90 backdrop-blur-sm border-b border-border">
|
||||||
{/* Topbar */}
|
<span className="text-xs font-mono text-muted-foreground tracking-widest uppercase">
|
||||||
<div className="fixed top-0 left-0 right-0 z-20 flex items-center justify-between px-8 py-4 bg-white/90 backdrop-blur-sm border-b border-border">
|
Documento di Soluzione · {header.clientName} · {header.date}
|
||||||
<span className="text-xs font-mono text-muted-foreground tracking-widest uppercase">
|
</span>
|
||||||
Documento di Soluzione · {header.clientName} · {header.date}
|
<span className="text-xs font-mono text-muted-foreground">
|
||||||
</span>
|
{String(current + 1).padStart(2, "0")} / {String(total).padStart(2, "0")}
|
||||||
<span className="text-xs font-mono text-muted-foreground">
|
</span>
|
||||||
{String(current + 1).padStart(2, "0")} / {String(total).padStart(2, "0")}
|
</div>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Contenuto slide */}
|
{/* Contenuto slide — riempie esattamente lo spazio tra le due barre fisse */}
|
||||||
<div className="flex-1 pt-16 pb-20">
|
<div className="h-full pt-16 pb-20 overflow-hidden">
|
||||||
{slides[current].component}
|
{slides[current].component}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom nav */}
|
{/* Bottom nav */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-20 flex items-center justify-between px-8 py-4 bg-white/90 backdrop-blur-sm border-t border-border">
|
<div className="fixed bottom-0 left-0 right-0 z-20 flex items-center justify-between px-8 py-4 bg-white/90 backdrop-blur-sm border-t border-border">
|
||||||
{/* Frecce */}
|
{/* Frecce */}
|
||||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||||
<button
|
<button
|
||||||
@@ -187,7 +191,6 @@ export function ProposalDeck({ proposal, onAccept, onReject }: Props) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function AcceptSection({ slug, tiers, header, onAccept, onReject }: Props
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
||||||
Accettazione formale
|
Accettazione formale
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ type Props = { number: string; title: string; subtitle: string };
|
|||||||
|
|
||||||
export function ChapterDivider({ number, title, subtitle }: Props) {
|
export function ChapterDivider({ number, title, subtitle }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-6">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-6">
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Capitolo</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Capitolo</p>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-8xl font-light text-primary">{number}</p>
|
<p className="text-8xl font-light text-primary">{number}</p>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type Props = {
|
|||||||
|
|
||||||
export function ClosingSection({ consultant, header, state, selectedTier, acceptedAt }: Props) {
|
export function ClosingSection({ consultant, header, state, selectedTier, acceptedAt }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-between px-16 py-24">
|
<div className="h-full flex flex-col justify-between px-16 py-24">
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Come si parte</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Come si parte</p>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export function ComparisonMatrixSection({ matrix, tiers }: Props) {
|
|||||||
const tierLetters = tiers.map((t) => t.tierLetter);
|
const tierLetters = tiers.map((t) => t.tierLetter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-8">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-8">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Riepilogo del lavoro</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Riepilogo del lavoro</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { header: AssembledProposal["header"] };
|
|||||||
|
|
||||||
export function CoverSection({ header }: Props) {
|
export function CoverSection({ header }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-between px-16 py-24">
|
<div className="h-full flex flex-col justify-between px-16 py-24">
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
||||||
Documento di Soluzione
|
Documento di Soluzione
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type Props = { deliverables: ProposalContent["deliverables"] };
|
|||||||
|
|
||||||
export function DeliverablesSection({ deliverables }: Props) {
|
export function DeliverablesSection({ deliverables }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center px-16 py-24">
|
<div className="h-full flex items-center px-16 py-24">
|
||||||
<div className="grid grid-cols-2 gap-8 w-full">
|
<div className="grid grid-cols-2 gap-8 w-full">
|
||||||
{/* Deliverable */}
|
{/* Deliverable */}
|
||||||
<div className="border border-primary/20 bg-primary/5 rounded-xl p-8 space-y-4">
|
<div className="border border-primary/20 bg-primary/5 rounded-xl p-8 space-y-4">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { consultant: ConsultantProfile };
|
|||||||
|
|
||||||
export function FactsSection({ consultant }: Props) {
|
export function FactsSection({ consultant }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-12">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-12">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Tre fatti</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Tre fatti</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const CHAPTERS = [
|
|||||||
|
|
||||||
export function IndexSection() {
|
export function IndexSection() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-8">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-8">
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sommario</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sommario</p>
|
||||||
<h2 className="text-5xl font-light text-foreground">
|
<h2 className="text-5xl font-light text-foreground">
|
||||||
Cinque <span className="text-primary">capitoli.</span>
|
Cinque <span className="text-primary">capitoli.</span>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function NextStepsSection({ consultant, header }: Props) {
|
|||||||
const expiryNote = `Valida ${header.validityDays} giorni dalla data di invio.`;
|
const expiryNote = `Valida ${header.validityDays} giorni dalla data di invio.`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Come procedere ora</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Come procedere ora</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ function formatEur(val: string | null | undefined): string {
|
|||||||
|
|
||||||
export function PricingSection({ tiers }: Props) {
|
export function PricingSection({ tiers }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Le tre opzioni</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Le tre opzioni</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { problem: ProblemNode };
|
|||||||
|
|
||||||
export function ProblemNodeSection({ problem }: Props) {
|
export function ProblemNodeSection({ problem }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-8 max-w-4xl">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-8 max-w-4xl">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-destructive uppercase">
|
<p className="text-xs font-mono tracking-widest text-destructive uppercase">
|
||||||
Problema {problem.id} · {problem.subtitle}
|
Problema {problem.id} · {problem.subtitle}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type Props = { scope: ProposalContent["scope"] };
|
|||||||
|
|
||||||
export function ScopeSection({ scope }: Props) {
|
export function ScopeSection({ scope }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center px-16 py-24">
|
<div className="h-full flex items-center px-16 py-24">
|
||||||
<div className="grid grid-cols-2 gap-12 w-full">
|
<div className="grid grid-cols-2 gap-12 w-full">
|
||||||
{/* Scope */}
|
{/* Scope */}
|
||||||
<div className="border border-primary/20 bg-primary/5 rounded-xl p-8 space-y-4">
|
<div className="border border-primary/20 bg-primary/5 rounded-xl p-8 space-y-4">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { solution: SolutionNode };
|
|||||||
|
|
||||||
export function SolutionNodeSection({ solution }: Props) {
|
export function SolutionNodeSection({ solution }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-8 max-w-4xl">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-8 max-w-4xl">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-primary uppercase">
|
<p className="text-xs font-mono tracking-widest text-primary uppercase">
|
||||||
{solution.subtitle}
|
{solution.subtitle}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { synthesis: ProposalContent["solutionSynthesis"] };
|
|||||||
|
|
||||||
export function SolutionSynthesisSection({ synthesis }: Props) {
|
export function SolutionSynthesisSection({ synthesis }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sintesi della soluzione</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sintesi della soluzione</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = { stages: ProposalContent["stagesRecap"]["stages"] };
|
|||||||
|
|
||||||
export function StagesRecapSection({ stages }: Props) {
|
export function StagesRecapSection({ stages }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Recap del progresso</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Recap del progresso</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type Props = { consultant: ConsultantProfile };
|
|||||||
|
|
||||||
export function StrategistSection({ consultant }: Props) {
|
export function StrategistSection({ consultant }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center px-16 py-24">
|
<div className="h-full flex items-center px-16 py-24">
|
||||||
<div className="grid grid-cols-3 gap-12 w-full">
|
<div className="grid grid-cols-3 gap-12 w-full">
|
||||||
{/* Foto */}
|
{/* Foto */}
|
||||||
<div className="flex flex-col items-center justify-center">
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type Props = {
|
|||||||
|
|
||||||
export function SynthesisDiagramSection({ synthesis }: Props) {
|
export function SynthesisDiagramSection({ synthesis }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sintesi del problema</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Sintesi del problema</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function TestimonialsSection({ testimonials, page, totalPages }: Props) {
|
|||||||
const slice = testimonials.slice((page - 1) * perPage, page * perPage);
|
const slice = testimonials.slice((page - 1) * perPage, page * perPage);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col px-16 py-24 space-y-8">
|
<div className="h-full flex flex-col px-16 py-24 space-y-8">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
||||||
Pagina {page} di {totalPages} · Cosa dicono di me
|
Pagina {page} di {totalPages} · Cosa dicono di me
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function TimelineSection({ timeline }: Props) {
|
|||||||
const weeks = Array.from({ length: timeline.totalWeeks }, (_, i) => i + 1);
|
const weeks = Array.from({ length: timeline.totalWeeks }, (_, i) => i + 1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-10">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Timeline</p>
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">Timeline</p>
|
||||||
<h2 className="text-5xl font-light text-foreground mt-4">
|
<h2 className="text-5xl font-light text-foreground mt-4">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function VisionSection({ vision, header }: Props) {
|
|||||||
const mid = Math.floor(words.length / 2);
|
const mid = Math.floor(words.length / 2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col justify-center px-16 py-24 space-y-8">
|
<div className="h-full flex flex-col justify-center px-16 py-24 space-y-8">
|
||||||
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
<p className="text-xs font-mono tracking-widest text-muted-foreground uppercase">
|
||||||
Dove andiamo
|
Dove andiamo
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user