infoalloggi-monorepo/apps/infoalloggi/src/components/banners.tsx

231 lines
6.6 KiB
TypeScript
Raw Normal View History

import { getCookie, setCookie } from "cookies-next/client";
import { add } from "date-fns";
2025-08-04 17:45:44 +02:00
import { X } from "lucide-react";
2025-08-28 18:27:07 +02:00
import Link from "next/link";
import { type MouseEvent, useEffect, useRef, useState } from "react";
2025-08-04 17:45:44 +02:00
import { IconMatrix, type IconType } from "~/components/IconComponents";
2025-08-28 18:27:07 +02:00
import { cn } from "~/lib/utils";
import type { Banners } from "~/schemas/public/Banners";
2025-08-04 17:45:44 +02:00
const defaultHideDuration = 365; // days
2025-08-04 17:45:44 +02:00
export const BannerFactory = (bannerData: Banners) => {
2025-08-28 18:27:07 +02:00
const NEW_BANNER_KEY = `bannercookie_${bannerData.idbanner}`;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
let bgColor = "";
let accentColor = "";
let closeHoverColor = "";
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
switch (bannerData.color) {
case "blu":
bgColor = "bg-blue-600";
accentColor = "bg-blue-800";
closeHoverColor = "hover:bg-blue-500";
break;
case "rosso":
bgColor = "bg-red-600";
accentColor = "bg-red-800";
closeHoverColor = "hover:bg-red-500";
break;
case "verde":
bgColor = "bg-green-600";
accentColor = "bg-green-800";
closeHoverColor = "hover:bg-green-500";
break;
case "indaco":
bgColor = "bg-indigo-600";
accentColor = "bg-indigo-800";
closeHoverColor = "hover:bg-indigo-500";
break;
default:
bgColor = "bg-indigo-600";
accentColor = "bg-indigo-800";
closeHoverColor = "hover:bg-indigo-500";
break;
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const NewBanner = () => {
const [isBannerToShow, setIsBannerToShow] = useState(false);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
useEffect(() => {
if (bannerData.is_unskippable === false) {
const cookievalue = getCookie(NEW_BANNER_KEY) !== "true";
2025-08-28 18:27:07 +02:00
setIsBannerToShow(cookievalue);
}
}, []);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const onClick = (e: MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
setCookie(NEW_BANNER_KEY, "true", {
expires: add(new Date(), {
days: bannerData.hide_duration || defaultHideDuration,
}),
2025-08-28 18:27:07 +02:00
});
setIsBannerToShow(false);
};
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!isBannerToShow && bannerData.is_unskippable === false) {
return null;
}
return (
<div className={cn("bg-indigo-600", bgColor)}>
<div className="mx-auto flex max-w-7xl items-start justify-between px-4 py-3 text-white sm:items-center md:px-8">
2025-08-28 18:27:07 +02:00
<div className="flex flex-1 items-start justify-center gap-x-4 sm:items-center">
<div
className={cn(
2025-10-10 16:18:43 +02:00
"flex flex-none items-center justify-center rounded-full bg-indigo-800 p-1.5 px-4 font-medium text-sm",
2025-08-28 18:27:07 +02:00
accentColor,
)}
>
{bannerData.titolo}{" "}
</div>
<p className="flex gap-2 p-2 font-medium">
<span> {bannerData.testo}</span>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
{bannerData.has_cta &&
bannerData.cta_href &&
bannerData.cta_icon && (
<Link
className="inline-flex items-center gap-x-1 font-semibold underline duration-150 hover:text-indigo-100"
2025-08-29 16:18:32 +02:00
href={bannerData.cta_href}
2025-08-28 18:27:07 +02:00
>
{bannerData.cta_text}
<IconMatrix
className="size-4"
2025-08-29 16:18:32 +02:00
type={bannerData.cta_icon as IconType}
2025-08-28 18:27:07 +02:00
/>
</Link>
)}
</p>
</div>
{bannerData.is_unskippable === false && (
<button
className={cn(
"rounded-lg p-2 ring-offset-2 duration-150 hover:bg-indigo-500 focus:ring",
closeHoverColor,
)}
onClick={onClick}
2025-08-29 16:18:32 +02:00
type="button"
2025-08-28 18:27:07 +02:00
>
<X className="size-5" />
</button>
)}
</div>
</div>
);
};
return <NewBanner />;
2025-08-04 17:45:44 +02:00
};
export function AnniversaryBanner() {
const [isVisible, setIsVisible] = useState(false);
const bannerRef = useRef<HTMLDivElement>(null);
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/20 leading-none transition-all duration-1000 md:text-[12rem] ${
isVisible ? "translate-x-0 opacity-100" : "translate-x-16 opacity-0"
}`}
>
25
</div>
{/* Main content - horizontal layout */}
<div className="relative 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">
25
</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="12.5K+" />
<Stat label="Contratti" 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>
);
}