feat: add note column to servizio_annunci table and implement AnnuncioNote component for admin annotations
This commit is contained in:
parent
8f662c56c1
commit
d6cd5f86b8
3 changed files with 89 additions and 8 deletions
2
apps/db/migrations/39_servizio_annuncio_note.up.sql
Normal file
2
apps/db/migrations/39_servizio_annuncio_note.up.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE IF EXISTS public.servizio_annunci
|
||||
ADD COLUMN IF NOT EXISTS note TEXT;
|
||||
|
|
@ -1,16 +1,22 @@
|
|||
import { Toolbox } from "lucide-react";
|
||||
import { NotebookPen, Toolbox } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import LoadingButton from "~/components/custom_ui/loading-button";
|
||||
import { Textarea } from "~/components/custom_ui/textarea";
|
||||
import {
|
||||
AdminActions,
|
||||
UserActions,
|
||||
} from "~/components/servizio/annuncio_actions";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { useMediaQuery } from "~/hooks/use-media-query";
|
||||
import { handleConsegna } from "~/lib/annuncio_details";
|
||||
import { getStorageUrl } from "~/lib/storage_utils";
|
||||
import { cn, formatCurrency } from "~/lib/utils";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
|
||||
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
|
||||
import { api } from "~/utils/api";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
Collapsible,
|
||||
|
|
@ -67,10 +73,11 @@ export const BasicAnnuncioCard = ({
|
|||
className?: string;
|
||||
data: AnnuncioRicerca;
|
||||
}) => {
|
||||
const { isAdmin } = useServizio();
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"w-full max-w-4xl gap-2 rounded-md border-none bg-muted py-3",
|
||||
"w-full max-w-7xl gap-2 rounded-md border-none bg-muted py-3",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -79,17 +86,87 @@ export const BasicAnnuncioCard = ({
|
|||
</CardHeader>
|
||||
<CardContent className="px-3">
|
||||
<div
|
||||
className={cn("@container flex w-full flex-col gap-4")}
|
||||
id="container"
|
||||
className={cn(
|
||||
"grid grid-cols-1 gap-4",
|
||||
isAdmin && "sm:grid-cols-[2fr_1fr]",
|
||||
)}
|
||||
>
|
||||
<AnnuncioDisplay data={data} />
|
||||
<div className="flex w-full flex-col gap-4">{interactions}</div>
|
||||
<div
|
||||
className={cn("@container flex w-full flex-col gap-4")}
|
||||
id="container"
|
||||
>
|
||||
<AnnuncioDisplay data={data} />
|
||||
|
||||
<div className="flex w-full flex-col gap-4">{interactions}</div>
|
||||
</div>
|
||||
{isAdmin && <AnnuncioNote />}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const AnnuncioNote = () => {
|
||||
const { servizioId } = useServizio();
|
||||
const { data } = useServizioAnnuncio();
|
||||
|
||||
const [newNote, setNewNote] = useState(data.note || "");
|
||||
const utils = api.useUtils();
|
||||
const { mutate, isPending } = api.servizio.updateServizioAnnunci.useMutation({
|
||||
onSuccess: async () => {
|
||||
toast.success("Note aggiornate");
|
||||
await utils.servizio.invalidate();
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Errore nell'aggiornamento note");
|
||||
},
|
||||
});
|
||||
const isDesktop = useMediaQuery("(min-width: 40rem)");
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
className={cn("space-y-2", isDesktop && "relative -top-10")}
|
||||
open={isDesktop || undefined}
|
||||
>
|
||||
<CollapsibleTrigger asChild>
|
||||
<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: { note: newNote },
|
||||
})
|
||||
}
|
||||
variant="success"
|
||||
>
|
||||
Salva
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnnuncioDisplay = ({
|
||||
data,
|
||||
className,
|
||||
|
|
@ -102,7 +179,7 @@ export const AnnuncioDisplay = ({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] @sm:gap-x-4 gap-x-1 gap-y-2 rounded-md bg-card md:max-w-3xl lg:gap-x-4 xl:gap-x-6",
|
||||
"grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] @sm:gap-x-4 gap-x-1 gap-y-2 rounded-md bg-card md:max-w-5xl lg:gap-x-4 xl:gap-x-6",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -131,7 +208,7 @@ export const AnnuncioDisplay = ({
|
|||
<span className="text-wrap">{data.titolo_it}</span>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex w-full max-w-xl items-center justify-between gap-2 md:col-span-1 md:pr-8">
|
||||
<div className="col-span-2 flex w-full max-w-xl items-center justify-between gap-2 pr-3 md:col-span-1 md:pr-8">
|
||||
<TipoBadge mini tipo={data.tipo} />
|
||||
<span className="max-w-auto truncate">
|
||||
{handleConsegna({
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ export default interface ServizioAnnunciTable {
|
|||
doc_contratto_added: ColumnType<boolean, boolean | undefined, boolean>;
|
||||
|
||||
doc_registrazione_added: ColumnType<boolean, boolean | undefined, boolean>;
|
||||
|
||||
note: ColumnType<string | null, string | null, string | null>;
|
||||
}
|
||||
|
||||
export type ServizioAnnunci = Selectable<ServizioAnnunciTable>;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue