feat: update service confirmation routing and enhance order management UI components
This commit is contained in:
parent
04f0c5e790
commit
73b1997c15
6 changed files with 127 additions and 18 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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}`);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue