feat: add IncrociDialog component and integrate into admin and user interactions
This commit is contained in:
parent
c7dc508821
commit
eef0030676
8 changed files with 276 additions and 111 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
import { ArrowRight, BadgePlus } from "lucide-react";
|
import { ArrowRight, BadgePlus, Edit, Printer } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { ContattoAnnuncio } from "~/components/annuncio-interactions/contatto_modal";
|
import { ContattoAnnuncio } from "~/components/annuncio-interactions/contatto_modal";
|
||||||
import LoadingButton from "~/components/custom_ui/loading-button";
|
import LoadingButton from "~/components/custom_ui/loading-button";
|
||||||
|
import { IncrociDialog } from "~/components/servizio/incroci";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { useAnnuncio } from "~/providers/AnnuncioProvider";
|
import { useAnnuncio } from "~/providers/AnnuncioProvider";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
|
|
@ -14,9 +15,13 @@ import { api } from "~/utils/api";
|
||||||
|
|
||||||
export const AnnuncioInteractions = ({ disabled }: { disabled?: boolean }) => {
|
export const AnnuncioInteractions = ({ disabled }: { disabled?: boolean }) => {
|
||||||
const { user } = useSession();
|
const { user } = useSession();
|
||||||
const { id, tipo } = useAnnuncio();
|
|
||||||
const { locale } = useTranslation();
|
const { locale } = useTranslation();
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
|
if (user.isAdmin) {
|
||||||
|
return <AdminInteractions />;
|
||||||
|
}
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full rounded-md bg-red-500 p-2 text-center font-semibold text-base text-white">
|
<div className="w-full rounded-md bg-red-500 p-2 text-center font-semibold text-base text-white">
|
||||||
|
|
@ -26,6 +31,39 @@ export const AnnuncioInteractions = ({ disabled }: { disabled?: boolean }) => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return <UserInteractions />;
|
||||||
|
};
|
||||||
|
const AdminInteractions = () => {
|
||||||
|
const { id } = useAnnuncio();
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col gap-6">
|
||||||
|
<Link
|
||||||
|
className="w-full"
|
||||||
|
href={`/area-riservata/admin/edit-annuncio/${id}`}
|
||||||
|
>
|
||||||
|
<Button className="w-full" type="button">
|
||||||
|
<Edit /> Modifica annuncio
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="w-full"
|
||||||
|
href={`/area-riservata/scheda-annuncio-stampa/${id}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Button className="w-full" type="button" variant="outline">
|
||||||
|
<Printer /> Scheda immobile
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<IncrociDialog annuncioId={id} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const UserInteractions = () => {
|
||||||
|
const { user } = useSession();
|
||||||
|
const { id, tipo } = useAnnuncio();
|
||||||
|
const { locale } = useTranslation();
|
||||||
|
if (!user) return null;
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col justify-around gap-3 md:gap-5">
|
<div className="flex w-full flex-col justify-around gap-3 md:gap-5">
|
||||||
{user ? (
|
{user ? (
|
||||||
|
|
@ -41,25 +79,11 @@ export const AnnuncioInteractions = ({ disabled }: { disabled?: boolean }) => {
|
||||||
) : (
|
) : (
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-2">
|
||||||
<ContattoAnnuncio />
|
<ContattoAnnuncio />
|
||||||
{/* <span className="w-full text-center text-sm">oppure</span>
|
|
||||||
<Link
|
|
||||||
aria-label="Login"
|
|
||||||
href={{
|
|
||||||
pathname: "/login",
|
|
||||||
query: { redirect: router.asPath },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button className="w-full">
|
|
||||||
<LogIn />
|
|
||||||
<span>Accedi se hai un account</span>
|
|
||||||
</Button>
|
|
||||||
</Link> */}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ServizioInteraction = ({
|
const ServizioInteraction = ({
|
||||||
userId,
|
userId,
|
||||||
tipologia,
|
tipologia,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { IncrociDialog } from "~/components/servizio/incroci";
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
|
|
@ -73,6 +74,10 @@ export const AdminActions = () => {
|
||||||
<h3 className="ml-1 font-semibold">Azioni Admin:</h3>
|
<h3 className="ml-1 font-semibold">Azioni Admin:</h3>
|
||||||
<div className="flex w-full max-w-fit flex-wrap justify-start gap-3">
|
<div className="flex w-full max-w-fit flex-wrap justify-start gap-3">
|
||||||
<AnnuncioContatti data={data} />
|
<AnnuncioContatti data={data} />
|
||||||
|
<IncrociDialog
|
||||||
|
annuncioId={data.id}
|
||||||
|
props={{ className: "w-fit", size: "sm" }}
|
||||||
|
/>
|
||||||
<AnnuncioConfermaPopover
|
<AnnuncioConfermaPopover
|
||||||
annuncioId={annuncioId}
|
annuncioId={annuncioId}
|
||||||
servizioId={servizioId}
|
servizioId={servizioId}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import type { Table } from "@tanstack/react-table";
|
import type { Table } from "@tanstack/react-table";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { NotebookPen, Toolbox } from "lucide-react";
|
import { Eye, NotebookPen, Toolbox } from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
import { type RefObject, useState } from "react";
|
import { type RefObject, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import LoadingButton from "~/components/custom_ui/loading-button";
|
import LoadingButton from "~/components/custom_ui/loading-button";
|
||||||
|
|
@ -11,12 +12,17 @@ import {
|
||||||
AnnuncioContatti,
|
AnnuncioContatti,
|
||||||
UserActions,
|
UserActions,
|
||||||
} from "~/components/servizio/annuncio_actions";
|
} from "~/components/servizio/annuncio_actions";
|
||||||
|
import { IncrociDialog } from "~/components/servizio/incroci";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||||
import { Textarea } from "~/components/ui/textarea";
|
import { Textarea } from "~/components/ui/textarea";
|
||||||
import { useMediaQuery } from "~/hooks/use-media-query";
|
import { useMediaQuery } from "~/hooks/use-media-query";
|
||||||
import { handleConsegna } from "~/lib/annuncio_details";
|
import { handleConsegna } from "~/lib/annuncio_details";
|
||||||
import { getStorageUrl } from "~/lib/storage_utils";
|
import { getStorageUrl } from "~/lib/storage_utils";
|
||||||
import { MutationPageRestore } from "~/lib/tableUtils";
|
import {
|
||||||
|
MutationPageRestore,
|
||||||
|
type onMutate,
|
||||||
|
type onSettled,
|
||||||
|
} from "~/lib/tableUtils";
|
||||||
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";
|
||||||
|
|
@ -125,21 +131,8 @@ export const AnnuncioCardRichieste = ({
|
||||||
tableRef: RefObject<Table<RichiesteData> | null>;
|
tableRef: RefObject<Table<RichiesteData> | null>;
|
||||||
data: ServizioAnnuncioData;
|
data: ServizioAnnuncioData;
|
||||||
}) => {
|
}) => {
|
||||||
const [newNote, setNewNote] = useState<string | null>(data.note);
|
|
||||||
const utils = api.useUtils();
|
|
||||||
const { onMutate, onSettled } = MutationPageRestore(tableRef);
|
const { onMutate, onSettled } = MutationPageRestore(tableRef);
|
||||||
const { mutate, isPending } = api.servizio.updateServizioAnnunci.useMutation({
|
|
||||||
onMutate,
|
|
||||||
onSettled,
|
|
||||||
onSuccess: async () => {
|
|
||||||
toast.success("Note aggiornate");
|
|
||||||
await utils.servizio.invalidate();
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
toast.error("Errore nell'aggiornamento note");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const isDesktop = useMediaQuery("(min-width: 40rem)");
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full max-w-7xl gap-2 rounded-md border-none bg-muted py-3">
|
<Card className="w-full max-w-7xl gap-2 rounded-md border-none bg-muted py-3">
|
||||||
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
|
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
|
||||||
|
|
@ -164,6 +157,15 @@ export const AnnuncioCardRichieste = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-4">
|
<div className="flex flex-wrap gap-4">
|
||||||
|
<Link href={`/annuncio/${data.codice}`} target="_blank">
|
||||||
|
<Button size="sm" type="button" variant="outline">
|
||||||
|
<Eye /> vista pubblica
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<IncrociDialog
|
||||||
|
annuncioId={data.id}
|
||||||
|
props={{ className: "w-fit" , size: "sm"}}
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<AnnuncioContatti data={data} />
|
<AnnuncioContatti data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -176,48 +178,13 @@ export const AnnuncioCardRichieste = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AnnuncioNote
|
||||||
<Collapsible
|
annuncioId={data.id}
|
||||||
className={cn("space-y-2", isDesktop && "relative -top-10")}
|
note={data.note}
|
||||||
open={isDesktop || undefined}
|
onMutate={onMutate}
|
||||||
>
|
onSettled={onSettled}
|
||||||
<CollapsibleTrigger asChild>
|
servizioId={data.servizio_id}
|
||||||
<Button
|
|
||||||
className={cn(isDesktop && "cursor-default")}
|
|
||||||
size="sm"
|
|
||||||
variant={isDesktop ? "flat" : "outline"}
|
|
||||||
>
|
|
||||||
<NotebookPen />
|
|
||||||
<span>Annotazioni</span>
|
|
||||||
</Button>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent className="">
|
|
||||||
<div className="flex w-full flex-col gap-2">
|
|
||||||
<Textarea
|
|
||||||
className="min-h-64"
|
|
||||||
id={`note-${data.id}`}
|
|
||||||
onChange={(e) => setNewNote(e.target.value)}
|
|
||||||
placeholder=""
|
|
||||||
value={newNote || ""}
|
|
||||||
/>
|
/>
|
||||||
<LoadingButton
|
|
||||||
disabled={newNote === data.note}
|
|
||||||
loading={isPending}
|
|
||||||
onClick={() => {
|
|
||||||
mutate({
|
|
||||||
annuncioId: data.id,
|
|
||||||
servizioId: data.servizio_id,
|
|
||||||
data: { note: newNote },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
variant="success"
|
|
||||||
>
|
|
||||||
Salva
|
|
||||||
</LoadingButton>
|
|
||||||
</div>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -228,14 +195,20 @@ const AnnuncioNote = ({
|
||||||
servizioId,
|
servizioId,
|
||||||
annuncioId,
|
annuncioId,
|
||||||
note,
|
note,
|
||||||
|
onMutate,
|
||||||
|
onSettled,
|
||||||
}: {
|
}: {
|
||||||
servizioId: ServizioServizioId;
|
servizioId: ServizioServizioId;
|
||||||
annuncioId: AnnunciId;
|
annuncioId: AnnunciId;
|
||||||
note: string | null;
|
note: string | null;
|
||||||
|
onMutate?: onMutate;
|
||||||
|
onSettled?: onSettled;
|
||||||
}) => {
|
}) => {
|
||||||
const [newNote, setNewNote] = useState<string | null>(note);
|
const [newNote, setNewNote] = useState<string | null>(note);
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
const { mutate, isPending } = api.servizio.updateServizioAnnunci.useMutation({
|
const { mutate, isPending } = api.servizio.updateServizioAnnunci.useMutation({
|
||||||
|
onMutate,
|
||||||
|
onSettled,
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
toast.success("Note aggiornate");
|
toast.success("Note aggiornate");
|
||||||
await utils.servizio.invalidate();
|
await utils.servizio.invalidate();
|
||||||
|
|
@ -245,12 +218,36 @@ const AnnuncioNote = ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const isDesktop = useMediaQuery("(min-width: 40rem)");
|
const isDesktop = useMediaQuery("(min-width: 40rem)");
|
||||||
|
if (isDesktop) {
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<div className="flex w-full flex-col gap-2">
|
||||||
className={cn("space-y-2", isDesktop && "relative -top-10")}
|
<Textarea
|
||||||
open={isDesktop || undefined}
|
className="min-h-64"
|
||||||
|
id={`note-${annuncioId}`}
|
||||||
|
onChange={(e) => setNewNote(e.target.value)}
|
||||||
|
placeholder=""
|
||||||
|
value={newNote || ""}
|
||||||
|
/>
|
||||||
|
<LoadingButton
|
||||||
|
disabled={newNote === note}
|
||||||
|
loading={isPending}
|
||||||
|
onClick={() => {
|
||||||
|
mutate({
|
||||||
|
annuncioId,
|
||||||
|
servizioId,
|
||||||
|
data: { note: newNote },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
variant="success"
|
||||||
>
|
>
|
||||||
|
Salva
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Collapsible className={cn("space-y-2", isDesktop && "relative -top-10")}>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
className={cn(isDesktop && "cursor-default")}
|
className={cn(isDesktop && "cursor-default")}
|
||||||
|
|
|
||||||
115
apps/infoalloggi/src/components/servizio/incroci.tsx
Normal file
115
apps/infoalloggi/src/components/servizio/incroci.tsx
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Waypoints } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Button, type ButtonProps } from "~/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "~/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "~/components/ui/table";
|
||||||
|
import { UserAvatar } from "~/components/user_avatar";
|
||||||
|
import { cn } from "~/lib/utils";
|
||||||
|
import type { AnnunciId } from "~/schemas/public/Annunci";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
|
export const IncrociDialog = ({
|
||||||
|
annuncioId,
|
||||||
|
props,
|
||||||
|
}: {
|
||||||
|
annuncioId: AnnunciId;
|
||||||
|
props?: ButtonProps;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
className={cn("w-full", props?.className)}
|
||||||
|
variant={props?.variant || "info"}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Waypoints /> Incroci
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-2xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Lista Incroci</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Lista utenti che hanno ricevuto i contatti di questo annuncio
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<IncrociTable annuncioId={annuncioId} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const IncrociTable = ({ annuncioId }: { annuncioId: AnnunciId }) => {
|
||||||
|
const { data: incroci, isLoading } = api.annunci.getIncrociAnnuncio.useQuery({
|
||||||
|
annuncioId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead className="">Utente</TableHead>
|
||||||
|
<TableHead>Data</TableHead>
|
||||||
|
<TableHead>Note</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{isLoading ? (
|
||||||
|
<p>Caricamento...</p>
|
||||||
|
) : (
|
||||||
|
incroci?.map((incrocio) => (
|
||||||
|
<TableRow key={incrocio.servizio_id}>
|
||||||
|
<TableCell>
|
||||||
|
<Link
|
||||||
|
aria-label="Visualizza Utente"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
href={`/area-riservata/admin/user-view/ricerca/${incrocio.user_id}`}
|
||||||
|
>
|
||||||
|
<UserAvatar
|
||||||
|
className="size-8"
|
||||||
|
userId={incrocio.user_id}
|
||||||
|
username={incrocio.username}
|
||||||
|
/>
|
||||||
|
<span className="font-semibold text-sm">
|
||||||
|
{incrocio.username}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Aggiunto: {format(incrocio.created_at, "dd/MM/yyyy HH:mm")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Aperto:{" "}
|
||||||
|
{incrocio.open_contatti_at
|
||||||
|
? format(incrocio.open_contatti_at, "dd/MM/yyyy HH:mm")
|
||||||
|
: "Non aperto"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="whitespace-pre-wrap">
|
||||||
|
{incrocio.note}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,20 @@
|
||||||
import type { Table } from "@tanstack/react-table";
|
import type { Table } from "@tanstack/react-table";
|
||||||
import type { RefObject } from "react";
|
import type { RefObject } from "react";
|
||||||
|
|
||||||
export const MutationPageRestore = (tableRef: RefObject<Table<any> | null>) => {
|
export type onMutate = () => {
|
||||||
|
pageIdx: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type onSettled = (
|
||||||
|
_a: any,
|
||||||
|
_b: any,
|
||||||
|
_c: any,
|
||||||
|
context?: { pageIdx?: number },
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export const MutationPageRestore = (
|
||||||
|
tableRef: RefObject<Table<any> | null>,
|
||||||
|
): { onMutate: onMutate; onSettled: onSettled } => {
|
||||||
return {
|
return {
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
const pageIdx = tableRef.current?.getState().pagination.pageIndex; // 0 based index
|
const pageIdx = tableRef.current?.getState().pagination.pageIndex; // 0 based index
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ import {
|
||||||
Car,
|
Car,
|
||||||
Clock,
|
Clock,
|
||||||
Copy,
|
Copy,
|
||||||
Edit,
|
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Printer,
|
|
||||||
Ruler,
|
Ruler,
|
||||||
Share2,
|
Share2,
|
||||||
TrafficCone,
|
TrafficCone,
|
||||||
|
|
@ -64,7 +62,6 @@ import { cn } from "~/lib/utils";
|
||||||
import { AnnuncioContext, useAnnuncio } from "~/providers/AnnuncioProvider";
|
import { AnnuncioContext, useAnnuncio } from "~/providers/AnnuncioProvider";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||||
import { useSession } from "~/providers/SessionProvider";
|
|
||||||
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
||||||
import type { AnnuncioData } from "~/server/controllers/annunci.controller";
|
import type { AnnuncioData } from "~/server/controllers/annunci.controller";
|
||||||
import { generateSSGHelper } from "~/server/utils/ssgHelper";
|
import { generateSSGHelper } from "~/server/utils/ssgHelper";
|
||||||
|
|
@ -121,7 +118,6 @@ const AnnuncioDettaglio: NextPage<AnnuncioProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnnuncioView = ({ data, flag }: AnnuncioProps) => {
|
const AnnuncioView = ({ data, flag }: AnnuncioProps) => {
|
||||||
const { user } = useSession();
|
|
||||||
useStaleImageReload();
|
useStaleImageReload();
|
||||||
|
|
||||||
const isDesktop = useMediaQuery("(min-width: 768px)");
|
const isDesktop = useMediaQuery("(min-width: 768px)");
|
||||||
|
|
@ -146,33 +142,7 @@ const AnnuncioView = ({ data, flag }: AnnuncioProps) => {
|
||||||
<div className="mb-6 flex flex-col md:mb-0 md:w-1/3">
|
<div className="mb-6 flex flex-col md:mb-0 md:w-1/3">
|
||||||
<div className="flex w-full flex-col flex-nowrap items-center justify-center gap-6">
|
<div className="flex w-full flex-col flex-nowrap items-center justify-center gap-6">
|
||||||
<CardInfos />
|
<CardInfos />
|
||||||
{user?.isAdmin ? (
|
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
className="w-full"
|
|
||||||
href={`/area-riservata/admin/edit-annuncio/${data.id}`}
|
|
||||||
>
|
|
||||||
<Button className="w-full" type="button">
|
|
||||||
<Edit /> Modifica annuncio
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
className="w-full"
|
|
||||||
href={`/area-riservata/scheda-annuncio-stampa/${data.id}`}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
className="w-full"
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<Printer /> Scheda immobile
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<AnnuncioInteractions disabled={flag === "true"} />
|
<AnnuncioInteractions disabled={flag === "true"} />
|
||||||
)}
|
|
||||||
<ComeFunziona />
|
<ComeFunziona />
|
||||||
{data.tipo &&
|
{data.tipo &&
|
||||||
(data.tipo === "Transitorio" || data.tipo === "Stabile") && (
|
(data.tipo === "Transitorio" || data.tipo === "Stabile") && (
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
removeAnnuncioMedia,
|
removeAnnuncioMedia,
|
||||||
type VideoToRemove,
|
type VideoToRemove,
|
||||||
} from "~/server/controllers/annunci.controller";
|
} from "~/server/controllers/annunci.controller";
|
||||||
|
import { getIncrociAnnuncio } from "~/server/controllers/servizio.controller";
|
||||||
import { db } from "~/server/db";
|
import { db } from "~/server/db";
|
||||||
import {
|
import {
|
||||||
invalidateCache,
|
invalidateCache,
|
||||||
|
|
@ -188,4 +189,13 @@ export const annunciRouter = createTRPCRouter({
|
||||||
...input,
|
...input,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
getIncrociAnnuncio: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
annuncioId: zAnnuncioId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return await getIncrociAnnuncio(input.annuncioId);
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1282,3 +1282,34 @@ export const getRichieste = async (): Promise<RichiesteData[]> => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getIncrociAnnuncio = async (annuncioId: AnnunciId) => {
|
||||||
|
try {
|
||||||
|
const data = await db
|
||||||
|
.selectFrom("servizio_annunci")
|
||||||
|
.innerJoin(
|
||||||
|
"servizio",
|
||||||
|
"servizio.servizio_id",
|
||||||
|
"servizio_annunci.servizio_id",
|
||||||
|
)
|
||||||
|
.innerJoin("users", "users.id", "servizio.user_id")
|
||||||
|
.where("servizio_annunci.annunci_id", "=", annuncioId)
|
||||||
|
.select([
|
||||||
|
"servizio_annunci.open_contatti_at",
|
||||||
|
"servizio_annunci.created_at",
|
||||||
|
"servizio.servizio_id",
|
||||||
|
"servizio.user_id",
|
||||||
|
"users.username",
|
||||||
|
"servizio_annunci.note",
|
||||||
|
])
|
||||||
|
.orderBy("servizio_annunci.created_at", "desc")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error fetching incroci annuncio: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue