add contract page with dynamic form elements

This commit is contained in:
Julien Aldon
2026-02-13 17:46:24 +01:00
parent ef7403f213
commit 7e42fbe106
34 changed files with 540 additions and 263 deletions

View File

@@ -1,62 +1,57 @@
import { ActionIcon, Group, TextInput, Tooltip } from "@mantine/core";
import { DatePickerInput } from "@mantine/dates";
import { IconX } from "@tabler/icons-react";
import { t } from "@/config/i18n";
import type { ShipmentInputs } from "@/services/resources/shipments";
import { Accordion, Group, Text } from "@mantine/core";
import type { Shipment } from "@/services/resources/shipments";
import { ProductForm } from "@/components/Products/Form";
import type { UseFormReturnType } from "@mantine/form";
import { useMemo } from "react";
import { computePrices } from "@/pages/Contract";
export type ShipmentFormProps = {
inputForm: UseFormReturnType<Record<string, string | number>>;
shipment: Shipment;
index: number;
setShipmentElement: (index: number, shipment: ShipmentInputs) => void;
deleteShipmentElement: (index: number) => void;
shipment: ShipmentInputs;
}
export default function ShipmentForm({
index,
setShipmentElement,
deleteShipmentElement,
shipment
shipment,
index,
inputForm,
}: ShipmentFormProps) {
return (
<Group justify="space-between" key={`shipment_${index}`}>
<Group grow maw="80%">
<TextInput
label={t("shipment name", {capfirst: true})}
placeholder={t("shipment name", {capfirst: true})}
radius="sm"
withAsterisk
value={shipment.name || ""}
onChange={(event) => {
const value = event.target.value;
setShipmentElement(index, {...shipment, name: value})
}}
/>
<DatePickerInput
label={t("shipment date", {capfirst: true})}
placeholder={t("shipment date", {capfirst: true})}
radius="sm"
withAsterisk
value={shipment.date || null}
onChange={(event) => {
const value = event || "";
setShipmentElement(index, {...shipment, date: value})
}}
/>
</Group>
<Tooltip label={t("remove shipment", {capfirst: true})}>
<ActionIcon
flex={{base: "1", md: "0"}}
style={{alignSelf: "flex-end"}}
color="red"
aria-label={t("remove shipment", {capfirst: true})}
onClick={() => {
deleteShipmentElement(index)
}}
>
<IconX/>
</ActionIcon>
</Tooltip>
</Group>
const shipmentPrice = useMemo(() => {
const values = Object
.entries(inputForm.getValues())
.filter(([key]) =>
key.includes("planned") &&
key.split("-")[1] === String(shipment.id)
);
return computePrices(values, shipment.products);
}, [inputForm, shipment.products]);
)
return (
<Accordion.Item value={String(index)}>
<Accordion.Control>
<Group justify="space-between">
<Text>{shipment.name}</Text>
<Text>{
Intl.NumberFormat(
"fr-FR",
{style: "currency", currency: "EUR"}
).format(shipmentPrice)
}</Text>
<Text mr="lg">{shipment.date}</Text>
</Group>
</Accordion.Control>
<Accordion.Panel>
{
shipment?.products.map((product) => (
<ProductForm
key={product.id}
product={product}
shipment={shipment}
inputForm={inputForm}
/>
))
}
</Accordion.Panel>
</Accordion.Item>
);
}