"use client"; import { parseAsArrayOf, parseAsBoolean, parseAsInteger, parseAsString, parseAsStringLiteral, 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): 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; tipo: RicercaUrlParams["tipo"]; comune: RicercaUrlParams["comune"] | null; consegna: RicercaUrlParams["consegna"] | null; minBudget: RicercaUrlParams["minBudget"] | null; maxBudget: RicercaUrlParams["maxBudget"] | null; sort: SortTypes; 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; reset: () => void; comuniOptions: string[]; tipologieOptions: string[]; consegnaOptions: number[]; sortOptions: typeof sortOptions; nFiltri: number; isLoading: boolean; }; export type AnnunciOptions = { comune: string; tipo: string; consegna: number; prezzo: number; }[]; const RicercaContext = createContext(undefined); export const useRicerca = () => { const context = useContext(RicercaContext); if (!context) { throw new Error("useRicerca must be used within a RicercaProvider"); } return context; }; export const RicercaProvider: FC<{ children: ReactNode; options: AnnunciOptions; }> = ({ children, options }) => { const [nFiltri, setNFiltri] = useState(0); const [isLoading, startTransition] = useTransition(); 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(); options.forEach((item) => { if (!comune || item.comune === comune) { if (!consegna || consegna.includes(item.consegna)) { if (!minBudget || item.prezzo >= minBudget) { if (!maxBudget || item.prezzo <= maxBudget) { set.add(item.tipo); } } } } }); return Array.from(set); }, [options, comune, consegna, minBudget, maxBudget]); const comuniOptions = useMemo(() => { const set = new Set(); options.forEach((item) => { if (!tipo || item.tipo === tipo) { if (!consegna || consegna.includes(item.consegna)) { if (!minBudget || item.prezzo >= minBudget) { if (!maxBudget || item.prezzo <= maxBudget) { set.add(item.comune); } } } } }); return Array.from(set).sort((a, b) => a.localeCompare(b)); }, [options, tipo, consegna, minBudget, maxBudget]); const consegnaOptions = useMemo(() => { const set = new Set(); options.forEach((item) => { if (!tipo || item.tipo === tipo) { if (!comune || item.comune === comune) { if (!minBudget || item.prezzo >= minBudget) { if (!maxBudget || item.prezzo <= maxBudget) { set.add(item.consegna); } } } } }); const currentMonth = new Date().getMonth() + 1; // 1-12 return Array.from(set).sort((a, b) => { // Calculate distance from current month (wrapping around) const distA = (a - currentMonth + 12) % 12; const distB = (b - currentMonth + 12) % 12; return distA - distB; }); }, [options, tipo, comune, minBudget, maxBudget]); 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), ); }; useEffect(() => { updateNFiltri(); }, [tipo, comune, consegna, minBudget, maxBudget, sort]); return ( {children} ); };