feat: implement AnnuncioDisplay component and integrate it into ServizioContent for better display of selected announcements
This commit is contained in:
parent
6c4c57fd69
commit
76bcf640ca
4 changed files with 106 additions and 187 deletions
|
|
@ -35,38 +35,20 @@ export const AnnuncioCard = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const BasicAnnuncioCard = ({
|
|
||||||
actions,
|
export const AnnuncioDisplay = ({
|
||||||
interactions,
|
|
||||||
className,
|
|
||||||
data,
|
data,
|
||||||
confirming,
|
confirming,
|
||||||
}: {
|
}: {
|
||||||
actions?: React.ReactNode;
|
|
||||||
interactions?: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
data: AnnuncioRicerca;
|
data: AnnuncioRicerca;
|
||||||
confirming?: boolean;
|
confirming?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cn("w-full gap-2 border-white py-3", className)}>
|
|
||||||
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
|
|
||||||
<CardTitle className="text-2xl">Annuncio {data.codice}</CardTitle>
|
|
||||||
{actions}
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="px-3">
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"@container flex w-full flex-col gap-4",
|
"grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] gap-x-4 gap-y-2 rounded-md md:max-w-3xl md:border md:border-white md:bg-[#e6e9ec]/50 lg:gap-x-4 xl:gap-x-6 dark:md:border-primary dark:md:bg-card",
|
||||||
confirming && "xl:flex-row xl:gap-6",
|
|
||||||
)}
|
|
||||||
id="container"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] gap-x-4 gap-y-2 rounded-md md:max-w-3xl md:border md:border-white md:bg-[#e6e9ec]/50 lg:gap-x-6 xl:gap-x-8 dark:md:border-primary dark:md:bg-card",
|
|
||||||
confirming && "xl:w-1/2",
|
confirming && "xl:w-1/2",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -114,6 +96,37 @@ export const BasicAnnuncioCard = ({
|
||||||
<AnnuncioDettaglio data={data} />
|
<AnnuncioDettaglio data={data} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BasicAnnuncioCard = ({
|
||||||
|
actions,
|
||||||
|
interactions,
|
||||||
|
className,
|
||||||
|
data,
|
||||||
|
confirming,
|
||||||
|
}: {
|
||||||
|
actions?: React.ReactNode;
|
||||||
|
interactions?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
data: AnnuncioRicerca;
|
||||||
|
confirming?: boolean;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Card className={cn("w-full gap-2 border-white py-3", className)}>
|
||||||
|
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
|
||||||
|
<CardTitle className="text-2xl">Annuncio {data.codice}</CardTitle>
|
||||||
|
{actions}
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="px-3">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"@container flex w-full flex-col gap-4",
|
||||||
|
confirming && "xl:flex-row xl:gap-6",
|
||||||
|
)}
|
||||||
|
id="container"
|
||||||
|
>
|
||||||
|
<AnnuncioDisplay confirming={confirming} data={data} />
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full flex-col gap-4",
|
"flex w-full flex-col gap-4",
|
||||||
|
|
@ -127,19 +140,3 @@ export const BasicAnnuncioCard = ({
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AnnuncioCardSkeleton = () => {
|
|
||||||
return (
|
|
||||||
<Card className="hidden w-full gap-2 rounded-md border-border py-3 opacity-30 lg:flex">
|
|
||||||
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
|
|
||||||
<CardTitle className="h-8 w-64 rounded-md bg-muted" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="h-max px-3">
|
|
||||||
<div className="flex w-full flex-col gap-4">
|
|
||||||
<div className="h-[194px] w-full rounded-md bg-muted" />
|
|
||||||
<div className="h-8 w-48 rounded-md bg-muted" />
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { useEffect } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import {
|
import {
|
||||||
AnnuncioCard,
|
AnnuncioCard,
|
||||||
AnnuncioCardSkeleton,
|
AnnuncioDisplay,
|
||||||
} from "~/components/servizio/annuncio_card";
|
} from "~/components/servizio/annuncio_card";
|
||||||
import { SaldoButton } from "~/components/servizio/interactions";
|
import { SaldoButton } from "~/components/servizio/interactions";
|
||||||
import { ServizioActions } from "~/components/servizio/servizio_actions";
|
import { ServizioActions } from "~/components/servizio/servizio_actions";
|
||||||
|
|
@ -26,7 +26,6 @@ import {
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { AlarmClockSVG } from "../svgs";
|
import { AlarmClockSVG } from "../svgs";
|
||||||
import { AnnunciCompatibili } from "./compatibili_dialog";
|
import { AnnunciCompatibili } from "./compatibili_dialog";
|
||||||
import { AnnunciSelezionatiDialog } from "./servizio_annunci_accordions";
|
|
||||||
|
|
||||||
const ServizioCard = ({
|
const ServizioCard = ({
|
||||||
cardClassName,
|
cardClassName,
|
||||||
|
|
@ -163,7 +162,34 @@ export const ServizioContent = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isAdmin && <AnnunciSelezionatiDialog annunci={servizio.annunci} />}
|
{servizio.annunci.length > 0 && (
|
||||||
|
<div className="w-full space-y-2">
|
||||||
|
<p>
|
||||||
|
Sono già stati selezionati {servizio.annunci.length} annunci
|
||||||
|
che potrebbero interessarti.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{servizio.annunci.map((data) => (
|
||||||
|
<div className="" key={data.id}>
|
||||||
|
<AnnuncioDisplay
|
||||||
|
data={{
|
||||||
|
...data,
|
||||||
|
modificato_il: data.modificato_il
|
||||||
|
? new Date(data.modificato_il)
|
||||||
|
: null,
|
||||||
|
media_updated_at: data.media_updated_at
|
||||||
|
? new Date(data.media_updated_at)
|
||||||
|
: null,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{servizio.annunci.length % 2 === 1 && (
|
||||||
|
<div className="size-full rounded-md border border-[var(--pattern-fg)] bg-[repeating-linear-gradient(45deg,_var(--pattern-fg)_0,_var(--pattern-fg)_1px,_transparent_0,_transparent_50%)] bg-[size:10px_10px] bg-fixed opacity-10 [--pattern-fg:#696969] dark:[--pattern-fg:#e1e1e1]" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
contentClassName={cn(!isAdmin && "gap-0")}
|
contentClassName={cn(!isAdmin && "gap-0")}
|
||||||
|
|
@ -297,7 +323,9 @@ export const ServizioContent = () => {
|
||||||
</ServizioAnnuncioProvider>
|
</ServizioAnnuncioProvider>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{servizio.annunci.length % 2 === 1 && <AnnuncioCardSkeleton />}
|
{servizio.annunci.length % 2 === 1 && (
|
||||||
|
<div className="size-full rounded-md border border-[var(--pattern-fg)] bg-[repeating-linear-gradient(45deg,_var(--pattern-fg)_0,_var(--pattern-fg)_1px,_transparent_0,_transparent_50%)] bg-[size:10px_10px] bg-fixed opacity-10 [--pattern-fg:#696969] dark:[--pattern-fg:#e1e1e1]" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import { ExternalLink, Trash2 } from "lucide-react";
|
import { Trash2 } from "lucide-react";
|
||||||
import Link from "next/link";
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import {
|
import { BasicAnnuncioCard } from "~/components/servizio/annuncio_card";
|
||||||
AnnuncioCard,
|
|
||||||
BasicAnnuncioCard,
|
|
||||||
} from "~/components/servizio/annuncio_card";
|
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
|
|
@ -13,105 +8,8 @@ import {
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "~/components/ui/accordion";
|
} from "~/components/ui/accordion";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
|
||||||
ServizioAnnuncioProvider,
|
|
||||||
useServizio,
|
|
||||||
} from "~/providers/ServizioProvider";
|
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
import type { ServizioData } from "~/server/controllers/servizio.controller";
|
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "../ui/dialog";
|
|
||||||
|
|
||||||
const DialogServizio = ({
|
|
||||||
trigger,
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
trigger: ReactNode;
|
|
||||||
title: string;
|
|
||||||
children: ReactNode;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-xl p-2 sm:max-w-5xl sm:p-6 md:max-w-7xl">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{title}</DialogTitle>
|
|
||||||
<DialogDescription className="sr-only">
|
|
||||||
Servizio Dialog
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
{children}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AnnunciSelezionatiDialog = ({
|
|
||||||
annunci,
|
|
||||||
}: {
|
|
||||||
annunci: ServizioData["annunci"];
|
|
||||||
}) => {
|
|
||||||
const { servizioId, userId } = useServizio();
|
|
||||||
return (
|
|
||||||
<DialogServizio
|
|
||||||
title="Annunci selezionati"
|
|
||||||
trigger={
|
|
||||||
<div className="relative flex w-full sm:w-fit">
|
|
||||||
<Button
|
|
||||||
className="w-full text-lg sm:w-fit"
|
|
||||||
size="xl"
|
|
||||||
variant="orange"
|
|
||||||
>
|
|
||||||
Annunci selezionati
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<span className="-translate-y-1/2 absolute top-0 right-0 flex min-w-6 origin-center translate-x-1/2 items-center justify-center rounded-full bg-destructive px-1 text-white">
|
|
||||||
{annunci.length || 0}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="flex max-h-[80vh] flex-col gap-2 overflow-y-auto">
|
|
||||||
{annunci.length > 0 ? (
|
|
||||||
annunci.map((data) => {
|
|
||||||
return (
|
|
||||||
<ServizioAnnuncioProvider
|
|
||||||
data={data}
|
|
||||||
key={data.id}
|
|
||||||
servizioId={servizioId}
|
|
||||||
userId={userId}
|
|
||||||
>
|
|
||||||
<AnnuncioCard className="border-2 border-sky-500" />
|
|
||||||
</ServizioAnnuncioProvider>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center justify-center gap-1 rounded-md bg-white p-4 sm:flex">
|
|
||||||
<span>
|
|
||||||
Nessun annuncio selezionato, fai una ricerca nella pagina{" "}
|
|
||||||
<Link
|
|
||||||
aria-label="Annunci Compatibili"
|
|
||||||
className="inline-flex items-center gap-1 text-blue-500 underline"
|
|
||||||
href="/annunci"
|
|
||||||
>
|
|
||||||
Annunci
|
|
||||||
<ExternalLink className="size-4" />
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</DialogServizio>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AnnunciRichiesti = ({ userId }: { userId: UsersId }) => {
|
export const AnnunciRichiesti = ({ userId }: { userId: UsersId }) => {
|
||||||
const { data, isLoading } = api.intrests.getUserInterestsAnnunci.useQuery({
|
const { data, isLoading } = api.intrests.getUserInterestsAnnunci.useQuery({
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,13 @@
|
||||||
|
|
||||||
@custom-variant search-cancel (&::-webkit-search-cancel-button);
|
@custom-variant search-cancel (&::-webkit-search-cancel-button);
|
||||||
|
|
||||||
@utility stripes-* {
|
/*
|
||||||
background-image: linear-gradient(45deg,
|
STRIPED BACKGROUND EXAMPLE:
|
||||||
--value([color]) 12.5%,
|
<div className="border border-[var(--pattern-fg)]
|
||||||
transparent 12.5%,
|
bg-[repeating-linear-gradient(45deg,_var(--pattern-fg)_0,_var(--pattern-fg)_1px,_transparent_0,_transparent_50%)]
|
||||||
transparent 50%,
|
bg-[size:10px_10px] bg-fixed
|
||||||
--value([color]) 50%,
|
[--pattern-fg:#696969] dark:[--pattern-fg:#e1e1e1]" />
|
||||||
--value([color]) 62.5%,
|
*/
|
||||||
transparent 62.5%,
|
|
||||||
transparent 100%);
|
|
||||||
background-size: 10.66px 10.66px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--font-sans: var(--font-inter);
|
--font-sans: var(--font-inter);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue