refactor: remove unused annunci data fetching and components; update ricerca logic
- Deleted the `annunci.ts` file which contained the database fetching logic. - Removed the `TestAppPage` component that relied on the deleted data fetching. - Eliminated the `AnnunciList` component that was dependent on the removed data. - Updated `annunci_map.tsx` to handle optional `selectComune` parameter. - Refactored `annunci_tutorial.tsx` to use `buildRicercaUrl` for navigation. - Adjusted `annuncio_card.tsx` for consistent styling and layout changes. - Modified `codiceRicerca.tsx` to fix z-index issue. - Enhanced `failed-loading.tsx` and `500Auth.tsx` for responsive design. - Updated `annunci.tsx` to include loading state for map and annunci list. - Refined filter handling in `Filters` component to use a single `setAllParams` method. - Introduced `FrequentSearches` component for better user navigation. - Updated `RicercaProvider` to streamline state management and URL building. - Adjusted server-side logic in `annunci.controller.ts` to ensure valid data.
This commit is contained in:
parent
cb97919157
commit
edaee780d6
22 changed files with 335 additions and 439 deletions
|
|
@ -59,27 +59,27 @@ const OnboardingServizio = ({
|
|||
<Text className="font-semibold">
|
||||
Annunci selezionati per te:
|
||||
</Text>
|
||||
<Section className="mb-[30px]" key={annuncio.codice}>
|
||||
<Row className="mb-[12px]">
|
||||
<Section className="mb-7.5" key={annuncio.codice}>
|
||||
<Row className="mb-3">
|
||||
<Img
|
||||
alt={`Annuncio image - ${annuncio.codice}`}
|
||||
className="block w-[240px] rounded-[4px] object-cover object-center"
|
||||
className="block w-60 rounded-lg object-cover object-center"
|
||||
height="140px"
|
||||
src={`${env.BASE_URL}/storage-api/get/${annuncio.immagine}?media=image`}
|
||||
width="100%"
|
||||
/>
|
||||
</Row>
|
||||
<Row className="block w-full">
|
||||
<div className="mb-[5px] flex h-[24px] w-fit items-center justify-center gap-1 rounded-full bg-indigo-600 px-2 font-semibold text-[12px] text-white leading-0">
|
||||
<div className="mb-1.25 flex h-6 w-fit items-center justify-center gap-1 rounded-full bg-indigo-600 px-2 font-semibold text-[12px] text-white leading-0">
|
||||
<strong>Codice: </strong> {annuncio.codice}
|
||||
</div>
|
||||
<Heading
|
||||
as="h2"
|
||||
className="mt-0 mb-[6px] font-bold text-[16px] leading-none"
|
||||
className="mt-0 mb-1.5 font-bold text-[16px] leading-none"
|
||||
>
|
||||
{annuncio.titolo || "N/A"}
|
||||
</Heading>
|
||||
<Text className="m-0 text-[14px] text-gray-500 leading-[24px]">
|
||||
<Text className="m-0 text-[14px] text-gray-500 leading-6">
|
||||
<strong>Prezzo:</strong> €
|
||||
{(annuncio.prezzo / 1e2).toFixed(2)}
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset
|
||||
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
|
||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/prezzi</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/guida</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/contatti</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/proprietari</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci?comune=Bassano+del+Grappa</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci?tipo=Transitorio</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci?comune=Bassano+del+Grappa&tipo=Transitorio</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci?tipo=Stabile</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/annunci?comune=Marostica</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/chi-siamo</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/termini-condizioni</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://www.infoalloggi.it/privacy-policy</loc>
|
||||
<lastmod>2025-08-23T15:09:55+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
|
||||
|
||||
</urlset>
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import type { Metadata } from "next";
|
||||
import "~/styles/globals.css";
|
||||
import "react-day-picker/dist/style.css";
|
||||
import { inter } from "~/utils/fonts";
|
||||
export const metadata: Metadata = {
|
||||
description: "Trova casa ora con Infoalloggi",
|
||||
icons: {
|
||||
apple: {
|
||||
sizes: "180x180",
|
||||
type: "image/png",
|
||||
url: "/favicon/apple-touch-icon.png",
|
||||
},
|
||||
icon: [
|
||||
{ sizes: "32x32", type: "image/png", url: "/favicon/favicon-32x32.png" },
|
||||
{ sizes: "16x16", type: "image/png", url: "/favicon/favicon-16x16.png" },
|
||||
],
|
||||
other: [
|
||||
{
|
||||
color: "#5bbad5",
|
||||
rel: "mask-icon",
|
||||
url: "/favicon/safari-pinned-tab.svg",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
manifest: "/site.webmanifest",
|
||||
title: "Infoalloggi.it",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html
|
||||
className={`${inter.variable} scrollbar-default font-sans`}
|
||||
lang="en"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
||||
import { db } from "~/server/db";
|
||||
|
||||
export async function getDataFromDB() {
|
||||
try {
|
||||
// Example query - adjust to your database schema
|
||||
const data = await db
|
||||
.selectFrom("annunci")
|
||||
.select("codice")
|
||||
.where("web", "=", true)
|
||||
.where("tipo", "=", TipologiaPosizioneEnum.Transitorio)
|
||||
.where("stato", "=", "Attivo")
|
||||
.execute();
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Database error:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { Suspense } from "react";
|
||||
import { getDataFromDB } from "~/_app/lib/annunci";
|
||||
import AnnunciList from "~/_app/ui/annunci";
|
||||
import { LoadingPage } from "~/components/loading";
|
||||
|
||||
export default function TestAppPage() {
|
||||
const annunci = getDataFromDB();
|
||||
|
||||
return (
|
||||
<div className="p-8">
|
||||
<h1 className="mb-4 font-bold text-2xl">Test App Page</h1>
|
||||
|
||||
<div className="rounded-lg border bg-gray-50 p-4">
|
||||
<h2 className="mb-2 font-semibold text-lg">Data from Database:</h2>
|
||||
|
||||
<Suspense fallback={<LoadingPage />}>
|
||||
<AnnunciList annunci={annunci} />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
"use client";
|
||||
import { use } from "react";
|
||||
import type { Annunci } from "~/schemas/public/Annunci";
|
||||
|
||||
type AnnunciProps = {
|
||||
annunci: Promise<Pick<Annunci, "codice">[]>;
|
||||
};
|
||||
|
||||
export default function AnnunciList({ annunci }: AnnunciProps) {
|
||||
const allAnnunci = use(annunci);
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{allAnnunci.map((ann) => (
|
||||
<li key={ann.codice}>{ann.codice}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
|
@ -53,14 +53,14 @@ const MapDisplay = memo(
|
|||
selectMaxBudget,
|
||||
}: {
|
||||
selectTipo: string;
|
||||
selectComune: string;
|
||||
selectComune: string | null;
|
||||
selectConsegna: number[] | null;
|
||||
setSelected: Dispatch<SetStateAction<AnnuncioRicercaWPosition | null>>;
|
||||
selectMinBudget: number | null;
|
||||
selectMaxBudget: number | null;
|
||||
}) => {
|
||||
const { data, isLoading } = api.annunci.getMapping.useQuery({
|
||||
comune: selectComune,
|
||||
comune: selectComune || undefined,
|
||||
consegna: selectConsegna || undefined,
|
||||
tipologia: selectTipo,
|
||||
minBudget: selectMinBudget || undefined,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
DialogTitle,
|
||||
} from "~/components/ui/dialog";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||
|
||||
const COOKIE_KEY = "tipologia_tutorial_shown";
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ export const CTA_TipologiaModal = () => {
|
|||
expires: add(new Date(), { days: 30 }), // Set cookie to expire in 30 days
|
||||
});
|
||||
if (v !== null) {
|
||||
window.location.assign(`/annunci?tipo=${v}`);
|
||||
window.location.assign(buildRicercaUrl({ tipo: v }));
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ const SkeletonCardAnnuncio = () => {
|
|||
<Skeleton className="h-10 w-full rounded-md" />
|
||||
<Skeleton className="h-10 w-full rounded-md" />
|
||||
</div>
|
||||
<div className="flex h-[8.5rem] flex-col justify-between">
|
||||
<div className="flex h-34 flex-col justify-between">
|
||||
<Skeleton className="h-20 w-full rounded-md" />
|
||||
|
||||
<div className="flex h-9 items-center justify-around gap-1.5">
|
||||
|
|
@ -451,7 +451,7 @@ export const CarouselAnnuncio = ({
|
|||
<Image
|
||||
alt={img.img}
|
||||
className={cn(
|
||||
"aspect-square max-h-92 w-full rounded-md bg-[#e6e9ec] object-cover sm:max-h-80 xl:max-h-[25rem]",
|
||||
"aspect-square max-h-92 w-full rounded-md bg-[#e6e9ec] object-cover sm:max-h-80 xl:max-h-100",
|
||||
immagini.length === 1 && "object-contain",
|
||||
)}
|
||||
height={400}
|
||||
|
|
@ -484,7 +484,7 @@ export const CarouselAnnuncio = ({
|
|||
key={`videoplayer-${video.video}`}
|
||||
>
|
||||
<VideoPlayer
|
||||
className="aspect-square max-h-92 object-cover sm:max-h-80 xl:max-h-[25rem]"
|
||||
className="aspect-square max-h-92 object-cover sm:max-h-80 xl:max-h-100"
|
||||
coverImage={getStorageUrl({
|
||||
storageId: video.thumb,
|
||||
params: {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const AutocompleteSearchBox = ({
|
|||
}, [searchValue, options]);
|
||||
|
||||
return (
|
||||
<div className="z-[5000] w-full" ref={wrapperRef}>
|
||||
<div className="z-5000 w-full" ref={wrapperRef}>
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex w-full items-center justify-center rounded-md bg-secondary dark:bg-primary",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default function FailedAnnuncioLoading() {
|
|||
const { t } = useTranslation();
|
||||
return (
|
||||
<main>
|
||||
<div className="mx-auto flex h-screen max-w-screen-xl items-center justify-start px-4 md:px-8">
|
||||
<div className="mx-auto flex h-screen max-w-7xl items-center justify-start px-4 md:px-8">
|
||||
<div className="mx-auto max-w-lg space-y-3 text-center">
|
||||
<HomeIcon className="mx-auto size-20 text-destructive" />
|
||||
<h3 className="font-semibold text-4xl text-accent-foreground sm:text-5xl">
|
||||
|
|
|
|||
97
apps/infoalloggi/src/components/frequent_searches.tsx
Normal file
97
apps/infoalloggi/src/components/frequent_searches.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import { ArrowRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||
import { Button } from "./ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
|
||||
|
||||
const it = {
|
||||
title: "Ricerche frequenti",
|
||||
transitorio: "Transitori",
|
||||
stabili: "Stabili",
|
||||
};
|
||||
|
||||
const en = {
|
||||
title: "Frequent Searches",
|
||||
transitorio: "Short-term",
|
||||
stabili: "Stable",
|
||||
};
|
||||
|
||||
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">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center font-bold text-3xl sm:text-2xl">
|
||||
{t.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="mx-auto flex max-w-fit flex-wrap items-center justify-center gap-4 px-2 sm:px-6">
|
||||
<Link href={buildRicercaUrl({ tipo: "Transitorio" })}>
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-transitorio bg-transitorio text-lg text-white"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
{t.transitorio}
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={buildRicercaUrl({ tipo: "Stabile" })}>
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-stabile bg-stabile text-lg text-white"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
{t.stabili}
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={buildRicercaUrl({
|
||||
comune: "Bassano del Grappa",
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-primary text-lg"
|
||||
size="lg"
|
||||
>
|
||||
Bassano del Grappa
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href={buildRicercaUrl({
|
||||
comune: "Bassano del Grappa",
|
||||
tipo: "Transitorio",
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-transitorio bg-transitorio text-lg text-white"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
{t.transitorio} Bassano
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href={buildRicercaUrl({
|
||||
comune: "Marostica",
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-primary text-lg"
|
||||
size="lg"
|
||||
>
|
||||
Marostica
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
@ -35,7 +35,7 @@ export const AnnunciCompatibili = () => {
|
|||
Esplora Annunci Compatibili
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-xl p-2 sm:max-w-5xl sm:p-6 md:max-w-7xl 2xl:max-w-[100rem]">
|
||||
<DialogContent className="max-w-xl p-2 sm:max-w-5xl sm:p-6 md:max-w-7xl 2xl:max-w-400">
|
||||
<DialogHeader className="flex-row flex-wrap items-center gap-5 pt-6">
|
||||
<DialogTitle>
|
||||
Annunci compatibili con i parametri del servizio
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
|
|||
stato: z.string().nullable(),
|
||||
|
||||
// DATI CONTRATTO
|
||||
tipo: z.string().nullable(),
|
||||
tipo: z.string(),
|
||||
titolo_en: z.string().nullable(),
|
||||
titolo_it: z.string().nullable(),
|
||||
unita_condominio: z.number().nullable(),
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ export type LangDict = {
|
|||
endtxt: string;
|
||||
endtxt2: string;
|
||||
};
|
||||
|
||||
annunci: {
|
||||
titolo: string;
|
||||
fs_tipologia: {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default function Custom500Auth() {
|
|||
const { t } = useTranslation();
|
||||
return (
|
||||
<main>
|
||||
<div className="mx-auto flex h-screen max-w-screen-xl items-center justify-start px-4 md:px-8">
|
||||
<div className="mx-auto flex h-screen max-w-7xl items-center justify-start px-4 md:px-8">
|
||||
<div className="mx-auto max-w-lg space-y-3 text-center">
|
||||
<ShieldExclamationIcon className="mx-auto size-20 text-red-600" />
|
||||
<h3 className="font-semibold text-4xl text-accent-foreground sm:text-5xl">
|
||||
|
|
|
|||
|
|
@ -105,14 +105,17 @@ export const getServerSideProps = (async () => {
|
|||
}) satisfies GetServerSideProps<AnnunciPageProps>;
|
||||
|
||||
const Ricerca = () => {
|
||||
const { map } = useRicerca();
|
||||
const { map, isLoading } = useRicerca();
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full space-y-5">
|
||||
<RicercaHeader />
|
||||
<div className="relative flex w-full gap-4">
|
||||
<RicercaSideFilters />
|
||||
<div className="flex-1">{map ? <MapSection /> : <AnnunciList />}</div>
|
||||
|
||||
<div className="flex-1">
|
||||
{isLoading ? <LoadingPage /> : map ? <MapSection /> : <AnnunciList />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -122,14 +125,12 @@ const Filters = () => {
|
|||
const { t } = useTranslation();
|
||||
const {
|
||||
tipo,
|
||||
setTipo,
|
||||
comune,
|
||||
setComune,
|
||||
sort,
|
||||
setSort,
|
||||
comuniOptions,
|
||||
tipologieOptions,
|
||||
ResetParams,
|
||||
setAllParams,
|
||||
reset,
|
||||
nFiltri,
|
||||
} = useRicerca();
|
||||
|
||||
|
|
@ -138,39 +139,55 @@ const Filters = () => {
|
|||
<SelectFilter
|
||||
defaultValue={""}
|
||||
handleReset={async () => {
|
||||
await setTipo("");
|
||||
await setAllParams({
|
||||
tipo: "",
|
||||
});
|
||||
}}
|
||||
id="tipologia-filter"
|
||||
label={t.annunci.fs_tipologia.titolo}
|
||||
onValueChange={async (value: string) => {
|
||||
await setTipo(value);
|
||||
await setAllParams({
|
||||
tipo: value,
|
||||
});
|
||||
}}
|
||||
options={tipologieOptions}
|
||||
placeholder={t.seleziona_placeholder}
|
||||
value={tipo}
|
||||
/>
|
||||
<SelectFilter
|
||||
defaultValue={""}
|
||||
defaultValue={undefined}
|
||||
handleReset={async () => {
|
||||
await setComune(null);
|
||||
await setAllParams({
|
||||
comune: null,
|
||||
});
|
||||
}}
|
||||
id="comune-filter"
|
||||
key={comune}
|
||||
label={t.annunci.comune}
|
||||
onValueChange={async (value: string) => {
|
||||
await setComune(value);
|
||||
await setAllParams({
|
||||
comune: value,
|
||||
});
|
||||
}}
|
||||
options={comuniOptions}
|
||||
placeholder={t.seleziona_placeholder}
|
||||
value={comune}
|
||||
value={comune || undefined}
|
||||
/>
|
||||
<ConsegnaFilter />
|
||||
<BudgetFilters />
|
||||
<SelectFilter
|
||||
defaultValue={sort}
|
||||
handleReset={async () => {
|
||||
await setSort("Recenti");
|
||||
await setAllParams({
|
||||
sort: "Recenti",
|
||||
});
|
||||
}}
|
||||
id="sort-filter"
|
||||
label={t.ordina_per}
|
||||
onValueChange={async (value: SortTypes) => {
|
||||
await setSort(value);
|
||||
await setAllParams({
|
||||
sort: value,
|
||||
});
|
||||
}}
|
||||
options={["Recenti", "Prezzo", "Consegna", "Mq"]}
|
||||
placeholder={t.seleziona_placeholder}
|
||||
|
|
@ -179,7 +196,7 @@ const Filters = () => {
|
|||
<Button
|
||||
className="flex flex-row gap-2"
|
||||
disabled={nFiltri === 0}
|
||||
onClick={ResetParams}
|
||||
onClick={reset}
|
||||
variant="destructive"
|
||||
>
|
||||
<FilterX /> {t.annunci.filtri_reset}
|
||||
|
|
@ -189,8 +206,11 @@ const Filters = () => {
|
|||
};
|
||||
|
||||
const RicercaSideFilters = () => {
|
||||
const isBigScreen = useMediaQuery("(min-width: 96rem)");
|
||||
|
||||
if (!isBigScreen) return null;
|
||||
return (
|
||||
<div className="hidden h-fit w-46 max-w-46 shrink-0 grow-0 flex-col gap-3 overflow-clip rounded-md bg-secondary p-2 outline-neutral-300 2xl:flex">
|
||||
<div className="flex h-fit w-46 max-w-46 shrink-0 grow-0 flex-col gap-3 overflow-clip rounded-md bg-secondary p-2 outline-neutral-300">
|
||||
<Filters />
|
||||
</div>
|
||||
);
|
||||
|
|
@ -230,7 +250,7 @@ const DrowdownFilters = () => {
|
|||
};
|
||||
|
||||
const RicercaHeader = () => {
|
||||
const { map, setMap } = useRicerca();
|
||||
const { map, setAllParams } = useRicerca();
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-10xl space-y-5">
|
||||
|
|
@ -240,7 +260,9 @@ const RicercaHeader = () => {
|
|||
<Button
|
||||
className="flex w-fit flex-row items-center gap-2 md:w-24"
|
||||
onClick={async () => {
|
||||
await setMap((v) => !v);
|
||||
await setAllParams({
|
||||
map: !map,
|
||||
});
|
||||
}}
|
||||
variant="default"
|
||||
>
|
||||
|
|
@ -281,10 +303,10 @@ const AnnunciList = () => {
|
|||
map,
|
||||
minBudget,
|
||||
maxBudget,
|
||||
isLoading: ricercaLoading,
|
||||
isLoading: isUpdatingRicerca,
|
||||
} = useRicerca();
|
||||
|
||||
const { data, status } = api.annunci.annunciRicerca.useQuery(
|
||||
const { data, status, isLoading } = api.annunci.annunciRicerca.useQuery(
|
||||
{
|
||||
comune: comune || undefined,
|
||||
consegna: consegna || undefined,
|
||||
|
|
@ -294,13 +316,13 @@ const AnnunciList = () => {
|
|||
maxBudget: maxBudget || undefined,
|
||||
},
|
||||
{
|
||||
enabled: !map,
|
||||
enabled: !map && !isUpdatingRicerca,
|
||||
placeholderData: keepPreviousData,
|
||||
staleTime: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
if (ricercaLoading) return <LoadingPage />;
|
||||
if (isLoading) return <LoadingPage />;
|
||||
if (status === "error") return <Status500 />;
|
||||
|
||||
return (
|
||||
|
|
@ -325,6 +347,7 @@ const AnnunciList = () => {
|
|||
|
||||
interface SelectFilterProps<T> {
|
||||
label: string;
|
||||
id: string;
|
||||
options: string[];
|
||||
defaultValue?: T;
|
||||
value: T | undefined;
|
||||
|
|
@ -335,6 +358,7 @@ interface SelectFilterProps<T> {
|
|||
|
||||
export const SelectFilter = <T extends string>({
|
||||
label,
|
||||
id,
|
||||
options,
|
||||
defaultValue,
|
||||
value,
|
||||
|
|
@ -350,6 +374,7 @@ export const SelectFilter = <T extends string>({
|
|||
"font-semibold text-muted-foreground",
|
||||
value !== defaultValue && "text-primary",
|
||||
)}
|
||||
htmlFor={id}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
|
|
@ -369,7 +394,10 @@ export const SelectFilter = <T extends string>({
|
|||
onValueChange={onValueChange}
|
||||
value={value}
|
||||
>
|
||||
<SelectTrigger className="w-full bg-primary-foreground text-primary">
|
||||
<SelectTrigger
|
||||
className="w-full bg-primary-foreground text-primary"
|
||||
id={id}
|
||||
>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
|
@ -386,12 +414,7 @@ export const SelectFilter = <T extends string>({
|
|||
|
||||
const ConsegnaFilter = () => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
consegna: consegna2,
|
||||
setConsegna: setConsegna2,
|
||||
|
||||
consegnaOptions,
|
||||
} = useRicerca();
|
||||
const { consegna: consegna2, setAllParams, consegnaOptions } = useRicerca();
|
||||
|
||||
const mappedConsegnaOptions = useMemo(() => {
|
||||
if (consegnaOptions.length === 0) return [];
|
||||
|
|
@ -404,14 +427,17 @@ const ConsegnaFilter = () => {
|
|||
|
||||
const onValueChange = async (value: number) => {
|
||||
if (consegna2?.includes(value)) {
|
||||
// Remove value
|
||||
const newConsegna2 = consegna2.filter((c) => c !== value);
|
||||
await setConsegna2(newConsegna2.length > 0 ? newConsegna2 : null);
|
||||
await setAllParams({
|
||||
consegna: newConsegna2.length > 0 ? newConsegna2 : null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Add value
|
||||
|
||||
const newConsegna2 = consegna2 ? [...consegna2, value] : [value];
|
||||
await setConsegna2(newConsegna2);
|
||||
await setAllParams({
|
||||
consegna: newConsegna2,
|
||||
});
|
||||
return;
|
||||
};
|
||||
return (
|
||||
|
|
@ -429,7 +455,9 @@ const ConsegnaFilter = () => {
|
|||
<button
|
||||
className="cursor-pointer"
|
||||
onClick={async () => {
|
||||
await setConsegna2(null);
|
||||
await setAllParams({
|
||||
consegna: null,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
|
|
@ -461,20 +489,24 @@ const ConsegnaFilter = () => {
|
|||
|
||||
const BudgetFilters = () => {
|
||||
const { t } = useTranslation();
|
||||
const { minBudget, setMinBudget, maxBudget, setMaxBudget } = useRicerca();
|
||||
const { minBudget, maxBudget, setAllParams } = useRicerca();
|
||||
|
||||
const minDebounced = useCallback(
|
||||
debounce(async (value: number | null) => {
|
||||
await setMinBudget(value);
|
||||
await setAllParams({
|
||||
minBudget: value,
|
||||
});
|
||||
}, 500),
|
||||
[setMinBudget],
|
||||
[setAllParams],
|
||||
);
|
||||
|
||||
const maxDebounced = useCallback(
|
||||
debounce(async (value: number | null) => {
|
||||
await setMaxBudget(value);
|
||||
await setAllParams({
|
||||
maxBudget: value,
|
||||
});
|
||||
}, 500),
|
||||
[setMaxBudget],
|
||||
[setAllParams],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
@ -486,6 +518,7 @@ const BudgetFilters = () => {
|
|||
"font-semibold text-muted-foreground",
|
||||
minBudget !== null && "text-primary",
|
||||
)}
|
||||
htmlFor="prezzoMIN"
|
||||
>
|
||||
{t.annunci.budgetMin}
|
||||
</Label>
|
||||
|
|
@ -493,7 +526,9 @@ const BudgetFilters = () => {
|
|||
<button
|
||||
className="cursor-pointer"
|
||||
onClick={async () => {
|
||||
await setMinBudget(null);
|
||||
await setAllParams({
|
||||
minBudget: null,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
|
|
@ -507,7 +542,7 @@ const BudgetFilters = () => {
|
|||
decimalScale={2}
|
||||
decimalSeparator=","
|
||||
fixedDecimalScale
|
||||
id="prezzo"
|
||||
id="prezzoMIN"
|
||||
min={0}
|
||||
onValueChange={(v) => {
|
||||
if (v && v > 0) {
|
||||
|
|
@ -529,6 +564,7 @@ const BudgetFilters = () => {
|
|||
"font-semibold text-muted-foreground",
|
||||
maxBudget !== null && "text-primary",
|
||||
)}
|
||||
htmlFor="prezzoMAX"
|
||||
>
|
||||
{t.annunci.budgetMax}
|
||||
</Label>
|
||||
|
|
@ -536,7 +572,9 @@ const BudgetFilters = () => {
|
|||
<button
|
||||
className="cursor-pointer"
|
||||
onClick={async () => {
|
||||
await setMaxBudget(null);
|
||||
await setAllParams({
|
||||
maxBudget: null,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
|
|
@ -549,7 +587,7 @@ const BudgetFilters = () => {
|
|||
decimalScale={2}
|
||||
decimalSeparator=","
|
||||
fixedDecimalScale
|
||||
id="prezzo"
|
||||
id="prezzoMAX"
|
||||
min={0}
|
||||
onValueChange={(v) => {
|
||||
if (v && v > 0) {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import { getStorageUrl } from "~/lib/storage_utils";
|
|||
import { cn } from "~/lib/utils";
|
||||
import { AnnuncioContext } from "~/providers/AnnuncioProvider";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||
import { useSession } from "~/providers/SessionProvider";
|
||||
import type { Annunci } from "~/schemas/public/Annunci";
|
||||
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
||||
|
|
@ -659,7 +660,7 @@ const AnnuncioFooter = ({
|
|||
external_videos,
|
||||
updated_at,
|
||||
}: {
|
||||
tipo: string | null;
|
||||
tipo: string;
|
||||
comune: string | null;
|
||||
consegna: number | null;
|
||||
videos: Pick<VideosRefs, "video" | "thumb">[];
|
||||
|
|
@ -706,14 +707,14 @@ const AnnuncioFooter = ({
|
|||
</div>
|
||||
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/annunci",
|
||||
query: {
|
||||
c: comune,
|
||||
f: consegna,
|
||||
t: tipo,
|
||||
},
|
||||
}}
|
||||
href={buildRicercaUrl({
|
||||
comune: comune || undefined,
|
||||
consegna: consegna ? [consegna] : undefined,
|
||||
tipo:
|
||||
tipo in ["Transitorio", "Stabile"]
|
||||
? (tipo as "Transitorio" | "Stabile")
|
||||
: undefined,
|
||||
})}
|
||||
target="_blank"
|
||||
>
|
||||
<Button className="my-10 w-full text-xl" variant="secondary">
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { ArrowRight, Telescope } from "lucide-react";
|
||||
import { Telescope } from "lucide-react";
|
||||
import type { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { AccordionComp } from "~/components/accordionComp";
|
||||
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 { Button } from "~/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { env } from "~/env";
|
||||
import type { LangDict } from "~/i18n/locales";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
|
|
@ -37,11 +36,11 @@ const Home: NextPage = () => {
|
|||
/>
|
||||
<meta content="Infoalloggi.it" property="og:site_name" />
|
||||
</Head>
|
||||
<div className="mx-5 mt-0 mb-12 sm:mt-[2rem]">
|
||||
<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-[19rem] max-w-full items-center justify-center min-[22rem]:h-[23rem] min-[25rem]:h-[26rem]">
|
||||
<div className="flex scale-50 items-center justify-center drop-shadow-xl sm:scale-100 md:max-h-96 min-[20rem]:scale-[0.6] min-[22rem]:scale-[0.7] min-[25rem]:scale-[0.8]">
|
||||
<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]">
|
||||
<HeroSvg />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -77,7 +76,7 @@ const TrovaCasaCTA = (props: { testi: LangDict }) => {
|
|||
<span className="text-destructive"> {testi.index.titolo_2}</span>{" "}
|
||||
{testi.index.titolo_3}
|
||||
</h1>
|
||||
<LogoSvg className="h-[3rem] w-auto sm:h-[4rem] md:h-[5rem]" />
|
||||
<LogoSvg className="h-12 w-auto sm:h-16 md:h-20" />
|
||||
<br />
|
||||
|
||||
<div className="flex flex-col space-y-4 lg:justify-start">
|
||||
|
|
@ -106,68 +105,3 @@ const TrovaCasaCTA = (props: { testi: LangDict }) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FrequentSearches = () => {
|
||||
const { locale } = useTranslation();
|
||||
return (
|
||||
<Card className="mx-auto mb-4 max-w-4xl gap-4 border-0 bg-secondary shadow-none">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center font-bold text-3xl sm:text-2xl">
|
||||
{locale === "en" ? "Frequent Searches" : "Ricerche frequenti"}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="mx-auto flex max-w-fit flex-wrap items-center justify-center gap-4 px-2 sm:px-6">
|
||||
<Link href="/annunci?tipo=Transitorio">
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-transitorio bg-transitorio text-black text-lg"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
Transitori
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/annunci?tipo=Stabile">
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-stabile bg-stabile text-black text-lg"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
Stabili
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/annunci?comune=Bassano+del+Grappa">
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-primary text-lg"
|
||||
size="lg"
|
||||
>
|
||||
Bassano del Grappa
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link href="/annunci?comune=Bassano+del+Grappa&tipo=Transitorio">
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-transitorio bg-transitorio text-black text-lg"
|
||||
size="lg"
|
||||
variant="flat"
|
||||
>
|
||||
Transitori Bassano
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link href="/annunci?comune=Marostica">
|
||||
<Button
|
||||
className="flex items-center gap-1 border-2 border-primary text-lg"
|
||||
size="lg"
|
||||
>
|
||||
Marostica
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { GetServerSideProps } from "next";
|
||||
import { env } from "~/env";
|
||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||
import { generateSSGHelper } from "~/server/utils/ssgHelper";
|
||||
|
||||
async function generateSiteMap() {
|
||||
|
|
@ -13,10 +14,11 @@ async function generateSiteMap() {
|
|||
`${env.NEXT_PUBLIC_BASE_URL}/proprietari`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/guida`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/annunci`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/annunci?t=Transitorio`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/annunci?c=Bassano+del+Grappa`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/annunci?t=Stabile`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}/annunci?c=Marostica`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}${buildRicercaUrl({ tipo: "Transitorio" })}`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}${buildRicercaUrl({ comune: "Bassano del Grappa" })}`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}${buildRicercaUrl({ comune: "Bassano del Grappa", tipo: "Transitorio" })}`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}${buildRicercaUrl({ tipo: "Stabile" })}`,
|
||||
`${env.NEXT_PUBLIC_BASE_URL}${buildRicercaUrl({ comune: "Marostica" })}`,
|
||||
];
|
||||
|
||||
const allUrls = [...staticUrls, ...dynamicUrls];
|
||||
|
|
|
|||
|
|
@ -5,64 +5,85 @@ import {
|
|||
parseAsInteger,
|
||||
parseAsString,
|
||||
parseAsStringLiteral,
|
||||
useQueryState,
|
||||
useQueryStates,
|
||||
} from "nuqs";
|
||||
import {
|
||||
createContext,
|
||||
type FC,
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
useTransition,
|
||||
} from "react";
|
||||
|
||||
const sortOptions = ["Recenti", "Prezzo", "Consegna", "Mq"] as const;
|
||||
export type SortTypes = (typeof sortOptions)[number];
|
||||
|
||||
type RicercaUrlParams = {
|
||||
map: boolean;
|
||||
tipo: string;
|
||||
comune: string;
|
||||
consegna: number[];
|
||||
minBudget: number;
|
||||
maxBudget: number;
|
||||
sort: SortTypes;
|
||||
};
|
||||
|
||||
export const paramsMapping = {
|
||||
map: "m",
|
||||
tipo: "t",
|
||||
comune: "c",
|
||||
consegna: "f",
|
||||
minBudget: "bmin",
|
||||
maxBudget: "bmax",
|
||||
sort: "s",
|
||||
} as const;
|
||||
|
||||
export const buildRicercaUrl = (params: Partial<RicercaUrlParams>): string => {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
const mappedKey = paramsMapping[key as keyof typeof paramsMapping];
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParams.set(mappedKey, value.toString());
|
||||
}
|
||||
});
|
||||
const queryString = searchParams.toString();
|
||||
return queryString ? `/annunci?${queryString}` : "/annunci";
|
||||
};
|
||||
|
||||
type RicercaContextType = {
|
||||
map: boolean;
|
||||
setMap: (
|
||||
value: boolean | ((old: boolean) => boolean | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
tipo: string;
|
||||
setTipo: (
|
||||
value: string | ((old: string) => string | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
comune: string;
|
||||
setComune: (
|
||||
value: string | ((old: string) => string | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
consegna: number[] | null;
|
||||
setConsegna: (
|
||||
value: number[] | ((old: number[] | null) => number[] | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
|
||||
minBudget: number | null;
|
||||
setMinBudget: (
|
||||
value: number | ((old: number | null) => number | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
maxBudget: number | null;
|
||||
setMaxBudget: (
|
||||
value: number | ((old: number | null) => number | null) | null,
|
||||
) => Promise<URLSearchParams>;
|
||||
|
||||
tipo: RicercaUrlParams["tipo"];
|
||||
comune: RicercaUrlParams["comune"] | null;
|
||||
consegna: RicercaUrlParams["consegna"] | null;
|
||||
minBudget: RicercaUrlParams["minBudget"] | null;
|
||||
maxBudget: RicercaUrlParams["maxBudget"] | null;
|
||||
sort: SortTypes;
|
||||
setSort: (
|
||||
value: SortTypes | ((old: SortTypes) => SortTypes | null) | null,
|
||||
|
||||
setAllParams: (
|
||||
params: Partial<{
|
||||
tipo: RicercaUrlParams["tipo"];
|
||||
comune: RicercaUrlParams["comune"] | null;
|
||||
consegna: RicercaUrlParams["consegna"] | null;
|
||||
minBudget: RicercaUrlParams["minBudget"] | null;
|
||||
maxBudget: RicercaUrlParams["maxBudget"] | null;
|
||||
sort: RicercaUrlParams["sort"];
|
||||
map: RicercaUrlParams["map"];
|
||||
}>,
|
||||
) => Promise<URLSearchParams>;
|
||||
reset: () => void;
|
||||
|
||||
comuniOptions: string[];
|
||||
tipologieOptions: string[];
|
||||
consegnaOptions: number[];
|
||||
sortOptions: typeof sortOptions;
|
||||
ResetParams: () => Promise<void>;
|
||||
|
||||
nFiltri: number;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const sortOptions = ["Recenti", "Prezzo", "Consegna", "Mq"] as const;
|
||||
|
||||
//sort as string union
|
||||
export type SortTypes = (typeof sortOptions)[number];
|
||||
|
||||
export type AnnunciOptions = {
|
||||
comune: string;
|
||||
tipo: string;
|
||||
|
|
@ -84,39 +105,36 @@ export const RicercaProvider: FC<{
|
|||
children: ReactNode;
|
||||
options: AnnunciOptions;
|
||||
}> = ({ children, options }) => {
|
||||
const [nFiltri, setNFiltri] = useState(0);
|
||||
const [isLoading, startTransition] = useTransition();
|
||||
|
||||
const [map, setMap] = useQueryState(
|
||||
"m",
|
||||
parseAsBoolean.withDefault(false).withOptions({ startTransition }),
|
||||
);
|
||||
const [tipo, setTipo] = useQueryState(
|
||||
"t",
|
||||
parseAsString.withDefault("").withOptions({ startTransition }),
|
||||
);
|
||||
const [comune, setComune] = useQueryState(
|
||||
"c",
|
||||
parseAsString.withDefault("").withOptions({ startTransition }),
|
||||
);
|
||||
const [consegna, setConsegna] = useQueryState(
|
||||
"f",
|
||||
parseAsArrayOf(parseAsInteger).withOptions({ startTransition }),
|
||||
);
|
||||
const [minBudget, setMinBudget] = useQueryState(
|
||||
"bmin",
|
||||
parseAsInteger.withOptions({ startTransition }),
|
||||
);
|
||||
const [maxBudget, setMaxBudget] = useQueryState(
|
||||
"bmax",
|
||||
parseAsInteger.withOptions({ startTransition }),
|
||||
);
|
||||
const [sort, setSort] = useQueryState(
|
||||
"s",
|
||||
parseAsStringLiteral(sortOptions)
|
||||
.withDefault("Recenti")
|
||||
.withOptions({ startTransition }),
|
||||
const [
|
||||
{ map, tipo, comune, consegna, minBudget, maxBudget, sort },
|
||||
setAllParams,
|
||||
] = useQueryStates(
|
||||
{
|
||||
map: parseAsBoolean.withDefault(false),
|
||||
tipo: parseAsString.withDefault(""),
|
||||
comune: parseAsString.withDefault(""),
|
||||
consegna: parseAsArrayOf(parseAsInteger),
|
||||
minBudget: parseAsInteger,
|
||||
maxBudget: parseAsInteger,
|
||||
sort: parseAsStringLiteral(sortOptions).withDefault("Recenti"),
|
||||
},
|
||||
{ startTransition, shallow: true, urlKeys: paramsMapping },
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setAllParams({
|
||||
tipo: "",
|
||||
comune: "",
|
||||
consegna: null,
|
||||
minBudget: null,
|
||||
maxBudget: null,
|
||||
sort: "Recenti",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tipologieOptions = useMemo(() => {
|
||||
const set = new Set<string>();
|
||||
options.forEach((item) => {
|
||||
|
|
@ -172,56 +190,43 @@ export const RicercaProvider: FC<{
|
|||
});
|
||||
}, [options, tipo, comune, minBudget, maxBudget]);
|
||||
|
||||
const ResetParams = async () => {
|
||||
await setTipo("");
|
||||
await setComune("");
|
||||
await setConsegna(null);
|
||||
await setMinBudget(null);
|
||||
await setMaxBudget(null);
|
||||
await setSort("Recenti");
|
||||
const updateNFiltri = () => {
|
||||
setNFiltri(
|
||||
[tipo, comune].reduce(
|
||||
(count, item) => (item !== "" ? count + 1 : count),
|
||||
0,
|
||||
) +
|
||||
(consegna !== null ? 1 : 0) +
|
||||
(sort !== "Recenti" ? 1 : 0) +
|
||||
(minBudget !== null ? 1 : 0) +
|
||||
(maxBudget !== null ? 1 : 0),
|
||||
);
|
||||
};
|
||||
|
||||
const nFiltri =
|
||||
[tipo, comune].reduce(
|
||||
(count, item) => (item !== "" ? count + 1 : count),
|
||||
0,
|
||||
) +
|
||||
(consegna !== null ? 1 : 0) +
|
||||
(sort !== "Recenti" ? 1 : 0) +
|
||||
(minBudget !== null ? 1 : 0) +
|
||||
(maxBudget !== null ? 1 : 0);
|
||||
useEffect(() => {
|
||||
updateNFiltri();
|
||||
}, [tipo, comune, consegna, minBudget, maxBudget, sort]);
|
||||
|
||||
return (
|
||||
<RicercaContext.Provider
|
||||
value={{
|
||||
comune,
|
||||
setComune,
|
||||
comuniOptions,
|
||||
|
||||
consegna,
|
||||
setConsegna,
|
||||
consegnaOptions,
|
||||
|
||||
tipo,
|
||||
setTipo,
|
||||
tipologieOptions,
|
||||
|
||||
minBudget,
|
||||
setMinBudget,
|
||||
|
||||
maxBudget,
|
||||
setMaxBudget,
|
||||
|
||||
sort,
|
||||
setSort,
|
||||
sortOptions: sortOptions,
|
||||
|
||||
map,
|
||||
setMap,
|
||||
tipo,
|
||||
comune,
|
||||
consegna,
|
||||
minBudget,
|
||||
maxBudget,
|
||||
sort,
|
||||
|
||||
setAllParams,
|
||||
reset,
|
||||
|
||||
comuniOptions,
|
||||
consegnaOptions,
|
||||
tipologieOptions,
|
||||
sortOptions,
|
||||
nFiltri,
|
||||
isLoading,
|
||||
ResetParams,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { revalidate } from "../utils/revalidationHelper";
|
|||
// analytics: true,
|
||||
// });
|
||||
export type AnnunciWithMedia = Annunci & {
|
||||
tipo: string;
|
||||
images: Pick<ImagesRefs, "img" | "thumb">[];
|
||||
videos: Pick<VideosRefs, "video" | "thumb">[];
|
||||
};
|
||||
|
|
@ -82,14 +83,15 @@ export const getAnnunciByCod = async ({
|
|||
.selectAll()
|
||||
.select((_eb) => [withImages(), withVideos()])
|
||||
.where("annunci.web", "=", true)
|
||||
.where("tipo", "is not", null)
|
||||
.where("codice", "=", cod)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!annuncio) {
|
||||
if (!annuncio || annuncio.tipo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return annuncio;
|
||||
return annuncio as AnnunciWithMedia;
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
cause: (e as Error).cause,
|
||||
|
|
@ -151,15 +153,16 @@ export const getAnnunciById_rawImgUrls = async ({
|
|||
.selectAll()
|
||||
.select((_eb) => [withImages(), withVideos()])
|
||||
.where("id", "=", id)
|
||||
.where("tipo", "is not", null)
|
||||
.executeTakeFirst();
|
||||
if (!annuncio) {
|
||||
if (!annuncio || annuncio.tipo === null) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Annuncio non trovato",
|
||||
});
|
||||
}
|
||||
|
||||
return annuncio;
|
||||
return annuncio as AnnunciWithMedia;
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue