From b7850f6994b80cdc293452df79b53c8498d74bea Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Fri, 7 Nov 2025 17:03:08 +0100 Subject: [PATCH] feat: update package dependencies and enhance UI components with badges; refactor service dialogs and remove unused components --- apps/infoalloggi/package-lock.json | 2 +- apps/infoalloggi/package.json | 2 +- .../annuncio_interactions.tsx | 2 +- .../src/components/annuncio_card.tsx | 8 +- .../components/custom_ui/nestableDialog.tsx | 344 ------------------ .../src/components/servizio/annuncio_card.tsx | 13 +- .../servizio/compatibili_dialog.tsx | 195 ++++++++++ .../src/components/servizio/main.tsx | 5 +- .../components/servizio/servizio_actions.tsx | 6 +- .../servizio/servizio_annunci_accordions.tsx | 57 +-- apps/infoalloggi/src/pages/test.tsx | 25 +- .../server/controllers/annunci.controller.ts | 9 + 12 files changed, 237 insertions(+), 431 deletions(-) delete mode 100644 apps/infoalloggi/src/components/custom_ui/nestableDialog.tsx create mode 100644 apps/infoalloggi/src/components/servizio/compatibili_dialog.tsx diff --git a/apps/infoalloggi/package-lock.json b/apps/infoalloggi/package-lock.json index 21fb1c1..6c26b52 100644 --- a/apps/infoalloggi/package-lock.json +++ b/apps/infoalloggi/package-lock.json @@ -64,7 +64,7 @@ "nuqs": "^2.4.3", "pg": "^8.16.3", "postcss": "^8.5.6", - "radix-ui": "^1.4.2", + "radix-ui": "^1.4.3", "react": "^19.1.0", "react-colorful": "^5.6.1", "react-day-picker": "^9.11.1", diff --git a/apps/infoalloggi/package.json b/apps/infoalloggi/package.json index 8a25f8b..9ddda76 100644 --- a/apps/infoalloggi/package.json +++ b/apps/infoalloggi/package.json @@ -75,7 +75,7 @@ "nuqs": "^2.4.3", "pg": "^8.16.3", "postcss": "^8.5.6", - "radix-ui": "^1.4.2", + "radix-ui": "^1.4.3", "react": "^19.1.0", "react-colorful": "^5.6.1", "react-day-picker": "^9.11.1", diff --git a/apps/infoalloggi/src/components/annuncio-interactions/annuncio_interactions.tsx b/apps/infoalloggi/src/components/annuncio-interactions/annuncio_interactions.tsx index 468bcc1..67d9e97 100644 --- a/apps/infoalloggi/src/components/annuncio-interactions/annuncio_interactions.tsx +++ b/apps/infoalloggi/src/components/annuncio-interactions/annuncio_interactions.tsx @@ -224,7 +224,7 @@ export const InteressatoButtonServizio = ({ variant="info" > - {isAdmin ? "Aggiungi annuncio" : "Sono interessato"} + {isAdmin ? "Aggiungi" : "Sono interessato"} ); }; diff --git a/apps/infoalloggi/src/components/annuncio_card.tsx b/apps/infoalloggi/src/components/annuncio_card.tsx index e3e01c0..e44982c 100644 --- a/apps/infoalloggi/src/components/annuncio_card.tsx +++ b/apps/infoalloggi/src/components/annuncio_card.tsx @@ -230,17 +230,17 @@ export const CardAnnuncio = ({ )}
-
-
Affitto {tipo}
-
+ Affitto {tipo} +
{t.card.codice} diff --git a/apps/infoalloggi/src/components/custom_ui/nestableDialog.tsx b/apps/infoalloggi/src/components/custom_ui/nestableDialog.tsx deleted file mode 100644 index 9894e07..0000000 --- a/apps/infoalloggi/src/components/custom_ui/nestableDialog.tsx +++ /dev/null @@ -1,344 +0,0 @@ -"use client"; - -import * as DialogPrimitive from "@radix-ui/react-dialog"; -import { X } from "lucide-react"; -import { - type ComponentPropsWithoutRef, - createContext, - type Dispatch, - type ElementRef, - forwardRef, - type HTMLAttributes, - type PointerEvent, - type ReactNode, - type SetStateAction, - useContext, - useEffect, - useRef, - useState, -} from "react"; -import { cn } from "~/lib/utils"; - -interface DialogContextValue { - innerOpen: boolean; - setInnerOpen: Dispatch>; -} - -const DialogContext = createContext(undefined); - -function Dialog({ children }: { children: ReactNode }) { - const [outerOpen, setOuterOpen] = useState(false); - const [innerOpen, setInnerOpen] = useState(false); - - return ( - - - {children} - - - ); -} - -const DialogTrigger = DialogPrimitive.Trigger; -const DialogPortal = DialogPrimitive.Portal; -const DialogClose = DialogPrimitive.Close; - -const DialogOverlay = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; - -const DialogContent = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => { - const context = useContext(DialogContext); - if (!context) throw new Error("DialogContent must be used within a Dialog"); - - return ( - - - - {children} - - - Close - - - - ); -}); -DialogContent.displayName = DialogPrimitive.Content.displayName; - -function InnerDialog({ children }: { children: ReactNode }) { - const context = useContext(DialogContext); - if (!context) throw new Error("InnerDialog must be used within a Dialog"); - - useEffect(() => { - const handleEscapeKeyDown = (event: KeyboardEvent) => { - if (event.key === "Escape" && context.innerOpen) { - context.setInnerOpen(false); - event.stopPropagation(); - } - }; - - document.addEventListener("keydown", handleEscapeKeyDown); - return () => { - document.removeEventListener("keydown", handleEscapeKeyDown); - }; - }, [context.innerOpen, context.setInnerOpen]); - - return ( - - {children} - - ); -} - -const InnerDialogTrigger = DialogPrimitive.Trigger; -const InnerDialogClose = DialogPrimitive.Close; - -interface InnerDialogContentProps - extends ComponentPropsWithoutRef { - position?: "default" | "bottom" | "top" | "left" | "right"; - draggable?: boolean; -} - -const InnerDialogContent = forwardRef< - ElementRef, - InnerDialogContentProps ->( - ( - { className, children, position = "default", draggable = false, ...props }, - ref, - ) => { - const context = useContext(DialogContext); - if (!context) - throw new Error("InnerDialogContent must be used within a Dialog"); - - const [isDragging, setIsDragging] = useState(false); - const [startY, setStartY] = useState(0); - const [currentY, setCurrentY] = useState(0); - const [isClosingByDrag, setIsClosingByDrag] = useState(false); - const contentRef = useRef(null); - - useEffect(() => { - if (context.innerOpen) { - setCurrentY(0); - setIsClosingByDrag(false); - } - }, [context.innerOpen]); - - const handlePointerDown = (e: PointerEvent) => { - if (!draggable) return; - setIsDragging(true); - setStartY(e.clientY - currentY); - e.currentTarget.setPointerCapture(e.pointerId); - }; - - const handlePointerMove = (e: PointerEvent) => { - if (!isDragging || !draggable) return; - const newY = e.clientY - startY; - setCurrentY(newY > 0 ? newY : 0); - }; - - const handlePointerUp = () => { - if (!draggable) return; - setIsDragging(false); - if (currentY > (contentRef.current?.offsetHeight || 0) / 2) { - setIsClosingByDrag(true); - context.setInnerOpen(false); - } else { - setCurrentY(0); - } - }; - - return ( - - -
{children}
- - - Close - -
-
- ); - }, -); -InnerDialogContent.displayName = "InnerDialogContent"; - -const InnerDialogHeader = ({ - className, - ...props -}: HTMLAttributes) => ( -
-); -InnerDialogHeader.displayName = "InnerDialogHeader"; - -const InnerDialogFooter = ({ - className, - ...props -}: HTMLAttributes) => ( -
-); -InnerDialogFooter.displayName = "InnerDialogFooter"; - -const InnerDialogTitle = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -InnerDialogTitle.displayName = "InnerDialogTitle"; - -const InnerDialogDescription = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -InnerDialogDescription.displayName = "InnerDialogDescription"; - -const DialogHeader = ({ - className, - ...props -}: HTMLAttributes) => ( -
-); -DialogHeader.displayName = "DialogHeader"; - -const DialogFooter = ({ - className, - ...props -}: HTMLAttributes) => ( -
-); -DialogFooter.displayName = "DialogFooter"; - -const DialogTitle = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogTitle.displayName = DialogPrimitive.Title.displayName; - -const DialogDescription = forwardRef< - ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogDescription.displayName = DialogPrimitive.Description.displayName; - -export type { InnerDialogContentProps }; -export { - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, - DialogClose, - InnerDialog, - InnerDialogTrigger, - InnerDialogContent, - InnerDialogHeader, - InnerDialogFooter, - InnerDialogTitle, - InnerDialogDescription, - InnerDialogClose, - DialogPortal, - DialogOverlay, -}; diff --git a/apps/infoalloggi/src/components/servizio/annuncio_card.tsx b/apps/infoalloggi/src/components/servizio/annuncio_card.tsx index 97243ee..2474387 100644 --- a/apps/infoalloggi/src/components/servizio/annuncio_card.tsx +++ b/apps/infoalloggi/src/components/servizio/annuncio_card.tsx @@ -29,6 +29,7 @@ import { cn, formatCurrency } from "~/lib/utils"; import { useTranslation } from "~/providers/I18nProvider"; import { useServizioAnnuncio } from "~/providers/ServizioProvider"; import type { Annunci } from "~/schemas/public/Annunci"; +import { Badge } from "../ui/badge"; type AnnuncioData = Pick< Annunci, @@ -138,17 +139,17 @@ export const BasicAnnuncioCard = ({ const TipoBadge = ({ tipo }: { tipo: Annunci["tipo"] }) => { return ( -
-
{tipo}
-
+ Affitto {tipo} + ); }; diff --git a/apps/infoalloggi/src/components/servizio/compatibili_dialog.tsx b/apps/infoalloggi/src/components/servizio/compatibili_dialog.tsx new file mode 100644 index 0000000..6caa703 --- /dev/null +++ b/apps/infoalloggi/src/components/servizio/compatibili_dialog.tsx @@ -0,0 +1,195 @@ +import { ExternalLink, UnfoldVertical } from "lucide-react"; +import Link from "next/link"; +import { replaceWithBr } from "~/lib/newlineToBr"; +import { cn, formatCurrency } from "~/lib/utils"; +import { useTranslation } from "~/providers/I18nProvider"; +import { useServizio } from "~/providers/ServizioProvider"; +import type { Annunci } from "~/schemas/public/Annunci"; +import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller"; +import { api } from "~/utils/api"; +import { CardAnnuncio, CarouselAnnuncio } from "../annuncio_card"; +import { InteressatoButtonServizio } from "../annuncio-interactions/annuncio_interactions"; +import { LoadingPage } from "../loading"; +import { Badge } from "../ui/badge"; +import { Button } from "../ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "../ui/dialog"; + +export const AnnunciCompatibili = () => { + const { servizioId } = useServizio(); + const { data, isLoading } = api.servizio.getCompatibileAnnunci.useQuery({ + servizioId, + }); + return ( + + + + + + + + Annunci compatibili con le preferenze del servizio + + + +
+ {isLoading ? ( + + ) : ( + <> + {!data || data.length === 0 ? ( +
+ + Nessun annuncio compatibile trovato. Amplia la ricerca per + trovare annunci compatibili. + +
+ ) : ( +
+ {data.map((annuncio) => ( +
+ +
+
+ +
+
+ +
+
+
+ ))} +
+ )} + + )} +
+ + + + +
+
+ ); +}; + +const TipoBadge = ({ tipo }: { tipo: Annunci["tipo"] }) => { + return ( + + Affitto {tipo} + + ); +}; + +const AnnuncioDettaglio = ({ data }: { data: AnnuncioRicerca }) => { + const { locale, t } = useTranslation(); + + return ( + + + + + + + Annuncio {data.codice} + + + + + Annuncio {data.codice} + + +
+
+ img.img)} + updated_at={data.media_updated_at} + videos={data.url_video} + /> +
+ +
+
+ + {data.stato === "Trattativa" && ( + + Annuncio in Trattativa + + )} +
+ +
+
+
+
{t.card.codice}
+ +
Cod: {data.codice}
+
+
+
{t.card.prezzo}
+ +
+ {formatCurrency(data.prezzo / 1e2)} +
+
+
+ +
+
{t.card.titolo}
+ +
+ {locale === "it" ? data.titolo_it : data.titolo_en} +
+
+ +
+
+
+
+
+
+ + + +
+
+ ); +}; diff --git a/apps/infoalloggi/src/components/servizio/main.tsx b/apps/infoalloggi/src/components/servizio/main.tsx index 950c44f..82c6f6c 100644 --- a/apps/infoalloggi/src/components/servizio/main.tsx +++ b/apps/infoalloggi/src/components/servizio/main.tsx @@ -22,8 +22,8 @@ import { useServizio, } from "~/providers/ServizioProvider"; import { api } from "~/utils/api"; +import { AnnunciCompatibili } from "./compatibili_dialog"; import { - AnnunciCompatibili, AnnunciSelezionatiAccordion, AnnunciSelezionatiDialog, } from "./servizio_annunci_accordions"; @@ -210,9 +210,6 @@ export const ServizioContent = () => { const annunciInConferma = servizio.annunci.filter( (a) => a.user_confirmed_at !== null, ); - const annunciNotInConferma = servizio.annunci.filter( - (a) => a.user_confirmed_at === null, - ); return ( { return (
{(isAdmin || canUserEdit) && ( -
+
{/* Azioni: */} @@ -230,7 +230,7 @@ const InterruzioneServizio = () => { servizio.annunci.some((a) => a.user_confirmed_at !== null)) } > - Interruzione Servizio + Richiedi Interruzione @@ -302,7 +302,7 @@ const EditPreferenze = () => { } > - Modifica Preferenze + Preferenze diff --git a/apps/infoalloggi/src/components/servizio/servizio_annunci_accordions.tsx b/apps/infoalloggi/src/components/servizio/servizio_annunci_accordions.tsx index fad45da..c1835ac 100644 --- a/apps/infoalloggi/src/components/servizio/servizio_annunci_accordions.tsx +++ b/apps/infoalloggi/src/components/servizio/servizio_annunci_accordions.tsx @@ -2,7 +2,6 @@ import { ExternalLink, Trash2 } from "lucide-react"; import Link from "next/link"; import type { ReactNode } from "react"; import toast from "react-hot-toast"; -import { InteressatoButtonServizio } from "~/components/annuncio-interactions/annuncio_interactions"; import { AnnuncioCard, BasicAnnuncioCard, @@ -21,8 +20,6 @@ import { import type { UsersId } from "~/schemas/public/Users"; import type { ServizioData } from "~/server/controllers/servizio.controller"; import { api } from "~/utils/api"; -import { CardAnnuncio } from "../annuncio_card"; -import { LoadingPage } from "../loading"; import { Dialog, DialogContent, @@ -44,7 +41,7 @@ const DialogServizio = ({ return ( {trigger} - + {title} @@ -245,58 +242,6 @@ export const AnnunciSelezionatiAccordion = ({ ); }; -export const AnnunciCompatibili = () => { - const { servizioId } = useServizio(); - const { data, isLoading } = api.servizio.getCompatibileAnnunci.useQuery({ - servizioId, - }); - - return ( - - Esplora Annunci Compatibili - - } - > -
- {isLoading ? ( - - ) : ( - <> - {!data || data.length === 0 ? ( -
- - Nessun annuncio compatibile trovato. Amplia la ricerca per - trovare annunci compatibili. - -
- ) : ( -
- {data.map((annuncio) => ( -
- - - -
- ))} -
- )} - - )} -
-
- ); -}; export const AnnunciRichiesti = ({ userId }: { userId: UsersId }) => { const { data, isLoading } = api.intrests.getUserInterestsAnnunci.useQuery({ diff --git a/apps/infoalloggi/src/pages/test.tsx b/apps/infoalloggi/src/pages/test.tsx index 8b446e0..386245e 100644 --- a/apps/infoalloggi/src/pages/test.tsx +++ b/apps/infoalloggi/src/pages/test.tsx @@ -2,7 +2,21 @@ import type { NextPage } from "next"; import Example from "~/components/KabanExample"; import { NoSSR } from "~/lib/nossr"; +const Test: NextPage = () => { + return ( +
+ + + +
+ ); +}; +export default Test; + /* + +ESEMPIO DI RICERCA IN NOMINATIM CON DEBOUNCE + import { useState } from "react"; import { useDebounce } from "react-use"; import z from "zod"; @@ -147,14 +161,3 @@ const Test: NextPage = () => { }; export default Test; */ - -const Test: NextPage = () => { - return ( -
- - - -
- ); -}; -export default Test; diff --git a/apps/infoalloggi/src/server/controllers/annunci.controller.ts b/apps/infoalloggi/src/server/controllers/annunci.controller.ts index 5c9b0c4..80a7a46 100644 --- a/apps/infoalloggi/src/server/controllers/annunci.controller.ts +++ b/apps/infoalloggi/src/server/controllers/annunci.controller.ts @@ -208,6 +208,9 @@ export const get_AnnunciPositionsHandler = async ({ "tipo", "titolo_it", "titolo_en", + "desc_en", + "desc_it", + "web", "modificato_il", "stato", "url_video", @@ -265,11 +268,14 @@ export type AnnuncioRicerca = Pick< | "tipo" | "titolo_it" | "titolo_en" + | "desc_en" + | "desc_it" | "modificato_il" | "stato" | "url_video" | "media_updated_at" | "homepage" + | "web" > & { images: Pick[]; }; @@ -307,6 +313,9 @@ export const getCursor_AnnunciHandler = async ({ "tipo", "titolo_it", "titolo_en", + "desc_en", + "desc_it", + "web", "modificato_il", "stato", "url_video",