feat: add note field to Users schema and implement NoteUtenteDialog for user notes management

This commit is contained in:
Marco Pedone 2026-03-17 18:47:35 +01:00
parent 58ea254180
commit 5327cc86c6
4 changed files with 73 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import {
ExternalLink,
Mail,
MessagesSquare,
NotebookPen,
Paperclip,
Search,
ShoppingBag,
@ -12,7 +13,18 @@ import {
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useRouter } from "next/router";
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 {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import { useUserViewContext } from "~/lib/userViewContext";
import { api } from "~/utils/api";
import { Confirm } from "../confirm";
@ -45,7 +57,7 @@ export const UserViewHeader = () => {
if (!data) return <div>Errore</div>;
return (
<div className="mb-5 flex flex-col space-y-5">
<div className="flex flex-row flex-wrap items-baseline gap-3">
<div className="flex flex-row flex-wrap items-center gap-3">
<div className="text-3xl">{data.username}</div>
<Link href={`https://wa.me/${data.telefono}`} target="_blank">
@ -72,6 +84,7 @@ export const UserViewHeader = () => {
<Mail /> Invita al sito
</Button>
</Confirm>
<NoteUtenteDialog />
</div>
<div className="flex flex-row flex-wrap gap-2">
<Link href={`/area-riservata/admin/user-view/edit-user/${data.id}`}>
@ -205,8 +218,8 @@ const IntestazioneMaker = () => {
}
// Format names
const cognome = titleCase(data.cognome);
const nome = titleCase(data.nome);
const cognome = data.cognome.toUpperCase();
const nome = data.nome.toUpperCase();
const viaResidenza = titleCase(data.via_residenza);
const comuneNascita = titleCase(comune_nascita.nome);
const comuneResidenza = titleCase(comune_residenza.nome);
@ -260,3 +273,51 @@ function titleCase(str: string | null | undefined): string {
})
.join(" ");
}
const NoteUtenteDialog = () => {
const { id, note } = useUserViewContext();
const [newNote, setNewNote] = useState(note || "");
const utils = api.useUtils();
const { mutate, isPending } = api.users.editUser.useMutation({
onSuccess: async () => {
toast.success("Note aggiornate con successo");
await utils.users.invalidate();
},
onError: (error) => {
toast.error(error.message);
},
});
return (
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="orange">
<NotebookPen className="size-4" /> Note
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Note Utente</DialogTitle>
<DialogDescription className="sr-only">note</DialogDescription>
</DialogHeader>
<div className="flex w-full flex-col gap-4">
<Textarea
className="min-h-64"
id="note"
onChange={(e) => setNewNote(e.target.value)}
placeholder=""
value={newNote}
/>
<LoadingButton
disabled={newNote === note}
loading={isPending}
onClick={() => mutate({ id, data: { note: newNote } })}
variant="success"
>
Salva
</LoadingButton>
</div>
</DialogContent>
</Dialog>
);
};

View file

@ -37,6 +37,8 @@ export default interface UsersTable {
salt: ColumnType<string, string, string>;
mustChangePassword: ColumnType<boolean, boolean | undefined, boolean>;
note: ColumnType<string | null, string | null, string | null>;
}
export type Users = Selectable<UsersTable>;

View file

@ -16,6 +16,7 @@ import {
getEmails,
} from "~/server/services/email.service";
import { NewMail } from "~/server/services/mailer";
import { CONDIZIONI_DEFAULT } from "~/server/services/prezziario.service";
import { genPdfSchedaAnnuncioBase64 } from "~/server/services/puppeteer.service";
import { zAnnuncioId, zOrdineId, zUserId } from "~/server/utils/zod_types";
@ -246,7 +247,7 @@ export const comunicazioniRouter = createTRPCRouter({
generate: {
type: "condizioni",
stringId: (ordine.testo_condizioni ||
"CONDIZIONI_CERCHI") as TestiEStringheStingaId,
CONDIZIONI_DEFAULT) as TestiEStringheStingaId,
},
encoding: "base64",
contentType: "application/pdf",
@ -266,7 +267,7 @@ export const comunicazioniRouter = createTRPCRouter({
userId: ordine.userid,
},
},
lock_expires: new Date(), // Lock expires in 1
lock_expires: new Date(Date.now() + 3000), // Lock for 30 sec
});
} catch (e) {
throw new Error(

View file

@ -1,4 +1,7 @@
import { apiProcedure, createTRPCRouter } from "~/server/api/trpc";
import {
apiProcedure,
createTRPCRouter,
} from "~/server/api/trpc";
import { processEvents } from "~/server/controllers/event_queue.controller";
export const eventQueueRouter = createTRPCRouter({