feat: add admin appunti management page and related functionality

- Introduced a new page for managing appunti in the admin area.
- Created AppuntiProvider to manage appunti state and API interactions.
- Added schemas for appunti and appunti groups.
- Implemented CRUD operations for appunti and appunti groups in the API.
- Removed potenziali related files and services as part of the refactor.
- Updated the public schema to include appunti and appunti groups.
- Improved error handling in the appunti service.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Marco Pedone 2026-04-29 15:12:52 +02:00
parent 984b1d7ae5
commit b3c70bfa1a
23 changed files with 582 additions and 591 deletions

View file

@ -5,7 +5,7 @@ SET message = ''
WHERE message IS NULL; WHERE message IS NULL;
ALTER TABLE IF EXISTS public.messages ALTER TABLE IF EXISTS public.messages
ALTER COLUMN IF EXISTS message SET NOT NULL; ALTER COLUMN message SET NOT NULL;
ALTER TABLE IF EXISTS public.messages ALTER TABLE IF EXISTS public.messages
ADD COLUMN IF NOT EXISTS reply_to_id UUID REFERENCES messages(messageid) ON DELETE SET NULL, ADD COLUMN IF NOT EXISTS reply_to_id UUID REFERENCES messages(messageid) ON DELETE SET NULL,

View file

@ -0,0 +1,33 @@
DO $$
BEGIN
-- 1. Rename Tables
IF EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'potenziali') THEN
ALTER TABLE public.potenziali RENAME TO appunti;
END IF;
IF EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'potenziali_groups') THEN
ALTER TABLE public.potenziali_groups RENAME TO appunti_groups;
END IF;
-- 2. Rename Column (IF EXISTS is supported here in PG 10+)
IF EXISTS (SELECT FROM information_schema.columns WHERE table_name='appunti' AND column_name='potenziali_group_id') THEN
ALTER TABLE public.appunti RENAME COLUMN potenziali_group_id TO appunti_group_id;
END IF;
-- 3. Rename Constraints (Requires manual check because RENAME CONSTRAINT IF EXISTS doesn't exist)
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'potenziali_pkey') THEN
ALTER TABLE public.appunti RENAME CONSTRAINT potenziali_pkey TO appunti_pkey;
END IF;
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'potenziali_assigned_user') THEN
ALTER TABLE public.appunti RENAME CONSTRAINT potenziali_assigned_user TO appunti_assigned_user;
END IF;
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'potenziali_group_fk') THEN
ALTER TABLE public.appunti RENAME CONSTRAINT potenziali_group_fk TO appunti_group_fk;
END IF;
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'potenziali_groups_pkey') THEN
ALTER TABLE public.appunti_groups RENAME CONSTRAINT potenziali_groups_pkey TO appunti_groups_pkey;
END IF;
END $$;

View file

@ -132,7 +132,6 @@ function zodUpdateInsertionDatesFix(path, lines) {
insideUpdateOrInsert = true; insideUpdateOrInsert = true;
} }
if (insideUpdateOrInsert && line.includes("z.date()")) { if (insideUpdateOrInsert && line.includes("z.date()")) {
console.log(`Fixing line in ${path}: ${line}`);
return line.replace("z.date()", "z.union([z.date(), z.string()]).pipe(z.coerce.date())"); return line.replace("z.date()", "z.union([z.date(), z.string()]).pipe(z.coerce.date())");
} }

View file

@ -28,6 +28,7 @@ import {
MessageSquareText, MessageSquareText,
MessagesSquare, MessagesSquare,
Milestone, Milestone,
NotebookPen,
Paperclip, Paperclip,
Pointer, Pointer,
Search, Search,
@ -102,6 +103,7 @@ const iconOptions = [
"bell", "bell",
"target", "target",
"thumbs-up", "thumbs-up",
"note"
] as const; ] as const;
export type IconType = (typeof iconOptions)[number]; export type IconType = (typeof iconOptions)[number];
@ -202,6 +204,8 @@ export const IconMatrix = ({ type, ...rest }: IconMatrixProps) => {
return <Target {...rest} />; return <Target {...rest} />;
case "thumbs-up": case "thumbs-up":
return <ThumbsUp {...rest} />; return <ThumbsUp {...rest} />;
case "note":
return <NotebookPen {...rest} />;
default: default:
throw new Error(`Invalid icon type: ${type satisfies never}`); throw new Error(`Invalid icon type: ${type satisfies never}`);
} }

View file

@ -11,10 +11,10 @@ import {
KanbanProvider, KanbanProvider,
} from "~/components/ui/kanban"; } from "~/components/ui/kanban";
import { ScrollArea, ScrollBar } from "~/components/ui/scroll-area"; import { ScrollArea, ScrollBar } from "~/components/ui/scroll-area";
import { usePotenziali } from "~/providers/PotenzialiProvider"; import { useAppunti } from "~/providers/AppuntiProvider";
import type { PotenzialiGroupsId } from "~/schemas/public/PotenzialiGroups"; import type { AppuntiGroupsId } from "~/schemas/public/AppuntiGroups";
import type { UsersId } from "~/schemas/public/Users"; import type { UsersId } from "~/schemas/public/Users";
import type { PotenzialeData } from "~/server/services/potenziali.service"; import type { AppuntiData } from "~/server/services/appunti.service";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { Confirm } from "../confirm"; import { Confirm } from "../confirm";
import { import {
@ -43,8 +43,8 @@ import {
import { Textarea } from "../ui/textarea"; import { Textarea } from "../ui/textarea";
import { UserAvatar } from "../user_avatar"; import { UserAvatar } from "../user_avatar";
export const Potenziali = () => { export const Appunti = () => {
const { data, columns, setData, updateCall, removeColumn } = usePotenziali(); const { data, columns, setData, updateCall, removeColumn } = useAppunti();
return ( return (
<ScrollArea className="w-screen pr-2 sm:w-full"> <ScrollArea className="w-screen pr-2 sm:w-full">
<KanbanProvider <KanbanProvider
@ -85,8 +85,8 @@ export const Potenziali = () => {
</div> </div>
</KanbanHeader> </KanbanHeader>
<KanbanCards className="mx-auto w-68" id={column.id}> <KanbanCards className="mx-auto w-68" id={column.id}>
{(data: PotenzialeData) => ( {(data: AppuntiData) => (
<PotenzialeCardItem <AppuntiCardItem
columnId={column.id} columnId={column.id}
data={data} data={data}
key={data.id} key={data.id}
@ -101,11 +101,11 @@ export const Potenziali = () => {
); );
}; };
// 1. Extract the card logic into a separate component // 1. Extract the card logic into a separate component
const PotenzialeCardItem = ({ const AppuntiCardItem = ({
data, data,
columnId, columnId,
}: { }: {
data: PotenzialeData; data: AppuntiData;
columnId: string; columnId: string;
}) => { }) => {
const [isEditingOpen, setIsEditingOpen] = useState(false); const [isEditingOpen, setIsEditingOpen] = useState(false);
@ -155,7 +155,7 @@ const PotenzialeCardItem = ({
export const NewColumn = () => { export const NewColumn = () => {
const [newColumnName, setNewColumnName] = useState(""); const [newColumnName, setNewColumnName] = useState("");
const { addColumn } = usePotenziali(); const { addColumn } = useAppunti();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<Credenza onOpenChange={setOpen} open={open}> <Credenza onOpenChange={setOpen} open={open}>
@ -194,11 +194,11 @@ export const NewColumn = () => {
); );
}; };
const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => { const NewCard = ({ columnId }: { columnId: AppuntiGroupsId }) => {
const [newItemName, setNewItemName] = useState(""); const [newItemName, setNewItemName] = useState("");
const [newItemDescrizione, setNewItemDescrizione] = useState(""); const [newItemDescrizione, setNewItemDescrizione] = useState("");
const [newItemUserid, setNewItemUserid] = useState<UsersId | null>(null); const [newItemUserid, setNewItemUserid] = useState<UsersId | null>(null);
const { addItem } = usePotenziali(); const { addItem } = useAppunti();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const resetState = () => { const resetState = () => {
@ -210,7 +210,7 @@ const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => {
addItem({ addItem({
name: newItemName, name: newItemName,
descrizione: newItemDescrizione, descrizione: newItemDescrizione,
potenziali_group_id: columnId, appunti_group_id: columnId,
userid: newItemUserid, userid: newItemUserid,
}); });
resetState(); resetState();
@ -319,11 +319,11 @@ const EditingItem = ({
}: { }: {
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
data: PotenzialeData; data: AppuntiData;
}) => { }) => {
const { updateItem, removeItem } = usePotenziali(); const { updateItem, removeItem } = useAppunti();
const [state, setState] = useState<PotenzialeData>(data); const [state, setState] = useState<AppuntiData>(data);
const [hasChanges, setHasChanges] = useState(false); const [hasChanges, setHasChanges] = useState(false);
const { data: admins, isLoading } = api.users.getAdmins.useQuery(); const { data: admins, isLoading } = api.users.getAdmins.useQuery();
useEffect(() => { useEffect(() => {

View file

@ -656,7 +656,7 @@ export const en: LangDict = {
{ {
icon: "cog", icon: "cog",
items: [ items: [
{ href: "/area-riservata/admin/potenziali", title: "Potenziali" }, { href: "/area-riservata/admin/appunti", title: "Appunti" },
{ href: "/area-riservata/admin/utenti", title: "Users" }, { href: "/area-riservata/admin/utenti", title: "Users" },
{ href: "/area-riservata/admin/richieste", title: "Requests" }, { href: "/area-riservata/admin/richieste", title: "Requests" },
{ href: "/area-riservata/admin/chats", title: "Chat List" }, { href: "/area-riservata/admin/chats", title: "Chat List" },
@ -741,9 +741,9 @@ export const en: LangDict = {
title: "Dashboard", title: "Dashboard",
}, },
{ {
href: "/area-riservata/admin/potenziali", href: "/area-riservata/admin/appunti",
icon: { className: "", icon: "contact" }, icon: { className: "", icon: "note" },
title: "Potenziali", title: "Notes",
}, },
{ {
href: "/area-riservata/admin/utenti", href: "/area-riservata/admin/utenti",

View file

@ -661,7 +661,7 @@ export const it: LangDict = {
{ {
icon: "cog", icon: "cog",
items: [ items: [
{ href: "/area-riservata/admin/potenziali", title: "Potenziali" }, { href: "/area-riservata/admin/appunti", title: "Appunti" },
{ href: "/area-riservata/admin/utenti", title: "Utenti" }, { href: "/area-riservata/admin/utenti", title: "Utenti" },
{ href: "/area-riservata/admin/richieste", title: "Richieste" }, { href: "/area-riservata/admin/richieste", title: "Richieste" },
{ href: "/area-riservata/admin/chats", title: "Lista Chat" }, { href: "/area-riservata/admin/chats", title: "Lista Chat" },
@ -737,9 +737,9 @@ export const it: LangDict = {
title: "Dashboard", title: "Dashboard",
}, },
{ {
href: "/area-riservata/admin/potenziali", href: "/area-riservata/admin/Appunti",
icon: { className: "", icon: "contact" }, icon: { className: "", icon: "note" },
title: "Potenziali", title: "Appunti",
}, },
{ {
href: "/area-riservata/admin/utenti", href: "/area-riservata/admin/utenti",

View file

@ -1,16 +1,14 @@
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import Head from "next/head"; import Head from "next/head";
import { NewColumn } from "~/components/area-riservata/potenziali"; import { NewColumn } from "~/components/area-riservata/appunti";
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"; import { AppuntiProvider } from "~/providers/AppuntiProvider";
const PotenzialiBoard = dynamic( const AppuntiBoard = dynamic(
() => () =>
import("~/components/area-riservata/potenziali").then( import("~/components/area-riservata/appunti").then((mod) => mod.Appunti),
(mod) => mod.Potenziali,
),
{ {
ssr: false, ssr: false,
loading: () => <LoadingPage />, loading: () => <LoadingPage />,
@ -18,33 +16,33 @@ const PotenzialiBoard = dynamic(
); );
/** /**
* Pagina di gestione potenziali per admin: /area-riservata/admin/potenziali * Pagina di gestione appunti per admin: /area-riservata/admin/appunti
*/ */
const AdminPotenziali: NextPageWithLayout = () => { const AdminAppunti: NextPageWithLayout = () => {
return ( return (
<PotenzialiProvider> <AppuntiProvider>
<Head> <Head>
<title>Potenziali - Infoalloggi.it</title> <title>Appunti - Infoalloggi.it</title>
</Head> </Head>
<div className="mx-1 w-full flex-1"> <div className="mx-1 w-full flex-1">
<div className="mx-auto w-full pt-4"> <div className="mx-auto w-full pt-4">
<div className="mx-3 flex flex-wrap items-start justify-between pb-5"> <div className="mx-3 flex flex-wrap items-start justify-between pb-5">
<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 Appunti
</h3> </h3>
</div> </div>
<NewColumn /> <NewColumn />
</div> </div>
<PotenzialiBoard /> <AppuntiBoard />
</div> </div>
</div> </div>
</PotenzialiProvider> </AppuntiProvider>
); );
}; };
export default AdminPotenziali; export default AdminAppunti;
AdminPotenziali.getLayout = function getLayout(page) { AdminAppunti.getLayout = function getLayout(page) {
return <AreaRiservataLayout>{page}</AreaRiservataLayout>; return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
}; };

View file

@ -265,11 +265,7 @@ const SezioneComuni = () => {
<DialogDescription className="sr-only">desc</DialogDescription> <DialogDescription className="sr-only">desc</DialogDescription>
</DialogHeader> </DialogHeader>
<FormComuni <FormComuni
handleRemove={() => handleRemove={() => deleteComune(selectedComune.id)}
deleteComune({
id: selectedComune.id,
})
}
initialValues={selectedComune} initialValues={selectedComune}
submitMutation={(values) => { submitMutation={(values) => {
editComune({ editComune({
@ -580,11 +576,7 @@ const SezioneProvincie = () => {
<DialogDescription className="sr-only">desc</DialogDescription> <DialogDescription className="sr-only">desc</DialogDescription>
</DialogHeader> </DialogHeader>
<FormProvincie <FormProvincie
handleRemove={() => handleRemove={() => deleteProvincia(selectedProvincia.id)}
deleteProvincia({
id: selectedProvincia.id,
})
}
initialValues={selectedProvincia} initialValues={selectedProvincia}
submitMutation={(values) => { submitMutation={(values) => {
editProvincia({ editProvincia({
@ -865,11 +857,7 @@ const SezioneNazioni = () => {
<DialogDescription className="sr-only">desc</DialogDescription> <DialogDescription className="sr-only">desc</DialogDescription>
</DialogHeader> </DialogHeader>
<FormNazioni <FormNazioni
handleRemove={() => handleRemove={() => deleteNazione(selectedNazione.id)}
deleteNazione({
id: selectedNazione.id,
})
}
initialValues={selectedNazione} initialValues={selectedNazione}
submitMutation={(values) => { submitMutation={(values) => {
editNazione({ editNazione({

View file

@ -0,0 +1,151 @@
import { createContext, useContext } from "react";
import toast from "react-hot-toast";
import type { AppuntiId, NewAppunti } from "~/schemas/public/Appunti";
import type {
AppuntiGroups,
AppuntiGroupsId,
} from "~/schemas/public/AppuntiGroups";
import type { AppuntiData } from "~/server/services/appunti.service";
import { api } from "~/utils/api";
type AppuntiContextType = {
data: AppuntiData[];
columns: AppuntiGroups[];
setData: (data: AppuntiData[]) => void;
updateCall: () => void;
addItem: (item: NewAppunti) => void;
removeItem: (id: AppuntiId) => void;
removeColumn: (id: AppuntiGroupsId) => void;
addColumn: (name: string) => void;
updateItem: (item: AppuntiData) => void;
};
const AppuntiContext = createContext<AppuntiContextType | null>(null);
type AppuntiProviderProps = {
children: React.ReactNode;
};
export const AppuntiProvider = ({ children }: AppuntiProviderProps) => {
const { data, isError } = api.appunti.getAppunti.useQuery(undefined, {
refetchInterval: 30000,
refetchIntervalInBackground: true,
refetchOnWindowFocus: true,
});
if (isError) {
console.error("Errore nel caricamento dei potenziali");
window.location.reload();
}
const { data: columns } = api.appunti.getAppuntiGroups.useQuery();
const utils = api.useUtils();
const { mutate: addItemMutation } = api.appunti.addAppunti.useMutation({
onSuccess: async () => {
toast.success("Elemento aggiunto con successo");
await utils.appunti.getAppunti.invalidate();
await utils.appunti.getAppuntiGroups.invalidate();
},
});
const addItem = (item: NewAppunti) => {
addItemMutation({
data: item,
});
};
const { mutate: removeItemMutation } = api.appunti.deleteAppunti.useMutation({
onSuccess: async () => {
toast.success("Elemento rimosso con successo");
await utils.appunti.getAppunti.invalidate();
await utils.appunti.getAppuntiGroups.invalidate();
},
});
const removeItem = (id: AppuntiId) => {
removeItemMutation(id);
};
const setData = (newData: AppuntiData[]) => {
utils.appunti.getAppunti.setData(undefined, newData);
};
const { mutate: updateAppuntiMutation } =
api.appunti.updateAppunti.useMutation({
onSuccess: async () => {
await utils.appunti.getAppunti.invalidate();
},
});
const updateCall = () => {
updateAppuntiMutation({
data: utils.appunti.getAppunti.getData() || [],
});
};
const { mutate: addColumnMutation } = api.appunti.addAppuntiGroup.useMutation(
{
onSuccess: async () => {
toast.success("Colonna aggiunta con successo");
await utils.appunti.getAppunti.invalidate();
await utils.appunti.getAppuntiGroups.invalidate();
},
},
);
const addColumn = (name: string) => {
addColumnMutation({
name,
});
};
const { mutate: updateItemMutation } = api.appunti.updateAppunto.useMutation({
onSuccess: async () => {
toast.success("Elemento aggiornato con successo");
await utils.appunti.getAppunti.invalidate();
},
});
const updateItem = (item: AppuntiData) => {
const { column, username: _username, ...rest } = item;
updateItemMutation({
appuntiId: item.id,
data: {
...rest,
appunti_group_id: column as AppuntiGroupsId,
},
});
};
const { mutate: removeColumnMutation } =
api.appunti.deleteAppuntiGroup.useMutation({
onSuccess: async () => {
toast.success("Colonna rimossa con successo");
await utils.appunti.getAppunti.invalidate();
await utils.appunti.getAppuntiGroups.invalidate();
},
});
const removeColumn = (id: AppuntiGroupsId) => {
removeColumnMutation(id);
};
return (
<AppuntiContext.Provider
value={{
data: data || [],
columns: columns || [],
setData,
updateCall,
addItem,
removeItem,
addColumn,
updateItem,
removeColumn,
}}
>
{children}
</AppuntiContext.Provider>
);
};
export const useAppunti = () => {
const context = useContext(AppuntiContext);
if (!context) {
throw new Error("useAppunti must be used within a AppuntiProvider");
}
return context;
};

View file

@ -1,155 +0,0 @@
import { createContext, useContext } from "react";
import toast from "react-hot-toast";
import type { NewPotenziali, PotenzialiId } from "~/schemas/public/Potenziali";
import type {
PotenzialiGroups,
PotenzialiGroupsId,
} from "~/schemas/public/PotenzialiGroups";
import type { PotenzialeData } from "~/server/services/potenziali.service";
import { api } from "~/utils/api";
type PotenzialiContextType = {
data: PotenzialeData[];
columns: PotenzialiGroups[];
setData: (data: PotenzialeData[]) => void;
updateCall: () => void;
addItem: (item: NewPotenziali) => void;
removeItem: (id: PotenzialiId) => void;
removeColumn: (id: PotenzialiGroupsId) => void;
addColumn: (name: string) => void;
updateItem: (item: PotenzialeData) => void;
};
const PotenzialiContext = createContext<PotenzialiContextType | null>(null);
type PotenzialiProviderProps = {
children: React.ReactNode;
};
export const PotenzialiProvider = ({ children }: PotenzialiProviderProps) => {
const { data, isError } = api.potenziali.getPotenziali.useQuery(undefined, {
refetchInterval: 30000,
refetchIntervalInBackground: true,
refetchOnWindowFocus: true,
});
if (isError) {
console.error("Errore nel caricamento dei potenziali");
window.location.reload();
}
const { data: columns } = api.potenziali.getPotenzialiGroups.useQuery();
const utils = api.useUtils();
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 { mutate: removeItemMutation } =
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,
});
};
const { mutate: updateItemMutation } =
api.potenziali.updatePotenziale.useMutation({
onSuccess: async () => {
toast.success("Elemento aggiornato con successo");
await utils.potenziali.getPotenziali.invalidate();
},
});
const updateItem = (item: PotenzialeData) => {
const { column, username: _username, ...rest } = item;
updateItemMutation({
potenzialiId: item.id,
data: {
...rest,
potenziali_group_id: column as PotenzialiGroupsId,
},
});
};
const { mutate: removeColumnMutation } =
api.potenziali.deletePotenzialeGroup.useMutation({
onSuccess: async () => {
toast.success("Colonna rimossa con successo");
await utils.potenziali.getPotenziali.invalidate();
await utils.potenziali.getPotenzialiGroups.invalidate();
},
});
const removeColumn = (id: PotenzialiGroupsId) => {
removeColumnMutation({
potenzialiGroupId: id,
});
};
return (
<PotenzialiContext.Provider
value={{
data: data || [],
columns: columns || [],
setData,
updateCall,
addItem,
removeItem,
addColumn,
updateItem,
removeColumn,
}}
>
{children}
</PotenzialiContext.Provider>
);
};
export const usePotenziali = () => {
const context = useContext(PotenzialiContext);
if (!context) {
throw new Error("usePotenziali must be used within a PotenzialiProvider");
}
return context;
};

View file

@ -0,0 +1,57 @@
import { usersId, type UsersId } from './Users';
import { appuntiGroupsId, type AppuntiGroupsId } from './AppuntiGroups';
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
import { z } from 'zod';
/** Identifier type for public.appunti */
export type AppuntiId = string & { __brand: 'public.appunti' };
/** Represents the table public.appunti */
export default interface AppuntiTable {
id: ColumnType<AppuntiId, AppuntiId | undefined, AppuntiId>;
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>;
appunti_group_id: ColumnType<AppuntiGroupsId, AppuntiGroupsId, AppuntiGroupsId>;
}
export type Appunti = Selectable<AppuntiTable>;
export type NewAppunti = Insertable<AppuntiTable>;
export type AppuntiUpdate = Updateable<AppuntiTable>;
export const appuntiId = z.uuid().transform(value => value as AppuntiId);
export const AppuntiSchema = z.object({
id: appuntiId,
name: z.string(),
descrizione: z.string().nullable(),
created_at: z.date(),
userid: usersId.nullable(),
appunti_group_id: appuntiGroupsId,
});
export const NewAppuntiSchema = z.object({
id: appuntiId.optional(),
name: z.string(),
descrizione: z.string().optional().nullable(),
created_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional(),
userid: usersId.optional().nullable(),
appunti_group_id: appuntiGroupsId,
});
export const AppuntiUpdateSchema = z.object({
id: appuntiId.optional(),
name: z.string().optional(),
descrizione: z.string().optional().nullable(),
created_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional(),
userid: usersId.optional().nullable(),
appunti_group_id: appuntiGroupsId.optional(),
});

View file

@ -0,0 +1,40 @@
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
import { z } from 'zod';
/** Identifier type for public.appunti_groups */
export type AppuntiGroupsId = string & { __brand: 'public.appunti_groups' };
/** Represents the table public.appunti_groups */
export default interface AppuntiGroupsTable {
id: ColumnType<AppuntiGroupsId, AppuntiGroupsId | undefined, AppuntiGroupsId>;
name: ColumnType<string, string, string>;
color: ColumnType<string, string | undefined, string>;
}
export type AppuntiGroups = Selectable<AppuntiGroupsTable>;
export type NewAppuntiGroups = Insertable<AppuntiGroupsTable>;
export type AppuntiGroupsUpdate = Updateable<AppuntiGroupsTable>;
export const appuntiGroupsId = z.uuid().transform(value => value as AppuntiGroupsId);
export const AppuntiGroupsSchema = z.object({
id: appuntiGroupsId,
name: z.string(),
color: z.string(),
});
export const NewAppuntiGroupsSchema = z.object({
id: appuntiGroupsId.optional(),
name: z.string(),
color: z.string().optional(),
});
export const AppuntiGroupsUpdateSchema = z.object({
id: appuntiGroupsId.optional(),
name: z.string().optional(),
color: z.string().optional(),
});

View file

@ -1,57 +0,0 @@
import { usersId, type UsersId } from './Users';
import { potenzialiGroupsId, type PotenzialiGroupsId } from './PotenzialiGroups';
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
import { z } from 'zod';
/** 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>;
export const potenzialiId = z.uuid().transform(value => value as PotenzialiId);
export const PotenzialiSchema = z.object({
id: potenzialiId,
name: z.string(),
descrizione: z.string().nullable(),
created_at: z.date(),
userid: usersId.nullable(),
potenziali_group_id: potenzialiGroupsId,
});
export const NewPotenzialiSchema = z.object({
id: potenzialiId.optional(),
name: z.string(),
descrizione: z.string().optional().nullable(),
created_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional(),
userid: usersId.optional().nullable(),
potenziali_group_id: potenzialiGroupsId,
});
export const PotenzialiUpdateSchema = z.object({
id: potenzialiId.optional(),
name: z.string().optional(),
descrizione: z.string().optional().nullable(),
created_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional(),
userid: usersId.optional().nullable(),
potenziali_group_id: potenzialiGroupsId.optional(),
});

View file

@ -1,40 +0,0 @@
import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
import { z } from 'zod';
/** 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>;
export const potenzialiGroupsId = z.uuid().transform(value => value as PotenzialiGroupsId);
export const PotenzialiGroupsSchema = z.object({
id: potenzialiGroupsId,
name: z.string(),
color: z.string(),
});
export const NewPotenzialiGroupsSchema = z.object({
id: potenzialiGroupsId.optional(),
name: z.string(),
color: z.string().optional(),
});
export const PotenzialiGroupsUpdateSchema = z.object({
id: potenzialiGroupsId.optional(),
name: z.string().optional(),
color: z.string().optional(),
});

View file

@ -5,11 +5,11 @@ import type { default as UsersTable } from './Users';
import type { default as ChatsEtichetteTable } from './ChatsEtichette'; import type { default as ChatsEtichetteTable } from './ChatsEtichette';
import type { default as TestiEStringheTable } from './TestiEStringhe'; import type { default as TestiEStringheTable } from './TestiEStringhe';
import type { default as UserEtichetteTable } from './UserEtichette'; import type { default as UserEtichetteTable } from './UserEtichette';
import type { default as AppuntiTable } from './Appunti';
import type { default as UsersStorageTable } from './UsersStorage'; 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 UserInvitesTable } from './UserInvites'; import type { default as UserInvitesTable } from './UserInvites';
import type { default as EmailsTable } from './Emails'; import type { default as EmailsTable } from './Emails';
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 RinnoviTable } from './Rinnovi'; import type { default as RinnoviTable } from './Rinnovi';
import type { default as EventQueueTable } from './EventQueue'; import type { default as EventQueueTable } from './EventQueue';
@ -21,10 +21,10 @@ 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 UsersAnagraficaTable } from './UsersAnagrafica'; import type { default as UsersAnagraficaTable } from './UsersAnagrafica';
import type { default as ProvincieTable } from './Provincie'; import type { default as ProvincieTable } from './Provincie';
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';
import type { default as AppuntiGroupsTable } from './AppuntiGroups';
import type { default as ImagesRefsTable } from './ImagesRefs'; import type { default as ImagesRefsTable } from './ImagesRefs';
import type { default as BannersTable } from './Banners'; import type { default as BannersTable } from './Banners';
import type { default as ServizioTable } from './Servizio'; import type { default as ServizioTable } from './Servizio';
@ -45,6 +45,8 @@ export default interface PublicSchema {
user_etichette: UserEtichetteTable; user_etichette: UserEtichetteTable;
appunti: AppuntiTable;
users_storage: UsersStorageTable; users_storage: UsersStorageTable;
servizio_annunci: ServizioAnnunciTable; servizio_annunci: ServizioAnnunciTable;
@ -53,8 +55,6 @@ export default interface PublicSchema {
emails: EmailsTable; emails: EmailsTable;
potenziali_groups: PotenzialiGroupsTable;
comuni: ComuniTable; comuni: ComuniTable;
rinnovi: RinnoviTable; rinnovi: RinnoviTable;
@ -77,14 +77,14 @@ export default interface PublicSchema {
provincie: ProvincieTable; provincie: ProvincieTable;
potenziali: PotenzialiTable;
videos_refs: VideosRefsTable; videos_refs: VideosRefsTable;
banlist: BanlistTable; banlist: BanlistTable;
nazioni: NazioniTable; nazioni: NazioniTable;
appunti_groups: AppuntiGroupsTable;
images_refs: ImagesRefsTable; images_refs: ImagesRefsTable;
banners: BannersTable; banners: BannersTable;

View file

@ -17,12 +17,12 @@ import { stripeReportsRouter } from "~/server/api/routers/stripe_reports";
import { testRouter } from "~/server/api/routers/test"; import { testRouter } from "~/server/api/routers/test";
import { usersRouter } from "~/server/api/routers/users"; import { usersRouter } from "~/server/api/routers/users";
import { createTRPCRouter } from "~/server/api/trpc"; import { createTRPCRouter } from "~/server/api/trpc";
import { appuntiRouter } from "./routers/appunti";
import { bannersRouter } from "./routers/banners"; 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 { inviteRouter } from "./routers/invite"; import { inviteRouter } from "./routers/invite";
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";
@ -54,7 +54,7 @@ export const appRouter = createTRPCRouter({
etichette: etichetteRouter, etichette: etichetteRouter,
strings: stringsRouter, strings: stringsRouter,
revalidation: revalidationRouter, revalidation: revalidationRouter,
potenziali: potenzialiRouter, appunti: appuntiRouter,
invite: inviteRouter, invite: inviteRouter,
}); });
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;

View file

@ -0,0 +1,76 @@
import z from "zod";
import {
AppuntiUpdateSchema,
appuntiId,
NewAppuntiSchema,
} from "~/schemas/public/Appunti";
import { appuntiGroupsId } from "~/schemas/public/AppuntiGroups";
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
import {
type AppuntiData,
addAppunti,
addAppuntiGroup,
deleteAppunti,
deleteAppuntiGroup,
getAppunti,
getAppuntiGroups,
updateAppunti,
updateAppunto,
} from "~/server/services/appunti.service";
export const appuntiRouter = createTRPCRouter({
getAppunti: adminProcedure.query(async () => {
return await getAppunti();
}),
getAppuntiGroups: adminProcedure.query(async () => {
return await getAppuntiGroups();
}),
addAppunti: adminProcedure
.input(
z.object({
data: NewAppuntiSchema,
}),
)
.mutation(async ({ input }) => {
return await addAppunti(input.data);
}),
updateAppunti: adminProcedure
.input(
z.object({
data: z.custom<AppuntiData[]>(),
}),
)
.mutation(async ({ input }) => {
return await updateAppunti(input.data);
}),
updateAppunto: adminProcedure
.input(
z.object({
appuntiId: appuntiId,
data: AppuntiUpdateSchema,
}),
)
.mutation(async ({ input }) => {
return await updateAppunto(input.appuntiId, input.data);
}),
deleteAppunti: adminProcedure.input(appuntiId).mutation(async ({ input }) => {
return await deleteAppunti(input);
}),
addAppuntiGroup: adminProcedure
.input(
z.object({
name: z.string().min(1).max(255),
}),
)
.mutation(async ({ input }) => {
return await addAppuntiGroup({ name: input.name });
}),
deleteAppuntiGroup: adminProcedure
.input(appuntiGroupsId)
.mutation(async ({ input }) => {
return await deleteAppuntiGroup(input);
}),
});

View file

@ -54,15 +54,9 @@ export const catastoRouter = createTRPCRouter({
const { id, data } = input; const { id, data } = input;
return await editComune({ id, data }); return await editComune({ id, data });
}), }),
deleteComune: adminProcedure deleteComune: adminProcedure.input(comuniId).mutation(async ({ input }) => {
.input( return await deleteComune(input);
z.object({ }),
id: comuniId,
}),
)
.mutation(async ({ input }) => {
return await deleteComune(input.id);
}),
getProvincie: publicProcedure.query(async () => { getProvincie: publicProcedure.query(async () => {
return await getProvincie(); return await getProvincie();
@ -84,13 +78,9 @@ export const catastoRouter = createTRPCRouter({
return await editProvincia({ id, data }); return await editProvincia({ id, data });
}), }),
deleteProvincia: adminProcedure deleteProvincia: adminProcedure
.input( .input(provincieId)
z.object({
id: provincieId,
}),
)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
return await deleteProvincia(input.id); return await deleteProvincia(input);
}), }),
getNazioni: publicProcedure.query(async () => { getNazioni: publicProcedure.query(async () => {
return await getNazioni(); return await getNazioni();
@ -111,13 +101,7 @@ export const catastoRouter = createTRPCRouter({
const { id, data } = input; const { id, data } = input;
return await editNazione({ id, data }); return await editNazione({ id, data });
}), }),
deleteNazione: adminProcedure deleteNazione: adminProcedure.input(nazioniId).mutation(async ({ input }) => {
.input( return await deleteNazione(input);
z.object({ }),
id: nazioniId,
}),
)
.mutation(async ({ input }) => {
return await deleteNazione(input.id);
}),
}); });

View file

@ -1,86 +0,0 @@
import z from "zod";
import {
NewPotenzialiSchema,
PotenzialiUpdateSchema,
potenzialiId,
} from "~/schemas/public/Potenziali";
import { potenzialiGroupsId } from "~/schemas/public/PotenzialiGroups";
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
import {
addPotenziale,
addPotenzialeGroup,
deletePotenziale,
deletePotenzialeGroup,
getPotenziali,
getPotenzialiGroups,
type PotenzialeData,
updatePotenziale,
updatePotenziali,
} from "~/server/services/potenziali.service";
export const potenzialiRouter = createTRPCRouter({
getPotenziali: adminProcedure.query(async () => {
return await getPotenziali();
}),
getPotenzialiGroups: adminProcedure.query(async () => {
return await getPotenzialiGroups();
}),
addPotenziale: adminProcedure
.input(
z.object({
data: NewPotenzialiSchema,
}),
)
.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: potenzialiId,
data: PotenzialiUpdateSchema,
}),
)
.mutation(async ({ input }) => {
return await updatePotenziale(input.potenzialiId, input.data);
}),
deletePotenziale: adminProcedure
.input(
z.object({
potenzialiId: potenzialiId,
}),
)
.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 });
}),
deletePotenzialeGroup: adminProcedure
.input(
z.object({
potenzialiGroupId: potenzialiGroupsId,
}),
)
.mutation(async ({ input }) => {
return await deletePotenzialeGroup(input.potenzialiGroupId);
}),
});

View file

@ -0,0 +1,162 @@
import { TRPCError } from "@trpc/server";
import type { Nullable } from "kysely";
import type {
Appunti,
AppuntiId,
AppuntiUpdate,
NewAppunti,
} from "~/schemas/public/Appunti";
import type {
AppuntiGroups,
AppuntiGroupsId,
NewAppuntiGroups,
} from "~/schemas/public/AppuntiGroups";
import type { Users } from "~/schemas/public/Users";
import { db } from "../db";
export type AppuntiData = Omit<Appunti, "appunti_group_id"> & {
column: string;
} & Nullable<Pick<Users, "username">>;
export const getAppunti = async (): Promise<AppuntiData[]> => {
try {
const appunti = await db
.selectFrom("appunti")
.select([
"appunti.id",
"appunti.name",
"appunti.created_at",
"appunti.descrizione",
"appunti.userid",
"appunti.appunti_group_id as column",
])
.innerJoin(
"appunti_groups",
"appunti.appunti_group_id",
"appunti_groups.id",
)
.leftJoin("users", "appunti.userid", "users.id")
.select("users.username")
.orderBy("appunti_groups.name", "asc")
.orderBy("appunti.created_at", "asc")
.execute();
return appunti.map((data) => ({
...data,
created_at: new Date(data.created_at),
}));
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to fetch Appunti, error: ${(e as Error).message}`,
});
}
};
export const getAppuntiGroups = async (): Promise<AppuntiGroups[]> => {
try {
return await db.selectFrom("appunti_groups").selectAll().execute();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to fetch Appunti groups, error: ${(e as Error).message}`,
});
}
};
export const addAppuntiGroup = async (data: NewAppuntiGroups) => {
try {
await db.insertInto("appunti_groups").values(data).execute();
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to add Appunti group, error: ${(e as Error).message}`,
});
}
};
export const deleteAppuntiGroup = async (id: AppuntiGroupsId) => {
try {
await db.deleteFrom("appunti_groups").where("id", "=", id).execute();
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to delete Appunti group, error: ${(e as Error).message}`,
});
}
};
export const addAppunti = async (data: NewAppunti) => {
try {
await db.insertInto("appunti").values(data).execute();
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to add Appunti, error: ${(e as Error).message}`,
});
}
};
export const deleteAppunti = async (id: AppuntiId) => {
try {
await db.deleteFrom("appunti").where("id", "=", id).execute();
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to delete Appunti, error: ${(e as Error).message}`,
});
}
};
export const updateAppunto = async (id: AppuntiId, data: AppuntiUpdate) => {
try {
await db.updateTable("appunti").set(data).where("id", "=", id).execute();
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to update Appunti, error: ${(e as Error).message}`,
});
}
};
export const updateAppunti = async (data: AppuntiData[]) => {
try {
const db_mappings = await db
.selectFrom("appunti")
.select(["id", "appunti_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.appunti_group_id !== item.column) {
const { column, username: _username, ...rest } = item;
return {
...rest,
appunti_group_id: column as AppuntiGroupsId,
};
}
return null;
})
.filter((item) => item !== null);
for (const item of updates) {
await db
.updateTable("appunti")
.set(item)
.where("id", "=", item.id)
.execute();
}
return { status: "success" };
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Failed to update appunti, error: ${(e as Error).message}`,
});
}
};

View file

@ -1,165 +0,0 @@
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,
} 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 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}`,
});
}
};

View file

@ -48,3 +48,5 @@ export const MessageUpdateEventSchema = z.discriminatedUnion("eventType", [
}), }),
z.object({ eventType: z.literal("read_receipt"), data: z.null() }), // all unread messages in this chat are now read z.object({ eventType: z.literal("read_receipt"), data: z.null() }), // all unread messages in this chat are now read
]); ]);