isDisabled bool
This commit is contained in:
parent
91a650079d
commit
d0f146b99e
15 changed files with 212 additions and 57 deletions
3
apps/db/migrations/nulla_fatto_flag.up.sql
Normal file
3
apps/db/migrations/nulla_fatto_flag.up.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- nulla_fatto_flag.up.sql
|
||||||
|
ALTER TABLE IF EXISTS public.servizio
|
||||||
|
ADD COLUMN IF NOT EXISTS "isDisabled" boolean NOT NULL DEFAULT false;
|
||||||
|
|
@ -70,8 +70,6 @@ ARG EXP_API_USER
|
||||||
ENV EXP_API_USER=$EXP_API_USER
|
ENV EXP_API_USER=$EXP_API_USER
|
||||||
ARG EXP_API_PASS
|
ARG EXP_API_PASS
|
||||||
ENV EXP_API_PASS=$EXP_API_PASS
|
ENV EXP_API_PASS=$EXP_API_PASS
|
||||||
ARG TILES_URL
|
|
||||||
ENV TILES_URL=$TILES_URL
|
|
||||||
ARG SKEBBY_USER
|
ARG SKEBBY_USER
|
||||||
ENV SKEBBY_USER=$SKEBBY_USER
|
ENV SKEBBY_USER=$SKEBBY_USER
|
||||||
ARG SKEBBY_PASS
|
ARG SKEBBY_PASS
|
||||||
|
|
|
||||||
|
|
@ -798,6 +798,7 @@ export const EditServizioModal = () => {
|
||||||
disabled={
|
disabled={
|
||||||
!isAdmin &&
|
!isAdmin &&
|
||||||
(servizio.isInterrotto ||
|
(servizio.isInterrotto ||
|
||||||
|
servizio.isDisabled ||
|
||||||
servizio.annunci.some((a) => a.status_conferma !== null))
|
servizio.annunci.some((a) => a.status_conferma !== null))
|
||||||
}
|
}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
PackageCheck,
|
PackageCheck,
|
||||||
|
SearchSlash,
|
||||||
SearchX,
|
SearchX,
|
||||||
Timer,
|
Timer,
|
||||||
Trash2,
|
Trash2,
|
||||||
|
|
@ -161,6 +162,9 @@ const ServizioLinkCard = ({
|
||||||
if (servizio.isInterrotto) {
|
if (servizio.isInterrotto) {
|
||||||
status = "interrotto";
|
status = "interrotto";
|
||||||
StatusIcon = SearchX;
|
StatusIcon = SearchX;
|
||||||
|
} else if (servizio.isDisabled) {
|
||||||
|
status = "disabled";
|
||||||
|
StatusIcon = SearchSlash;
|
||||||
} else if (!servizio.isOkAcconto) {
|
} else if (!servizio.isOkAcconto) {
|
||||||
status = "in_attesa";
|
status = "in_attesa";
|
||||||
StatusIcon = ClockFading;
|
StatusIcon = ClockFading;
|
||||||
|
|
@ -223,6 +227,7 @@ const ServizioLinkCard = ({
|
||||||
className={cn(
|
className={cn(
|
||||||
status === "attivo" && "border-green-500",
|
status === "attivo" && "border-green-500",
|
||||||
status === "interrotto" && "border-red-500",
|
status === "interrotto" && "border-red-500",
|
||||||
|
status === "disabled" && "border-gray-500",
|
||||||
status === "scaduto" && "border-gray-500",
|
status === "scaduto" && "border-gray-500",
|
||||||
status === "completato" && "border-blue-500",
|
status === "completato" && "border-blue-500",
|
||||||
"transition-shadow",
|
"transition-shadow",
|
||||||
|
|
@ -243,6 +248,7 @@ const ServizioLinkCard = ({
|
||||||
"flex items-center gap-2",
|
"flex items-center gap-2",
|
||||||
status === "attivo" && "text-green-500",
|
status === "attivo" && "text-green-500",
|
||||||
status === "interrotto" && "text-red-500",
|
status === "interrotto" && "text-red-500",
|
||||||
|
status === "disabled" && "text-gray-500",
|
||||||
status === "scaduto" && "text-gray-500",
|
status === "scaduto" && "text-gray-500",
|
||||||
status === "completato" && "text-blue-500",
|
status === "completato" && "text-blue-500",
|
||||||
)}
|
)}
|
||||||
|
|
@ -436,6 +442,61 @@ const Main = () => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (servizio.isDisabled) {
|
||||||
|
if (isAdmin) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex w-full items-center justify-center rounded-md bg-gray-300 p-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<SearchSlash className="size-5" />
|
||||||
|
<span>Servizio Disattivato</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="@container grid grid-flow-row @sm:grid-cols-2 grid-cols-1 gap-4">
|
||||||
|
{servizio.annunci.map((data) => {
|
||||||
|
return (
|
||||||
|
<ServizioAnnuncioProvider
|
||||||
|
data={data}
|
||||||
|
key={data.id}
|
||||||
|
servizioId={servizio.servizio_id}
|
||||||
|
>
|
||||||
|
<AnnuncioCard />
|
||||||
|
</ServizioAnnuncioProvider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{servizio.annunci.length > 1 &&
|
||||||
|
servizio.annunci.length % 2 === 1 && (
|
||||||
|
<div className="@sm:block hidden size-full rounded-md border border-(--pattern-fg) bg-[repeating-linear-gradient(45deg,var(--pattern-fg)_0,var(--pattern-fg)_1px,transparent_0,transparent_50%)] bg-size-[10px_10px] bg-fixed opacity-10 [--pattern-fg:#696969] dark:[--pattern-fg:#e1e1e1]" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col items-center justify-center gap-4 py-10 text-center">
|
||||||
|
<div className="flex size-14 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-500/10">
|
||||||
|
<SearchSlash className="size-7" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p className="font-semibold text-xl">
|
||||||
|
{t.servizio.servizio_disattivato}
|
||||||
|
</p>
|
||||||
|
<p className="max-w-sm text-muted-foreground text-sm">
|
||||||
|
{t.servizio.servizio_disattivato_desc}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
isAdmin
|
||||||
|
? `/area-riservata/admin/user-view/ricerca/${userId}`
|
||||||
|
: "/area-riservata/dashboard"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button variant="secondary">Torna alla lista dei servizi</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
// mostra onboarding
|
// mostra onboarding
|
||||||
if (!servizio.onboardOk) {
|
if (!servizio.onboardOk) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
Bug,
|
|
||||||
Check,
|
Check,
|
||||||
Cog,
|
Cog,
|
||||||
Euro,
|
Euro,
|
||||||
|
|
@ -7,7 +6,6 @@ import {
|
||||||
Logs,
|
Logs,
|
||||||
Plus,
|
Plus,
|
||||||
Search,
|
Search,
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
@ -27,11 +25,6 @@ import { LoadingPage } from "~/components/loading";
|
||||||
import { InterruzioneServizio } from "~/components/servizio/interruzione";
|
import { InterruzioneServizio } from "~/components/servizio/interruzione";
|
||||||
import { EditServizioModal } from "~/components/servizio/parametri_ricerca";
|
import { EditServizioModal } from "~/components/servizio/parametri_ricerca";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from "~/components/ui/collapsible";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -568,33 +561,11 @@ const EditServizioAdmin = () => {
|
||||||
</CredenzaDescription>
|
</CredenzaDescription>
|
||||||
</CredenzaHeader>
|
</CredenzaHeader>
|
||||||
<CredenzaBody className="max-h-[80vh] w-full space-y-2 overflow-y-auto pb-5">
|
<CredenzaBody className="max-h-[80vh] w-full space-y-2 overflow-y-auto pb-5">
|
||||||
<FormEditServizio initialData={servizio} onSubmit={onSubmit} />
|
<FormEditServizio
|
||||||
|
handleRemove={() => remove({ servizioId })}
|
||||||
<Collapsible>
|
initialData={servizio}
|
||||||
<CollapsibleTrigger asChild>
|
onSubmit={onSubmit}
|
||||||
<Button variant="warning">
|
/>
|
||||||
<Bug /> Debug Info Servizio
|
|
||||||
</Button>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent>
|
|
||||||
{(() => {
|
|
||||||
const { annunci, ...rest } = servizio;
|
|
||||||
return <pre>{JSON.stringify(rest, null, 2)}</pre>;
|
|
||||||
})()}
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
<Confirm
|
|
||||||
description="Sei sicuro di voler eliminare questo servizio? L'azione è irreversibile."
|
|
||||||
onConfirm={() => {
|
|
||||||
remove({ servizioId });
|
|
||||||
}}
|
|
||||||
title="Elimina servizio"
|
|
||||||
>
|
|
||||||
<Button aria-label="Elimina Servizio" variant="destructive">
|
|
||||||
<Trash2 className="size-6" />
|
|
||||||
Elimina Servizio
|
|
||||||
</Button>
|
|
||||||
</Confirm>
|
|
||||||
</CredenzaBody>
|
</CredenzaBody>
|
||||||
</CredenzaContent>
|
</CredenzaContent>
|
||||||
</Credenza>
|
</Credenza>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Logs,
|
Logs,
|
||||||
ReceiptEuro,
|
ReceiptEuro,
|
||||||
|
SearchSlash,
|
||||||
SearchX,
|
SearchX,
|
||||||
UserCog,
|
UserCog,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
@ -75,6 +76,7 @@ type RichiesteTableProps = {
|
||||||
const StatusMapping = {
|
const StatusMapping = {
|
||||||
attesa_attivazione: "Attesa di attivazione",
|
attesa_attivazione: "Attesa di attivazione",
|
||||||
interrotto: "Interrotto",
|
interrotto: "Interrotto",
|
||||||
|
disabled: "Disattivato",
|
||||||
onboarding: "Onboarding",
|
onboarding: "Onboarding",
|
||||||
scaduto: "Scaduto",
|
scaduto: "Scaduto",
|
||||||
consulenza_da_pagare: "Consulenza da pagare",
|
consulenza_da_pagare: "Consulenza da pagare",
|
||||||
|
|
@ -93,6 +95,9 @@ function statusParser(data: RichiesteData): keyof typeof StatusMapping {
|
||||||
if (data.isInterrotto) {
|
if (data.isInterrotto) {
|
||||||
return "interrotto";
|
return "interrotto";
|
||||||
}
|
}
|
||||||
|
if (data.isDisabled) {
|
||||||
|
return "disabled";
|
||||||
|
}
|
||||||
if (!data.onboardOk) {
|
if (!data.onboardOk) {
|
||||||
return "onboarding";
|
return "onboarding";
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +385,9 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
||||||
} else if (row.original.isInterrotto) {
|
} else if (row.original.isInterrotto) {
|
||||||
decorrenzaColor = "text-destructive";
|
decorrenzaColor = "text-destructive";
|
||||||
decorrenzaTooltip = "Servizio interrotto";
|
decorrenzaTooltip = "Servizio interrotto";
|
||||||
|
} else if (row.original.isDisabled) {
|
||||||
|
decorrenzaColor = "text-muted-foreground";
|
||||||
|
decorrenzaTooltip = "Servizio disattivato";
|
||||||
} else if (row.original.isOkAcconto && !row.original.isOkSaldo) {
|
} else if (row.original.isOkAcconto && !row.original.isOkSaldo) {
|
||||||
decorrenzaColor = "text-warning";
|
decorrenzaColor = "text-warning";
|
||||||
decorrenzaTooltip = "Attesa di saldo";
|
decorrenzaTooltip = "Attesa di saldo";
|
||||||
|
|
@ -452,6 +460,10 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
||||||
"border-destructive bg-destructive/10 text-destructive";
|
"border-destructive bg-destructive/10 text-destructive";
|
||||||
icon = <SearchX className="stroke-red-500 dark:stroke-red-400" />;
|
icon = <SearchX className="stroke-red-500 dark:stroke-red-400" />;
|
||||||
break;
|
break;
|
||||||
|
case "disabled":
|
||||||
|
badgeClass = "border-gray-500 bg-muted/10 text-muted-foreground";
|
||||||
|
icon = <SearchSlash className="" />;
|
||||||
|
break;
|
||||||
case "onboarding":
|
case "onboarding":
|
||||||
badgeClass = "border-pink-500 bg-pink-500/10 text-pink-600";
|
badgeClass = "border-pink-500 bg-pink-500/10 text-pink-600";
|
||||||
icon = (
|
icon = (
|
||||||
|
|
@ -543,6 +555,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
||||||
accessorKey: "interruzione_expires_at",
|
accessorKey: "interruzione_expires_at",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
if (row.original.isInterrotto) return "---";
|
if (row.original.isInterrotto) return "---";
|
||||||
|
if (row.original.isDisabled) return "---";
|
||||||
if (!row.original.decorrenza) return "---";
|
if (!row.original.decorrenza) return "---";
|
||||||
const expiresAt = add(row.original.decorrenza, {
|
const expiresAt = add(row.original.decorrenza, {
|
||||||
days: row.original.interruzioneDays,
|
days: row.original.interruzioneDays,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ export const env = createEnv({
|
||||||
POSTGRES_USER: z.string(),
|
POSTGRES_USER: z.string(),
|
||||||
STRIPE_SECRET_KEY: z.string(),
|
STRIPE_SECRET_KEY: z.string(),
|
||||||
STRIPE_WEBHOOK_SECRET: z.string(),
|
STRIPE_WEBHOOK_SECRET: z.string(),
|
||||||
TILES_URL: z.string(),
|
|
||||||
SKEBBY_USER: z.string(),
|
SKEBBY_USER: z.string(),
|
||||||
SKEBBY_PASS: z.string(),
|
SKEBBY_PASS: z.string(),
|
||||||
REVALIDATION_SECRET: z.string(),
|
REVALIDATION_SECRET: z.string(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { X } from "lucide-react";
|
import { Bug, Trash2, X } from "lucide-react";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
import { Confirm } from "~/components/confirm";
|
||||||
import { Counter } from "~/components/counter";
|
import { Counter } from "~/components/counter";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
|
|
@ -12,26 +13,33 @@ import {
|
||||||
} from "~/components/custom_ui/form";
|
} from "~/components/custom_ui/form";
|
||||||
import { DateInput } from "~/components/custom_ui/inputDate";
|
import { DateInput } from "~/components/custom_ui/inputDate";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "~/components/ui/collapsible";
|
||||||
import { Label } from "~/components/ui/label";
|
import { Label } from "~/components/ui/label";
|
||||||
import { Switch } from "~/components/ui/switch";
|
import { Switch } from "~/components/ui/switch";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
import { useZodForm } from "~/lib/zodForm";
|
import { useZodForm } from "~/lib/zodForm";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import type { Servizio } from "~/schemas/public/Servizio";
|
import { ServizioSchema } from "~/schemas/public/Servizio";
|
||||||
|
import type { ServizioData } from "~/server/controllers/servizio.controller";
|
||||||
import { DEFAULT_SERVIZIO_INTERRUZIONE_DURATION_DAYS } from "~/utils/config";
|
import { DEFAULT_SERVIZIO_INTERRUZIONE_DURATION_DAYS } from "~/utils/config";
|
||||||
|
|
||||||
const Schema = z.object({
|
const Schema = ServizioSchema.pick({
|
||||||
decorrenza: z.date().nullable(),
|
decorrenza: true,
|
||||||
isInterrotto: z.boolean(),
|
isInterrotto: true,
|
||||||
isOkAcconto: z.boolean(),
|
isOkAcconto: true,
|
||||||
isOkConsulenza: z.boolean(),
|
isOkConsulenza: true,
|
||||||
isOkSaldo: z.boolean(),
|
isOkSaldo: true,
|
||||||
skipPayment: z.boolean(),
|
skipPayment: true,
|
||||||
skipControlloDoc: z.boolean(),
|
skipControlloDoc: true,
|
||||||
skipDocMotivazione: z.boolean(),
|
skipDocMotivazione: true,
|
||||||
onboardOk: z.boolean(),
|
onboardOk: true,
|
||||||
interruzioneDays: z.number(),
|
interruzioneDays: true,
|
||||||
acceptedInterr: z.boolean(),
|
acceptedInterr: true,
|
||||||
|
isDisabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormValues = z.infer<typeof Schema>;
|
export type FormValues = z.infer<typeof Schema>;
|
||||||
|
|
@ -39,16 +47,29 @@ export type FormValues = z.infer<typeof Schema>;
|
||||||
export const FormEditServizio = ({
|
export const FormEditServizio = ({
|
||||||
initialData,
|
initialData,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
handleRemove,
|
||||||
}: {
|
}: {
|
||||||
initialData: Servizio | undefined;
|
initialData: ServizioData | undefined;
|
||||||
onSubmit: (fields: FormValues) => void;
|
onSubmit: (fields: FormValues) => void;
|
||||||
|
handleRemove: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
// This can come from your database or API.
|
// This can come from your database or API.
|
||||||
let defaultValues: FormValues;
|
let defaultValues: FormValues;
|
||||||
|
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
defaultValues = {
|
defaultValues = {
|
||||||
...initialData,
|
decorrenza: initialData.decorrenza,
|
||||||
|
isInterrotto: initialData.isInterrotto,
|
||||||
|
isOkAcconto: initialData.isOkAcconto,
|
||||||
|
isOkConsulenza: initialData.isOkConsulenza,
|
||||||
|
isOkSaldo: initialData.isOkSaldo,
|
||||||
|
skipPayment: initialData.skipPayment,
|
||||||
|
skipControlloDoc: initialData.skipControlloDoc,
|
||||||
|
skipDocMotivazione: initialData.skipDocMotivazione,
|
||||||
|
onboardOk: initialData.onboardOk,
|
||||||
|
interruzioneDays: initialData.interruzioneDays,
|
||||||
|
acceptedInterr: initialData.acceptedInterr,
|
||||||
|
isDisabled: initialData.isDisabled,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
defaultValues = {
|
defaultValues = {
|
||||||
|
|
@ -63,6 +84,7 @@ export const FormEditServizio = ({
|
||||||
onboardOk: false,
|
onboardOk: false,
|
||||||
interruzioneDays: DEFAULT_SERVIZIO_INTERRUZIONE_DURATION_DAYS,
|
interruzioneDays: DEFAULT_SERVIZIO_INTERRUZIONE_DURATION_DAYS,
|
||||||
acceptedInterr: false,
|
acceptedInterr: false,
|
||||||
|
isDisabled: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const { locale, t } = useTranslation();
|
const { locale, t } = useTranslation();
|
||||||
|
|
@ -147,6 +169,7 @@ export const FormEditServizio = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
@ -280,6 +303,7 @@ export const FormEditServizio = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
@ -359,7 +383,62 @@ export const FormEditServizio = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit">Salva</Button>
|
<hr />
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="isDisabled"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2">
|
||||||
|
<FormLabel htmlFor="isDisabled">DISABILITATO</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
className="data-[state=checked]:bg-neutral-700"
|
||||||
|
defaultChecked={field.value}
|
||||||
|
id="isDisabled"
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
Disabilita il servizio, chiude la posizione senza eliminare.
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Collapsible className="space-y-2">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Button type="submit">Salva</Button>
|
||||||
|
|
||||||
|
<CollapsibleTrigger asChild>
|
||||||
|
<Button variant="warning">
|
||||||
|
<Bug /> Debug Info Servizio
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
|
||||||
|
<Confirm
|
||||||
|
description="Sei sicuro di voler eliminare questo servizio? L'azione è irreversibile."
|
||||||
|
onConfirm={() => {
|
||||||
|
handleRemove();
|
||||||
|
}}
|
||||||
|
title="Elimina servizio"
|
||||||
|
>
|
||||||
|
<Button aria-label="Elimina Servizio" variant="destructive">
|
||||||
|
<Trash2 className="size-6" />
|
||||||
|
Elimina Servizio
|
||||||
|
</Button>
|
||||||
|
</Confirm>
|
||||||
|
</div>
|
||||||
|
<CollapsibleContent>
|
||||||
|
{(() => {
|
||||||
|
if (!initialData) return null;
|
||||||
|
const { annunci, ...rest } = initialData;
|
||||||
|
return <pre>{JSON.stringify(rest, null, 2)}</pre>;
|
||||||
|
})()}
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1320,6 +1320,7 @@ The Stable Rent service, ideal for those with demonstrable work references and f
|
||||||
interrotto: "Interrupted",
|
interrotto: "Interrupted",
|
||||||
scaduto: "Expired",
|
scaduto: "Expired",
|
||||||
completato: "Completed",
|
completato: "Completed",
|
||||||
|
disabled: "Disabled",
|
||||||
},
|
},
|
||||||
torna_alla_dashboard: "Dashboard",
|
torna_alla_dashboard: "Dashboard",
|
||||||
dettaglio_servizio: "Service Details",
|
dettaglio_servizio: "Service Details",
|
||||||
|
|
@ -1330,7 +1331,9 @@ The Stable Rent service, ideal for those with demonstrable work references and f
|
||||||
durata_servizio: "Service Duration:",
|
durata_servizio: "Service Duration:",
|
||||||
servizio_interrotto: "Service Interrupted",
|
servizio_interrotto: "Service Interrupted",
|
||||||
servizio_interrotto_desc:
|
servizio_interrotto_desc:
|
||||||
"This service cannot be reused as it has been interrupted.",
|
"This service cannot be used as it has been interrupted.",
|
||||||
|
servizio_disattivato: "Service Disabled",
|
||||||
|
servizio_disattivato_desc: "This service cannot be used.",
|
||||||
non_attivo: "Service not yet activated",
|
non_attivo: "Service not yet activated",
|
||||||
attiva_cta: "Activate the Service Now!",
|
attiva_cta: "Activate the Service Now!",
|
||||||
per_te_1: "Already selected",
|
per_te_1: "Already selected",
|
||||||
|
|
|
||||||
|
|
@ -1319,6 +1319,7 @@ export const it: LangDict = {
|
||||||
in_attesa: "In Attesa di Attivazione",
|
in_attesa: "In Attesa di Attivazione",
|
||||||
scaduto: "Scaduto",
|
scaduto: "Scaduto",
|
||||||
completato: "Completato",
|
completato: "Completato",
|
||||||
|
disabled: "Disattivato",
|
||||||
},
|
},
|
||||||
torna_alla_dashboard: "Dashboard",
|
torna_alla_dashboard: "Dashboard",
|
||||||
dettaglio_servizio: "Dettaglio Servizio",
|
dettaglio_servizio: "Dettaglio Servizio",
|
||||||
|
|
@ -1329,7 +1330,9 @@ export const it: LangDict = {
|
||||||
durata_servizio: "Durata servizio:",
|
durata_servizio: "Durata servizio:",
|
||||||
servizio_interrotto: "Servizio interrotto",
|
servizio_interrotto: "Servizio interrotto",
|
||||||
servizio_interrotto_desc:
|
servizio_interrotto_desc:
|
||||||
"Non è possibile riutilizzare questo servizio, poichè è stato interrotto.",
|
"Non è possibile utilizzare questo servizio, poichè è stato interrotto.",
|
||||||
|
servizio_disattivato: "Servizio disattivato",
|
||||||
|
servizio_disattivato_desc: "Non è possibile utilizzare questo servizio.",
|
||||||
non_attivo: "Servizio non ancora attivato",
|
non_attivo: "Servizio non ancora attivato",
|
||||||
attiva_cta: "Attiva il Servizio Ora!",
|
attiva_cta: "Attiva il Servizio Ora!",
|
||||||
per_te_1: "Sono già stati selezionati",
|
per_te_1: "Sono già stati selezionati",
|
||||||
|
|
|
||||||
|
|
@ -779,6 +779,7 @@ export type LangDict = {
|
||||||
in_attesa: string;
|
in_attesa: string;
|
||||||
scaduto: string;
|
scaduto: string;
|
||||||
completato: string;
|
completato: string;
|
||||||
|
disabled: string;
|
||||||
};
|
};
|
||||||
torna_alla_dashboard: string;
|
torna_alla_dashboard: string;
|
||||||
dettaglio_servizio: string;
|
dettaglio_servizio: string;
|
||||||
|
|
@ -789,6 +790,8 @@ export type LangDict = {
|
||||||
durata_servizio: string;
|
durata_servizio: string;
|
||||||
servizio_interrotto: string;
|
servizio_interrotto: string;
|
||||||
servizio_interrotto_desc: string;
|
servizio_interrotto_desc: string;
|
||||||
|
servizio_disattivato: string;
|
||||||
|
servizio_disattivato_desc: string;
|
||||||
non_attivo: string;
|
non_attivo: string;
|
||||||
attiva_cta: string;
|
attiva_cta: string;
|
||||||
per_te_1: string;
|
per_te_1: string;
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ export default interface ServizioTable {
|
||||||
interruzioneDays: ColumnType<number, number | undefined, number>;
|
interruzioneDays: ColumnType<number, number | undefined, number>;
|
||||||
|
|
||||||
acceptedInterr: ColumnType<boolean, boolean | undefined, boolean>;
|
acceptedInterr: ColumnType<boolean, boolean | undefined, boolean>;
|
||||||
|
|
||||||
|
isDisabled: ColumnType<boolean, boolean | undefined, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Servizio = Selectable<ServizioTable>;
|
export type Servizio = Selectable<ServizioTable>;
|
||||||
|
|
@ -115,6 +117,7 @@ export const ServizioSchema = z.object({
|
||||||
onboardOk: z.boolean(),
|
onboardOk: z.boolean(),
|
||||||
interruzioneDays: z.number(),
|
interruzioneDays: z.number(),
|
||||||
acceptedInterr: z.boolean(),
|
acceptedInterr: z.boolean(),
|
||||||
|
isDisabled: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NewServizioSchema = z.object({
|
export const NewServizioSchema = z.object({
|
||||||
|
|
@ -150,6 +153,7 @@ export const NewServizioSchema = z.object({
|
||||||
onboardOk: z.boolean().optional(),
|
onboardOk: z.boolean().optional(),
|
||||||
interruzioneDays: z.number().optional(),
|
interruzioneDays: z.number().optional(),
|
||||||
acceptedInterr: z.boolean().optional(),
|
acceptedInterr: z.boolean().optional(),
|
||||||
|
isDisabled: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServizioUpdateSchema = z.object({
|
export const ServizioUpdateSchema = z.object({
|
||||||
|
|
@ -185,4 +189,5 @@ export const ServizioUpdateSchema = z.object({
|
||||||
onboardOk: z.boolean().optional(),
|
onboardOk: z.boolean().optional(),
|
||||||
interruzioneDays: z.number().optional(),
|
interruzioneDays: z.number().optional(),
|
||||||
acceptedInterr: z.boolean().optional(),
|
acceptedInterr: z.boolean().optional(),
|
||||||
|
isDisabled: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
@ -198,6 +198,7 @@ export const servizioRouter = createTRPCRouter({
|
||||||
!servizio ||
|
!servizio ||
|
||||||
servizio.isOkAcconto ||
|
servizio.isOkAcconto ||
|
||||||
servizio.isInterrotto ||
|
servizio.isInterrotto ||
|
||||||
|
servizio.isDisabled ||
|
||||||
servizio.decorrenza !== null
|
servizio.decorrenza !== null
|
||||||
) {
|
) {
|
||||||
return { available: false, userId: null };
|
return { available: false, userId: null };
|
||||||
|
|
|
||||||
|
|
@ -1143,6 +1143,7 @@ export type RichiesteData = Pick<
|
||||||
| "servizio_id"
|
| "servizio_id"
|
||||||
| "user_id"
|
| "user_id"
|
||||||
| "isInterrotto"
|
| "isInterrotto"
|
||||||
|
| "isDisabled"
|
||||||
| "isOkAcconto"
|
| "isOkAcconto"
|
||||||
| "isOkSaldo"
|
| "isOkSaldo"
|
||||||
| "decorrenza"
|
| "decorrenza"
|
||||||
|
|
@ -1184,6 +1185,7 @@ export const getRichieste = async (): Promise<RichiesteData[]> => {
|
||||||
"servizio.created_at",
|
"servizio.created_at",
|
||||||
"servizio.user_id",
|
"servizio.user_id",
|
||||||
"servizio.isInterrotto",
|
"servizio.isInterrotto",
|
||||||
|
"servizio.isDisabled",
|
||||||
"servizio.isOkAcconto",
|
"servizio.isOkAcconto",
|
||||||
"servizio.isOkSaldo",
|
"servizio.isOkSaldo",
|
||||||
"servizio.decorrenza",
|
"servizio.decorrenza",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import type {
|
||||||
ServizioUpdate,
|
ServizioUpdate,
|
||||||
} from "~/schemas/public/Servizio";
|
} from "~/schemas/public/Servizio";
|
||||||
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
||||||
|
import { clearUserEmailQueue } from "~/server/controllers/event_queue.controller";
|
||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
|
|
||||||
export const addServizio = async (data: NewServizio) => {
|
export const addServizio = async (data: NewServizio) => {
|
||||||
|
|
@ -78,7 +79,14 @@ export const updateServizio = async ({
|
||||||
data: ServizioUpdate;
|
data: ServizioUpdate;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
return await db
|
const isDisabledPrev = await db
|
||||||
|
.selectFrom("servizio")
|
||||||
|
.select("isDisabled")
|
||||||
|
.where("servizio_id", "=", servizioId)
|
||||||
|
.executeTakeFirstOrThrow(
|
||||||
|
() => new Error(`Servizio not found - servizioId: ${servizioId}`),
|
||||||
|
);
|
||||||
|
const updated = await db
|
||||||
.updateTable("servizio")
|
.updateTable("servizio")
|
||||||
.set(data)
|
.set(data)
|
||||||
.where("servizio_id", "=", servizioId)
|
.where("servizio_id", "=", servizioId)
|
||||||
|
|
@ -87,6 +95,11 @@ export const updateServizio = async ({
|
||||||
() =>
|
() =>
|
||||||
new Error(`Failed to update servizio - servizioId: ${servizioId}`),
|
new Error(`Failed to update servizio - servizioId: ${servizioId}`),
|
||||||
);
|
);
|
||||||
|
if (!isDisabledPrev.isDisabled && updated.isDisabled) {
|
||||||
|
// Only the on isDisabled false->true change
|
||||||
|
await clearUserEmailQueue(updated.user_id);
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue