feat(ui): editorial off-grid restyling — landing + tutte le pagine app
Direzione visiva "asimmetrico/off-grid" applicata in modo coerente:
titoli mega che sbordano dal viewport (bleed-left), chapter markers
giganti in stroke trasparente come art di sfondo, eyebrow verticale
sul bordo destro, mesh-blob fluttuanti, cursor-glow che segue il mouse.
- Landing marketing rifatta da zero: hero cinema con mockup ruotato in
absolute, marquee infinito di keyword, stats sparse a quote diverse,
bento di feature ruotate, mockup browser 3D che sborda a destra,
CTA finale dark con "?" gigante.
- Dashboard loggata: greeting bleed-left, hero card "/plan" ruotata,
nav asimmetrica con chapter markers per ogni voce.
- /plan, /onboarding, /profile, /tracking, /fasting, /shopping: stesso
trattamento chrome (titolo bleed, chapter marker, atmosfera mesh-blob),
contenuti funzionali invariati.
Componenti nuovi:
- CursorGlow (client) — alone radiale che segue il mouse, easing morbido
- EditorialHeader (server) — header riusabile con chapter+eyebrow+title
- EditorialAtmosphere (server) — preset di mesh-blob per pagina
CSS utilities: text-mega, text-chapter, text-stroke, bleed-left/right,
btn-gradient, glow-ring, tilt-3d, animate-marquee, animate-float-{x,y,z},
animate-ring-trace. Tutte rispettano prefers-reduced-motion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d17c5ef869
commit
d467e212cb
12 files changed
+1855
-316
No files matched your search
@@ -3,6 +3,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
|
||||
@@ -41,29 +42,58 @@ function FastingPageContent({
|
||||
const t = useTranslations('Fasting');
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-5xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 07 — Digiuno" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo VII</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-6 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 07 — Digiuno" />
|
||||
<main className="relative overflow-hidden pb-24 pt-10 sm:pt-12">
|
||||
<CursorGlow color="hsl(var(--pomodoro))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--pomodoro animate-float-y -right-32 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oro animate-float-x -left-24 top-72 h-72 w-72 opacity-40"
|
||||
/>
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
VII
|
||||
</span>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo VII — Digiuno
|
||||
</p>
|
||||
</div>
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-8 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up relative mt-10 max-w-2xl text-2xl italic leading-snug [animation-delay:240ms] md:ml-[16.66%]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:360ms]" />
|
||||
<div className="rule animate-rule-in relative my-12 [animation-delay:360ms]" />
|
||||
|
||||
<FastingPauseToggle initial={pauseStatus} />
|
||||
<div className="relative">
|
||||
<FastingPauseToggle initial={pauseStatus} />
|
||||
|
||||
<FastTimer active={active} />
|
||||
<FastTimer active={active} />
|
||||
|
||||
<div className="animate-fade-up mt-16 [animation-delay:540ms]">
|
||||
<p className="editorial-eyebrow mb-6">{t('historyTitle')}</p>
|
||||
<FastHistory events={history} />
|
||||
</div>
|
||||
</main>
|
||||
<div className="animate-fade-up mt-16 [animation-delay:540ms]">
|
||||
<p className="editorial-eyebrow mb-6">{t('historyTitle')}</p>
|
||||
<FastHistory events={history} />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
|
||||
@@ -47,24 +48,57 @@ function OnboardingPageContent({
|
||||
const t = useTranslations('Onboarding');
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-5xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 03 — Inizio" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo III</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-6 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 03 — Inizio" />
|
||||
<main className="relative overflow-hidden pb-24 pt-10 sm:pt-12">
|
||||
<CursorGlow color="hsl(var(--oro))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oro animate-float-y -right-24 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--pomodoro animate-float-x -left-32 top-72 h-80 w-80 opacity-40"
|
||||
/>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:360ms]" />
|
||||
{/* Chapter "III" gigante in stroke */}
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
III
|
||||
</span>
|
||||
|
||||
<OnboardingFlow initialProfile={profile} initialPreferences={preferences} />
|
||||
</main>
|
||||
{/* Eyebrow verticale destra */}
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo III — Inizio
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rule-thick mt-16" />
|
||||
{/* Titolo bleed-left */}
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-8 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block">{t('title')}</span>
|
||||
</h1>
|
||||
|
||||
<p className="font-display text-ink-soft animate-fade-up relative mt-10 max-w-2xl text-2xl italic leading-snug [animation-delay:240ms] md:ml-[16.66%]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="rule animate-rule-in my-12 [animation-delay:360ms]" />
|
||||
|
||||
<div className="relative">
|
||||
<OnboardingFlow initialProfile={profile} initialPreferences={preferences} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div className="rule-thick" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -4,6 +4,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
@@ -55,75 +56,109 @@ function PlanPageContent({
|
||||
const t = useTranslations('Plan');
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-6xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 05 — Cucina" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<div className="grid items-end gap-6 md:grid-cols-12">
|
||||
<div className="md:col-span-8">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo V</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 05 — Cucina" />
|
||||
<main className="relative overflow-hidden py-10 sm:py-12">
|
||||
<CursorGlow color="hsl(var(--pomodoro))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--pomodoro animate-float-x -left-32 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oliva animate-float-y -right-32 top-64 h-80 w-80 opacity-40"
|
||||
/>
|
||||
|
||||
{/* Chapter "V" gigante in stroke */}
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
V
|
||||
</span>
|
||||
|
||||
{/* Eyebrow verticale destra */}
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo V — Cucina
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Titolo che sborda */}
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-12 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block italic">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-5 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{plan ? formatWeek(plan.weekStart) : t('noPlan')}
|
||||
</p>
|
||||
{plan?.fastingProtocol ? (
|
||||
<p className="animate-fade-up mt-4 [animation-delay:300ms]">
|
||||
<span className="editorial-eyebrow">{t('window')}</span>{' '}
|
||||
<span className="font-display text-pomodoro ml-2 text-base italic">
|
||||
{t(`protocolLabel.${plan.fastingProtocol}`)}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
{plan?.currentPhase === 'TRANSITION' && plan.phase2Week ? (
|
||||
<p className="animate-fade-up mt-3 [animation-delay:330ms]">
|
||||
<span className="editorial-eyebrow">{t('phase2Eyebrow')}</span>{' '}
|
||||
<span className="font-display text-oliva ml-2 text-base italic">
|
||||
{t('phase2Week', { n: plan.phase2Week })}
|
||||
</span>
|
||||
</p>
|
||||
) : plan?.currentPhase === 'MAINTENANCE' ? (
|
||||
<p className="animate-fade-up mt-3 [animation-delay:330ms]">
|
||||
<span className="editorial-eyebrow">{t('phase3Eyebrow')}</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="animate-fade-up flex flex-col items-stretch gap-3 [animation-delay:360ms] md:col-span-4 md:items-end">
|
||||
<form action={regeneratePlan}>
|
||||
<Button type="submit" variant={plan ? 'outline' : 'default'} size="lg">
|
||||
{plan ? t('regenerate') : t('generate')}
|
||||
</Button>
|
||||
</form>
|
||||
{plan ? (
|
||||
<>
|
||||
<Link
|
||||
href="/shopping"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('linkShopping')} →
|
||||
</Link>
|
||||
<a
|
||||
href="/api/plan-export"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('exportPdf')} ↗
|
||||
</a>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:420ms]" />
|
||||
|
||||
{plan ? (
|
||||
<div className="animate-fade-up [animation-delay:480ms]">
|
||||
<PlanWeek plan={plan} dailyTarget={dailyTarget} />
|
||||
<div className="relative mt-12 grid grid-cols-12 items-end gap-6">
|
||||
<div className="col-span-12 md:col-span-7 md:col-start-2">
|
||||
<p className="font-display text-ink-soft animate-fade-up text-2xl italic leading-snug [animation-delay:240ms]">
|
||||
{plan ? formatWeek(plan.weekStart) : t('noPlan')}
|
||||
</p>
|
||||
<div className="animate-fade-up mt-5 flex flex-wrap items-baseline gap-x-6 gap-y-2 [animation-delay:300ms]">
|
||||
{plan?.fastingProtocol ? (
|
||||
<span className="flex items-baseline gap-2">
|
||||
<span className="editorial-eyebrow">{t('window')}</span>
|
||||
<span className="font-display text-pomodoro text-base italic">
|
||||
{t(`protocolLabel.${plan.fastingProtocol}`)}
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
{plan?.currentPhase === 'TRANSITION' && plan.phase2Week ? (
|
||||
<span className="flex items-baseline gap-2">
|
||||
<span className="editorial-eyebrow">{t('phase2Eyebrow')}</span>
|
||||
<span className="font-display text-oliva text-base italic">
|
||||
{t('phase2Week', { n: plan.phase2Week })}
|
||||
</span>
|
||||
</span>
|
||||
) : plan?.currentPhase === 'MAINTENANCE' ? (
|
||||
<span className="editorial-eyebrow">{t('phase3Eyebrow')}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="animate-fade-up col-span-12 flex flex-col items-stretch gap-3 [animation-delay:360ms] md:col-span-4 md:col-start-9 md:items-end">
|
||||
<form action={regeneratePlan}>
|
||||
<Button type="submit" variant={plan ? 'outline' : 'default'} size="lg">
|
||||
{plan ? t('regenerate') : t('generate')}
|
||||
</Button>
|
||||
</form>
|
||||
{plan ? (
|
||||
<>
|
||||
<Link
|
||||
href="/shopping"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('linkShopping')} →
|
||||
</Link>
|
||||
<a
|
||||
href="/api/plan-export"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('exportPdf')} ↗
|
||||
</a>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
|
||||
<div className="rule animate-rule-in my-12 [animation-delay:420ms]" />
|
||||
|
||||
{plan ? (
|
||||
<div className="animate-fade-up relative [animation-delay:480ms]">
|
||||
<PlanWeek plan={plan} dailyTarget={dailyTarget} />
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
|
||||
@@ -56,48 +57,75 @@ function ProfilePageContent({
|
||||
const t = useTranslations('Profile');
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-4xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 04 — Profilo" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo IV</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-6 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 04 — Profilo" />
|
||||
<main className="relative overflow-hidden pb-24 pt-10 sm:pt-12">
|
||||
<CursorGlow color="hsl(var(--oliva))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oliva animate-float-y -right-32 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oro animate-float-x -left-24 top-72 h-72 w-72 opacity-35"
|
||||
/>
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
IV
|
||||
</span>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo IV — Profilo
|
||||
</p>
|
||||
</div>
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-8 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up relative mt-10 max-w-2xl text-2xl italic leading-snug [animation-delay:240ms] md:ml-[16.66%]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:360ms]" />
|
||||
<div className="rule animate-rule-in my-12 [animation-delay:360ms]" />
|
||||
|
||||
<div className="animate-fade-up [animation-delay:300ms]">
|
||||
<ProfileForm initial={initial} />
|
||||
</div>
|
||||
<div className="animate-fade-up [animation-delay:300ms]">
|
||||
<ProfileForm initial={initial} />
|
||||
</div>
|
||||
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:420ms]" />
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:420ms]" />
|
||||
|
||||
<div className="animate-fade-up [animation-delay:480ms]">
|
||||
<PreferencesSection initial={preferences} />
|
||||
</div>
|
||||
<div className="animate-fade-up [animation-delay:480ms]">
|
||||
<PreferencesSection initial={preferences} />
|
||||
</div>
|
||||
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:600ms]" />
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:600ms]" />
|
||||
|
||||
<div className="animate-fade-up [animation-delay:660ms]">
|
||||
<NotificationsPanel initial={notifications} />
|
||||
</div>
|
||||
<div className="animate-fade-up [animation-delay:660ms]">
|
||||
<NotificationsPanel initial={notifications} />
|
||||
</div>
|
||||
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:780ms]" />
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:780ms]" />
|
||||
|
||||
<div className="animate-fade-up [animation-delay:840ms]">
|
||||
<AchievementsPanel unlocked={achievements} />
|
||||
</div>
|
||||
<div className="animate-fade-up [animation-delay:840ms]">
|
||||
<AchievementsPanel unlocked={achievements} />
|
||||
</div>
|
||||
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:900ms]" />
|
||||
<div className="rule animate-rule-in my-16 [animation-delay:900ms]" />
|
||||
|
||||
<div className="animate-fade-up [animation-delay:960ms]">
|
||||
<DataPanel />
|
||||
</div>
|
||||
</main>
|
||||
<div className="animate-fade-up [animation-delay:960ms]">
|
||||
<DataPanel />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
@@ -51,52 +52,78 @@ function ShoppingPageContent({ list }: { list: ShoppingList | null }) {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-5xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 09 — La spesa" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<div className="grid items-end gap-6 md:grid-cols-12">
|
||||
<div className="md:col-span-8">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo IX</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-5 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{list ? formatWeek(list.plan.weekStart) : t('noPlan')}
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 09 — La spesa" />
|
||||
<main className="relative overflow-hidden pb-24 pt-10 sm:pt-12">
|
||||
<CursorGlow color="hsl(var(--oliva))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oliva animate-float-y -right-32 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--pomodoro animate-float-x -left-24 top-72 h-72 w-72 opacity-35"
|
||||
/>
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
IX
|
||||
</span>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo IX — La spesa
|
||||
</p>
|
||||
</div>
|
||||
{list ? (
|
||||
<aside className="animate-fade-up [animation-delay:360ms] md:col-span-4 md:text-right">
|
||||
<p className="editorial-eyebrow">{t('totalLabel')}</p>
|
||||
<p className="font-display text-ink mt-2 font-mono text-3xl tabular-nums">
|
||||
{totalItems}
|
||||
</p>
|
||||
{typeof estimatedCost === 'number' && estimatedCost > 0 ? (
|
||||
<p className="text-ink-soft mt-3 font-mono text-xs uppercase tracking-widest">
|
||||
{t('estimatedCost')}{' '}
|
||||
<span className="text-ink not-italic">€ {estimatedCost.toFixed(2)}</span>
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-8 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div className="relative mt-12 grid grid-cols-12 items-end gap-6">
|
||||
<p className="font-display text-ink-soft animate-fade-up col-span-12 max-w-2xl text-2xl italic leading-snug [animation-delay:240ms] md:col-span-7 md:col-start-2">
|
||||
{list ? formatWeek(list.plan.weekStart) : t('noPlan')}
|
||||
</p>
|
||||
{list ? (
|
||||
<aside className="animate-fade-up col-span-12 [animation-delay:360ms] md:col-span-3 md:col-start-10 md:text-right">
|
||||
<p className="editorial-eyebrow">{t('totalLabel')}</p>
|
||||
<p className="font-display text-ink mt-2 font-mono text-3xl tabular-nums">
|
||||
{totalItems}
|
||||
</p>
|
||||
) : null}
|
||||
</aside>
|
||||
) : (
|
||||
<div className="animate-fade-up [animation-delay:360ms] md:col-span-4 md:text-right">
|
||||
<Button asChild size="lg">
|
||||
<Link href="/plan">{t('viewPlan')}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:420ms]" />
|
||||
|
||||
{list ? (
|
||||
<div className="animate-fade-up [animation-delay:480ms]">
|
||||
<ShoppingChecklist list={list} />
|
||||
{typeof estimatedCost === 'number' && estimatedCost > 0 ? (
|
||||
<p className="text-ink-soft mt-3 font-mono text-xs uppercase tracking-widest">
|
||||
{t('estimatedCost')}{' '}
|
||||
<span className="text-ink not-italic">€ {estimatedCost.toFixed(2)}</span>
|
||||
</p>
|
||||
) : null}
|
||||
</aside>
|
||||
) : (
|
||||
<div className="animate-fade-up col-span-12 [animation-delay:360ms] md:col-span-3 md:col-start-10 md:text-right">
|
||||
<Button asChild size="lg">
|
||||
<Link href="/plan">{t('viewPlan')}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
|
||||
<div className="rule-thick mt-16" />
|
||||
<div className="rule animate-rule-in relative my-12 [animation-delay:420ms]" />
|
||||
|
||||
{list ? (
|
||||
<div className="animate-fade-up relative [animation-delay:480ms]">
|
||||
<ShoppingChecklist list={list} />
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
|
||||
<div className="rule-thick" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { redirect } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CursorGlow } from '@/components/cursor-glow';
|
||||
import { Masthead } from '@/components/masthead';
|
||||
import { getServerSession } from '@/lib/auth';
|
||||
|
||||
@@ -63,59 +64,86 @@ function TrackingPageContent({
|
||||
const alertActive = alertKg != null && latest != null && latest.weightKg >= alertKg;
|
||||
|
||||
return (
|
||||
<div className="mx-auto min-h-screen max-w-5xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 06 — Tracking" />
|
||||
<main className="py-12 sm:py-16">
|
||||
<p className="editorial-eyebrow animate-fade-up">Capitolo VI</p>
|
||||
<h1 className="font-display text-display text-ink animate-fade-up mt-3 font-medium leading-[0.95] tracking-tight [animation-delay:120ms]">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up mt-6 max-w-2xl text-xl italic leading-snug [animation-delay:240ms]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<p className="animate-fade-up mt-4 [animation-delay:300ms]">
|
||||
<a
|
||||
href="/api/tracking-export"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
<div className="relative">
|
||||
<div className="mx-auto min-h-screen max-w-7xl px-6 sm:px-10">
|
||||
<Masthead issueLabel="N. 06 — Tracking" />
|
||||
<main className="relative overflow-hidden pb-24 pt-10 sm:pt-12">
|
||||
<CursorGlow color="hsl(var(--oro))" size={520} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oro animate-float-y -right-32 top-12 h-[28rem] w-[28rem] opacity-50"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="mesh-blob mesh-blob--oliva animate-float-x -left-24 top-72 h-72 w-72 opacity-40"
|
||||
/>
|
||||
<span
|
||||
aria-hidden
|
||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
||||
>
|
||||
{t('exportPdf')} ↗
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div className="rule animate-rule-in my-10 [animation-delay:360ms]" />
|
||||
|
||||
<DailyCheckIn initial={todayCheckIn} />
|
||||
|
||||
{alertActive && alertKg != null && latest != null ? (
|
||||
<aside className="border-pomodoro/40 bg-pomodoro/5 mb-10 border-2 border-dashed p-5">
|
||||
<p className="editorial-eyebrow">{t('alertEyebrow')}</p>
|
||||
<p className="font-display text-ink mt-3 text-base leading-snug">
|
||||
{t('alertMessage', {
|
||||
current: latest.weightKg.toFixed(1),
|
||||
threshold: alertKg.toFixed(1),
|
||||
})}
|
||||
VI
|
||||
</span>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
||||
style={{ writingMode: 'vertical-rl' }}
|
||||
>
|
||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">
|
||||
Capitolo VI — Tracking
|
||||
</p>
|
||||
<p className="font-display text-ink-soft mt-2 text-sm italic leading-snug">
|
||||
{t('alertHint')}
|
||||
</p>
|
||||
</aside>
|
||||
) : null}
|
||||
</div>
|
||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-8 font-medium [animation-delay:120ms]">
|
||||
<span className="text-ink block">
|
||||
{t('title')}
|
||||
<span className="text-pomodoro">.</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p className="font-display text-ink-soft animate-fade-up relative mt-10 max-w-2xl text-2xl italic leading-snug [animation-delay:240ms] md:ml-[16.66%]">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="grid gap-12 md:grid-cols-12">
|
||||
<section className="animate-fade-up [animation-delay:300ms] md:col-span-7">
|
||||
<p className="editorial-eyebrow mb-6">{t('newEntry')}</p>
|
||||
<WeightEntryForm latest={latest} />
|
||||
</section>
|
||||
<aside className="md:border-ink/15 animate-fade-up [animation-delay:420ms] md:col-span-5 md:border-l md:pl-10">
|
||||
<p className="editorial-eyebrow mb-6">{t('history')}</p>
|
||||
<WeightHistory entries={entries} goalKg={goalKg} startKg={startKg} />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
<p className="animate-fade-up relative mt-6 [animation-delay:300ms] md:ml-[16.66%]">
|
||||
<a
|
||||
href="/api/tracking-export"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('exportPdf')} ↗
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div className="rule animate-rule-in relative my-12 [animation-delay:360ms]" />
|
||||
|
||||
<DailyCheckIn initial={todayCheckIn} />
|
||||
|
||||
{alertActive && alertKg != null && latest != null ? (
|
||||
<aside className="border-pomodoro/40 bg-pomodoro/5 mb-10 border-2 border-dashed p-5">
|
||||
<p className="editorial-eyebrow">{t('alertEyebrow')}</p>
|
||||
<p className="font-display text-ink mt-3 text-base leading-snug">
|
||||
{t('alertMessage', {
|
||||
current: latest.weightKg.toFixed(1),
|
||||
threshold: alertKg.toFixed(1),
|
||||
})}
|
||||
</p>
|
||||
<p className="font-display text-ink-soft mt-2 text-sm italic leading-snug">
|
||||
{t('alertHint')}
|
||||
</p>
|
||||
</aside>
|
||||
) : null}
|
||||
|
||||
<div className="relative grid gap-12 md:grid-cols-12">
|
||||
<section className="animate-fade-up [animation-delay:300ms] md:col-span-7">
|
||||
<p className="editorial-eyebrow mb-6">{t('newEntry')}</p>
|
||||
<WeightEntryForm latest={latest} />
|
||||
</section>
|
||||
<aside className="md:border-ink/15 animate-fade-up [animation-delay:420ms] md:col-span-5 md:border-l md:pl-10">
|
||||
<p className="editorial-eyebrow mb-6">{t('history')}</p>
|
||||
<WeightHistory entries={entries} goalKg={goalKg} startKg={startKg} />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user