feat: add AnniversaryBanner component with visibility animation and update Home page layout

This commit is contained in:
Marco Pedone 2026-03-24 12:03:31 +01:00
parent d306548209
commit c2e8d68b47
4 changed files with 129 additions and 11 deletions

View file

@ -2,7 +2,7 @@ import { getCookie, setCookie } from "cookies-next/client";
import { add } from "date-fns";
import { X } from "lucide-react";
import Link from "next/link";
import { type MouseEvent, useEffect, useState } from "react";
import { type MouseEvent, useEffect, useRef, useState } from "react";
import { IconMatrix, type IconType } from "~/components/IconComponents";
import { cn } from "~/lib/utils";
import type { Banners } from "~/schemas/public/Banners";
@ -116,3 +116,119 @@ export const BannerFactory = (bannerData: Banners) => {
};
return <NewBanner />;
};
export function AnniversaryBanner() {
const [isVisible, setIsVisible] = useState(false);
const bannerRef = useRef<HTMLDivElement>(null);
const fundingDate = new Date("2000-08-01");
const currentDate = new Date();
const yearsOfExperience =
currentDate.getFullYear() - fundingDate.getFullYear();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry?.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.3 },
);
if (bannerRef.current) {
observer.observe(bannerRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div
className="relative mx-auto w-full max-w-5xl overflow-hidden rounded-lg bg-secondary"
ref={bannerRef}
>
{/* Decorative background "25" */}
<div
className={`absolute top-1/2 -right-8 -translate-y-1/2 font-bold text-[8rem] text-primary/10 leading-none transition-all duration-1000 md:text-[12rem] ${
isVisible ? "translate-x-0 opacity-100" : "translate-x-16 opacity-0"
}`}
>
{yearsOfExperience}
</div>
{/* Main content - horizontal layout */}
<div className="relative z-10 mx-auto flex max-w-7xl flex-col items-center justify-between gap-6 px-6 py-6 text-primary md:flex-row md:gap-8 md:px-8 md:py-8">
{/* Left: Badge + Number */}
<div
className={`flex items-center gap-4 transition-all delay-100 duration-700 md:gap-6 ${
isVisible ? "translate-x-0 opacity-100" : "-translate-x-8 opacity-0"
}`}
>
<div className="flex items-center gap-2">
<div className="h-px w-6 bg-primary" />
<span className="whitespace-nowrap font-medium text-[10px] uppercase tracking-[0.2em] md:text-xs">
Da più di
</span>
</div>
<div className="flex items-baseline gap-2">
<span className="font-bold text-5xl leading-none tracking-tight md:text-6xl lg:text-7xl">
{yearsOfExperience}
</span>
<span className="font-light text-sm uppercase tracking-[0.2em] md:text-base">
Anni
</span>
</div>
</div>
{/* Center: Tagline */}
<div
className={`flex-1 text-center text-primary transition-all delay-300 duration-700 md:text-left ${
isVisible ? "translate-y-0 opacity-100" : "translate-y-4 opacity-0"
}`}
>
<p className="font-light text-sm md:text-base lg:text-lg">
Un quarto di secolo di{" "}
<span className="font-semibold">eccellenza</span>,{" "}
<span className="font-semibold">innovazione</span>, e{" "}
<span className="font-semibold">fiducia</span>
</p>
</div>
{/* Right: Mini stats */}
<div
className={`flex items-center gap-6 transition-all delay-500 duration-700 md:gap-8 lg:gap-10 ${
isVisible ? "translate-x-0 opacity-100" : "translate-x-8 opacity-0"
}`}
>
<Stat label="Clienti" value="7500+" />
<Stat label="Affitti" value="10K+" />
</div>
</div>
{/* Accent lines */}
<div
className={`absolute bottom-0 left-0 h-1 bg-primary transition-all delay-700 duration-1000 ${
isVisible ? "w-24 md:w-32" : "w-0"
}`}
/>
<div
className={`absolute top-0 right-0 h-1 bg-primary transition-all delay-700 duration-1000 ${
isVisible ? "w-24 md:w-32" : "w-0"
}`}
/>
</div>
);
}
function Stat({ value, label }: { value: string; label: string }) {
return (
<div className="flex flex-col items-center gap-0.5">
<span className="font-bold text-primary text-xl md:text-2xl">
{value}
</span>
<span className="font-medium text-primary text-xs uppercase tracking-wider">
{label}
</span>
</div>
);
}

View file

@ -21,7 +21,7 @@ export const FrequentSearches = () => {
const { locale } = useTranslation();
const t = locale === "en" ? en : it;
return (
<Card className="mx-auto mb-4 max-w-4xl gap-4 border-0 bg-secondary shadow-none">
<Card className="mx-auto w-full max-w-5xl gap-4 border-0 bg-secondary shadow-none">
<CardHeader>
<CardTitle className="text-center font-bold text-3xl sm:text-2xl">
{t.title}

View file

@ -39,7 +39,7 @@ export function MobileNav({
}: MobileNavProps) {
const session = useSession();
const { t } = useTranslation();
const verySmallScreen = useMediaQuery("(max-width: 320px)");
const verySmallScreen = useMediaQuery("(max-width: 280px)");
return (
<>
<Sheet onOpenChange={setOpen} open={open}>
@ -127,7 +127,7 @@ export function MobileNav({
</Sheet>
<Link
aria-label="mainNavLink"
className="ml-3 flex shrink-0 items-center md:hidden"
className="flex shrink-0 items-center md:hidden"
href="/"
>
{verySmallScreen ? (

View file

@ -3,9 +3,9 @@ import type { NextPage } from "next";
import Head from "next/head";
import Link from "next/link";
import { AccordionComp } from "~/components/accordionComp";
import { AnniversaryBanner } from "~/components/banners";
import { CodiceBox } from "~/components/codiceRicerca";
import { ComeFunziona } from "~/components/expand_guida";
import { FrequentSearches } from "~/components/frequent_searches";
import { PricingChoice } from "~/components/prezzi";
import { HeroSvg, LogoSvg } from "~/components/svgs";
import { env } from "~/env";
@ -42,8 +42,8 @@ const Home: NextPage = () => {
<div className="mx-5 mt-0 mb-12 sm:mt-8">
<main className="mx-auto mb-4 max-w-8xl space-y-5 lg:mb-20">
<div className="flex flex-col items-center justify-center lg:flex-row lg:justify-around">
<div className="-mt-2 xs:mt-5 flex h-76 max-w-full items-center justify-center min-[22rem]:h-92 min-[25rem]:h-104">
<div className="flex scale-50 xxs:scale-[0.6] items-center justify-center drop-shadow-xl sm:scale-100 md:max-h-96 min-[22rem]:scale-[0.7] min-[25rem]:scale-[0.8]">
<div className="-mt-2 xs:mt-5 flex h-68 max-w-full items-center justify-center min-[22rem]:h-76 min-[25rem]:h-92 min-[27rem]:h-104">
<div className="flex scale-50 xxs:scale-[0.5] items-center justify-center drop-shadow-xl sm:scale-100 md:max-h-92 min-[22rem]:scale-[0.58] min-[25rem]:scale-[0.7]">
<HeroSvg />
</div>
</div>
@ -51,13 +51,15 @@ const Home: NextPage = () => {
<TrovaCasaCTA testi={t} />
</div>
</main>
<div className="w-full space-y-10 md:space-y-20">
<FrequentSearches />
<div className="mx-auto max-w-4xl px-1 sm:px-0">
<div className="flex w-full flex-col gap-y-4 md:gap-y-6">
<AnniversaryBanner />
{/* <FrequentSearches /> */}
<div className="mx-auto w-full max-w-5xl">
<ComeFunziona />
</div>
<AccordionComp
className="mb-4 max-w-4xl py-4 md:py-8"
className="w-full max-w-5xl"
texts={t.index.accordion_minifaq}
/>