feat: add note field to Users schema and implement NoteUtenteDialog for user notes management
This commit is contained in:
parent
58ea254180
commit
5327cc86c6
4 changed files with 73 additions and 6 deletions
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue