feat: update service confirmation routing and enhance order management UI components

This commit is contained in:
Marco Pedone 2026-03-30 17:36:35 +02:00
parent 04f0c5e790
commit 73b1997c15
6 changed files with 127 additions and 18 deletions

View file

@ -1,6 +1,10 @@
TODOS: TODOS:
- Log email fallite e retry - Log email fallite e retry
conferma presa visione deve rimandare a servizio giusto non /ricerrca
AFTER MVP: AFTER MVP:
- TODO migrazione app router https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#migrating-from-pages-to-app - TODO migrazione app router https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#migrating-from-pages-to-app
- Sezione "La mia locazione" con i dati della locazione attuale dell'utente - Sezione "La mia locazione" con i dati della locazione attuale dell'utente

View file

@ -51,6 +51,8 @@ import { cn, formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider"; import { useTranslation } from "~/providers/I18nProvider";
import { useServizio } from "~/providers/ServizioProvider"; import { useServizio } from "~/providers/ServizioProvider";
import type { AnnunciId } from "~/schemas/public/Annunci"; import type { AnnunciId } from "~/schemas/public/Annunci";
import type { Ordini } from "~/schemas/public/Ordini";
import type { Prezziario } from "~/schemas/public/Prezziario";
import type { ServizioServizioId } from "~/schemas/public/Servizio"; import type { ServizioServizioId } from "~/schemas/public/Servizio";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { UsersId } from "~/schemas/public/Users"; import type { UsersId } from "~/schemas/public/Users";
@ -215,6 +217,25 @@ const ServizioPrezzo = () => {
}; };
const ServizioOrders = () => { const ServizioOrders = () => {
const { isAdmin, servizio } = useServizio(); const { isAdmin, servizio } = useServizio();
return (
<OrdersModal isAdmin={isAdmin} ordini={servizio.ordini}>
<Button className="flex-1" variant="outline">
<Logs />
<span>Ordini</span>
</Button>
</OrdersModal>
);
};
export const OrdersModal = ({
children,
isAdmin,
ordini,
}: {
children: React.ReactNode;
isAdmin: boolean;
ordini: (Ordini & Pick<Prezziario, "testo_condizioni">)[];
}) => {
const [openOrders, setOpenOrders] = useState(false); const [openOrders, setOpenOrders] = useState(false);
const [openEdit, setOpenEdit] = useState(false); const [openEdit, setOpenEdit] = useState(false);
@ -231,12 +252,7 @@ const ServizioOrders = () => {
return ( return (
<> <>
<Dialog onOpenChange={setOpenOrders} open={openOrders}> <Dialog onOpenChange={setOpenOrders} open={openOrders}>
<DialogTrigger asChild> <DialogTrigger asChild>{children}</DialogTrigger>
<Button className="flex-1" variant="outline">
<Logs />
<span>Ordini</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl"> <DialogContent className="sm:max-w-2xl">
<DialogHeader className="flex-row items-center gap-4"> <DialogHeader className="flex-row items-center gap-4">
<DialogTitle>Ordini Servizio</DialogTitle> <DialogTitle>Ordini Servizio</DialogTitle>
@ -266,14 +282,14 @@ const ServizioOrders = () => {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{servizio.ordini.length === 0 && ( {ordini.length === 0 && (
<tr> <tr>
<td className="py-4 text-center" colSpan={4}> <td className="py-4 text-center" colSpan={4}>
<span>Nessun ordine disponibile</span> <span>Nessun ordine disponibile</span>
</td> </td>
</tr> </tr>
)} )}
{servizio.ordini.map((ordine) => ( {ordini.map((ordine) => (
<TableRow key={ordine.ordine_id}> <TableRow key={ordine.ordine_id}>
<TableCell> <TableCell>
{ordine.created_at.toLocaleDateString("it-IT")} {ordine.created_at.toLocaleDateString("it-IT")}

View file

@ -13,6 +13,7 @@ import {
CircleCheckBig, CircleCheckBig,
ClockFading, ClockFading,
ExternalLink, ExternalLink,
Logs,
SearchX, SearchX,
UserCog, UserCog,
} from "lucide-react"; } from "lucide-react";
@ -21,6 +22,7 @@ import { useRef } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { NotePopover } from "~/components/notePopover"; import { NotePopover } from "~/components/notePopover";
import { AnnuncioCardRichieste } from "~/components/servizio/annuncio_card"; import { AnnuncioCardRichieste } from "~/components/servizio/annuncio_card";
import { OrdersModal } from "~/components/servizio/servizio_actions";
import { WhatsAppIcon2 } from "~/components/svgs"; import { WhatsAppIcon2 } from "~/components/svgs";
import { Badge } from "~/components/ui/badge"; import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
@ -73,6 +75,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
status: "Status", status: "Status",
tipologia: "Tipo", tipologia: "Tipo",
annunci: "Annunci", annunci: "Annunci",
ordini: "Ordini",
interruzione_expires_at: "Scad. interr.", interruzione_expires_at: "Scad. interr.",
actions: "Azioni", actions: "Azioni",
}; };
@ -309,7 +312,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
contents = ( contents = (
<> <>
<ClockFading className="stroke-yellow-500 dark:stroke-yellow-400" /> <ClockFading className="stroke-yellow-500 dark:stroke-yellow-400" />
Attesa pagamento acconto Attesa pag. acconto
</> </>
); );
} }
@ -408,7 +411,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
contents = ( contents = (
<> <>
<CircleCheckBig className="stroke-blue-500 dark:stroke-blue-400" /> <CircleCheckBig className="stroke-blue-500 dark:stroke-blue-400" />
Attesa accettazione conferma Attesa accett. conferma
</> </>
); );
return; return;
@ -484,6 +487,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
</Tooltip> </Tooltip>
); );
}, },
enableSorting: false,
header: ({ column }) => ( header: ({ column }) => (
<DataTableColumnHeader <DataTableColumnHeader
column={column} column={column}
@ -503,7 +507,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
size="sm" size="sm"
variant="outline" variant="outline"
> >
{annunci.length} Annunci {annunci.length}
</Button> </Button>
</SheetTrigger> </SheetTrigger>
<SheetContent className="w-4/5 sm:max-w-3xl"> <SheetContent className="w-4/5 sm:max-w-3xl">
@ -529,6 +533,23 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
), ),
enableSorting: false, enableSorting: false,
}, },
{
accessorKey: "ordini",
cell: ({ row }) => {
return (
<OrdersModal isAdmin={true} ordini={row.original.ordini_servizio}>
<Button className="flex-1" size="sm" variant="outline">
<Logs />
</Button>
</OrdersModal>
);
},
header: ({ column }) => (
<DataTableColumnHeader column={column} title={columns_titles.ordini} />
),
enableSorting: false,
},
{ {
cell: ({ row }) => { cell: ({ row }) => {
return ( return (

View file

@ -268,9 +268,7 @@ export const FormEditOrder = ({ prezziario, data, close }: FormProps) => {
<FormMessage /> <FormMessage />
</div> </div>
<FormDescription> <FormDescription>Imposta se attivo</FormDescription>
Imposta se il servizio è pagato
</FormDescription>
</FormItem> </FormItem>
)} )}
/> />

View file

@ -33,9 +33,11 @@ import {
SelectValue, SelectValue,
} from "~/components/ui/select"; } from "~/components/ui/select";
import { Switch } from "~/components/ui/switch"; import { Switch } from "~/components/ui/switch";
import { PAYMENT_TYPES } from "~/i18n/stripe";
import { cn, formatCurrency } from "~/lib/utils"; import { cn, formatCurrency } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm"; import { useZodForm } from "~/lib/zodForm";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum"; import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
import type { Prezziario } from "~/schemas/public/Prezziario"; import type { Prezziario } from "~/schemas/public/Prezziario";
import type { ServizioServizioId } from "~/schemas/public/Servizio"; import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { UsersId } from "~/schemas/public/Users"; import type { UsersId } from "~/schemas/public/Users";
@ -62,6 +64,8 @@ export const FormNewOrder = ({
created_at: z.date(), created_at: z.date(),
amount_cent: z.number(), amount_cent: z.number(),
sconto: z.number().min(0).max(100), sconto: z.number().min(0).max(100),
paymentmethod: z.enum(PAYMENT_TYPES).nullable(),
paymentstatus: z.enum(PaymentStatusEnum).nullable(),
}); });
type FormValues = z.infer<typeof schema>; type FormValues = z.infer<typeof schema>;
@ -72,6 +76,8 @@ export const FormNewOrder = ({
sconto: 0, sconto: 0,
amount_cent: 0, amount_cent: 0,
isActive: false, isActive: false,
paymentmethod: null,
paymentstatus: null,
}, },
}); });
const utils = api.useUtils(); const utils = api.useUtils();
@ -244,12 +250,76 @@ export const FormNewOrder = ({
<FormMessage /> <FormMessage />
</div> </div>
<FormDescription> <FormDescription>Imposta se attivo</FormDescription>
Imposta se il servizio è pagato
</FormDescription>
</FormItem> </FormItem>
)} )}
/> />
<div className="grid grid-cols-1 items-center gap-4 sm:grid-cols-2 sm:gap-6">
<FormField
control={form.control}
name="paymentstatus"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="titolo">Stato Pagameto</FormLabel>
<FormMessage />
</div>
<FormControl>
<Select
defaultValue={field.value || ""}
onValueChange={field.onChange}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleziona tipologia" />
</SelectTrigger>
</FormControl>
<SelectContent id="select-paymentstatus">
{Object.values(PaymentStatusEnum).map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="paymentmethod"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="titolo">
Metodo di Pagamento
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Select
defaultValue={field.value || ""}
onValueChange={field.onChange}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleziona tipologia" />
</SelectTrigger>
</FormControl>
<SelectContent id="select-paymentmethod">
{PAYMENT_TYPES.map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</div>
<FormField <FormField
control={form.control} control={form.control}
name="sconto" name="sconto"

View file

@ -106,7 +106,7 @@ const ConfirmSection = ({
}); });
if (user.isAdmin) { if (user.isAdmin) {
await router.push( await router.push(
`/area-riservata/admin/user-view/ricerca/${data.userId}`, `/area-riservata/admin/user-view/servizio/${data.userId}/${servizioId}`,
); );
} else { } else {
await router.push(`/area-riservata/servizio/${servizioId}`); await router.push(`/area-riservata/servizio/${servizioId}`);