feat: enhance user management and document handling; refactor service and schema integrations
This commit is contained in:
parent
fa8dcb7d86
commit
d894f9d6ec
16 changed files with 381 additions and 98 deletions
26
apps/db/migrations/24_servizio_annunci_id.up.sql
Normal file
26
apps/db/migrations/24_servizio_annunci_id.up.sql
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
-- Add column only if it doesn't exist
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'servizio_annunci'
|
||||||
|
AND column_name = 'servizio_annunci_id'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.servizio_annunci
|
||||||
|
ADD COLUMN servizio_annunci_id serial NOT NULL;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Add primary key only if it doesn't exist
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'servizio_annunci_pkey'
|
||||||
|
AND conrelid = 'public.servizio_annunci'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.servizio_annunci
|
||||||
|
ADD PRIMARY KEY (servizio_annunci_id);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
@ -5,6 +5,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
PGADMIN_DEFAULT_EMAIL: m.pedone98@gmail.com
|
PGADMIN_DEFAULT_EMAIL: m.pedone98@gmail.com
|
||||||
PGADMIN_DEFAULT_PASSWORD: rootpost
|
PGADMIN_DEFAULT_PASSWORD: rootpost
|
||||||
|
PGADMIN_CONFIG_CONSOLE_LOG_LEVEL: 10
|
||||||
ports:
|
ports:
|
||||||
- "5050:80"
|
- "5050:80"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
import { ExternalLink, Trash2 } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { useSession } from "~/providers/SessionProvider";
|
||||||
|
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
|
||||||
|
import type { UserWithAnagrafica } from "~/server/services/user.service";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
import { Confirm } from "../confirm";
|
||||||
|
import { ImageFlbk } from "../ImageWithFallback";
|
||||||
|
import { LoadingPage } from "../loading";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "../ui/card";
|
||||||
|
import { UploadModal } from "../upload_modal";
|
||||||
|
|
||||||
|
export const DocumentiPersonali = ({
|
||||||
|
userData,
|
||||||
|
}: {
|
||||||
|
userData: UserWithAnagrafica;
|
||||||
|
}) => {
|
||||||
|
const utils = api.useUtils();
|
||||||
|
const { mutate, isPending } = api.users.editUserAnagrafica.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
await utils.users.getClientProfilo.invalidate({
|
||||||
|
userId: userData.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
toast.error(`Errore aggiornamento servizio: ${err.message}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const handleUpload = (
|
||||||
|
field: "doc_personale_fronte_ref" | "doc_personale_retro_ref",
|
||||||
|
id: UsersStorageUserStorageId,
|
||||||
|
) => {
|
||||||
|
mutate({
|
||||||
|
userId: userData.id,
|
||||||
|
data: { [field]: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { user } = useSession();
|
||||||
|
if (!user) return null;
|
||||||
|
if (isPending) return <LoadingPage />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Documenti personali</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
<p>I tuoi documenti sono necessari per usufruire del servizio.</p>
|
||||||
|
<p>
|
||||||
|
Nel caso di un unico documento (es: scansione fronte e retro del
|
||||||
|
documento) caricarlo 2 volte.
|
||||||
|
</p>
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="mb-2 font-medium">
|
||||||
|
Documento d'identità - Fronte
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{userData.doc_personale_fronte?.storageId ? (
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<ImageFlbk
|
||||||
|
alt="documento personale fronte"
|
||||||
|
className="size-24 rounded-md object-cover"
|
||||||
|
height={50}
|
||||||
|
src={`/storage-api/get/${userData.doc_personale_fronte.storageId}`}
|
||||||
|
width={50}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-center gap-2">
|
||||||
|
<Link
|
||||||
|
href={`/area-riservata/allegato-view/${userData.doc_personale_fronte.storageId}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Button size="sm">
|
||||||
|
<span>Visualizza</span>
|
||||||
|
<ExternalLink />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Confirm
|
||||||
|
description="Sei sicuro di voler eliminare il documento?"
|
||||||
|
onConfirm={() => {
|
||||||
|
mutate({
|
||||||
|
userId: userData.id,
|
||||||
|
data: { doc_personale_fronte_ref: null },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
title="Elimina Documento"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
aria-label="Elimina Doc"
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
|
<span>Elimina Documento</span>
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</Confirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<UploadModal
|
||||||
|
cb_onUpload={({ userStorageIds }) =>
|
||||||
|
userStorageIds.length > 0 &&
|
||||||
|
userStorageIds[0] &&
|
||||||
|
handleUpload("doc_personale_fronte_ref", userStorageIds[0])
|
||||||
|
}
|
||||||
|
isAdmin={user.isAdmin}
|
||||||
|
maxFiles={1}
|
||||||
|
userId={userData.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="mb-2 font-medium">Documento d'identità - Retro</h4>
|
||||||
|
{userData.doc_personale_retro?.storageId ? (
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<ImageFlbk
|
||||||
|
alt="documento personale retro"
|
||||||
|
className="size-24 rounded-md object-cover"
|
||||||
|
height={50}
|
||||||
|
src={`/storage-api/get/${userData.doc_personale_retro.storageId}`}
|
||||||
|
width={50}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-center gap-2">
|
||||||
|
<Link
|
||||||
|
href={`/area-riservata/allegato-view/${userData.doc_personale_retro.storageId}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Button size="sm">
|
||||||
|
<span>Visualizza</span>
|
||||||
|
<ExternalLink />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Confirm
|
||||||
|
description="Sei sicuro di voler eliminare il documento?"
|
||||||
|
onConfirm={() => {
|
||||||
|
mutate({
|
||||||
|
userId: userData.id,
|
||||||
|
data: { doc_personale_retro_ref: null },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
title="Elimina Documento"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
aria-label="Elimina Doc"
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
|
<span>Elimina Documento</span>
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</Confirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<UploadModal
|
||||||
|
cb_onUpload={({ userStorageIds }) =>
|
||||||
|
userStorageIds.length > 0 &&
|
||||||
|
userStorageIds[0] &&
|
||||||
|
handleUpload("doc_personale_retro_ref", userStorageIds[0])
|
||||||
|
}
|
||||||
|
isAdmin={user.isAdmin}
|
||||||
|
maxFiles={1}
|
||||||
|
userId={userData.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
I tuoi dati saranno conservati solamente per la durata del servizio,
|
||||||
|
in conformità con la nostra{" "}
|
||||||
|
<Link className="underline" href="/privacy-policy" target="_blank">
|
||||||
|
Informativa sulla Privacy
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -57,7 +57,7 @@ export const AdminLavoraConferma = () => {
|
||||||
Conferma
|
Conferma
|
||||||
</Button>
|
</Button>
|
||||||
</CredenzaTrigger>
|
</CredenzaTrigger>
|
||||||
<CredenzaContent className="max-h-[90vh] w-full px-3 sm:max-w-4xl">
|
<CredenzaContent className="max-h-[90vh] w-full px-3 sm:max-w-6xl">
|
||||||
<CredenzaHeader>
|
<CredenzaHeader>
|
||||||
<CredenzaTitle>Lavorazione conferma</CredenzaTitle>
|
<CredenzaTitle>Lavorazione conferma</CredenzaTitle>
|
||||||
<CredenzaDescription className="sr-only">
|
<CredenzaDescription className="sr-only">
|
||||||
|
|
@ -65,7 +65,7 @@ export const AdminLavoraConferma = () => {
|
||||||
</CredenzaDescription>
|
</CredenzaDescription>
|
||||||
</CredenzaHeader>
|
</CredenzaHeader>
|
||||||
<CredenzaBody className="max-h-[80vh] w-full overflow-y-auto pb-5">
|
<CredenzaBody className="max-h-[80vh] w-full overflow-y-auto pb-5">
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-4">
|
||||||
<DocSection />
|
<DocSection />
|
||||||
<Caparra />
|
<Caparra />
|
||||||
{servizio.tipologia === TipologiaPosizioneEnum.Transitorio && (
|
{servizio.tipologia === TipologiaPosizioneEnum.Transitorio && (
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ const DocCheckpoint = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userId, servizio } = useServizio();
|
const { userId, servizio } = useServizio();
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
const { mutate, isPending } = api.servizio.updateServizio.useMutation({
|
const { mutate, isPending } = api.users.editUserAnagrafica.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await utils.servizio.getAllServizioAnnunci.invalidate({
|
await utils.servizio.getAllServizioAnnunci.invalidate({
|
||||||
userId,
|
userId,
|
||||||
|
|
@ -134,8 +134,10 @@ const DocCheckpoint = () => {
|
||||||
id: UsersStorageUserStorageId,
|
id: UsersStorageUserStorageId,
|
||||||
) => {
|
) => {
|
||||||
mutate({
|
mutate({
|
||||||
servizioId: servizio.servizio_id,
|
userId,
|
||||||
data: { [field]: id },
|
data: {
|
||||||
|
[field]: id,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ export const ProfileFormAccount = ({
|
||||||
toast.success(t.profile.aggiornato);
|
toast.success(t.profile.aggiornato);
|
||||||
await utils.auth.getSession.invalidate();
|
await utils.auth.getSession.invalidate();
|
||||||
await utils.users.getUser.invalidate();
|
await utils.users.getUser.invalidate();
|
||||||
await utils.users.getClientProfilo.invalidate();
|
await utils.users.getClientProfilo.invalidate({
|
||||||
|
userId: id,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -210,15 +210,16 @@ export const ProfileFormAnagrafica = ({
|
||||||
toast.success(t.profile.aggiornato);
|
toast.success(t.profile.aggiornato);
|
||||||
await utils.auth.getSession.invalidate();
|
await utils.auth.getSession.invalidate();
|
||||||
await utils.users.getUser.invalidate();
|
await utils.users.getUser.invalidate();
|
||||||
await utils.users.getClientProfilo.invalidate();
|
await utils.users.getClientProfilo.invalidate({
|
||||||
|
userId: userData.id,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(fields: ProfileFormValues) {
|
function onSubmit(fields: ProfileFormValues) {
|
||||||
mutate({
|
mutate({
|
||||||
...fields,
|
userId: userData.id,
|
||||||
idanagrafica: userData.idanagrafica || undefined,
|
data: { ...fields },
|
||||||
userid: userData.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const nazioni_options = GetNazioni();
|
const nazioni_options = GetNazioni();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
Fingerprint,
|
Fingerprint,
|
||||||
|
IdCard,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
Landmark,
|
Landmark,
|
||||||
Lock,
|
Lock,
|
||||||
|
|
@ -10,6 +11,7 @@ import {
|
||||||
import type { GetServerSideProps } from "next";
|
import type { GetServerSideProps } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { DocumentiPersonali } from "~/components/area-riservata/documenti_personali";
|
||||||
import { AreaRiservataLayoutUserView } from "~/components/Layout";
|
import { AreaRiservataLayoutUserView } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
import { Status500 } from "~/components/status-page";
|
import { Status500 } from "~/components/status-page";
|
||||||
|
|
@ -46,7 +48,7 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
const { data: userData, isLoading } = api.users.getClientProfilo.useQuery({
|
const { data: userData, isLoading } = api.users.getClientProfilo.useQuery({
|
||||||
id: userId,
|
userId,
|
||||||
});
|
});
|
||||||
const { mutate: delete_mutate } = api.users.deleteUser.useMutation({
|
const { mutate: delete_mutate } = api.users.deleteUser.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
|
|
@ -60,7 +62,9 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
||||||
const { mutate: ban_mutate } = api.users.blockUser.useMutation({
|
const { mutate: ban_mutate } = api.users.blockUser.useMutation({
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
await utils.users.getUsers.invalidate();
|
await utils.users.getUsers.invalidate();
|
||||||
await utils.users.getClientProfilo.invalidate();
|
await utils.users.getClientProfilo.invalidate({
|
||||||
|
userId,
|
||||||
|
});
|
||||||
if (data[0]?.isBlocked) {
|
if (data[0]?.isBlocked) {
|
||||||
toast("Utente bloccato con successo", { icon: "🔒" });
|
toast("Utente bloccato con successo", { icon: "🔒" });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -93,6 +97,13 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
||||||
<Fingerprint className="size-5" />
|
<Fingerprint className="size-5" />
|
||||||
Anagrafica
|
Anagrafica
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
className="flex items-center gap-1"
|
||||||
|
value="documenti"
|
||||||
|
>
|
||||||
|
<IdCard className="size-5" />
|
||||||
|
Documenti
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
className="flex items-center gap-1"
|
className="flex items-center gap-1"
|
||||||
value="password"
|
value="password"
|
||||||
|
|
@ -184,6 +195,9 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
||||||
<TabsContent className="mt-5 h-full space-y-6" value="anagrafica">
|
<TabsContent className="mt-5 h-full space-y-6" value="anagrafica">
|
||||||
<ProfileFormAnagrafica userData={userData} />
|
<ProfileFormAnagrafica userData={userData} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
<TabsContent className="mt-5 h-full space-y-6" value="documenti">
|
||||||
|
<DocumentiPersonali userData={userData} />
|
||||||
|
</TabsContent>
|
||||||
<TabsContent className="mt-5 space-y-6" value="password">
|
<TabsContent className="mt-5 space-y-6" value="password">
|
||||||
<OverridePasswordForm id={userData.id} />
|
<OverridePasswordForm id={userData.id} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -226,7 +240,7 @@ export const getServerSideProps = (async (context) => {
|
||||||
const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
||||||
if (helpers) {
|
if (helpers) {
|
||||||
await helpers.trpc.users.getClientProfilo.prefetch({
|
await helpers.trpc.users.getClientProfilo.prefetch({
|
||||||
id: parsed.data,
|
userId: parsed.data,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { GetServerSideProps } from "next";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { DocumentiPersonali } from "~/components/area-riservata/documenti_personali";
|
||||||
import { AreaRiservataLayout } from "~/components/Layout";
|
import { AreaRiservataLayout } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
import { Status500 } from "~/components/status-page";
|
import { Status500 } from "~/components/status-page";
|
||||||
|
|
@ -18,13 +18,14 @@ import { ProfileFormAnagrafica } from "~/forms/FormProfilo_Anagrafica";
|
||||||
import type { NextPageWithLayout } from "~/pages/_app";
|
import type { NextPageWithLayout } from "~/pages/_app";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import { useEnforcedSession } from "~/providers/SessionProvider";
|
import { useEnforcedSession } from "~/providers/SessionProvider";
|
||||||
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
const Dashboard: NextPageWithLayout = () => {
|
const Dashboard: NextPageWithLayout = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { user } = useEnforcedSession();
|
||||||
|
|
||||||
const { data: userData, isLoading } = api.users.getClientProfilo.useQuery({
|
const { data: userData, isLoading } = api.users.getClientProfilo.useQuery({
|
||||||
id: undefined,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
const session = useEnforcedSession();
|
const session = useEnforcedSession();
|
||||||
if (isLoading) return <LoadingPage />;
|
if (isLoading) return <LoadingPage />;
|
||||||
|
|
@ -58,6 +59,7 @@ const Dashboard: NextPageWithLayout = () => {
|
||||||
<TabsList className="mb-2">
|
<TabsList className="mb-2">
|
||||||
<TabsTrigger value="account">Account</TabsTrigger>
|
<TabsTrigger value="account">Account</TabsTrigger>
|
||||||
<TabsTrigger value="anagrafica">Anagrafica</TabsTrigger>
|
<TabsTrigger value="anagrafica">Anagrafica</TabsTrigger>
|
||||||
|
<TabsTrigger value="documenti">Documenti</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent className="flex flex-col gap-8" value="account">
|
<TabsContent className="flex flex-col gap-8" value="account">
|
||||||
<Card>
|
<Card>
|
||||||
|
|
@ -106,6 +108,9 @@ const Dashboard: NextPageWithLayout = () => {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
<TabsContent className="h-full space-y-6" value="documenti">
|
||||||
|
<DocumentiPersonali userData={userData} />
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -116,23 +121,6 @@ const Dashboard: NextPageWithLayout = () => {
|
||||||
};
|
};
|
||||||
export default Dashboard;
|
export default Dashboard;
|
||||||
|
|
||||||
export const getServerSideProps = (async (context) => {
|
|
||||||
const access_token = context.req.cookies.access_token;
|
|
||||||
|
|
||||||
const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
|
||||||
if (helpers) {
|
|
||||||
await helpers.trpc.users.getClientProfilo.prefetch({ id: undefined });
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
trpcState: helpers.trpc.dehydrate(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
props: {},
|
|
||||||
};
|
|
||||||
}) satisfies GetServerSideProps;
|
|
||||||
|
|
||||||
Dashboard.getLayout = function getLayout(page) {
|
Dashboard.getLayout = function getLayout(page) {
|
||||||
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
|
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,6 @@ export default interface ServizioTable {
|
||||||
|
|
||||||
isOkConsulenza: ColumnType<boolean, boolean | undefined, boolean>;
|
isOkConsulenza: ColumnType<boolean, boolean | undefined, boolean>;
|
||||||
|
|
||||||
doc_personale_fronte_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
|
||||||
|
|
||||||
doc_personale_retro_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
|
||||||
|
|
||||||
doc_motivazione_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
doc_motivazione_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
||||||
|
|
||||||
scadenza_motivazione_transitoria: ColumnType<Date | null, Date | string | null, Date | string | null>;
|
scadenza_motivazione_transitoria: ColumnType<Date | null, Date | string | null, Date | string | null>;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,13 @@ import type { ServizioServizioId } from './Servizio';
|
||||||
import type { AnnunciId } from './Annunci';
|
import type { AnnunciId } from './Annunci';
|
||||||
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
||||||
|
|
||||||
|
/** Identifier type for public.servizio_annunci */
|
||||||
|
export type ServizioAnnunciServizioAnnunciId = number & { __brand: 'public.servizio_annunci' };
|
||||||
|
|
||||||
/** Represents the table public.servizio_annunci */
|
/** Represents the table public.servizio_annunci */
|
||||||
export default interface ServizioAnnunciTable {
|
export default interface ServizioAnnunciTable {
|
||||||
|
servizio_annunci_id: ColumnType<ServizioAnnunciServizioAnnunciId, ServizioAnnunciServizioAnnunciId | undefined, ServizioAnnunciServizioAnnunciId>;
|
||||||
|
|
||||||
servizio_id: ColumnType<ServizioServizioId, ServizioServizioId, ServizioServizioId>;
|
servizio_id: ColumnType<ServizioServizioId, ServizioServizioId, ServizioServizioId>;
|
||||||
|
|
||||||
annunci_id: ColumnType<AnnunciId, AnnunciId, AnnunciId>;
|
annunci_id: ColumnType<AnnunciId, AnnunciId, AnnunciId>;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// This file is automatically generated by Kanel. Do not modify manually.
|
// This file is automatically generated by Kanel. Do not modify manually.
|
||||||
|
|
||||||
import type { UsersId } from './Users';
|
import type { UsersId } from './Users';
|
||||||
|
import type { UsersStorageUserStorageId } from './UsersStorage';
|
||||||
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
||||||
|
|
||||||
/** Identifier type for public.users_anagrafica */
|
/** Identifier type for public.users_anagrafica */
|
||||||
|
|
@ -48,6 +49,10 @@ export default interface UsersAnagraficaTable {
|
||||||
codice_destinatario: ColumnType<string | null, string | null, string | null>;
|
codice_destinatario: ColumnType<string | null, string | null, string | null>;
|
||||||
|
|
||||||
legale_rappresentante: ColumnType<string | null, string | null, string | null>;
|
legale_rappresentante: ColumnType<string | null, string | null, string | null>;
|
||||||
|
|
||||||
|
doc_personale_fronte_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
||||||
|
|
||||||
|
doc_personale_retro_ref: ColumnType<UsersStorageUserStorageId | null, UsersStorageUserStorageId | null, UsersStorageUserStorageId | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UsersAnagrafica = Selectable<UsersAnagraficaTable>;
|
export type UsersAnagrafica = Selectable<UsersAnagraficaTable>;
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,14 @@ export const usersRouter = createTRPCRouter({
|
||||||
return await editAccountHandler({ ctx, input });
|
return await editAccountHandler({ ctx, input });
|
||||||
}),
|
}),
|
||||||
editUserAnagrafica: protectedProcedure
|
editUserAnagrafica: protectedProcedure
|
||||||
.input(z.custom<UsersAnagraficaUpdate>())
|
.input(
|
||||||
|
z.object({
|
||||||
|
userId: zUserId,
|
||||||
|
data: z.custom<UsersAnagraficaUpdate>(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
return await editAnagraficaHandler({ input });
|
return await editAnagraficaHandler({ ...input });
|
||||||
}),
|
}),
|
||||||
getActiveUsersWithoutChat: adminProcedure.query(async () => {
|
getActiveUsersWithoutChat: adminProcedure.query(async () => {
|
||||||
return await getActiveUserWithoutChatHandler({ db });
|
return await getActiveUserWithoutChatHandler({ db });
|
||||||
|
|
@ -62,15 +67,13 @@ export const usersRouter = createTRPCRouter({
|
||||||
getClientProfilo: protectedProcedure
|
getClientProfilo: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
id: zUserId.optional(),
|
userId: zUserId,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ input }) => {
|
||||||
const userId = input.id || ctx.session.id;
|
|
||||||
|
|
||||||
return getUserWithAnagraficaHandler({
|
return getUserWithAnagraficaHandler({
|
||||||
db,
|
db,
|
||||||
userId: userId,
|
userId: input.userId,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
getUser: protectedProcedure
|
getUser: protectedProcedure
|
||||||
|
|
|
||||||
|
|
@ -613,6 +613,11 @@ export const getAllServizioAnnunci = async (userId: UsersId) => {
|
||||||
return await db
|
return await db
|
||||||
.selectFrom("servizio")
|
.selectFrom("servizio")
|
||||||
.where("user_id", "=", userId)
|
.where("user_id", "=", userId)
|
||||||
|
.leftJoin(
|
||||||
|
"users_anagrafica",
|
||||||
|
"servizio.user_id",
|
||||||
|
"users_anagrafica.userid",
|
||||||
|
)
|
||||||
.select((eb) => [
|
.select((eb) => [
|
||||||
"servizio.servizio_id",
|
"servizio.servizio_id",
|
||||||
"servizio.created_at",
|
"servizio.created_at",
|
||||||
|
|
@ -632,12 +637,12 @@ export const getAllServizioAnnunci = async (userId: UsersId) => {
|
||||||
.selectFrom("users_storage")
|
.selectFrom("users_storage")
|
||||||
.select([
|
.select([
|
||||||
"users_storage.storageId as storageId",
|
"users_storage.storageId as storageId",
|
||||||
"servizio.doc_personale_fronte_ref as ref",
|
"users_anagrafica.doc_personale_fronte_ref as ref",
|
||||||
])
|
])
|
||||||
.whereRef(
|
.whereRef(
|
||||||
"users_storage.user_storage_id",
|
"users_storage.user_storage_id",
|
||||||
"=",
|
"=",
|
||||||
"servizio.doc_personale_fronte_ref",
|
"users_anagrafica.doc_personale_fronte_ref",
|
||||||
),
|
),
|
||||||
).as("doc_personale_fronte"),
|
).as("doc_personale_fronte"),
|
||||||
jsonObjectFrom(
|
jsonObjectFrom(
|
||||||
|
|
@ -645,12 +650,12 @@ export const getAllServizioAnnunci = async (userId: UsersId) => {
|
||||||
.selectFrom("users_storage")
|
.selectFrom("users_storage")
|
||||||
.select([
|
.select([
|
||||||
"users_storage.storageId as storageId",
|
"users_storage.storageId as storageId",
|
||||||
"servizio.doc_personale_retro_ref as ref",
|
"users_anagrafica.doc_personale_retro_ref as ref",
|
||||||
])
|
])
|
||||||
.whereRef(
|
.whereRef(
|
||||||
"users_storage.user_storage_id",
|
"users_storage.user_storage_id",
|
||||||
"=",
|
"=",
|
||||||
"servizio.doc_personale_retro_ref",
|
"users_anagrafica.doc_personale_retro_ref",
|
||||||
),
|
),
|
||||||
).as("doc_personale_retro"),
|
).as("doc_personale_retro"),
|
||||||
jsonObjectFrom(
|
jsonObjectFrom(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import type { Users, UsersId, UsersUpdate } from "~/schemas/public/Users";
|
||||||
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
|
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
|
||||||
import type { Context } from "~/server/api/trpc";
|
import type { Context } from "~/server/api/trpc";
|
||||||
import { db, type Querier } from "~/server/db";
|
import { db, type Querier } from "~/server/db";
|
||||||
import { upsertAnagrafica } from "~/server/services/user.service";
|
|
||||||
import { checkCtxAdmin, checkCtxSession } from "~/server/utils/ctxChecker";
|
import { checkCtxAdmin, checkCtxSession } from "~/server/utils/ctxChecker";
|
||||||
|
|
||||||
type EditAccountInput = {
|
type EditAccountInput = {
|
||||||
|
|
@ -58,14 +57,66 @@ export const editAccountHandler = async ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export const editAnagraficaHandler = async ({
|
export const editAnagraficaHandler = async ({
|
||||||
input,
|
userId,
|
||||||
|
data,
|
||||||
}: {
|
}: {
|
||||||
input: UsersAnagraficaUpdate;
|
userId: UsersId;
|
||||||
|
data: UsersAnagraficaUpdate;
|
||||||
}) => {
|
}) => {
|
||||||
return await upsertAnagrafica({
|
try {
|
||||||
data: input,
|
// biome-ignore lint/correctness/noUnusedVariables: <ignore>
|
||||||
db,
|
const { idanagrafica, userid, ...rest } = data;
|
||||||
|
if (!userId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Errore interno, utente non trovato",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (anagraficaId) {
|
||||||
|
// return await db
|
||||||
|
// .updateTable("users_anagrafica")
|
||||||
|
// .set({
|
||||||
|
// ...rest,
|
||||||
|
// })
|
||||||
|
// .where("userid", "=", userId)
|
||||||
|
// .returningAll()
|
||||||
|
// .executeTakeFirst();
|
||||||
|
// }
|
||||||
|
// const searched = await db
|
||||||
|
// .selectFrom("users_anagrafica")
|
||||||
|
// .select("idanagrafica")
|
||||||
|
// .where("userid", "=", userId)
|
||||||
|
// .executeTakeFirst();
|
||||||
|
// if (searched) {
|
||||||
|
// return await db
|
||||||
|
// .updateTable("users_anagrafica")
|
||||||
|
// .set({
|
||||||
|
// ...rest,
|
||||||
|
// })
|
||||||
|
// .where("userid", "=", userId)
|
||||||
|
// .returningAll()
|
||||||
|
// .executeTakeFirst();
|
||||||
|
// }
|
||||||
|
return await db
|
||||||
|
.insertInto("users_anagrafica")
|
||||||
|
.values({
|
||||||
|
...rest,
|
||||||
|
userid: userId,
|
||||||
|
})
|
||||||
|
.onConflict((oc) =>
|
||||||
|
oc.column("userid").doUpdateSet({
|
||||||
|
...rest,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirst();
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Errore updateAnagrafica: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteUserHandler = async ({
|
export const deleteUserHandler = async ({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { jsonObjectFrom } from "kysely/helpers/postgres";
|
||||||
import type { NewUsers, UsersId } from "~/schemas/public/Users";
|
import type { NewUsers, UsersId } from "~/schemas/public/Users";
|
||||||
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
|
|
||||||
import type { Session } from "~/server/api/trpc";
|
import type { Session } from "~/server/api/trpc";
|
||||||
import { db, type Querier } from "~/server/db";
|
import { db, type Querier } from "~/server/db";
|
||||||
|
|
||||||
|
|
@ -125,6 +125,34 @@ export const getUserWithAnagraficaHandler = async ({
|
||||||
.where("id", "=", userId)
|
.where("id", "=", userId)
|
||||||
.leftJoin("users_anagrafica", "users_anagrafica.userid", "users.id")
|
.leftJoin("users_anagrafica", "users_anagrafica.userid", "users.id")
|
||||||
.selectAll("users_anagrafica")
|
.selectAll("users_anagrafica")
|
||||||
|
.select((eb) => [
|
||||||
|
jsonObjectFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom("users_storage")
|
||||||
|
.select([
|
||||||
|
"users_storage.storageId as storageId",
|
||||||
|
"users_anagrafica.doc_personale_fronte_ref as ref",
|
||||||
|
])
|
||||||
|
.whereRef(
|
||||||
|
"users_storage.user_storage_id",
|
||||||
|
"=",
|
||||||
|
"users_anagrafica.doc_personale_fronte_ref",
|
||||||
|
),
|
||||||
|
).as("doc_personale_fronte"),
|
||||||
|
jsonObjectFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom("users_storage")
|
||||||
|
.select([
|
||||||
|
"users_storage.storageId as storageId",
|
||||||
|
"users_anagrafica.doc_personale_retro_ref as ref",
|
||||||
|
])
|
||||||
|
.whereRef(
|
||||||
|
"users_storage.user_storage_id",
|
||||||
|
"=",
|
||||||
|
"users_anagrafica.doc_personale_retro_ref",
|
||||||
|
),
|
||||||
|
).as("doc_personale_retro"),
|
||||||
|
])
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
|
|
@ -133,45 +161,3 @@ export const getUserWithAnagraficaHandler = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const upsertAnagrafica = async ({
|
|
||||||
db,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
db: Querier;
|
|
||||||
data: UsersAnagraficaUpdate;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
const { idanagrafica, userid, ...rest } = data;
|
|
||||||
if (!userid) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Errore interno, utente non trovato",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (idanagrafica) {
|
|
||||||
return await db
|
|
||||||
.updateTable("users_anagrafica")
|
|
||||||
.set({
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
.where("userid", "=", userid)
|
|
||||||
.returningAll()
|
|
||||||
.executeTakeFirst();
|
|
||||||
}
|
|
||||||
return await db
|
|
||||||
.insertInto("users_anagrafica")
|
|
||||||
.values({
|
|
||||||
userid,
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
.onConflict((oc) => oc.column("userid").doNothing())
|
|
||||||
.returningAll()
|
|
||||||
.executeTakeFirst();
|
|
||||||
} catch (e) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: `Errore updateAnagrafica: ${(e as Error).message}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue