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:
- Log email fallite e retry
conferma presa visione deve rimandare a servizio giusto non /ricerrca
AFTER MVP:
- 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

View file

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

View file

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

View file

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

View file

@ -33,9 +33,11 @@ import {
SelectValue,
} from "~/components/ui/select";
import { Switch } from "~/components/ui/switch";
import { PAYMENT_TYPES } from "~/i18n/stripe";
import { cn, formatCurrency } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
import type { Prezziario } from "~/schemas/public/Prezziario";
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { UsersId } from "~/schemas/public/Users";
@ -62,6 +64,8 @@ export const FormNewOrder = ({
created_at: z.date(),
amount_cent: z.number(),
sconto: z.number().min(0).max(100),
paymentmethod: z.enum(PAYMENT_TYPES).nullable(),
paymentstatus: z.enum(PaymentStatusEnum).nullable(),
});
type FormValues = z.infer<typeof schema>;
@ -72,6 +76,8 @@ export const FormNewOrder = ({
sconto: 0,
amount_cent: 0,
isActive: false,
paymentmethod: null,
paymentstatus: null,
},
});
const utils = api.useUtils();
@ -244,12 +250,76 @@ export const FormNewOrder = ({
<FormMessage />
</div>
<FormDescription>
Imposta se il servizio è pagato
</FormDescription>
<FormDescription>Imposta se attivo</FormDescription>
</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
control={form.control}
name="sconto"

View file

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