From fecac3428d2b4ddd8e49191f86f559ad901d48cb Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Fri, 8 May 2026 16:18:36 +0200 Subject: [PATCH] disabled logic for event_queue --- apps/db/migrations/55_nulla_fatto_flag.up.sql | 6 +++ apps/db/migrations/nulla_fatto_flag.up.sql | 3 -- apps/infoalloggi/emails/gereric-email.tsx | 23 +++-------- .../area-riservata/comunicazioni.tsx | 1 + .../src/components/custom_ui/multiselect.tsx | 11 ++--- .../src/forms/FormEditQueueMail.tsx | 24 +++++++++++ apps/infoalloggi/src/forms/FormNewMail.tsx | 40 ++++++++++++++++--- apps/infoalloggi/src/lib/utils.ts | 8 ++++ .../user-view/comunicazioni/[userId].tsx | 1 + .../src/schemas/public/EventQueue.ts | 5 +++ .../src/server/api/routers/comunicazioni.ts | 3 ++ .../controllers/event_queue.controller.ts | 19 +++++++++ .../server/controllers/servizio.controller.ts | 6 +-- .../src/server/services/servizio.service.ts | 4 +- apps/infoalloggi/tsconfig.json | 2 + 15 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 apps/db/migrations/55_nulla_fatto_flag.up.sql delete mode 100644 apps/db/migrations/nulla_fatto_flag.up.sql diff --git a/apps/db/migrations/55_nulla_fatto_flag.up.sql b/apps/db/migrations/55_nulla_fatto_flag.up.sql new file mode 100644 index 0000000..acac83c --- /dev/null +++ b/apps/db/migrations/55_nulla_fatto_flag.up.sql @@ -0,0 +1,6 @@ +-- 55_nulla_fatto_flag.up.sql +ALTER TABLE IF EXISTS public.servizio + ADD COLUMN IF NOT EXISTS "isDisabled" boolean NOT NULL DEFAULT false; + +ALTER TABLE IF EXISTS public.event_queue + ADD COLUMN IF NOT EXISTS "isActive" boolean NOT NULL DEFAULT true; \ No newline at end of file diff --git a/apps/db/migrations/nulla_fatto_flag.up.sql b/apps/db/migrations/nulla_fatto_flag.up.sql deleted file mode 100644 index 9325b17..0000000 --- a/apps/db/migrations/nulla_fatto_flag.up.sql +++ /dev/null @@ -1,3 +0,0 @@ --- nulla_fatto_flag.up.sql -ALTER TABLE IF EXISTS public.servizio - ADD COLUMN IF NOT EXISTS "isDisabled" boolean NOT NULL DEFAULT false; \ No newline at end of file diff --git a/apps/infoalloggi/emails/gereric-email.tsx b/apps/infoalloggi/emails/gereric-email.tsx index 68693d3..6ce096f 100644 --- a/apps/infoalloggi/emails/gereric-email.tsx +++ b/apps/infoalloggi/emails/gereric-email.tsx @@ -5,8 +5,8 @@ import Base from "./base"; export const GenericMailTag = "generic"; -const props_base = z.object({ - title: z.string(), +export const GenericPropsSchema = z.object({ + title: z.string({error:"Il campo non può essere vuoto"}).nonempty("Il campo non può essere vuoto"), noreply: z.boolean().optional(), link: z .object({ @@ -14,21 +14,10 @@ const props_base = z.object({ label: z.string().optional(), }) .optional(), + corpo: z.custom().optional(), + testo: z.string({error:"Il campo non può essere vuoto"}).nonempty("Il campo non può essere vuoto"), }); -const props_content = z.union([ - z.object({ - corpo: z.custom(), - testo: z.undefined().optional(), - }), - z.object({ - testo: z.string(), - corpo: z.undefined().optional(), - }), -]); - -export const GenericPropsSchema = props_base.and(props_content); - const Generic = ({ title, noreply = false, @@ -41,9 +30,7 @@ const Generic = ({ <> {title}
- {corpo ? ( - corpo - ) : ( + {corpo || ( {testo} diff --git a/apps/infoalloggi/src/components/area-riservata/comunicazioni.tsx b/apps/infoalloggi/src/components/area-riservata/comunicazioni.tsx index 3d7a8f1..fb4feff 100644 --- a/apps/infoalloggi/src/components/area-riservata/comunicazioni.tsx +++ b/apps/infoalloggi/src/components/area-riservata/comunicazioni.tsx @@ -176,6 +176,7 @@ export const AdminComunicazioni = () => { minute: "2-digit", })} + {!email.isActive &&
(Disattivata)
} ); })} diff --git a/apps/infoalloggi/src/components/custom_ui/multiselect.tsx b/apps/infoalloggi/src/components/custom_ui/multiselect.tsx index 3b8bdc5..029002b 100644 --- a/apps/infoalloggi/src/components/custom_ui/multiselect.tsx +++ b/apps/infoalloggi/src/components/custom_ui/multiselect.tsx @@ -13,7 +13,7 @@ import ReactSelect, { type MenuListProps, } from "react-select"; import type {} from "react-select/base"; -import { cn } from "~/lib/utils"; +import { cn, passwordManagerIgnore } from "~/lib/utils"; declare module "react-select/base" { export interface Props< @@ -37,7 +37,7 @@ type BaseProps = { disabled?: boolean; menuPlacement?: "auto" | "bottom" | "top"; defaultValue?: ListOption | ListOption[]; - autoComplete?: HTMLInputAutoCompleteAttribute; + //autoComplete?: HTMLInputAutoCompleteAttribute; }; type MultiProps = { @@ -119,8 +119,8 @@ const CustomInput = ( ); }; @@ -154,13 +154,13 @@ export const MultiSelect = ({ placeholder, disabled, menuPlacement, - autoComplete, + //autoComplete, }: MultiSelectProps) => { const [menuOpen, setMenuOpen] = useState(false); return ( @@ -197,6 +197,7 @@ export const MultiSelect = ({ MenuList: VirtualizedMenuList, Input: CustomInput, }} + data-bitwarden-ignore defaultValue={defaultValue} inputId={elementId} instanceId={elementId} diff --git a/apps/infoalloggi/src/forms/FormEditQueueMail.tsx b/apps/infoalloggi/src/forms/FormEditQueueMail.tsx index 5b4ed18..0ef04f2 100644 --- a/apps/infoalloggi/src/forms/FormEditQueueMail.tsx +++ b/apps/infoalloggi/src/forms/FormEditQueueMail.tsx @@ -13,6 +13,7 @@ import { import { DateInput } from "~/components/custom_ui/inputDate"; import { Button } from "~/components/ui/button"; import Input from "~/components/ui/input"; +import { Switch } from "~/components/ui/switch"; import { Textarea } from "~/components/ui/textarea"; import { useZodForm } from "~/lib/zodForm"; import { useTranslation } from "~/providers/I18nProvider"; @@ -22,6 +23,7 @@ const Schema = z.object({ lock_date: z.date(), lock_time: z.string(), data: z.string(), + isActive: z.boolean(), }); type FormValues = z.infer; @@ -43,6 +45,7 @@ export const FormEditQueueMail = ({ lock_date: new Date(data.lock_expires), lock_time: format(new Date(data.lock_expires), "HH:mm:ss"), data: JSON.stringify(data.data, null, 2), + isActive: data.isActive, }, }); @@ -54,6 +57,7 @@ export const FormEditQueueMail = ({ "T" + fields.lock_time, ), + isActive: fields.isActive, }); } @@ -119,6 +123,26 @@ export const FormEditQueueMail = ({ )} /> + ( + +
+ Attivo + + + + +
+
+ )} + />
void; }; @@ -45,7 +47,8 @@ const Schema = z expires_time: z.string(), mailType: z.enum(TemplateKeys), mailProps: z.record(z.string(), z.unknown()).optional(), - subject: z.string(), + subject: z.string().nonempty("Il campo non può essere vuoto"), + isActive: z.boolean(), }) .superRefine((data, ctx) => { const propsSchema = Templates[data.mailType as TemplateType].props; @@ -55,7 +58,7 @@ const Schema = z for (const issue of result.error.issues) { ctx.addIssue({ ...issue, - path: ["mailProps", ...(issue.path ?? [])], + path: ["mailProps", ...issue.path], }); } } @@ -71,6 +74,7 @@ export const FormAddCodaEmail = ({ submitMutation }: FormProps) => { mailType: TemplateKeys[0], mailProps: undefined, subject: "", + isActive: true, }, }); @@ -84,12 +88,14 @@ export const FormAddCodaEmail = ({ submitMutation }: FormProps) => { lock_expires: Date; template: MailsTemplates; subject: string; + isActive: boolean; } = { lock_expires: new Date( `${fields.expires_date.toISOString().split("T")[0]}T${fields.expires_time}`, ), template, subject: fields.subject, + isActive: fields.isActive, }; submitMutation(values); } @@ -103,10 +109,11 @@ export const FormAddCodaEmail = ({ submitMutation }: FormProps) => { useWatch({ name: "mailProps", control: form.control, - compute: (props) => { + compute: async (props) => { if (!props) return; if ("title" in props) { form.setValue("subject", props.title as string); + await form.trigger("subject"); return; } }, @@ -216,6 +223,26 @@ export const FormAddCodaEmail = ({ submitMutation }: FormProps) => { )} /> + ( + +
+ Attivo + + + + +
+
+ )} + /> @@ -332,12 +359,11 @@ const FieldRenderer = ({ return ( (
- + {label} {optional && ( @@ -352,11 +378,13 @@ const FieldRenderer = ({ field.onChange(e.target.checked)} type="checkbox" /> ) : ( field.onChange(Number(e.target.value)) diff --git a/apps/infoalloggi/src/lib/utils.ts b/apps/infoalloggi/src/lib/utils.ts index 5d677e3..58c3bba 100644 --- a/apps/infoalloggi/src/lib/utils.ts +++ b/apps/infoalloggi/src/lib/utils.ts @@ -69,3 +69,11 @@ export const debounce = ( }, delay); }; }; + +export const passwordManagerIgnore = { + autocomplete: "off", + "data-1p-ignore": true, // 1Password + "data-lpignore": "true", // LastPass + "data-protonpass-ignore": "true", // ProtonPass + "data-bwignore": "1", // Bitwarden +}; diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/comunicazioni/[userId].tsx b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/comunicazioni/[userId].tsx index 59487e8..7717146 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/comunicazioni/[userId].tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/comunicazioni/[userId].tsx @@ -279,6 +279,7 @@ const Coda = ({ userId }: { userId: UsersId }) => { minute: "2-digit", })}
+ {!email.isActive &&
(Disattivata)
}
); })} diff --git a/apps/infoalloggi/src/schemas/public/EventQueue.ts b/apps/infoalloggi/src/schemas/public/EventQueue.ts index 8c8c8a6..4061e2e 100644 --- a/apps/infoalloggi/src/schemas/public/EventQueue.ts +++ b/apps/infoalloggi/src/schemas/public/EventQueue.ts @@ -11,6 +11,8 @@ export default interface EventQueueTable { lock_expires: ColumnType; data: ColumnType; + + isActive: ColumnType; } export type EventQueue = Selectable; @@ -25,16 +27,19 @@ export const EventQueueSchema = z.object({ event_id: eventQueueEventId, lock_expires: z.date(), data: z.unknown().nullable(), + isActive: z.boolean(), }); export const NewEventQueueSchema = z.object({ event_id: eventQueueEventId.optional(), lock_expires: z.union([z.date(), z.string()]).pipe(z.coerce.date()), data: z.unknown().optional().nullable(), + isActive: z.boolean().optional(), }); export const EventQueueUpdateSchema = z.object({ event_id: eventQueueEventId.optional(), lock_expires: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional(), data: z.unknown().optional().nullable(), + isActive: z.boolean().optional(), }); \ No newline at end of file diff --git a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts index 7c5464f..04f4ea2 100644 --- a/apps/infoalloggi/src/server/api/routers/comunicazioni.ts +++ b/apps/infoalloggi/src/server/api/routers/comunicazioni.ts @@ -236,6 +236,7 @@ export const comunicazioniRouter = createTRPCRouter({ userId: usersId, template: z.custom(), subject: z.string(), + isActive: z.boolean(), }), ) .mutation(async ({ input }) => { @@ -260,6 +261,7 @@ export const comunicazioniRouter = createTRPCRouter({ }, }, lock_expires: input.lock_expires, + isActive: input.isActive, }); return { success: true }; } catch (error) { @@ -298,6 +300,7 @@ export const comunicazioniRouter = createTRPCRouter({ }), ) .mutation(async ({ input }) => { + console.log(input.data); return await updateEvent(input.eventId, input.data); }), }); diff --git a/apps/infoalloggi/src/server/controllers/event_queue.controller.ts b/apps/infoalloggi/src/server/controllers/event_queue.controller.ts index e8120c8..b184cf9 100644 --- a/apps/infoalloggi/src/server/controllers/event_queue.controller.ts +++ b/apps/infoalloggi/src/server/controllers/event_queue.controller.ts @@ -129,6 +129,7 @@ const getEventsFromQueue = async () => { const events = await db .selectFrom("event_queue") .selectAll() + .where("event_queue.isActive", "=", true) .where("event_queue.lock_expires", "<=", new Date()) .execute(); return events; @@ -213,6 +214,7 @@ export const getEmailQueue = async () => { .as("username"), ]) .where(sql`data->>'tipo'`, "=", "email") + .execute(); return events; } catch (e) { @@ -286,6 +288,23 @@ export const clearUserEmailQueue = async (userId: UsersId) => { } }; +export const disableUserEmailQueue = async (userId: UsersId) => { + try { + await db + .updateTable("event_queue") + .set({ isActive: false }) + .where(sql`data->>'tipo'`, "=", "email") + .where(sql`data->'data'->>'userId'`, "=", userId) + .execute(); + } catch (e) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: `Error while disabling user events from queue, ${(e as Error).message}`, + cause: e, + }); + } +}; + export const updateEvent = async ( eventId: EventQueueEventId, data: EventQueueUpdate, diff --git a/apps/infoalloggi/src/server/controllers/servizio.controller.ts b/apps/infoalloggi/src/server/controllers/servizio.controller.ts index f8ae8a4..e12bb5a 100644 --- a/apps/infoalloggi/src/server/controllers/servizio.controller.ts +++ b/apps/infoalloggi/src/server/controllers/servizio.controller.ts @@ -21,7 +21,7 @@ import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage"; import { addEventToQueue, canSendNotification, - clearUserEmailQueue, + disableUserEmailQueue, nextTimeForNotification, } from "~/server/controllers/event_queue.controller"; import { @@ -927,8 +927,8 @@ export const interruzioneServizio = async ( }, }); - // cleanup coda notifiche: rimuove notifiche future relative a questo servizio - await clearUserEmailQueue(utente.id); + // disabilita coda notifiche: rimuove notifiche future relative a questo servizio + await disableUserEmailQueue(utente.id); return true; } catch (e) { diff --git a/apps/infoalloggi/src/server/services/servizio.service.ts b/apps/infoalloggi/src/server/services/servizio.service.ts index c4ce998..9a526f7 100644 --- a/apps/infoalloggi/src/server/services/servizio.service.ts +++ b/apps/infoalloggi/src/server/services/servizio.service.ts @@ -7,7 +7,7 @@ import type { ServizioUpdate, } from "~/schemas/public/Servizio"; import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci"; -import { clearUserEmailQueue } from "~/server/controllers/event_queue.controller"; +import { disableUserEmailQueue } from "~/server/controllers/event_queue.controller"; import { db } from "../db"; export const addServizio = async (data: NewServizio) => { @@ -97,7 +97,7 @@ export const updateServizio = async ({ ); if (!isDisabledPrev.isDisabled && updated.isDisabled) { // Only the on isDisabled false->true change - await clearUserEmailQueue(updated.user_id); + await disableUserEmailQueue(updated.user_id); } return updated; } catch (e) { diff --git a/apps/infoalloggi/tsconfig.json b/apps/infoalloggi/tsconfig.json index 54cef03..665d386 100644 --- a/apps/infoalloggi/tsconfig.json +++ b/apps/infoalloggi/tsconfig.json @@ -16,6 +16,8 @@ "moduleResolution": "Bundler", "noEmit": true, "noUncheckedIndexedAccess": true, + "allowUnreachableCode": false, + "noFallthroughCasesInSwitch": true, "rootDir": ".", "paths": { "~/*": ["./src/*"],