refactor: enhance styling and layout of AnnuncioCard and AnnuncioDettaglio components

This commit is contained in:
Marco Pedone 2025-12-15 18:58:13 +01:00
parent 831ae0edef
commit 496a947f97
6 changed files with 116 additions and 139 deletions

View file

@ -1,9 +1,6 @@
import { ExternalLink } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { AnnuncioActions } from "~/components/servizio/annuncio_actions";
import { Interactions } from "~/components/servizio/interactions";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { handleConsegna } from "~/lib/annuncio_details";
import { getStorageUrl } from "~/lib/storage_utils";
@ -45,35 +42,40 @@ export const BasicAnnuncioCard = ({
const { t } = useTranslation();
return (
<Card className={cn("w-full border-white py-5", className)}>
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 pb-0">
<CardTitle className="text-2xl">Codice: {data.codice}</CardTitle>
<Card className={cn("w-full border-white py-3", className)}>
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-0">
<CardTitle className="text-2xl">Annuncio {data.codice}</CardTitle>
{actions}
</CardHeader>
<CardContent className="px-4">
<div className="flex w-full flex-col gap-4 md:flex-row md:gap-10">
<Image
alt={data.codice}
className="h-auto max-h-72 w-full rounded-md bg-[#e6e9ec] object-contain sm:max-w-xs"
height={1080}
priority
src={
(data?.images[0] &&
getStorageUrl({
storageId: data.images[0].img,
params: {
cacheKey: data.media_updated_at?.toISOString(),
media: "image",
},
})) ||
"/fallback-image.png"
}
width={1920}
/>
<div className="flex w-full flex-col gap-5">
{interactions}
<CardContent className="px-3">
<div className="flex w-full flex-col gap-4 md:gap-10">
<div className="grid grid-cols-[6rem_auto] gap-2 gap-x-4 md:grid-cols-[12rem_auto] md:gap-x-10">
<div className="row-span-3">
<Image
alt={data.codice}
className="size-24 rounded-md bg-[#e6e9ec] object-cover md:size-48"
height={1080}
priority
src={
(data?.images[0] &&
getStorageUrl({
storageId: data.images[0].img,
params: {
cacheKey: data.media_updated_at?.toISOString(),
media: "image",
},
})) ||
"/fallback-image.png"
}
width={1920}
/>
</div>
<div className="flex flex-wrap items-center gap-3">
<div className="flex w-full max-w-xl flex-wrap gap-5 md:col-span-1">
<span className="text-wrap">{data.titolo_it}</span>
</div>
<div className="col-span-2 flex w-full max-w-xl flex-wrap items-center justify-between gap-3 md:col-span-1">
<TipoBadge tipo={data.tipo} />
<span>
{handleConsegna({
@ -84,25 +86,15 @@ export const BasicAnnuncioCard = ({
subito: t.card.consegna_subito,
})}
</span>
<span>{formatCurrency(data.prezzo / 1e2)}</span>
<span className="font-semibold">
{formatCurrency(data.prezzo / 1e2)}
</span>
</div>
<div className="flex w-full flex-wrap items-center gap-5">
<span className="text-wrap">{data.titolo_it || data.codice}</span>
<div className="flex flex-wrap items-center gap-2">
<AnnuncioDettaglio data={data} />
{data.stato !== "Sospeso" && data.web && (
<Link
aria-label="Pagina Annuncio"
href={`/annuncio/${data.codice}`}
>
<Button variant="outline">
<ExternalLink className="size-6" />
</Button>
</Link>
)}
</div>
<div className="col-span-2 w-full max-w-xl md:col-span-1">
<AnnuncioDettaglio data={data} />
</div>
</div>
<div className="flex w-full flex-col gap-5">{interactions}</div>
</div>
</CardContent>
</Card>

View file

@ -1,4 +1,4 @@
import { ExternalLink, UnfoldVertical } from "lucide-react";
import { Expand, ExternalLink } from "lucide-react";
import Link from "next/link";
import { replaceWithBr } from "~/lib/newlineToBr";
import { cn, formatCurrency } from "~/lib/utils";
@ -6,16 +6,17 @@ import { useTranslation } from "~/providers/I18nProvider";
import type { Annunci } from "~/schemas/public/Annunci";
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
import { CarouselAnnuncio } from "../annuncio_card";
import {
Credenza,
CredenzaBody,
CredenzaContent,
CredenzaDescription,
CredenzaHeader,
CredenzaTitle,
CredenzaTrigger,
} from "../custom_ui/credenza";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
export const TipoBadge = ({ tipo }: { tipo: Annunci["tipo"] }) => {
return (
@ -37,70 +38,78 @@ export const AnnuncioDettaglio = ({ data }: { data: AnnuncioRicerca }) => {
const { locale, t } = useTranslation();
return (
<Dialog>
<DialogTrigger asChild>
<Button aria-label="Dettagli Annuncio" variant="outline">
<UnfoldVertical className="size-6" /> Dettagli annuncio
<Credenza>
<CredenzaTrigger asChild>
<Button
aria-label="Dettagli Annuncio"
className="w-full"
size="sm"
variant="outline"
>
<Expand /> Dettaglio
</Button>
</DialogTrigger>
<DialogContent className="max-h-[70vh] max-w-xl overflow-y-auto sm:max-w-6xl">
<DialogHeader className="flex-row items-center gap-4">
<DialogTitle className="text-xl">Annuncio {data.codice}</DialogTitle>
</CredenzaTrigger>
<CredenzaContent className="max-w-xl sm:max-w-6xl md:max-h-[90vh] md:overflow-y-auto">
<CredenzaHeader className="flex-row items-center justify-between gap-4 md:justify-start">
<CredenzaTitle className="text-xl">
Annuncio {data.codice}
</CredenzaTitle>
<Link href={`/annuncio/${data.codice}`} target="_blank">
<Button size="sm" variant="info">
<span>Vai alla pagina completa</span>{" "}
<ExternalLink className="size-4" />
<span>Pagina completa</span> <ExternalLink />
</Button>
</Link>
<DialogDescription className="sr-only">
Annuncio {data.codice}
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2 px-2">
<div className="w-full contain-inline-size">
<CarouselAnnuncio
immagini={data.images}
updated_at={data.media_updated_at}
videos={data.videos}
/>
</div>
<div>
<div className="flex items-center gap-4">
<TipoBadge tipo={data.tipo} />
{data.stato === "Trattativa" && (
<Badge className="bg-violet-500 font-semibold text-md">
Annuncio in Trattativa
</Badge>
)}
<CredenzaDescription className="sr-only">
Dettaglio
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody className="h-full overflow-auto pb-5">
<div className="flex flex-col gap-2 px-2">
<div className="w-full contain-inline-size">
<CarouselAnnuncio
immagini={data.images}
updated_at={data.media_updated_at}
videos={data.videos}
/>
</div>
<div className="mt-1 space-y-2">
<div className="flex flex-row justify-around">
<div className="text-center">
<dt className="sr-only">{t.card.codice}</dt>
<div className="space-y-3">
<div className="flex items-center gap-4">
<TipoBadge tipo={data.tipo} />
{data.stato === "Trattativa" && (
<Badge className="bg-violet-500 font-semibold text-md">
Annuncio in Trattativa
</Badge>
)}
</div>
<dd className="font-semibold text-lg">Cod: {data.codice}</dd>
<div className="space-y-2">
<div className="flex flex-row justify-around">
<div className="text-center">
<dt className="sr-only">{t.card.codice}</dt>
<dd className="font-semibold text-lg">
Cod: {data.codice}
</dd>
</div>
<div className="text-center">
<dt className="sr-only">{t.card.prezzo}</dt>
<dd className="font-semibold text-lg">
{formatCurrency(data.prezzo / 1e2)}
</dd>
</div>
</div>
<div className="text-center">
<dt className="sr-only">{t.card.prezzo}</dt>
<dd className="font-semibold text-lg">
{formatCurrency(data.prezzo / 1e2)}
<div className="">
<dt className="sr-only">{t.card.titolo}</dt>
<dd className="font-semibold text-base md:text-lg">
{locale === "it" ? data.titolo_it : data.titolo_en}
</dd>
</div>
</div>
<div className="mb-3">
<dt className="sr-only">{t.card.titolo}</dt>
<dd className="font-semibold text-base md:text-lg">
{locale === "it" ? data.titolo_it : data.titolo_en}
</dd>
</div>
<div className="mb-3">
<dd
<div
className="text-base"
dangerouslySetInnerHTML={{
__html: replaceWithBr(
@ -111,8 +120,8 @@ export const AnnuncioDettaglio = ({ data }: { data: AnnuncioRicerca }) => {
</div>
</div>
</div>
</div>
</DialogContent>
</Dialog>
</CredenzaBody>
</CredenzaContent>
</Credenza>
);
};

View file

@ -190,7 +190,7 @@ export const Interactions = () => {
const { data } = useServizioAnnuncio();
return (
<div className="flex flex-col gap-3">
<div className="flex w-full flex-col gap-3">
<ContattiProprietarioModal />
<SezioneConferma />

View file

@ -43,7 +43,7 @@ const ServizioCard = ({
return (
<Card
className={cn(
"gap-2 rounded-none rounded-b-md border-primary border-t-0",
"gap-2 rounded-none rounded-b-md border-primary border-t-0 pb-0",
cardClassName,
)}
>
@ -218,32 +218,6 @@ export const ServizioContent = () => {
<ServizioCard
content={
<>
{/* <div className="flex w-full flex-col gap-8 sm:flex-row sm:items-start">
<div className="flex flex-2 flex-col gap-2 sm:max-w-1/2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<UserCircle className="size-6 text-primary" />
<span className="font-medium">Contatti sbloccati</span>
</div>
<span className="text-sm">
{openContattiAnnunci.length} / 10
</span>
</div>
<Progress
className="h-2"
value={openContattiAnnunci.length * 10}
/>
</div>
<div className="flex flex-col flex-wrap gap-2 sm:flex-row sm:items-center">
<div className="flex items-center gap-2">
<CalendarClock className="size-6 text-primary" />
<span className="font-medium">Durata servizio:</span>
</div>
<span>
{`${new Date(servizio.decorrenza).toLocaleDateString("it-IT")} - ${new Date(add(servizio.decorrenza, { days: 60 })).toLocaleDateString("it-IT")}`}
</span>
</div>
</div> */}
{annuncioConfermato.length > 0 && (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">

View file

@ -49,7 +49,7 @@ export const ContattiProprietarioModal = () => {
{isUnlocked ? (
<Button
aria-label="Vedi contatti proprietario"
className="w-fit"
className="w-full md:w-fit"
variant="success"
>
<CircleUser className="size-7" />
@ -58,7 +58,7 @@ export const ContattiProprietarioModal = () => {
) : (
<Button
aria-label="Contatta proprietario"
className="w-fit"
className="w-full md:w-fit"
variant="warning"
>
<CircleUser className="size-7" />

View file

@ -765,7 +765,9 @@ export const PasswordSVG = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
);
/*
from: https://www.svgrepo.com/svg/477007/alarm-clock
*/
export const AlarmClockSVG = (props: SVGProps<SVGSVGElement>) => (
<svg
className="icon"