refactor: Remove AnnunciGrid component and update Annunci page to use CardAnnuncio directly
This commit is contained in:
parent
608e6a8e10
commit
7b0469467f
12 changed files with 290 additions and 263 deletions
|
|
@ -1,14 +0,0 @@
|
||||||
import { CardAnnuncio } from "~/components/annuncio_card";
|
|
||||||
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
|
|
||||||
|
|
||||||
//TODO usare anche in accordion e dialog pag ricerca servizio
|
|
||||||
|
|
||||||
export const AnnunciGrid = ({ pagedata }: { pagedata: AnnuncioRicerca[] }) => {
|
|
||||||
return (
|
|
||||||
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3">
|
|
||||||
{pagedata.map((annuncio) => (
|
|
||||||
<CardAnnuncio key={annuncio.codice} {...annuncio} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -7,6 +7,7 @@ import LoadingButton from "~/components/custom_ui/loading-button";
|
||||||
import { Button, buttonVariants } from "~/components/ui/button";
|
import { Button, buttonVariants } from "~/components/ui/button";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
import { useAnnuncio } from "~/providers/AnnuncioProvider";
|
import { useAnnuncio } from "~/providers/AnnuncioProvider";
|
||||||
|
import { useServizio } from "~/providers/ServizioProvider";
|
||||||
import type { SessionContextType } from "~/providers/SessionProvider";
|
import type { SessionContextType } from "~/providers/SessionProvider";
|
||||||
import type { AnnunciId } from "~/schemas/public/Annunci";
|
import type { AnnunciId } from "~/schemas/public/Annunci";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
|
|
@ -108,10 +109,14 @@ const ServizioInteraction = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <InteressatoButton annuncioId={annuncioId} />;
|
return <InteressatoButtonUserOnly annuncioId={annuncioId} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InteressatoButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
|
const InteressatoButtonUserOnly = ({
|
||||||
|
annuncioId,
|
||||||
|
}: {
|
||||||
|
annuncioId: AnnunciId;
|
||||||
|
}) => {
|
||||||
const { data: hasInterest, isLoading } =
|
const { data: hasInterest, isLoading } =
|
||||||
api.intrests.hasUserInterest.useQuery({
|
api.intrests.hasUserInterest.useQuery({
|
||||||
annuncioId,
|
annuncioId,
|
||||||
|
|
@ -161,36 +166,57 @@ const InteressatoButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InteressatoButtonBatchV = ({
|
export const InteressatoButtonServizio = ({
|
||||||
annuncioId,
|
annuncioId,
|
||||||
hasInterest,
|
|
||||||
}: {
|
}: {
|
||||||
annuncioId: AnnunciId;
|
annuncioId: AnnunciId;
|
||||||
hasInterest: boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const { isAdmin, servizioId, userId } = useServizio();
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
const { mutate: add } = api.intrests.addIntrest.useMutation({
|
|
||||||
|
const { mutate: adminAdd, isPending: isAdminPending } =
|
||||||
|
api.servizio.addServizioAnnunci.useMutation({
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Errore durante l'aggiunta dell'annuncio");
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
|
||||||
|
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
|
||||||
|
servizioId,
|
||||||
|
});
|
||||||
|
await utils.servizio.AnnuncioServizioTipologiaMatch.invalidate({
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
await utils.servizio.getCompatibileAnnunci.invalidate({
|
||||||
|
servizioId,
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.success("Annuncio aggiunto alla ricerca");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: add, isPending } = api.intrests.addIntrest.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await utils.intrests.getUserInterests.invalidate();
|
await utils.intrests.getUserInterests.invalidate();
|
||||||
toast.success("Richiesta di interesse inviata con successo!");
|
toast.success("Richiesta di interesse inviata con successo!");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasInterest) {
|
|
||||||
return (
|
return (
|
||||||
<span className="flex w-full items-center justify-center gap-2 rounded-md bg-green-500 p-2 font-semibold text-sm text-white">
|
<LoadingButton
|
||||||
<BadgePlus className="inline size-5" />
|
|
||||||
Hai già mandato una richiesta
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
aria-label="Sono interessato"
|
aria-label="Sono interessato"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
//size="xl"
|
loading={isPending || isAdminPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (isAdmin && servizioId) {
|
||||||
|
adminAdd({
|
||||||
|
servizioId,
|
||||||
|
annunci: [annuncioId],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
add({
|
add({
|
||||||
annuncioId,
|
annuncioId,
|
||||||
});
|
});
|
||||||
|
|
@ -198,7 +224,7 @@ export const InteressatoButtonBatchV = ({
|
||||||
variant="info"
|
variant="info"
|
||||||
>
|
>
|
||||||
<BadgePlus className="size-6" />
|
<BadgePlus className="size-6" />
|
||||||
Sono interessato
|
{isAdmin ? "Aggiungi annuncio" : "Sono interessato"}
|
||||||
</Button>
|
</LoadingButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ import { VideoPlayer } from "./videoPlayer";
|
||||||
|
|
||||||
type CardAnnuncioProps = AnnuncioRicerca & {
|
type CardAnnuncioProps = AnnuncioRicerca & {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
noLink?: boolean;
|
||||||
|
onlyFirstImage?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CardAnnuncio = ({
|
export const CardAnnuncio = ({
|
||||||
|
|
@ -55,23 +57,36 @@ export const CardAnnuncio = ({
|
||||||
updated_at,
|
updated_at,
|
||||||
images,
|
images,
|
||||||
homepage,
|
homepage,
|
||||||
|
noLink = false,
|
||||||
|
onlyFirstImage = false,
|
||||||
}: CardAnnuncioProps) => {
|
}: CardAnnuncioProps) => {
|
||||||
const { t, locale } = useTranslation();
|
const { t, locale } = useTranslation();
|
||||||
const currentMonth = new Date().getMonth() + 1;
|
const currentMonth = new Date().getMonth() + 1;
|
||||||
const isAvailableNow = consegna === 0 || consegna === currentMonth;
|
const isAvailableNow = consegna === 0 || consegna === currentMonth;
|
||||||
|
|
||||||
|
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
if (noLink) {
|
||||||
|
return <div className="flex h-full flex-col gap-2 p-2">{children}</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className="flex h-full flex-col gap-2 p-2"
|
||||||
|
href={`/annuncio/${codice}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-[31.1rem] rounded-md bg-white shadow-neutral-100 shadow-sm hover:shadow-md hover:outline-neutral-600",
|
"relative h-[31.1rem] rounded-md bg-white shadow-neutral-100 shadow-sm outline outline-neutral-300 hover:shadow-md hover:outline-neutral-600",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
id={`card-annuncio-${id}`}
|
id={`card-annuncio-${id}`}
|
||||||
>
|
>
|
||||||
<Link
|
<Wrapper>
|
||||||
className="flex h-full flex-col gap-2 p-2"
|
|
||||||
href={`/annuncio/${codice}`} //duration-700 ease-in-out animate-in fade-in
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={cn("group relative overflow-clip text-clip rounded-md")}
|
className={cn("group relative overflow-clip text-clip rounded-md")}
|
||||||
>
|
>
|
||||||
|
|
@ -120,7 +135,7 @@ export const CardAnnuncio = ({
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!onlyFirstImage ? (
|
||||||
<Carousel opts={{ loop: true }}>
|
<Carousel opts={{ loop: true }}>
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
{images?.map((img, idx) => (
|
{images?.map((img, idx) => (
|
||||||
|
|
@ -151,7 +166,9 @@ export const CardAnnuncio = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
cacheKey={updated_at?.toString() || new Date().toString()}
|
cacheKey={
|
||||||
|
updated_at?.toString() || new Date().toString()
|
||||||
|
}
|
||||||
className="h-56"
|
className="h-56"
|
||||||
coverImage={
|
coverImage={
|
||||||
images?.[0]
|
images?.[0]
|
||||||
|
|
@ -168,7 +185,10 @@ export const CardAnnuncio = ({
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="block opacity-0 transition-all duration-200 group-hover:opacity-60 disabled:group-hover:opacity-0"
|
className="block opacity-0 transition-all duration-200 group-hover:opacity-60 disabled:group-hover:opacity-0"
|
||||||
onClick={(e) => e.preventDefault()}
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
onKeyDown={(e) => e.stopPropagation()}
|
onKeyDown={(e) => e.stopPropagation()}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|
@ -176,13 +196,38 @@ export const CardAnnuncio = ({
|
||||||
<CarouselPrevious
|
<CarouselPrevious
|
||||||
className="left-2 cursor-pointer"
|
className="left-2 cursor-pointer"
|
||||||
disabled={!images || images.length === 1}
|
disabled={!images || images.length === 1}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Add this to the buttons too
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<CarouselNext
|
<CarouselNext
|
||||||
className="right-2 cursor-pointer"
|
className="right-2 cursor-pointer"
|
||||||
disabled={!images || images.length === 1}
|
disabled={!images || images.length === 1}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Add this to the buttons too
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Carousel>
|
</Carousel>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{images && images.length > 0 && images[0] ? (
|
||||||
|
<ImageFlbk
|
||||||
|
alt={t.card.alt_immagine}
|
||||||
|
blurDataURL={`/storage-api/get/${images[0].thumb}?image=true`}
|
||||||
|
className={"h-64 w-full object-cover"}
|
||||||
|
height={1080}
|
||||||
|
priority={true}
|
||||||
|
src={`/storage-api/get/${images[0].img}?image=true`}
|
||||||
|
width={1920}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-64 w-full items-center justify-center bg-neutral-200 text-neutral-500"></div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full grow flex-col gap-2">
|
<div className="flex h-full grow flex-col gap-2">
|
||||||
<div
|
<div
|
||||||
|
|
@ -270,7 +315,7 @@ export const CardAnnuncio = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Wrapper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,14 @@ const LoadingButton = ({
|
||||||
}: ButtonProps & { loading?: boolean }) => {
|
}: ButtonProps & { loading?: boolean }) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={cn(buttonVariants({ className, size, variant }))}
|
className={cn(
|
||||||
|
buttonVariants({ className, size, variant }),
|
||||||
|
loading && "text-transparent",
|
||||||
|
)}
|
||||||
|
disabled={loading || props.disabled}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{loading && (
|
{loading && <LoaderCircle className="absolute animate-spin text-muted" />}
|
||||||
<LoaderCircle
|
|
||||||
aria-hidden="true"
|
|
||||||
className="-ms-1 me-2 animate-spin"
|
|
||||||
size={16}
|
|
||||||
strokeWidth={2}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{props.children}
|
{props.children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,16 @@ import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
FolderKanban,
|
FolderKanban,
|
||||||
Pen,
|
Pen,
|
||||||
Plus,
|
|
||||||
ShieldUser,
|
ShieldUser,
|
||||||
Trash2,
|
Trash2,
|
||||||
Wrench,
|
Wrench,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRef, useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
import { ExtIcon } from "~/components/area-riservata/allegati";
|
import { ExtIcon } from "~/components/area-riservata/allegati";
|
||||||
import { Confirm } from "~/components/confirm";
|
import { Confirm } from "~/components/confirm";
|
||||||
import {
|
|
||||||
AnimatedButton,
|
|
||||||
type AnimatedButtonRef,
|
|
||||||
} from "~/components/custom_ui/animated-button";
|
|
||||||
import {
|
import {
|
||||||
Credenza,
|
Credenza,
|
||||||
CredenzaBody,
|
CredenzaBody,
|
||||||
|
|
@ -62,7 +57,6 @@ import { UploadModal } from "~/components/upload_modal";
|
||||||
import { cn, formatCurrency } from "~/lib/utils";
|
import { cn, formatCurrency } from "~/lib/utils";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
|
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
|
||||||
import type { AnnunciId } from "~/schemas/public/Annunci";
|
|
||||||
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
||||||
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
||||||
import type { FileMetadataWithAdmin } from "~/server/services/storage.service";
|
import type { FileMetadataWithAdmin } from "~/server/services/storage.service";
|
||||||
|
|
@ -117,7 +111,7 @@ export const AnnuncioActions = () => {
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
align="end"
|
align="end"
|
||||||
className="flex w-fit flex-col items-center gap-2"
|
className="flex w-fit flex-col items-center gap-4"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/area-riservata/admin/edit-annuncio/${annuncioId}`}
|
href={`/area-riservata/admin/edit-annuncio/${annuncioId}`}
|
||||||
|
|
@ -1213,51 +1207,3 @@ const SendConferma = ({
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AddButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
|
|
||||||
const { servizioId, userId } = useServizio();
|
|
||||||
const utils = api.useUtils();
|
|
||||||
const { mutate } = api.servizio.addServizioAnnunci.useMutation({
|
|
||||||
onError: (error) => {
|
|
||||||
console.error(error);
|
|
||||||
toast.error("Errore durante l'aggiunta dell'annuncio");
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
|
|
||||||
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
|
|
||||||
servizioId,
|
|
||||||
});
|
|
||||||
await utils.servizio.AnnuncioServizioTipologiaMatch.invalidate({
|
|
||||||
userId,
|
|
||||||
});
|
|
||||||
await utils.servizio.getCompatibileAnnunci.invalidate({
|
|
||||||
servizioId,
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.success("Annuncio aggiunto alla ricerca");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const buttonRef = useRef<AnimatedButtonRef>(null);
|
|
||||||
return (
|
|
||||||
<AnimatedButton
|
|
||||||
className="group flex w-full gap-2"
|
|
||||||
onClick={async () => {
|
|
||||||
buttonRef.current?.sparkle();
|
|
||||||
//wait for the animation to finish
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
mutate({ annunci: [annuncioId], servizioId });
|
|
||||||
}}
|
|
||||||
ref={buttonRef}
|
|
||||||
sparklesClassName="fill-yellow-500 stroke-transparent"
|
|
||||||
sparklesIcon="thumbs-up"
|
|
||||||
sparklesNumber={15}
|
|
||||||
sparklesRadius={60}
|
|
||||||
sparklesScale={[1, 1.5]}
|
|
||||||
variant="info"
|
|
||||||
>
|
|
||||||
<Plus className="size-5" />
|
|
||||||
<span>Aggiungi agli annunci da contattare</span>
|
|
||||||
</AnimatedButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { add } from "date-fns";
|
import { add } from "date-fns";
|
||||||
import { Package, PackageCheck, UserCircle } from "lucide-react";
|
import { CalendarClock, Package, PackageCheck, UserCircle } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
@ -209,7 +209,7 @@ export const ServizioContent = () => {
|
||||||
<ServizioCard
|
<ServizioCard
|
||||||
content={
|
content={
|
||||||
<>
|
<>
|
||||||
<div className="flex w-full flex-col gap-4 sm:flex-row sm:gap-8">
|
<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 flex-2 flex-col gap-2 sm:max-w-1/2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -225,10 +225,15 @@ export const ServizioContent = () => {
|
||||||
value={openContattiAnnunci.length * 10}
|
value={openContattiAnnunci.length * 10}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
<span>
|
||||||
{`Durata servizio: ${new Date(servizio.decorrenza).toLocaleDateString("it-IT")} - ${new Date(add(servizio.decorrenza, { days: 60 })).toLocaleDateString("it-IT")}`}
|
{`${new Date(servizio.decorrenza).toLocaleDateString("it-IT")} - ${new Date(add(servizio.decorrenza, { days: 60 })).toLocaleDateString("it-IT")}`}
|
||||||
</span>
|
</span>
|
||||||
{/* <ServizioDuration decorrenza={servizio.decorrenza} /> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{annuncioConfermato.length > 0 && (
|
{annuncioConfermato.length > 0 && (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
@ -245,8 +250,8 @@ export const ServizioContent = () => {
|
||||||
>
|
>
|
||||||
<h3>
|
<h3>
|
||||||
{a.codice} - confermato il:{" "}
|
{a.codice} - confermato il:{" "}
|
||||||
{/** biome-ignore lint/style/noNonNullAssertion: <already checked above> */}
|
{a.accettato_conferma_at &&
|
||||||
{new Date(a.accettato_conferma_at!).toLocaleDateString(
|
new Date(a.accettato_conferma_at).toLocaleDateString(
|
||||||
"it",
|
"it",
|
||||||
{
|
{
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ const EditPreferenze = () => {
|
||||||
Preferenze
|
Preferenze
|
||||||
</CredenzaDescription>
|
</CredenzaDescription>
|
||||||
</CredenzaHeader>
|
</CredenzaHeader>
|
||||||
<CredenzaBody className="max-h-[80vh] w-full max-w-3xl overflow-y-auto pb-5">
|
<CredenzaBody className="max-h-[80vh] w-full max-w-3xl overflow-y-auto pb-5 sm:max-w-5xl">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoadingPage />
|
<LoadingPage />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ import { ExternalLink, Trash2 } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { InteressatoButtonBatchV } from "~/components/annuncio-interactions/annuncio_interactions";
|
import { InteressatoButtonServizio } from "~/components/annuncio-interactions/annuncio_interactions";
|
||||||
import { AddButton } from "~/components/servizio/annuncio_actions";
|
|
||||||
import {
|
import {
|
||||||
AnnuncioCard,
|
AnnuncioCard,
|
||||||
BasicAnnuncioCard,
|
BasicAnnuncioCard,
|
||||||
|
|
@ -22,6 +21,7 @@ import {
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
import type { ServizioData } from "~/server/controllers/servizio.controller";
|
import type { ServizioData } from "~/server/controllers/servizio.controller";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
import { CardAnnuncio } from "../annuncio_card";
|
||||||
import { LoadingPage } from "../loading";
|
import { LoadingPage } from "../loading";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -44,7 +44,7 @@ const DialogServizio = ({
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
||||||
<DialogContent className="md:max-w-5xl">
|
<DialogContent className="p-2 sm:max-w-5xl sm:p-6 md:max-w-7xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{title}</DialogTitle>
|
<DialogTitle>{title}</DialogTitle>
|
||||||
<DialogDescription className="sr-only">
|
<DialogDescription className="sr-only">
|
||||||
|
|
@ -221,25 +221,21 @@ export const AnnunciSelezionatiAccordion = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const AnnunciCompatibili = () => {
|
export const AnnunciCompatibili = () => {
|
||||||
const { servizioId, userId, isAdmin } = useServizio();
|
const { servizioId } = useServizio();
|
||||||
const { data, isLoading } = api.servizio.getCompatibileAnnunci.useQuery({
|
const { data, isLoading } = api.servizio.getCompatibileAnnunci.useQuery({
|
||||||
servizioId,
|
servizioId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: userIntrests } = api.intrests.getUserInterests.useQuery({
|
|
||||||
userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogServizio
|
<DialogServizio
|
||||||
title="Annunci compatibili"
|
title="Annunci compatibili con le preferenze del servizio"
|
||||||
trigger={
|
trigger={
|
||||||
<Button className="w-full bg-purple-500 sm:w-fit">
|
<Button className="w-full bg-purple-500 sm:w-fit">
|
||||||
Esplora Annunci Compatibili
|
Esplora Annunci Compatibili
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex max-h-[80vh] flex-col gap-2 overflow-y-auto">
|
<div className="flex max-h-[80vh] flex-col gap-2 overflow-y-auto p-0.5 sm:p-2">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoadingPage />
|
<LoadingPage />
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -252,25 +248,23 @@ export const AnnunciCompatibili = () => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
data.map((a) => (
|
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 sm:gap-y-8 lg:grid-cols-3">
|
||||||
<BasicAnnuncioCard
|
{data.map((annuncio) => (
|
||||||
className="border-2 border-neutral-500"
|
<div
|
||||||
data={a}
|
className="flex flex-col justify-center gap-1"
|
||||||
interactions={
|
key={annuncio.codice}
|
||||||
<>
|
>
|
||||||
{isAdmin ? (
|
<CardAnnuncio
|
||||||
<AddButton annuncioId={a.id} />
|
{...annuncio}
|
||||||
) : (
|
className="outline outline-neutral-500"
|
||||||
<InteressatoButtonBatchV
|
noLink
|
||||||
annuncioId={a.id}
|
onlyFirstImage
|
||||||
hasInterest={userIntrests?.includes(a.id) || false}
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</>
|
<InteressatoButtonServizio annuncioId={annuncio.id} />
|
||||||
}
|
</div>
|
||||||
key={a.id}
|
))}
|
||||||
/>
|
</div>
|
||||||
))
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,19 @@ export const FormNewServizio = ({
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
|
{isLimited ? (
|
||||||
|
<div className="space-y-0">
|
||||||
|
<p className="font-semibold text-lg">
|
||||||
|
{field.value
|
||||||
|
? format(field.value, "dd/MM/yyyy")
|
||||||
|
: "Errore data invalida"}
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
La scadenza non può essere modificata. Contatta il
|
||||||
|
supporto se necessario.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|
@ -507,6 +520,7 @@ export const FormNewServizio = ({
|
||||||
/>
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
)}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ import type { GetServerSideProps } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { AccordionComp } from "~/components/accordionComp";
|
import { AccordionComp } from "~/components/accordionComp";
|
||||||
import { AnnunciGrid } from "~/components/annunci_grid";
|
|
||||||
import { MapSection } from "~/components/annunci_map";
|
import { MapSection } from "~/components/annunci_map";
|
||||||
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
|
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
|
||||||
|
import { CardAnnuncio } from "~/components/annuncio_card";
|
||||||
import { CodiceBox } from "~/components/codiceRicerca";
|
import { CodiceBox } from "~/components/codiceRicerca";
|
||||||
import { Layout } from "~/components/Layout";
|
import { Layout } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
|
|
@ -367,7 +367,11 @@ const AnnunciList = () => {
|
||||||
{status === "pending" ? (
|
{status === "pending" ? (
|
||||||
<LoadingPage />
|
<LoadingPage />
|
||||||
) : (
|
) : (
|
||||||
<AnnunciGrid pagedata={data.annunci} />
|
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3">
|
||||||
|
{data.annunci.map((annuncio) => (
|
||||||
|
<CardAnnuncio key={annuncio.codice} {...annuncio} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ const NewServizioModal = ({
|
||||||
Nuovo Servizio
|
Nuovo Servizio
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-h-[80%] overflow-auto p-4 sm:max-w-5xl">
|
<DialogContent className="max-h-[80%] w-full overflow-auto p-4 sm:max-w-5xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Creazione Servizio</DialogTitle>
|
<DialogTitle>Creazione Servizio</DialogTitle>
|
||||||
<DialogDescription className="sr-only">desc</DialogDescription>
|
<DialogDescription className="sr-only">desc</DialogDescription>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import {
|
||||||
import { db } from "~/server/db";
|
import { db } from "~/server/db";
|
||||||
import { NewMail, type NewMailProps } from "~/server/services/mailer";
|
import { NewMail, type NewMailProps } from "~/server/services/mailer";
|
||||||
import { getUser } from "~/server/services/user.service";
|
import { getUser } from "~/server/services/user.service";
|
||||||
|
import type { AnnuncioRicerca } from "./annunci.controller";
|
||||||
|
|
||||||
export const addServizio = async (data: NewServizio) => {
|
export const addServizio = async (data: NewServizio) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1514,7 +1515,9 @@ export const SendContactEmail = async ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCompatibileAnnunci = async (servizioId: ServizioServizioId) => {
|
export const getCompatibileAnnunci = async (
|
||||||
|
servizioId: ServizioServizioId,
|
||||||
|
): Promise<AnnuncioRicerca[]> => {
|
||||||
try {
|
try {
|
||||||
const servizio = await getServizioById(servizioId);
|
const servizio = await getServizioById(servizioId);
|
||||||
if (!servizio) {
|
if (!servizio) {
|
||||||
|
|
@ -1527,18 +1530,25 @@ export const getCompatibileAnnunci = async (servizioId: ServizioServizioId) => {
|
||||||
let qry = db
|
let qry = db
|
||||||
.selectFrom("annunci")
|
.selectFrom("annunci")
|
||||||
.select((eb) => [
|
.select((eb) => [
|
||||||
"annunci.id",
|
"id",
|
||||||
"annunci.codice",
|
"codice",
|
||||||
"annunci.prezzo",
|
"comune",
|
||||||
"annunci.desc_en",
|
"provincia",
|
||||||
"annunci.desc_it",
|
"mq",
|
||||||
"annunci.titolo_en",
|
"prezzo",
|
||||||
"annunci.titolo_it",
|
"desc_en",
|
||||||
"annunci.tipo",
|
"desc_it",
|
||||||
"annunci.consegna",
|
"titolo_en",
|
||||||
"annunci.stato",
|
"titolo_it",
|
||||||
"annunci.web",
|
"tipo",
|
||||||
"annunci.updated_at",
|
"consegna",
|
||||||
|
"stato",
|
||||||
|
"web",
|
||||||
|
"updated_at",
|
||||||
|
"homepage",
|
||||||
|
"numero_camere",
|
||||||
|
"url_video",
|
||||||
|
"modificato_il",
|
||||||
jsonArrayFrom(
|
jsonArrayFrom(
|
||||||
eb
|
eb
|
||||||
.selectFrom("images_refs")
|
.selectFrom("images_refs")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue