feat: implement manual order creation dialog and associated form components
This commit is contained in:
parent
51d58a3008
commit
fd7da28535
9 changed files with 475 additions and 9 deletions
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { FormNewOrder } from "~/forms/FormNewOrdine";
|
||||||
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
||||||
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
import {
|
||||||
|
Credenza,
|
||||||
|
CredenzaBody,
|
||||||
|
CredenzaClose,
|
||||||
|
CredenzaContent,
|
||||||
|
CredenzaDescription,
|
||||||
|
CredenzaFooter,
|
||||||
|
CredenzaHeader,
|
||||||
|
CredenzaTitle,
|
||||||
|
CredenzaTrigger,
|
||||||
|
} from "../custom_ui/credenza";
|
||||||
|
import { LoadingPage } from "../loading";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
|
export const OrdineManualeDialog = ({
|
||||||
|
userId,
|
||||||
|
servizioId,
|
||||||
|
}: {
|
||||||
|
userId: UsersId;
|
||||||
|
servizioId: ServizioServizioId;
|
||||||
|
}) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<Credenza onOpenChange={setOpen} open={open}>
|
||||||
|
<CredenzaTrigger asChild>
|
||||||
|
<Button size="sm" variant="outline">
|
||||||
|
Nuovo Ordine Manuale
|
||||||
|
</Button>
|
||||||
|
</CredenzaTrigger>
|
||||||
|
<CredenzaContent className="max-h-[90vh] w-full px-3 sm:max-w-5xl">
|
||||||
|
<CredenzaHeader>
|
||||||
|
<CredenzaTitle>Nuovo Ordine Manuale</CredenzaTitle>
|
||||||
|
<CredenzaDescription className="sr-only">
|
||||||
|
nuovo ordine modal
|
||||||
|
</CredenzaDescription>
|
||||||
|
</CredenzaHeader>
|
||||||
|
<Content servizioId={servizioId} setOpen={setOpen} userId={userId} />
|
||||||
|
</CredenzaContent>
|
||||||
|
</Credenza>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Content = ({
|
||||||
|
userId,
|
||||||
|
servizioId,
|
||||||
|
setOpen,
|
||||||
|
}: {
|
||||||
|
userId: UsersId;
|
||||||
|
servizioId: ServizioServizioId;
|
||||||
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}) => {
|
||||||
|
const { data, isLoading } = api.prezziario.getPrezziario.useQuery();
|
||||||
|
|
||||||
|
if (isLoading) return <LoadingPage />;
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CredenzaBody className="max-h-[80vh] w-full overflow-y-auto pb-5">
|
||||||
|
<p className="text-center text-muted-foreground text-sm">
|
||||||
|
Errore nel caricamento del prezziario.
|
||||||
|
</p>
|
||||||
|
</CredenzaBody>
|
||||||
|
|
||||||
|
<CredenzaFooter>
|
||||||
|
<CredenzaClose asChild>
|
||||||
|
<Button aria-label="Close Credenza" variant="outline">
|
||||||
|
Chiudi
|
||||||
|
</Button>
|
||||||
|
</CredenzaClose>
|
||||||
|
</CredenzaFooter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormNewOrder
|
||||||
|
prezziario={data}
|
||||||
|
servizioId={servizioId}
|
||||||
|
setOpen={setOpen}
|
||||||
|
userId={userId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Info,
|
Info,
|
||||||
Plus,
|
Plus,
|
||||||
|
Search,
|
||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
Trash2,
|
Trash2,
|
||||||
UserCircle,
|
UserCircle,
|
||||||
|
|
@ -78,6 +79,14 @@ export const ServizioActions = () => {
|
||||||
<span className="font-semibold">Azioni Admin:</span>
|
<span className="font-semibold">Azioni Admin:</span>
|
||||||
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
||||||
<EditServizioAdmin />
|
<EditServizioAdmin />
|
||||||
|
<Link
|
||||||
|
href={`/area-riservata/admin/user-view/ordini/${userId}?servizioId=${servizio.servizio_id}`}
|
||||||
|
>
|
||||||
|
<Button variant="warning">
|
||||||
|
<Search />
|
||||||
|
<span>Ordini</span>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ServizioInfos />
|
<ServizioInfos />
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ export const OrdiniTable = ({ data, isAdmin, userId }: PaymentsTableProps) => {
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
columnName: "packid",
|
columnName: "packid",
|
||||||
placeholder: "Cerca pagamento...",
|
placeholder: "Cerca pacchetto...",
|
||||||
};
|
};
|
||||||
|
|
||||||
const pinnedFiltri: PinnedFiltro[] = [
|
const pinnedFiltri: PinnedFiltro[] = [
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export const PaymentsTable = ({
|
||||||
paid_at: row.paid_at,
|
paid_at: row.paid_at,
|
||||||
raw_status: row.paymentstatus,
|
raw_status: row.paymentstatus,
|
||||||
status: StatusMapping(row.paymentstatus),
|
status: StatusMapping(row.paymentstatus),
|
||||||
|
intent_id: row.intent_id,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -164,6 +165,7 @@ export const PaymentsTable = ({
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>Azioni</DropdownMenuLabel>
|
<DropdownMenuLabel>Azioni</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuSub>
|
<DropdownMenuSub>
|
||||||
<DropdownMenuSubTrigger>
|
<DropdownMenuSubTrigger>
|
||||||
<Wrench />
|
<Wrench />
|
||||||
|
|
@ -206,7 +208,7 @@ export const PaymentsTable = ({
|
||||||
remove({ pagamentoId: data.id })
|
remove({ pagamentoId: data.id })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Trash2 className="size-6" />
|
<Trash2 className="size-5" />
|
||||||
Elimina Pagamento
|
Elimina Pagamento
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
|
||||||
258
apps/infoalloggi/src/forms/FormNewOrdine.tsx
Normal file
258
apps/infoalloggi/src/forms/FormNewOrdine.tsx
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { it } from "date-fns/locale";
|
||||||
|
import { CalendarIcon } from "lucide-react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { z } from "zod/v4";
|
||||||
|
import {
|
||||||
|
CredenzaBody,
|
||||||
|
CredenzaClose,
|
||||||
|
CredenzaFooter,
|
||||||
|
} from "~/components/custom_ui/credenza";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "~/components/custom_ui/form";
|
||||||
|
import { MultiSelect } from "~/components/custom_ui/multiselect";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
|
import { Calendar } from "~/components/ui/calendar";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "~/components/ui/popover";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "~/components/ui/select";
|
||||||
|
import { Switch } from "~/components/ui/switch";
|
||||||
|
import { cn, formatCurrency } from "~/lib/utils";
|
||||||
|
import { useZodForm } from "~/lib/zodForm";
|
||||||
|
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
|
||||||
|
import type { Prezziario } from "~/schemas/public/Prezziario";
|
||||||
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
||||||
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
|
import { zPrezziarioId } from "~/server/utils/zod_types";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
|
type FormProps = {
|
||||||
|
prezziario: Prezziario[];
|
||||||
|
servizioId: ServizioServizioId;
|
||||||
|
userId: UsersId;
|
||||||
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FormNewOrder = ({
|
||||||
|
prezziario,
|
||||||
|
servizioId,
|
||||||
|
userId,
|
||||||
|
setOpen,
|
||||||
|
}: FormProps) => {
|
||||||
|
const schema = z.object({
|
||||||
|
packid: zPrezziarioId,
|
||||||
|
isActive: z.boolean(),
|
||||||
|
type: z.custom<OrderTypeEnum>(),
|
||||||
|
created_at: z.date(),
|
||||||
|
});
|
||||||
|
type FormValues = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
z.config(z.locales.it());
|
||||||
|
|
||||||
|
const form = useZodForm(schema, {
|
||||||
|
defaultValues: {},
|
||||||
|
});
|
||||||
|
const utils = api.useUtils();
|
||||||
|
const { mutate: createOrder } = api.servizio.createOrdine.useMutation({
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(`Errore nella creazione dell'ordine: ${error.message}`);
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success("Ordine creato con successo");
|
||||||
|
await utils.servizio.getOrdini.invalidate({
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
setOpen(false);
|
||||||
|
form.reset();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
function onSubmit(fields: FormValues) {
|
||||||
|
createOrder({
|
||||||
|
data: {
|
||||||
|
...fields,
|
||||||
|
servizio_id: servizioId,
|
||||||
|
userid: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
|
<CredenzaBody className="max-h-[80vh] w-full pb-5">
|
||||||
|
<div className="space-y-8 px-0.5">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="packid"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2">
|
||||||
|
<FormLabel htmlFor="select-pack">Servizio</FormLabel>
|
||||||
|
<FormMessage />
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<MultiSelect
|
||||||
|
defaultValue={undefined}
|
||||||
|
elementId="select-pack"
|
||||||
|
elementName="packid"
|
||||||
|
isMulti={false}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
const selected = zPrezziarioId.safeParse(value.value);
|
||||||
|
if (selected.success) {
|
||||||
|
field.onChange(selected.data);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Invalid packid selected",
|
||||||
|
value.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={prezziario.map((p) => ({
|
||||||
|
label: `${p.idprezziario} - ${p.nome_it} (${formatCurrency(p.prezzo_cent / 100)})`,
|
||||||
|
value: p.idprezziario,
|
||||||
|
}))}
|
||||||
|
placeholder="Seleziona..."
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="type"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2">
|
||||||
|
<FormLabel htmlFor="titolo">Tipologia</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-color">
|
||||||
|
{Object.values(OrderTypeEnum).map((option) => (
|
||||||
|
<SelectItem key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="created_at"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex w-full flex-col">
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2">
|
||||||
|
<FormLabel htmlFor="created_at">Data Ordine</FormLabel>
|
||||||
|
<FormMessage />
|
||||||
|
</div>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
className={cn(
|
||||||
|
"h-10.5 w-full pl-3 text-left font-medium",
|
||||||
|
!field.value && "text-muted-foreground",
|
||||||
|
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
|
||||||
|
)}
|
||||||
|
id="created_at"
|
||||||
|
variant={"outline"}
|
||||||
|
>
|
||||||
|
{field.value ? (
|
||||||
|
format(field.value, "PPP", {
|
||||||
|
locale: it,
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<span>Scegli data</span>
|
||||||
|
)}
|
||||||
|
<CalendarIcon className="ml-auto size-4 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent align="start" className="w-auto p-0">
|
||||||
|
<Calendar
|
||||||
|
autoFocus
|
||||||
|
captionLayout="dropdown"
|
||||||
|
defaultMonth={new Date()}
|
||||||
|
endMonth={new Date(2050, 12)}
|
||||||
|
locale={it}
|
||||||
|
mode="single"
|
||||||
|
onSelect={field.onChange}
|
||||||
|
selected={field.value || undefined}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="isActive"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2">
|
||||||
|
<FormLabel htmlFor="isActive">Attivo</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
className="data-[state=checked]:bg-neutral-700"
|
||||||
|
defaultChecked={field.value}
|
||||||
|
id="isActive"
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
Imposta se il servizio è attivo
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CredenzaBody>
|
||||||
|
|
||||||
|
<CredenzaFooter>
|
||||||
|
<Button type="submit" variant="success">
|
||||||
|
Salva
|
||||||
|
</Button>
|
||||||
|
<CredenzaClose asChild>
|
||||||
|
<Button aria-label="Close Credenza" variant="outline">
|
||||||
|
Chiudi
|
||||||
|
</Button>
|
||||||
|
</CredenzaClose>
|
||||||
|
</CredenzaFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,20 +1,26 @@
|
||||||
import type { GetServerSideProps } from "next";
|
import type { GetServerSideProps } from "next";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { OrdineManualeDialog } from "~/components/area-riservata/ordine_manuale";
|
||||||
import { AreaRiservataLayoutUserView } from "~/components/Layout";
|
import { AreaRiservataLayoutUserView } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
import { Status500 } from "~/components/status-page";
|
import { Status500 } from "~/components/status-page";
|
||||||
import { OrdiniTable } from "~/components/tables/orders-table";
|
import { OrdiniTable } from "~/components/tables/orders-table";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
import type { NextPageWithLayout } from "~/pages/_app";
|
import type { NextPageWithLayout } from "~/pages/_app";
|
||||||
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
||||||
import { zUserId } from "~/server/utils/zod_types";
|
import { zServizioId, zUserId } from "~/server/utils/zod_types";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
type OrdiniUserProps = {
|
type OrdiniUserProps = {
|
||||||
userId: UsersId;
|
userId: UsersId;
|
||||||
|
servizioId: ServizioServizioId | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OrdiniUser: NextPageWithLayout<OrdiniUserProps> = ({
|
const OrdiniUser: NextPageWithLayout<OrdiniUserProps> = ({
|
||||||
userId,
|
userId,
|
||||||
|
servizioId,
|
||||||
}: OrdiniUserProps) => {
|
}: OrdiniUserProps) => {
|
||||||
const { data, isLoading } = api.servizio.getOrdini.useQuery({
|
const { data, isLoading } = api.servizio.getOrdini.useQuery({
|
||||||
userId,
|
userId,
|
||||||
|
|
@ -24,10 +30,31 @@ const OrdiniUser: NextPageWithLayout<OrdiniUserProps> = ({
|
||||||
if (!data) return <Status500 />;
|
if (!data) return <Status500 />;
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-1 flex-col items-start gap-4">
|
<main className="flex flex-1 flex-col items-start gap-4">
|
||||||
<div className="flex">
|
<div className="flex w-full flex-wrap items-center justify-between gap-2">
|
||||||
<h3 className="font-bold text-2xl">Ordini</h3>
|
<h3 className="flex items-center gap-2 font-bold text-2xl">
|
||||||
|
Ordini{" "}
|
||||||
|
<span className="text-sm">
|
||||||
|
{servizioId ? `- Servizio ${servizioId}` : ""}
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
{servizioId && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<OrdineManualeDialog servizioId={servizioId} userId={userId} />
|
||||||
|
<Link href={`/area-riservata/admin/user-view/ordini/${userId}`}>
|
||||||
|
<Button size="sm" variant="destructive">
|
||||||
|
Rimuovi filtro
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<OrdiniTable data={data} isAdmin userId={userId} />
|
)}
|
||||||
|
</div>
|
||||||
|
<OrdiniTable
|
||||||
|
data={data.filter((ordine) =>
|
||||||
|
servizioId ? ordine.servizio_id === servizioId : true,
|
||||||
|
)}
|
||||||
|
isAdmin
|
||||||
|
userId={userId}
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -55,10 +82,19 @@ export const getServerSideProps = (async (context) => {
|
||||||
await helpers.trpc.servizio.getOrdini.prefetch({
|
await helpers.trpc.servizio.getOrdini.prefetch({
|
||||||
userId: parsed.data,
|
userId: parsed.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let servizioId: ServizioServizioId | null = null;
|
||||||
|
|
||||||
|
const parsedServizioId = zServizioId.safeParse(context.query?.servizioId);
|
||||||
|
if (parsedServizioId.success) {
|
||||||
|
servizioId = parsedServizioId.data;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
userId: parsed.data,
|
userId: parsed.data,
|
||||||
trpcState: helpers.trpc.dehydrate(),
|
trpcState: helpers.trpc.dehydrate(),
|
||||||
|
servizioId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
import type { OrdiniUpdate } from "~/schemas/public/Ordini";
|
import type { NewOrdini, OrdiniUpdate } from "~/schemas/public/Ordini";
|
||||||
import type { PaymentsUpdate } from "~/schemas/public/Payments";
|
import type { PaymentsUpdate } from "~/schemas/public/Payments";
|
||||||
import type { NewServizio, ServizioUpdate } from "~/schemas/public/Servizio";
|
import type { NewServizio, ServizioUpdate } from "~/schemas/public/Servizio";
|
||||||
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
publicProcedure,
|
publicProcedure,
|
||||||
} from "~/server/api/trpc";
|
} from "~/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
|
createOrdine,
|
||||||
getOrdineById,
|
getOrdineById,
|
||||||
getOrdini,
|
getOrdini,
|
||||||
removeOrder,
|
removeOrder,
|
||||||
|
|
@ -34,6 +35,7 @@ import {
|
||||||
getServiziByUserId,
|
getServiziByUserId,
|
||||||
getServizioAnnuncio,
|
getServizioAnnuncio,
|
||||||
getServizioById,
|
getServizioById,
|
||||||
|
getServizioOrdini,
|
||||||
getServizioPagamentoData,
|
getServizioPagamentoData,
|
||||||
getUserPagamenti,
|
getUserPagamenti,
|
||||||
InteractionLogicTipologia,
|
InteractionLogicTipologia,
|
||||||
|
|
@ -166,6 +168,16 @@ export const servizioRouter = createTRPCRouter({
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
return await getOrdineRicevutaData({ ctx, ordineId: input.ordineId });
|
return await getOrdineRicevutaData({ ctx, ordineId: input.ordineId });
|
||||||
}),
|
}),
|
||||||
|
getServizioOrdini: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
servizioId: zServizioId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return await getServizioOrdini(input.servizioId);
|
||||||
|
}),
|
||||||
|
|
||||||
getOrdini: protectedProcedure
|
getOrdini: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -363,7 +375,15 @@ export const servizioRouter = createTRPCRouter({
|
||||||
servizioId: input.servizioId,
|
servizioId: input.servizioId,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
createOrdine: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
data: z.custom<NewOrdini>(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await createOrdine(input.data);
|
||||||
|
}),
|
||||||
updateOrder: adminProcedure
|
updateOrder: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { jsonArrayFrom } from "kysely/helpers/postgres";
|
import { jsonArrayFrom } from "kysely/helpers/postgres";
|
||||||
import type { OrdiniOrdineId, OrdiniUpdate } from "~/schemas/public/Ordini";
|
import type {
|
||||||
|
NewOrdini,
|
||||||
|
OrdiniOrdineId,
|
||||||
|
OrdiniUpdate,
|
||||||
|
} from "~/schemas/public/Ordini";
|
||||||
import type { UsersId } from "~/schemas/public/Users";
|
import type { UsersId } from "~/schemas/public/Users";
|
||||||
import { db, type Querier } from "~/server/db";
|
import { db, type Querier } from "~/server/db";
|
||||||
|
|
||||||
|
|
@ -61,6 +65,21 @@ export const getOrdineById = async (ordineId: OrdiniOrdineId) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createOrdine = async (data: NewOrdini) => {
|
||||||
|
try {
|
||||||
|
return await db
|
||||||
|
.insertInto("ordini")
|
||||||
|
.values(data)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error creating ordine: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const removeOrder = async (ordineId: OrdiniOrdineId) => {
|
export const removeOrder = async (ordineId: OrdiniOrdineId) => {
|
||||||
try {
|
try {
|
||||||
await db
|
await db
|
||||||
|
|
|
||||||
|
|
@ -1847,3 +1847,37 @@ export const getOrdineRicevutaData = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServizioOrdini = async (servizioId: ServizioServizioId) => {
|
||||||
|
try {
|
||||||
|
const data = await db
|
||||||
|
.selectFrom("ordini")
|
||||||
|
.selectAll("ordini")
|
||||||
|
.select((eb) => [
|
||||||
|
jsonArrayFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom("payments")
|
||||||
|
.whereRef("payments.ordine_id", "=", "ordini.ordine_id")
|
||||||
|
.selectAll("payments")
|
||||||
|
.orderBy("created_at", "desc"),
|
||||||
|
).as("pagamenti"),
|
||||||
|
])
|
||||||
|
.where("servizio_id", "=", servizioId)
|
||||||
|
.orderBy("created_at", "desc")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return data.map((ordine) => ({
|
||||||
|
...ordine,
|
||||||
|
pagamenti: ordine.pagamenti.map((payment) => ({
|
||||||
|
...payment,
|
||||||
|
created_at: new Date(payment.created_at),
|
||||||
|
paid_at: payment.paid_at ? new Date(payment.paid_at) : null,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Error fetching servizio ordini: ${(e as Error).message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue