ultisuite-client/components/landing/landing-hero.tsx
R3D347HR4Y 359931c2f3
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: implement forgot password and signup flows with new layouts and components
- Added new layout and page components for forgot password and signup functionalities, enhancing user experience.
- Integrated authentication flow handling for password recovery and account creation, utilizing dynamic metadata.
- Updated login form to include links for forgot password and signup, improving navigation between authentication states.
- Refactored CSS styles for login components to ensure consistent design across different authentication pages.
2026-06-19 22:34:23 +02:00

158 lines
5.6 KiB
TypeScript

"use client"
import Link from "next/link"
import { Icon } from "@iconify/react"
import { LandingReveal } from "@/components/landing/landing-reveal"
import { LANDING_APPS, LANDING_APP_DEMO_TAB } from "@/components/landing/landing-data"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { getSignupUrl } from "@/lib/auth/oidc-config"
function HeroDock({
authenticated,
onOpenDemo,
}: {
authenticated: boolean
onOpenDemo: (demoTabId: string | null) => void
}) {
const apps = LANDING_APPS.filter((app) => app.href)
return (
<div className="flex flex-wrap items-center justify-center gap-3 sm:gap-4">
{apps.map((app, index) => {
const tileClass =
"landing-dock-tile landing-glass group flex size-14 items-center justify-center rounded-2xl transition-transform hover:scale-110 sm:size-16"
const tileStyle = { "--float-delay": `${index * 0.55}s` } as React.CSSProperties
const icon = (
<img
src={app.icon}
alt={app.name}
className="size-8 object-contain transition-transform group-hover:scale-110 sm:size-9"
draggable={false}
/>
)
if (authenticated) {
return (
<Link
key={app.name}
href={app.href!}
title={app.name}
className={tileClass}
style={tileStyle}
>
{icon}
</Link>
)
}
return (
<button
key={app.name}
type="button"
title={app.name}
className={tileClass}
style={tileStyle}
onClick={() => {
const demoTab = app.href ? (LANDING_APP_DEMO_TAB[app.href] ?? null) : null
onOpenDemo(demoTab)
}}
>
{icon}
</button>
)
})}
</div>
)
}
export function LandingHero({ onOpenDemo }: { onOpenDemo: (demoTabId: string | null) => void }) {
const identity = useChromeIdentity()
return (
<section className="relative px-4 pb-10 pt-14 sm:px-6 sm:pt-20">
<div className="mx-auto flex w-full max-w-5xl flex-col items-center gap-8 text-center">
<LandingReveal>
<span className="landing-glass inline-flex items-center gap-2.5 rounded-full px-4 py-1.5 text-xs font-medium text-[var(--landing-muted)] sm:text-sm">
<span className="landing-pulse-dot" aria-hidden />
Open source · Souveraine · Prête pour l'IA
</span>
</LandingReveal>
<LandingReveal delay={0.08}>
<h1 className="text-balance text-4xl font-bold leading-[1.06] tracking-tight sm:text-6xl lg:text-7xl">
Toute votre suite de travail.
<br />
<span className="landing-gradient-text">Chez vous.</span>
</h1>
</LandingReveal>
<LandingReveal delay={0.16}>
<p className="mx-auto max-w-2xl text-balance text-base leading-relaxed text-[var(--landing-muted)] sm:text-lg">
Mails, fichiers, documents collaboratifs, contacts et assistant IA :
l'alternative complète à Google Workspace et Microsoft 365,
open source et hébergée sur <em className="not-italic font-semibold text-[var(--landing-fg)]">votre</em> infrastructure.
</p>
</LandingReveal>
<LandingReveal delay={0.24} className="flex flex-col items-center gap-4">
{identity ? (
<>
<p className="text-sm text-[var(--landing-muted)]">
Bonjour {identity.firstName} votre suite vous attend.
</p>
<div className="flex flex-wrap items-center justify-center gap-3">
<Link
href="/mail/inbox"
className="landing-cta landing-cta--primary h-12 px-7 text-base"
>
Ouvrir Ultimail
<Icon icon="mdi:arrow-right" className="size-5" aria-hidden />
</Link>
<Link
href="/drive"
className="landing-cta landing-cta--ghost h-12 px-7 text-base"
>
Ouvrir UltiDrive
</Link>
</div>
</>
) : (
<>
<div className="flex flex-wrap items-center justify-center gap-3">
<Link
href="/login"
className="landing-cta landing-cta--primary h-12 px-7 text-base"
>
Se connecter
<Icon icon="mdi:arrow-right" className="size-5" aria-hidden />
</Link>
<a
href={getSignupUrl()}
className="landing-cta landing-cta--ghost h-12 px-7 text-base"
>
Créer un compte
</a>
</div>
<a
href="#demo"
className="group inline-flex items-center gap-1.5 text-sm font-medium text-[var(--landing-muted)] transition-colors hover:text-[var(--landing-fg)]"
>
ou essayez la démo interactive, sans compte
<Icon
icon="mdi:arrow-down"
className="size-4 transition-transform group-hover:translate-y-0.5"
aria-hidden
/>
</a>
</>
)}
</LandingReveal>
<LandingReveal delay={0.32} className="w-full">
<HeroDock authenticated={Boolean(identity)} onOpenDemo={onOpenDemo} />
</LandingReveal>
</div>
</section>
)
}