feat: implement Potenziali and PotenzialiGroups management with CRUD operations and UI enhancements
This commit is contained in:
parent
3143870cfd
commit
cfcddea9c8
11 changed files with 673 additions and 142 deletions
37
apps/db/migrations/28_potenziali.up.sql
Normal file
37
apps/db/migrations/28_potenziali.up.sql
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
-- Potenziali table
|
||||||
|
CREATE TABLE IF NOT EXISTS public.potenziali (
|
||||||
|
id UUID DEFAULT gen_random_uuid () NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
descrizione TEXT,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL,
|
||||||
|
userid UUID,
|
||||||
|
potenziali_group_id UUID NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Primary Key
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'potenziali_pkey'
|
||||||
|
AND conrelid = 'public.potenziali'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.potenziali
|
||||||
|
ADD CONSTRAINT potenziali_pkey PRIMARY KEY (id);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Foreign Key to Users
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'potenziali_assigned_user'
|
||||||
|
AND conrelid = 'public.potenziali'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.potenziali
|
||||||
|
ADD CONSTRAINT potenziali_assigned_user
|
||||||
|
FOREIGN KEY (userid) REFERENCES public.users (id)
|
||||||
|
ON UPDATE CASCADE ON DELETE SET NULL;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
45
apps/db/migrations/29_potenziali_groups.up.sql
Normal file
45
apps/db/migrations/29_potenziali_groups.up.sql
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
-- Potenziali Groups Table
|
||||||
|
CREATE TABLE IF NOT EXISTS public.potenziali_groups (
|
||||||
|
id UUID DEFAULT gen_random_uuid () NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
color TEXT DEFAULT 'blue' NOT NULL
|
||||||
|
);
|
||||||
|
-- Primary Key
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'potenziali_groups_pkey'
|
||||||
|
AND conrelid = 'public.potenziali_groups'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.potenziali_groups
|
||||||
|
ADD CONSTRAINT potenziali_groups_pkey PRIMARY KEY (id);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Foreign Key to Potenziali Groups
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'potenziali_group_fk'
|
||||||
|
AND conrelid = 'public.potenziali'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.potenziali
|
||||||
|
ADD CONSTRAINT potenziali_group_fk
|
||||||
|
FOREIGN KEY (potenziali_group_id) REFERENCES public.potenziali_groups (id)
|
||||||
|
ON UPDATE CASCADE ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Create Index on Potenziali Group ID
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_indexes
|
||||||
|
WHERE indexname = 'idx_potenziali_group_id'
|
||||||
|
) THEN
|
||||||
|
CREATE INDEX idx_potenziali_group_id
|
||||||
|
ON public.potenziali (potenziali_group_id);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { Plus } from "lucide-react";
|
||||||
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
KanbanBoard,
|
KanbanBoard,
|
||||||
KanbanCard,
|
KanbanCard,
|
||||||
|
|
@ -8,12 +10,9 @@ import {
|
||||||
KanbanHeader,
|
KanbanHeader,
|
||||||
KanbanProvider,
|
KanbanProvider,
|
||||||
} from "~/components/ui/kanban";
|
} from "~/components/ui/kanban";
|
||||||
import {
|
import { usePotenziali } from "~/providers/PotenzialiProvider";
|
||||||
type ElementoPotenziali,
|
import type { PotenzialiGroupsId } from "~/schemas/public/PotenzialiGroups";
|
||||||
PotenzialiProvider,
|
import type { PotenzialeData } from "~/server/services/potenziali.service";
|
||||||
usePotenziali,
|
|
||||||
} from "~/providers/PotenzialiProvider";
|
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
|
||||||
import {
|
import {
|
||||||
Credenza,
|
Credenza,
|
||||||
CredenzaBody,
|
CredenzaBody,
|
||||||
|
|
@ -21,81 +20,27 @@ import {
|
||||||
CredenzaDescription,
|
CredenzaDescription,
|
||||||
CredenzaHeader,
|
CredenzaHeader,
|
||||||
CredenzaTitle,
|
CredenzaTitle,
|
||||||
|
CredenzaTrigger,
|
||||||
} from "../custom_ui/credenza";
|
} from "../custom_ui/credenza";
|
||||||
import {
|
import Input from "../custom_ui/input";
|
||||||
ContextMenu,
|
import { Textarea } from "../custom_ui/textarea";
|
||||||
ContextMenuContent,
|
import { Button } from "../ui/button";
|
||||||
ContextMenuItem,
|
import { Label } from "../ui/label";
|
||||||
ContextMenuTrigger,
|
|
||||||
} from "../ui/context-menu";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from "../ui/dialog";
|
|
||||||
import { UserAvatar } from "../user_avatar";
|
import { UserAvatar } from "../user_avatar";
|
||||||
|
|
||||||
const columns = [
|
export const Potenziali = () => {
|
||||||
{ id: "todo", name: "To Do", color: "red" },
|
const { data, columns, setData, updateCall, setEditingId, editingId } =
|
||||||
{ id: "inprogress", name: "In Progress", color: "blue" },
|
usePotenziali();
|
||||||
{ id: "done", name: "Done", color: "green" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const SampleData: ElementoPotenziali[] = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Mario Rossi",
|
|
||||||
column: "todo",
|
|
||||||
descrizione:
|
|
||||||
"Single, cerca appartamento in centro da subito, budget 800 euro",
|
|
||||||
createdAt: new Date(),
|
|
||||||
owner: {
|
|
||||||
userId: "019b9a25-eff1-420a-9017-b0add205d7b6" as UsersId,
|
|
||||||
username: "Marco Pedone",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Luigi Bianchi",
|
|
||||||
column: "inprogress",
|
|
||||||
descrizione: "Coppia, cerca bilocale in periferia, budget 600 euro",
|
|
||||||
createdAt: new Date(),
|
|
||||||
owner: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
name: "Giovanni Verdi",
|
|
||||||
column: "done",
|
|
||||||
descrizione:
|
|
||||||
"Famiglia con 2 figli, cerca trilocale vicino a scuole, budget 1000 euro",
|
|
||||||
createdAt: new Date(),
|
|
||||||
owner: {
|
|
||||||
userId: "019b9a25-eff1-420a-9017-b0add205d7b6" as UsersId,
|
|
||||||
username: "Marco Pedone",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Potenziali = () => {
|
|
||||||
return (
|
|
||||||
<PotenzialiProvider data={SampleData}>
|
|
||||||
<Kaban />
|
|
||||||
</PotenzialiProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Kaban = () => {
|
|
||||||
const { data, updateData, setEditingId, editingId } = usePotenziali();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<KanbanProvider
|
<KanbanProvider
|
||||||
className="w-max"
|
className="w-max"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
onDataChange={updateData}
|
onDataChange={setData}
|
||||||
|
onDragEnd={() => {
|
||||||
|
updateCall();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{(column) => (
|
{(column) => (
|
||||||
<KanbanBoard
|
<KanbanBoard
|
||||||
|
|
@ -110,10 +55,12 @@ const Kaban = () => {
|
||||||
style={{ backgroundColor: column.color }}
|
style={{ backgroundColor: column.color }}
|
||||||
/>
|
/>
|
||||||
<span>{column.name}</span>
|
<span>{column.name}</span>
|
||||||
|
|
||||||
|
<NewCard columnId={column.id} />
|
||||||
</div>
|
</div>
|
||||||
</KanbanHeader>
|
</KanbanHeader>
|
||||||
<KanbanCards className="w-68" id={column.id}>
|
<KanbanCards className="w-68" id={column.id}>
|
||||||
{(data: ElementoPotenziali) => (
|
{(data: PotenzialeData) => (
|
||||||
<KanbanCard
|
<KanbanCard
|
||||||
cardOnClick={() => {
|
cardOnClick={() => {
|
||||||
setEditingId(data.id);
|
setEditingId(data.id);
|
||||||
|
|
@ -132,16 +79,16 @@ const Kaban = () => {
|
||||||
{data.descrizione}
|
{data.descrizione}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{data.owner && (
|
{data.userid && (
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
className="size-6 shrink-0 text-sm"
|
className="size-6 shrink-0 text-sm"
|
||||||
userId={data.owner.userId}
|
userId={data.userid}
|
||||||
username={data.owner.username}
|
username={data.username || "Utente"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-2 text-muted-foreground text-xs">
|
<p className="mt-2 text-muted-foreground text-xs">
|
||||||
{format(data.createdAt, "dd/MM/yyyy HH:mm")}
|
{format(data.created_at, "dd/MM/yyyy HH:mm")}
|
||||||
</p>
|
</p>
|
||||||
</KanbanCard>
|
</KanbanCard>
|
||||||
)}
|
)}
|
||||||
|
|
@ -163,6 +110,101 @@ const Kaban = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NewColumn = () => {
|
||||||
|
const [newColumnName, setNewColumnName] = useState("");
|
||||||
|
const { addColumn } = usePotenziali();
|
||||||
|
return (
|
||||||
|
<Credenza>
|
||||||
|
<CredenzaTrigger asChild>
|
||||||
|
<Button>Nuova colonna</Button>
|
||||||
|
</CredenzaTrigger>
|
||||||
|
<CredenzaContent className="max-h-[90vh]">
|
||||||
|
<CredenzaHeader>
|
||||||
|
<CredenzaTitle className="text-center font-semibold text-2xl">
|
||||||
|
Nuova colonna
|
||||||
|
</CredenzaTitle>
|
||||||
|
<CredenzaDescription className="sr-only">desc</CredenzaDescription>
|
||||||
|
</CredenzaHeader>
|
||||||
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto p-2">
|
||||||
|
<Label htmlFor="titolo">Titolo</Label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
defaultValue={newColumnName}
|
||||||
|
id="titolo"
|
||||||
|
onChange={(e) => setNewColumnName(e.target.value)}
|
||||||
|
type="string"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={!newColumnName.length}
|
||||||
|
onClick={() => {
|
||||||
|
addColumn(newColumnName);
|
||||||
|
setNewColumnName("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Aggiungi
|
||||||
|
</Button>
|
||||||
|
</CredenzaBody>
|
||||||
|
</CredenzaContent>
|
||||||
|
</Credenza>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => {
|
||||||
|
const [newItemName, setNewItemName] = useState("");
|
||||||
|
const [newItemDescrizione, setNewItemDescrizione] = useState("");
|
||||||
|
const { addItem } = usePotenziali();
|
||||||
|
return (
|
||||||
|
<Credenza>
|
||||||
|
<CredenzaTrigger asChild>
|
||||||
|
<Button className="ml-auto" size="sm">
|
||||||
|
<Plus className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</CredenzaTrigger>
|
||||||
|
<CredenzaContent className="max-h-[90vh]">
|
||||||
|
<CredenzaHeader>
|
||||||
|
<CredenzaTitle className="text-center font-semibold text-2xl">
|
||||||
|
Nuova colonna
|
||||||
|
</CredenzaTitle>
|
||||||
|
<CredenzaDescription className="sr-only">desc</CredenzaDescription>
|
||||||
|
</CredenzaHeader>
|
||||||
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto p-2">
|
||||||
|
<Label htmlFor="titolo">Titolo</Label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
defaultValue={newItemName}
|
||||||
|
id="titolo"
|
||||||
|
onChange={(e) => setNewItemName(e.target.value)}
|
||||||
|
type="string"
|
||||||
|
/>
|
||||||
|
<Label htmlFor="descrizione">Descrizione</Label>
|
||||||
|
<Textarea
|
||||||
|
className="min-h-64"
|
||||||
|
defaultValue={newItemDescrizione}
|
||||||
|
id="descrizione"
|
||||||
|
onChange={(e) => setNewItemDescrizione(e.target.value)}
|
||||||
|
placeholder=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={!newItemName.length}
|
||||||
|
onClick={() => {
|
||||||
|
addItem({
|
||||||
|
name: newItemName,
|
||||||
|
descrizione: newItemDescrizione,
|
||||||
|
potenziali_group_id: columnId,
|
||||||
|
});
|
||||||
|
setNewItemName("");
|
||||||
|
setNewItemDescrizione("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Aggiungi
|
||||||
|
</Button>
|
||||||
|
</CredenzaBody>
|
||||||
|
</CredenzaContent>
|
||||||
|
</Credenza>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const EditingContent = () => {
|
const EditingContent = () => {
|
||||||
const { getEditingItem } = usePotenziali();
|
const { getEditingItem } = usePotenziali();
|
||||||
const item = getEditingItem();
|
const item = getEditingItem();
|
||||||
|
|
@ -177,11 +219,9 @@ const EditingContent = () => {
|
||||||
</CredenzaHeader>
|
</CredenzaHeader>
|
||||||
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto pb-5">
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto pb-5">
|
||||||
<p>Descrizione: {item.descrizione}</p>
|
<p>Descrizione: {item.descrizione}</p>
|
||||||
<p>Creato il: {format(item.createdAt, "dd/MM/yyyy HH:mm")}</p>
|
<p>Creato il: {format(item.created_at, "dd/MM/yyyy HH:mm")}</p>
|
||||||
<p>Utente Assegnato: {item.owner ? item.owner.username : "Nessuno"}</p>
|
<p>Utente Assegnato: {item.userid ? item.username : "Nessuno"}</p>
|
||||||
</CredenzaBody>
|
</CredenzaBody>
|
||||||
</CredenzaContent>
|
</CredenzaContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Potenziali;
|
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,43 @@
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { NewColumn } from "~/components/area-riservata/potenziali";
|
||||||
import { AreaRiservataLayout } from "~/components/Layout";
|
import { AreaRiservataLayout } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
import type { NextPageWithLayout } from "~/pages/_app";
|
import type { NextPageWithLayout } from "~/pages/_app";
|
||||||
|
import { PotenzialiProvider } from "~/providers/PotenzialiProvider";
|
||||||
|
|
||||||
const Potenziali = dynamic(
|
const PotenzialiBoard = dynamic(
|
||||||
() => import("~/components/area-riservata/potenziali"),
|
() =>
|
||||||
|
import("~/components/area-riservata/potenziali").then(
|
||||||
|
(mod) => mod.Potenziali,
|
||||||
|
),
|
||||||
{
|
{
|
||||||
loading: () => <LoadingPage />,
|
|
||||||
ssr: false,
|
ssr: false,
|
||||||
|
loading: () => <LoadingPage />,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const AdminPotenziali: NextPageWithLayout = () => {
|
const AdminPotenziali: NextPageWithLayout = () => {
|
||||||
return (
|
return (
|
||||||
|
<PotenzialiProvider>
|
||||||
<div className="mx-1 flex-1 overflow-auto">
|
<div className="mx-1 flex-1 overflow-auto">
|
||||||
<div className="mx-auto pt-4">
|
<div className="mx-auto pt-4">
|
||||||
<div className="mx-3 items-start justify-between md:flex">
|
<div className="mx-3 flex-wrap items-start justify-between pb-5 sm:flex">
|
||||||
<div className="flex max-w-lg items-center gap-3">
|
<div className="flex max-w-lg items-center gap-3">
|
||||||
<h3 className="font-bold text-accent-foreground text-xl sm:text-2xl">
|
<h3 className="font-bold text-accent-foreground text-xl sm:text-2xl">
|
||||||
Potenziali
|
Potenziali
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<NewColumn />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4">
|
|
||||||
<Potenziali />
|
<PotenzialiBoard />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</PotenzialiProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default AdminPotenziali;
|
export default AdminPotenziali;
|
||||||
|
|
||||||
// export const getServerSideProps = (async (context) => {
|
|
||||||
// const access_token = context.req.cookies.access_token;
|
|
||||||
|
|
||||||
// const helpers = await TrpcAuthedFetchingIstance({ access_token });
|
|
||||||
// if (helpers) {
|
|
||||||
// await helpers.trpc.etichette.getAllEtichette.prefetch();
|
|
||||||
// return {
|
|
||||||
// props: {
|
|
||||||
// trpcState: helpers.trpc.dehydrate(),
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// return {
|
|
||||||
// props: {},
|
|
||||||
// };
|
|
||||||
// }) satisfies GetServerSideProps;
|
|
||||||
|
|
||||||
AdminPotenziali.getLayout = function getLayout(page) {
|
AdminPotenziali.getLayout = function getLayout(page) {
|
||||||
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
|
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,109 @@
|
||||||
import { createContext, useContext, useState } from "react";
|
import { createContext, useContext, useState } from "react";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import toast from "react-hot-toast";
|
||||||
|
import type { NewPotenziali, PotenzialiId } from "~/schemas/public/Potenziali";
|
||||||
|
import type { PotenzialiGroups } from "~/schemas/public/PotenzialiGroups";
|
||||||
|
import type { PotenzialeData } from "~/server/services/potenziali.service";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
export type ElementoPotenziali = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
column: string;
|
|
||||||
descrizione: string;
|
|
||||||
createdAt: Date;
|
|
||||||
owner: { userId: UsersId; username: string } | null;
|
|
||||||
};
|
|
||||||
type PotenzialiContextType = {
|
type PotenzialiContextType = {
|
||||||
data: ElementoPotenziali[];
|
data: PotenzialeData[];
|
||||||
editingId: string | null;
|
columns: PotenzialiGroups[];
|
||||||
updateData: (data: ElementoPotenziali[]) => void;
|
editingId: PotenzialiId | null;
|
||||||
setEditingId: (id: string | null) => void;
|
setData: (data: PotenzialeData[]) => void;
|
||||||
getEditingItem: () => ElementoPotenziali | null;
|
updateCall: () => void;
|
||||||
addItem: (item: ElementoPotenziali) => void;
|
setEditingId: (id: PotenzialiId | null) => void;
|
||||||
removeItem: (id: string) => void;
|
getEditingItem: () => PotenzialeData | null;
|
||||||
|
addItem: (item: NewPotenziali) => void;
|
||||||
|
removeItem: (id: PotenzialiId) => void;
|
||||||
|
addColumn: (name: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PotenzialiContext = createContext<PotenzialiContextType | null>(null);
|
const PotenzialiContext = createContext<PotenzialiContextType | null>(null);
|
||||||
|
|
||||||
type PotenzialiProviderProps = {
|
type PotenzialiProviderProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
data: ElementoPotenziali[];
|
|
||||||
};
|
};
|
||||||
export const PotenzialiProvider = ({
|
export const PotenzialiProvider = ({ children }: PotenzialiProviderProps) => {
|
||||||
children,
|
const { data } = api.potenziali.getPotenziali.useQuery();
|
||||||
data: initial,
|
|
||||||
}: PotenzialiProviderProps) => {
|
const { data: columns } = api.potenziali.getPotenzialiGroups.useQuery();
|
||||||
const [data, setData] = useState<ElementoPotenziali[]>(initial);
|
|
||||||
const [editingId, setEditingId] = useState<string | null>(null);
|
const [editingId, setEditingId] = useState<PotenzialiId | null>(null);
|
||||||
|
|
||||||
const getEditingItem = () => {
|
const getEditingItem = () => {
|
||||||
return data.find((item) => item.id === editingId) || null;
|
return data?.find((item) => item.id === editingId) || null;
|
||||||
};
|
};
|
||||||
const addItem = (item: ElementoPotenziali) => {
|
const utils = api.useUtils();
|
||||||
setData((prev) => [...prev, item]);
|
const { mutate: addItemMutation } = api.potenziali.addPotenziale.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Elemento aggiunto con successo");
|
||||||
|
await utils.potenziali.getPotenziali.invalidate();
|
||||||
|
await utils.potenziali.getPotenzialiGroups.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const addItem = (item: NewPotenziali) => {
|
||||||
|
addItemMutation({
|
||||||
|
data: item,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
const removeItem = (id: string) => {
|
const { mutate: removeItemMutation } =
|
||||||
setData((prev) => prev.filter((item) => item.id !== id));
|
api.potenziali.deletePotenziale.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Elemento rimosso con successo");
|
||||||
|
await utils.potenziali.getPotenziali.invalidate();
|
||||||
|
await utils.potenziali.getPotenzialiGroups.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const removeItem = (id: PotenzialiId) => {
|
||||||
|
removeItemMutation({
|
||||||
|
potenzialiId: id,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
const setData = (newData: PotenzialeData[]) => {
|
||||||
|
utils.potenziali.getPotenziali.setData(undefined, newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutate: updatePotenzialiMutation } =
|
||||||
|
api.potenziali.updatePotenziali.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
await utils.potenziali.getPotenziali.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateCall = () => {
|
||||||
|
updatePotenzialiMutation({
|
||||||
|
data: utils.potenziali.getPotenziali.getData() || [],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutate: addColumnMutation } =
|
||||||
|
api.potenziali.addPotenzialeGroup.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Colonna aggiunta con successo");
|
||||||
|
await utils.potenziali.getPotenziali.invalidate();
|
||||||
|
await utils.potenziali.getPotenzialiGroups.invalidate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const addColumn = (name: string) => {
|
||||||
|
addColumnMutation({
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PotenzialiContext.Provider
|
<PotenzialiContext.Provider
|
||||||
value={{
|
value={{
|
||||||
data,
|
data: data || [],
|
||||||
|
columns: columns || [],
|
||||||
editingId,
|
editingId,
|
||||||
updateData: setData,
|
setData,
|
||||||
|
updateCall,
|
||||||
setEditingId,
|
setEditingId,
|
||||||
getEditingItem,
|
getEditingItem,
|
||||||
addItem,
|
addItem,
|
||||||
removeItem,
|
removeItem,
|
||||||
|
addColumn,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
30
apps/infoalloggi/src/schemas/public/Potenziali.ts
Normal file
30
apps/infoalloggi/src/schemas/public/Potenziali.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// @generated
|
||||||
|
// This file is automatically generated by Kanel. Do not modify manually.
|
||||||
|
|
||||||
|
import type { UsersId } from './Users';
|
||||||
|
import type { PotenzialiGroupsId } from './PotenzialiGroups';
|
||||||
|
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
||||||
|
|
||||||
|
/** Identifier type for public.potenziali */
|
||||||
|
export type PotenzialiId = string & { __brand: 'public.potenziali' };
|
||||||
|
|
||||||
|
/** Represents the table public.potenziali */
|
||||||
|
export default interface PotenzialiTable {
|
||||||
|
id: ColumnType<PotenzialiId, PotenzialiId | undefined, PotenzialiId>;
|
||||||
|
|
||||||
|
name: ColumnType<string, string, string>;
|
||||||
|
|
||||||
|
descrizione: ColumnType<string | null, string | null, string | null>;
|
||||||
|
|
||||||
|
created_at: ColumnType<Date, Date | string | undefined, Date | string>;
|
||||||
|
|
||||||
|
userid: ColumnType<UsersId | null, UsersId | null, UsersId | null>;
|
||||||
|
|
||||||
|
potenziali_group_id: ColumnType<PotenzialiGroupsId, PotenzialiGroupsId, PotenzialiGroupsId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Potenziali = Selectable<PotenzialiTable>;
|
||||||
|
|
||||||
|
export type NewPotenziali = Insertable<PotenzialiTable>;
|
||||||
|
|
||||||
|
export type PotenzialiUpdate = Updateable<PotenzialiTable>;
|
||||||
22
apps/infoalloggi/src/schemas/public/PotenzialiGroups.ts
Normal file
22
apps/infoalloggi/src/schemas/public/PotenzialiGroups.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// @generated
|
||||||
|
// This file is automatically generated by Kanel. Do not modify manually.
|
||||||
|
|
||||||
|
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
||||||
|
|
||||||
|
/** Identifier type for public.potenziali_groups */
|
||||||
|
export type PotenzialiGroupsId = string & { __brand: 'public.potenziali_groups' };
|
||||||
|
|
||||||
|
/** Represents the table public.potenziali_groups */
|
||||||
|
export default interface PotenzialiGroupsTable {
|
||||||
|
id: ColumnType<PotenzialiGroupsId, PotenzialiGroupsId | undefined, PotenzialiGroupsId>;
|
||||||
|
|
||||||
|
name: ColumnType<string, string, string>;
|
||||||
|
|
||||||
|
color: ColumnType<string, string | undefined, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PotenzialiGroups = Selectable<PotenzialiGroupsTable>;
|
||||||
|
|
||||||
|
export type NewPotenzialiGroups = Insertable<PotenzialiGroupsTable>;
|
||||||
|
|
||||||
|
export type PotenzialiGroupsUpdate = Updateable<PotenzialiGroupsTable>;
|
||||||
|
|
@ -11,6 +11,7 @@ import type { default as UsersStorageTable } from './UsersStorage';
|
||||||
import type { default as ServizioAnnunciTable } from './ServizioAnnunci';
|
import type { default as ServizioAnnunciTable } from './ServizioAnnunci';
|
||||||
import type { default as EmailsTable } from './Emails';
|
import type { default as EmailsTable } from './Emails';
|
||||||
import type { default as PaymentsTable } from './Payments';
|
import type { default as PaymentsTable } from './Payments';
|
||||||
|
import type { default as PotenzialiGroupsTable } from './PotenzialiGroups';
|
||||||
import type { default as ComuniTable } from './Comuni';
|
import type { default as ComuniTable } from './Comuni';
|
||||||
import type { default as EventQueueTable } from './EventQueue';
|
import type { default as EventQueueTable } from './EventQueue';
|
||||||
import type { default as OrdiniTable } from './Ordini';
|
import type { default as OrdiniTable } from './Ordini';
|
||||||
|
|
@ -21,6 +22,7 @@ import type { default as EtichetteTable } from './Etichette';
|
||||||
import type { default as MessagesTable } from './Messages';
|
import type { default as MessagesTable } from './Messages';
|
||||||
import type { default as ProvincieTable } from './Provincie';
|
import type { default as ProvincieTable } from './Provincie';
|
||||||
import type { default as UsersAnagraficaTable } from './UsersAnagrafica';
|
import type { default as UsersAnagraficaTable } from './UsersAnagrafica';
|
||||||
|
import type { default as PotenzialiTable } from './Potenziali';
|
||||||
import type { default as VideosRefsTable } from './VideosRefs';
|
import type { default as VideosRefsTable } from './VideosRefs';
|
||||||
import type { default as BanlistTable } from './Banlist';
|
import type { default as BanlistTable } from './Banlist';
|
||||||
import type { default as NazioniTable } from './Nazioni';
|
import type { default as NazioniTable } from './Nazioni';
|
||||||
|
|
@ -49,6 +51,8 @@ export default interface PublicSchema {
|
||||||
|
|
||||||
payments: PaymentsTable;
|
payments: PaymentsTable;
|
||||||
|
|
||||||
|
potenziali_groups: PotenzialiGroupsTable;
|
||||||
|
|
||||||
comuni: ComuniTable;
|
comuni: ComuniTable;
|
||||||
|
|
||||||
event_queue: EventQueueTable;
|
event_queue: EventQueueTable;
|
||||||
|
|
@ -69,6 +73,8 @@ export default interface PublicSchema {
|
||||||
|
|
||||||
users_anagrafica: UsersAnagraficaTable;
|
users_anagrafica: UsersAnagraficaTable;
|
||||||
|
|
||||||
|
potenziali: PotenzialiTable;
|
||||||
|
|
||||||
videos_refs: VideosRefsTable;
|
videos_refs: VideosRefsTable;
|
||||||
|
|
||||||
banlist: BanlistTable;
|
banlist: BanlistTable;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { bannersRouter } from "./routers/banners";
|
||||||
import { catastoRouter } from "./routers/catasto";
|
import { catastoRouter } from "./routers/catasto";
|
||||||
import { etichetteRouter } from "./routers/etichette";
|
import { etichetteRouter } from "./routers/etichette";
|
||||||
import { flagsRouter } from "./routers/flags";
|
import { flagsRouter } from "./routers/flags";
|
||||||
|
import { potenzialiRouter } from "./routers/potenziali";
|
||||||
import { revalidationRouter } from "./routers/revalidation";
|
import { revalidationRouter } from "./routers/revalidation";
|
||||||
import { stringsRouter } from "./routers/strings";
|
import { stringsRouter } from "./routers/strings";
|
||||||
import { syncRouter } from "./routers/sync";
|
import { syncRouter } from "./routers/sync";
|
||||||
|
|
@ -55,5 +56,6 @@ export const appRouter = createTRPCRouter({
|
||||||
etichette: etichetteRouter,
|
etichette: etichetteRouter,
|
||||||
strings: stringsRouter,
|
strings: stringsRouter,
|
||||||
revalidation: revalidationRouter,
|
revalidation: revalidationRouter,
|
||||||
|
potenziali: potenzialiRouter,
|
||||||
});
|
});
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|
|
||||||
121
apps/infoalloggi/src/server/api/routers/potenziali.ts
Normal file
121
apps/infoalloggi/src/server/api/routers/potenziali.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import z from "zod";
|
||||||
|
import type {
|
||||||
|
NewPotenziali,
|
||||||
|
PotenzialiId,
|
||||||
|
PotenzialiUpdate,
|
||||||
|
} from "~/schemas/public/Potenziali";
|
||||||
|
import type { PotenzialiGroupsId } from "~/schemas/public/PotenzialiGroups";
|
||||||
|
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
|
||||||
|
import {
|
||||||
|
addPotenziale,
|
||||||
|
addPotenzialeGroup,
|
||||||
|
deletePotenziale,
|
||||||
|
deletePotenzialeGroup,
|
||||||
|
getPotenziali,
|
||||||
|
getPotenzialiGroups,
|
||||||
|
type PotenzialeData,
|
||||||
|
updatePotenziale,
|
||||||
|
updatePotenzialeGroup,
|
||||||
|
updatePotenziali,
|
||||||
|
} from "~/server/services/potenziali.service";
|
||||||
|
|
||||||
|
export const zPotenzialiId = z.custom<PotenzialiId>(
|
||||||
|
(val) => typeof val === "string" && val.length > 0,
|
||||||
|
{ message: "Invalid PotenzialiId" },
|
||||||
|
);
|
||||||
|
|
||||||
|
export const zNewPotenziali = z.custom<NewPotenziali>(
|
||||||
|
(val) => {
|
||||||
|
return typeof val === "object" && val !== null;
|
||||||
|
},
|
||||||
|
{ message: "Invalid NewPotenziali" },
|
||||||
|
);
|
||||||
|
|
||||||
|
export const zPotenzialiUpdate = z.custom<PotenzialiUpdate>(
|
||||||
|
(val) => {
|
||||||
|
return typeof val === "object" && val !== null;
|
||||||
|
},
|
||||||
|
{ message: "Invalid PotenzialiUpdate" },
|
||||||
|
);
|
||||||
|
|
||||||
|
export const zPotenzialiGroupId = z.custom<PotenzialiGroupsId>(
|
||||||
|
(val) => typeof val === "string" && val.length > 0,
|
||||||
|
{ message: "Invalid PotenzialiGroupsId" },
|
||||||
|
);
|
||||||
|
|
||||||
|
export const potenzialiRouter = createTRPCRouter({
|
||||||
|
getPotenziali: adminProcedure.query(async () => {
|
||||||
|
return await getPotenziali();
|
||||||
|
}),
|
||||||
|
getPotenzialiGroups: adminProcedure.query(async () => {
|
||||||
|
return await getPotenzialiGroups();
|
||||||
|
}),
|
||||||
|
addPotenziale: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
data: zNewPotenziali,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await addPotenziale(input.data);
|
||||||
|
}),
|
||||||
|
updatePotenziali: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
data: z.custom<PotenzialeData[]>(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await updatePotenziali(input.data);
|
||||||
|
}),
|
||||||
|
updatePotenziale: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
potenzialiId: zPotenzialiId,
|
||||||
|
data: zPotenzialiUpdate,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await updatePotenziale(input.potenzialiId, input.data);
|
||||||
|
}),
|
||||||
|
deletePotenziale: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
potenzialiId: zPotenzialiId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await deletePotenziale(input.potenzialiId);
|
||||||
|
}),
|
||||||
|
|
||||||
|
addPotenzialeGroup: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
name: z.string().min(1).max(255),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await addPotenzialeGroup({ name: input.name });
|
||||||
|
}),
|
||||||
|
updatePotenzialeGroup: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
potenzialiGroupId: zPotenzialiGroupId,
|
||||||
|
name: z.string().min(1).max(255),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await updatePotenzialeGroup(input.potenzialiGroupId, {
|
||||||
|
name: input.name,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
deletePotenzialeGroup: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
potenzialiGroupId: zPotenzialiGroupId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await deletePotenzialeGroup(input.potenzialiGroupId);
|
||||||
|
}),
|
||||||
|
});
|
||||||
185
apps/infoalloggi/src/server/services/potenziali.service.ts
Normal file
185
apps/infoalloggi/src/server/services/potenziali.service.ts
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import type { Nullable } from "kysely";
|
||||||
|
import type {
|
||||||
|
NewPotenziali,
|
||||||
|
Potenziali,
|
||||||
|
PotenzialiId,
|
||||||
|
PotenzialiUpdate,
|
||||||
|
} from "~/schemas/public/Potenziali";
|
||||||
|
import type {
|
||||||
|
NewPotenzialiGroups,
|
||||||
|
PotenzialiGroups,
|
||||||
|
PotenzialiGroupsId,
|
||||||
|
PotenzialiGroupsUpdate,
|
||||||
|
} from "~/schemas/public/PotenzialiGroups";
|
||||||
|
import type { Users } from "~/schemas/public/Users";
|
||||||
|
import { db } from "../db";
|
||||||
|
|
||||||
|
export type PotenzialeData = Omit<Potenziali, "potenziali_group_id"> & {
|
||||||
|
column: string;
|
||||||
|
} & Nullable<Pick<Users, "username">>;
|
||||||
|
|
||||||
|
export const getPotenziali = async (): Promise<PotenzialeData[]> => {
|
||||||
|
try {
|
||||||
|
const potenziali = await db
|
||||||
|
.selectFrom("potenziali")
|
||||||
|
.select([
|
||||||
|
"potenziali.id",
|
||||||
|
"potenziali.name",
|
||||||
|
"potenziali.created_at",
|
||||||
|
"potenziali.descrizione",
|
||||||
|
"potenziali.userid",
|
||||||
|
"potenziali.potenziali_group_id as column",
|
||||||
|
])
|
||||||
|
.innerJoin(
|
||||||
|
"potenziali_groups",
|
||||||
|
"potenziali.potenziali_group_id",
|
||||||
|
"potenziali_groups.id",
|
||||||
|
)
|
||||||
|
.leftJoin("users", "potenziali.userid", "users.id")
|
||||||
|
.select("users.username")
|
||||||
|
.orderBy("potenziali_groups.name", "asc")
|
||||||
|
.orderBy("potenziali.created_at", "asc")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return potenziali.map((data) => ({
|
||||||
|
...data,
|
||||||
|
created_at: new Date(data.created_at),
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to fetch potenziali, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPotenzialiGroups = async (): Promise<PotenzialiGroups[]> => {
|
||||||
|
try {
|
||||||
|
return await db.selectFrom("potenziali_groups").selectAll().execute();
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to fetch potenziali groups, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addPotenzialeGroup = async (data: NewPotenzialiGroups) => {
|
||||||
|
try {
|
||||||
|
await db.insertInto("potenziali_groups").values(data).execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to add potenziale group, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePotenzialeGroup = async (id: PotenzialiGroupsId) => {
|
||||||
|
try {
|
||||||
|
await db.deleteFrom("potenziali_groups").where("id", "=", id).execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to delete potenziale group, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePotenzialeGroup = async (
|
||||||
|
id: PotenzialiGroupsId,
|
||||||
|
data: PotenzialiGroupsUpdate,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await db
|
||||||
|
.updateTable("potenziali_groups")
|
||||||
|
.set(data)
|
||||||
|
.where("id", "=", id)
|
||||||
|
.execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to update potenziale group, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addPotenziale = async (data: NewPotenziali) => {
|
||||||
|
try {
|
||||||
|
await db.insertInto("potenziali").values(data).execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to add potenziale, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePotenziale = async (id: PotenzialiId) => {
|
||||||
|
try {
|
||||||
|
await db.deleteFrom("potenziali").where("id", "=", id).execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to delete potenziale, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePotenziale = async (
|
||||||
|
id: PotenzialiId,
|
||||||
|
data: PotenzialiUpdate,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await db.updateTable("potenziali").set(data).where("id", "=", id).execute();
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to update potenziale, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePotenziali = async (data: PotenzialeData[]) => {
|
||||||
|
try {
|
||||||
|
const db_mappings = await db
|
||||||
|
.selectFrom("potenziali")
|
||||||
|
.select(["id", "potenziali_group_id"])
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
const updates = data
|
||||||
|
.map((item) => {
|
||||||
|
const db_item = db_mappings.find((db_item) => db_item.id === item.id);
|
||||||
|
if (db_item && db_item.potenziali_group_id !== item.column) {
|
||||||
|
const { column, username: _username, ...rest } = item;
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
potenziali_group_id: column as PotenzialiGroupsId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((item) => item !== null);
|
||||||
|
|
||||||
|
for (const item of updates) {
|
||||||
|
await db
|
||||||
|
.updateTable("potenziali")
|
||||||
|
.set(item)
|
||||||
|
.where("id", "=", item.id)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
return { status: "success" };
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Failed to update potenziali, error: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue