feat: add AnniversaryBanner component with visibility animation and update Home page layout
This commit is contained in:
parent
d306548209
commit
c2e8d68b47
4 changed files with 129 additions and 11 deletions
|
|
@ -2,7 +2,7 @@ import { getCookie, setCookie } from "cookies-next/client";
|
||||||
import { add } from "date-fns";
|
import { add } from "date-fns";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import Link from "next/link";
|
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 { IconMatrix, type IconType } from "~/components/IconComponents";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
import type { Banners } from "~/schemas/public/Banners";
|
import type { Banners } from "~/schemas/public/Banners";
|
||||||
|
|
@ -116,3 +116,119 @@ export const BannerFactory = (bannerData: Banners) => {
|
||||||
};
|
};
|
||||||
return <NewBanner />;
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export const FrequentSearches = () => {
|
||||||
const { locale } = useTranslation();
|
const { locale } = useTranslation();
|
||||||
const t = locale === "en" ? en : it;
|
const t = locale === "en" ? en : it;
|
||||||
return (
|
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>
|
<CardHeader>
|
||||||
<CardTitle className="text-center font-bold text-3xl sm:text-2xl">
|
<CardTitle className="text-center font-bold text-3xl sm:text-2xl">
|
||||||
{t.title}
|
{t.title}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export function MobileNav({
|
||||||
}: MobileNavProps) {
|
}: MobileNavProps) {
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const verySmallScreen = useMediaQuery("(max-width: 320px)");
|
const verySmallScreen = useMediaQuery("(max-width: 280px)");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Sheet onOpenChange={setOpen} open={open}>
|
<Sheet onOpenChange={setOpen} open={open}>
|
||||||
|
|
@ -127,7 +127,7 @@ export function MobileNav({
|
||||||
</Sheet>
|
</Sheet>
|
||||||
<Link
|
<Link
|
||||||
aria-label="mainNavLink"
|
aria-label="mainNavLink"
|
||||||
className="ml-3 flex shrink-0 items-center md:hidden"
|
className="flex shrink-0 items-center md:hidden"
|
||||||
href="/"
|
href="/"
|
||||||
>
|
>
|
||||||
{verySmallScreen ? (
|
{verySmallScreen ? (
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import type { NextPage } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { AccordionComp } from "~/components/accordionComp";
|
import { AccordionComp } from "~/components/accordionComp";
|
||||||
|
import { AnniversaryBanner } from "~/components/banners";
|
||||||
import { CodiceBox } from "~/components/codiceRicerca";
|
import { CodiceBox } from "~/components/codiceRicerca";
|
||||||
import { ComeFunziona } from "~/components/expand_guida";
|
import { ComeFunziona } from "~/components/expand_guida";
|
||||||
import { FrequentSearches } from "~/components/frequent_searches";
|
|
||||||
import { PricingChoice } from "~/components/prezzi";
|
import { PricingChoice } from "~/components/prezzi";
|
||||||
import { HeroSvg, LogoSvg } from "~/components/svgs";
|
import { HeroSvg, LogoSvg } from "~/components/svgs";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
|
|
@ -42,8 +42,8 @@ const Home: NextPage = () => {
|
||||||
<div className="mx-5 mt-0 mb-12 sm:mt-8">
|
<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">
|
<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="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="-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.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="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 />
|
<HeroSvg />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -51,13 +51,15 @@ const Home: NextPage = () => {
|
||||||
<TrovaCasaCTA testi={t} />
|
<TrovaCasaCTA testi={t} />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div className="w-full space-y-10 md:space-y-20">
|
|
||||||
<FrequentSearches />
|
<div className="flex w-full flex-col gap-y-4 md:gap-y-6">
|
||||||
<div className="mx-auto max-w-4xl px-1 sm:px-0">
|
<AnniversaryBanner />
|
||||||
|
{/* <FrequentSearches /> */}
|
||||||
|
<div className="mx-auto w-full max-w-5xl">
|
||||||
<ComeFunziona />
|
<ComeFunziona />
|
||||||
</div>
|
</div>
|
||||||
<AccordionComp
|
<AccordionComp
|
||||||
className="mb-4 max-w-4xl py-4 md:py-8"
|
className="w-full max-w-5xl"
|
||||||
texts={t.index.accordion_minifaq}
|
texts={t.index.accordion_minifaq}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue