feat: update dependencies and improve UI components; add ComeFunziona component for service explanation

This commit is contained in:
Marco Pedone 2026-01-12 16:00:53 +01:00
parent d3b9174ab8
commit b9dd1f6f25
9 changed files with 108 additions and 13 deletions

View file

@ -59,7 +59,7 @@
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "^0.1.2", "leaflet-defaulticon-compatibility": "^0.1.2",
"lucide-react": "^0.536.0", "lucide-react": "^0.536.0",
"next": "^15.4.10", "next": "15.4.10",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nextjs-progressbar": "^0.0.16", "nextjs-progressbar": "^0.0.16",
"nodemailer": "^7.0.5", "nodemailer": "^7.0.5",

View file

@ -1,6 +1,5 @@
import { ArrowRight, BadgePlus, LogIn } from "lucide-react"; import { ArrowRight, BadgePlus } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { ContattoAnnuncio } from "~/components/annuncio-interactions/contatto_modal"; import { ContattoAnnuncio } from "~/components/annuncio-interactions/contatto_modal";
import LoadingButton from "~/components/custom_ui/loading-button"; import LoadingButton from "~/components/custom_ui/loading-button";
@ -19,7 +18,6 @@ export const AnnuncioInteractions = ({
session: SessionContextType; session: SessionContextType;
disabled?: boolean; disabled?: boolean;
}) => { }) => {
const router = useRouter();
const { id, tipo } = useAnnuncio(); const { id, tipo } = useAnnuncio();
if (disabled) { if (disabled) {
@ -44,7 +42,7 @@ export const AnnuncioInteractions = ({
) : ( ) : (
<div className="flex w-full flex-col gap-2"> <div className="flex w-full flex-col gap-2">
<ContattoAnnuncio /> <ContattoAnnuncio />
<span className="w-full text-center text-sm">oppure</span> {/* <span className="w-full text-center text-sm">oppure</span>
<Link <Link
aria-label="Login" aria-label="Login"
href={{ href={{
@ -56,7 +54,7 @@ export const AnnuncioInteractions = ({
<LogIn className="size-5" /> <LogIn className="size-5" />
<span>Accedi se hai un account</span> <span>Accedi se hai un account</span>
</Button> </Button>
</Link> </Link> */}
</div> </div>
)} )}
</div> </div>

View file

@ -1,4 +1,4 @@
import { MessageCircleQuestion } from "lucide-react"; import { MessageCirclePlus } from "lucide-react";
import { import {
Credenza, Credenza,
CredenzaBody, CredenzaBody,
@ -19,7 +19,7 @@ export const ContattoAnnuncio = () => {
<Credenza> <Credenza>
<CredenzaTrigger asChild> <CredenzaTrigger asChild>
<Button className="w-full" variant="destructive"> <Button className="w-full" variant="destructive">
<MessageCircleQuestion className="size-5" /> Contattaci <MessageCirclePlus className="size-5" /> Contattaci
</Button> </Button>
</CredenzaTrigger> </CredenzaTrigger>
<CredenzaContent className="max-h-[90vh]"> <CredenzaContent className="max-h-[90vh]">

View file

@ -0,0 +1,92 @@
import { HelpCircle } from "lucide-react";
import Link from "next/link";
import {
ExpandableScreen,
ExpandableScreenContent,
ExpandableScreenTrigger,
} from "./expandable_screen";
import { Button } from "./ui/button";
import { Separator } from "./ui/separator";
const texts: {
title: string;
parts: (string | { href: string; text: string })[];
}[] = [
{
title: "Costo del servizio",
parts: [
"Il costo del servizio è personalizzato in base alla tipologia e parametri della tua ricerca.",
"Per contattare i proprietari ti chiederemo un acconto di 35€ sul totale.",
"Potrai contattare tutti i proprietari degli immobili che desideri durante la durata del servizio.",
{ href: "/prezzi", text: "Scopri i prezzi" },
],
},
{
title: "Come procedere",
parts: [
"Contattaci per un immobile, prepareremo per te degli annunci compatibili alle tue esigenze e ti invieremo un invito via email per accedere alla piattaforma.",
"Una volta effettuato l'accesso, potrai acquistare il servizio e visualizzare i contatti dei proprietari degli immobili.",
{
href: "/annunci",
text: "Vai agli annunci",
},
],
},
{
title: "Nulla di fatto",
parts: [
"Se non trovi un immobile che soddisfi le tue esigenze, avrai 14 giorni di tempo per interrompere il servizio senza necessità di ulteriori pagamenti.",
],
},
];
export const ComeFunziona = () => {
return (
<ExpandableScreen
contentRadius="14px"
layoutId="cta-card"
triggerRadius="100px"
>
<ExpandableScreenTrigger className="w-full">
<Button className="w-full">
<HelpCircle className="size-5" />
<span>Scopri come funziona</span>
</Button>
</ExpandableScreenTrigger>
<ExpandableScreenContent className="bg-primary text-primary-foreground">
<div className="h-full space-y-6 p-6 py-12 sm:space-y-16 sm:px-10">
{texts.map((text, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: <ok>
<div className="space-y-6" key={index}>
<div className="space-y-2">
<h2 className="font-semibold text-4xl">{text.title}</h2>
{text.parts.map((p, i) => {
return typeof p === "object" ? (
<Link
className="text-2xl underline after:content-['_↗'] hover:text-secondary"
href={p.href}
// biome-ignore lint/suspicious/noArrayIndexKey: <ok>
key={`${index}-link-${i}`}
>
{p.text}
</Link>
) : (
<p
className="text-2xl"
// biome-ignore lint/suspicious/noArrayIndexKey: <ok>
key={`${index}-${i}`}
>
{p}
</p>
);
})}
</div>
<Separator />
</div>
))}
</div>
</ExpandableScreenContent>
</ExpandableScreen>
);
};

View file

@ -92,7 +92,7 @@ const Faqs: Record<FaqKeys, Faq> = {
}, },
perche_devo_pagare: { perche_devo_pagare: {
description: description:
"Payment is required to unlock the owner's contacts and schedule an appointment for the visit. The price varies depending on the type of search and allows you to visit 10 properties.", "Payment is required to unlock the owner's contacts and schedule an appointment for the visit. The price varies depending on the type of search and allows you to visit the properties.",
title: "Why do I have to pay to visit a property?", title: "Why do I have to pay to visit a property?",
}, },
perche_pagare_consulenza: { perche_pagare_consulenza: {
@ -147,7 +147,7 @@ const Faqs: Record<FaqKeys, Faq> = {
}, },
quanto_costa: { quanto_costa: {
description: description:
"The cost of the service varies depending on the type of search and the length of stay. For more information on prices, visit the dedicated page. The service lasts 60 days from the date of purchase, during which you can visit up to 10 properties.", "The cost of the service varies depending on the type of search and the length of stay. For more information on prices, visit the dedicated page. The service lasts 60 days from the date of purchase, during which you can contact the owners directly.",
title: "How much does the service cost? And how long does it last?", title: "How much does the service cost? And how long does it last?",
}, },
se_non_trovo: { se_non_trovo: {

View file

@ -94,7 +94,7 @@ const Faqs: Record<FaqKeys, Faq> = {
}, },
perche_devo_pagare: { perche_devo_pagare: {
description: description:
"Il pagamento è necessario per sbloccare i contatti del proprietario e fissare un appuntamento per la visita. Il prezzo varia in base alla tipologia di ricerca e ti permette di visitare 10 immobili.", "Il pagamento è necessario per sbloccare i contatti del proprietario e fissare un appuntamento per la visita. Il prezzo varia in base alla tipologia di ricerca e ti permette di visitare gli immobili.",
title: "Perchè devo pagare per visitare un immobile?", title: "Perchè devo pagare per visitare un immobile?",
}, },
perche_pagare_consulenza: { perche_pagare_consulenza: {
@ -150,7 +150,7 @@ const Faqs: Record<FaqKeys, Faq> = {
}, },
quanto_costa: { quanto_costa: {
description: description:
"Il costo del servizio varia in base alla tipologia di ricerca e alla durata della permanenza. Per maggiori informazioni sui prezzi, visita la pagina dedicata. Il servizio ha una durata di 60 giorni dalla data di acquisto, durante i quali potrai visitare fino a 10 immobili.", "Il costo del servizio varia in base alla tipologia di ricerca e alla durata della permanenza. Per maggiori informazioni sui prezzi, visita la pagina dedicata. Il servizio ha una durata di 60 giorni dalla data di acquisto, durante i quali potrai contattare direttamente i proprietari.",
title: "Quanto mi costa il servizio? E quanto dura?", title: "Quanto mi costa il servizio? E quanto dura?",
}, },
se_non_trovo: { se_non_trovo: {

View file

@ -23,6 +23,7 @@ import { CarouselAnnuncio } from "~/components/annuncio_card";
import { AnnuncioInteractions } from "~/components/annuncio-interactions/annuncio_interactions"; import { AnnuncioInteractions } from "~/components/annuncio-interactions/annuncio_interactions";
import { TouchProvider } from "~/components/custom_ui/HybridTooltip"; import { TouchProvider } from "~/components/custom_ui/HybridTooltip";
import Input from "~/components/custom_ui/input"; import Input from "~/components/custom_ui/input";
import { ComeFunziona } from "~/components/expand_guida";
import FailedAnnuncioLoading from "~/components/failed-loading"; import FailedAnnuncioLoading from "~/components/failed-loading";
import { IconMatrix, type IconType } from "~/components/IconComponents"; import { IconMatrix, type IconType } from "~/components/IconComponents";
import InformationBubble from "~/components/InformationBubble"; import InformationBubble from "~/components/InformationBubble";
@ -190,6 +191,7 @@ const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
session={session} session={session}
/> />
)} )}
<ComeFunziona />
{data.tipo && {data.tipo &&
(data.tipo === "Transitorio" || data.tipo === "Stabile") && ( (data.tipo === "Transitorio" || data.tipo === "Stabile") && (
<> <>

View file

@ -4,6 +4,7 @@ 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 { CodiceBox } from "~/components/codiceRicerca"; import { CodiceBox } from "~/components/codiceRicerca";
import { ComeFunziona } from "~/components/expand_guida";
import { PricingChoice } from "~/components/prezzi"; import { PricingChoice } from "~/components/prezzi";
import { HeroSvg, LogoSvg } from "~/components/svgs"; import { HeroSvg, LogoSvg } from "~/components/svgs";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
@ -49,6 +50,9 @@ const Home: NextPage = () => {
</div> </div>
</main> </main>
<div className="w-full space-y-10 md:space-y-20"> <div className="w-full space-y-10 md:space-y-20">
<div className="mx-auto max-w-4xl px-1 sm:px-0">
<ComeFunziona />
</div>
<FrequentSearches /> <FrequentSearches />
<AccordionComp <AccordionComp

View file

@ -4,7 +4,6 @@ import {
adminProcedure, adminProcedure,
apiProcedure, apiProcedure,
createTRPCRouter, createTRPCRouter,
protectedApiProcedure,
protectedProcedure, protectedProcedure,
publicProcedure, publicProcedure,
} from "~/server/api/trpc"; } from "~/server/api/trpc";