add contract page with dynamic form elements
This commit is contained in:
@@ -67,7 +67,7 @@ export default function FormModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="50%"
|
||||
w={{base: "100%", md: "80%", lg: "50%"}}
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentForm ? t("edit form") : t('create form')}
|
||||
@@ -80,24 +80,27 @@ export default function FormModal({
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("contact season", {capfirst: true})}
|
||||
placeholder={t("contact season", {capfirst: true})}
|
||||
label={t("contract season", {capfirst: true})}
|
||||
placeholder={t("contract season", {capfirst: true})}
|
||||
description={t("contract season recommandation", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('season')}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("start date", {capfirst: true})}
|
||||
placeholder={t("start date", {capfirst: true})}
|
||||
withAsterisk
|
||||
{...form.getInputProps('start')}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("end date", {capfirst: true})}
|
||||
placeholder={t("end date", {capfirst: true})}
|
||||
withAsterisk
|
||||
{...form.getInputProps('end')}
|
||||
/>
|
||||
<Group grow>
|
||||
<DatePickerInput
|
||||
label={t("start date", {capfirst: true})}
|
||||
placeholder={t("start date", {capfirst: true})}
|
||||
withAsterisk
|
||||
{...form.getInputProps('start')}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("end date", {capfirst: true})}
|
||||
placeholder={t("end date", {capfirst: true})}
|
||||
withAsterisk
|
||||
{...form.getInputProps('end')}
|
||||
/>
|
||||
</Group>
|
||||
<Select
|
||||
label={t("referer", {capfirst: true})}
|
||||
placeholder={t("referer", {capfirst: true})}
|
||||
@@ -127,7 +130,6 @@ export default function FormModal({
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
@@ -139,7 +141,6 @@ export default function FormModal({
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentForm?.id)
|
||||
form.reset();
|
||||
}
|
||||
}}
|
||||
>{currentForm ? t("edit form", {capfirst: true}) : t('create form', {capfirst: true})}</Button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { deleteForm} from "@/services/api";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
@@ -12,6 +12,7 @@ export type FormRowProps = {
|
||||
export default function FormRow({
|
||||
form,
|
||||
}: FormRowProps) {
|
||||
const [searchParams, _] = useSearchParams();
|
||||
const deleteMutation = deleteForm();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -30,7 +31,7 @@ export default function FormRow({
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/forms/${form.id}/edit`);
|
||||
navigate(`/dashboard/forms/${form.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
|
||||
@@ -6,7 +6,7 @@ export function Navbar() {
|
||||
return (
|
||||
<nav>
|
||||
<NavLink to="/">{t("home", {capfirst: true})}</NavLink>
|
||||
<NavLink to="/dashboard">{t("dashboard", {capfirst: true})}</NavLink>
|
||||
<NavLink to="/dashboard/productors">{t("dashboard", {capfirst: true})}</NavLink>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export default function ProductorsFilter({
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by type", {capfirst: true})}
|
||||
@@ -43,6 +44,7 @@ export default function ProductorsFilter({
|
||||
onFilterChange(values, 'types')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
@@ -45,7 +45,7 @@ export function ProductorModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="50%"
|
||||
w={{base: "100%", md: "80%", lg: "50%"}}
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create productor", {capfirst: true})}
|
||||
@@ -87,7 +87,6 @@ export function ProductorModal({
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
@@ -99,7 +98,6 @@ export function ProductorModal({
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProductor?.id)
|
||||
form.reset();
|
||||
}
|
||||
}}
|
||||
>{currentProductor ? t("edit productor", {capfirst: true}) : t('create productor', {capfirst: true})}</Button>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import type { Productor } from "@/services/resources/productors";
|
||||
import { deleteProductor } from "@/services/api";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductorRowProps = {
|
||||
productor: Productor;
|
||||
@@ -12,6 +12,7 @@ export type ProductorRowProps = {
|
||||
export default function ProductorRow({
|
||||
productor,
|
||||
}: ProductorRowProps) {
|
||||
const [searchParams, _] = useSearchParams();
|
||||
const deleteMutation = deleteProductor();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -28,7 +29,7 @@ export default function ProductorRow({
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/productors/${productor.id}/edit`);
|
||||
navigate(`/dashboard/productors/${productor.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
|
||||
@@ -33,6 +33,7 @@ export default function ProductsFilters({
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by productor", {capfirst: true})}
|
||||
@@ -43,6 +44,7 @@ export default function ProductsFilters({
|
||||
onFilterChange(values, 'productors')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
44
frontend/src/components/Products/Form/index.tsx
Normal file
44
frontend/src/components/Products/Form/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { t } from "@/config/i18n";
|
||||
import { ProductUnit, type Product } from "@/services/resources/products";
|
||||
import type { Shipment } from "@/services/resources/shipments";
|
||||
import { Group, NumberInput } from "@mantine/core";
|
||||
import type { UseFormReturnType } from "@mantine/form";
|
||||
|
||||
export type ProductFormProps = {
|
||||
inputForm: UseFormReturnType<Record<string, string | number>>;
|
||||
product: Product;
|
||||
shipment?: Shipment;
|
||||
}
|
||||
|
||||
export function ProductForm({
|
||||
inputForm,
|
||||
product,
|
||||
shipment,
|
||||
}: ProductFormProps) {
|
||||
return (
|
||||
<Group mb="sm" grow>
|
||||
<NumberInput
|
||||
label={
|
||||
`${product.name}
|
||||
${product.quantity || ""}${product.quantity ? product.quantity_unit : ""}
|
||||
${
|
||||
product.price ?
|
||||
Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(product.price) :
|
||||
product.price_kg && Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(product.price_kg)
|
||||
}
|
||||
${product.price ? `/ ${t(ProductUnit[product.unit])}` : "/ kg"}`
|
||||
}
|
||||
description={`${t("enter quantity", {capfirst: true})} ${t('in')} ${t(ProductUnit[product.unit])}`}
|
||||
aria-label={t("enter quantity")}
|
||||
placeholder={`${t("enter quantity", {capfirst: true})} ${t('in')} ${t(ProductUnit[product.unit])}`}
|
||||
{...inputForm.getInputProps(shipment ? `planned-${shipment.id}-${product.id}` : `recurrent-${product.id}`)}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Button, Group, Modal, NumberInput, Select, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { Button, Group, Modal, NumberInput, Pill, Select, TextInput, Title, Tooltip, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { productToProductInputs, type Product, type ProductInputs } from "@/services/resources/products";
|
||||
import { IconCancel, IconInfoCircle } from "@tabler/icons-react";
|
||||
import { ProductQuantityUnit, productToProductInputs, ProductUnit, type Product, type ProductInputs } from "@/services/resources/products";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { getProductors } from "@/services/api";
|
||||
|
||||
@@ -24,7 +24,8 @@ export function ProductModal({
|
||||
unit: null,
|
||||
price: null,
|
||||
price_kg: null,
|
||||
weight: null,
|
||||
quantity: null,
|
||||
quantity_unit: null,
|
||||
type: null,
|
||||
productor_id: null,
|
||||
},
|
||||
@@ -33,8 +34,10 @@ export function ProductModal({
|
||||
!value ? `${t("name", {capfirst: true})} ${t('is required')}` : null,
|
||||
unit: (value) =>
|
||||
!value ? `${t("unit", {capfirst: true})} ${t('is required')}` : null,
|
||||
price: (value) =>
|
||||
!value ? `${t("price", {capfirst: true})} ${t('is required')}` : null,
|
||||
price: (value, values) =>
|
||||
!value && !values.price_kg ? `${t("price or price_kg", {capfirst: true})} ${t('is required')}` : null,
|
||||
price_kg: (value, values) =>
|
||||
!value && !values.price ? `${t("price or price_kg", {capfirst: true})} ${t('is required')}` : null,
|
||||
type: (value) =>
|
||||
!value ? `${t("type", {capfirst: true})} ${t('is required')}` : null,
|
||||
productor_id: (value) =>
|
||||
@@ -54,70 +57,85 @@ export function ProductModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="50%"
|
||||
w={{base: "100%", md: "80%", lg: "50%"}}
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create product", {capfirst: true})}
|
||||
>
|
||||
<Title order={4}>{t("informations", {capfirst: true})}</Title>
|
||||
<TextInput
|
||||
label={t("product name", {capfirst: true})}
|
||||
placeholder={t("product name", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Select
|
||||
label={t("product type", {capfirst: true})}
|
||||
placeholder={t("product type", {capfirst: true})}
|
||||
radius="sm"
|
||||
data={[
|
||||
{value: "1", label: t("planned")},
|
||||
{value: "2", label: t("reccurent")}
|
||||
]}
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
<Select
|
||||
label={t("product unit", {capfirst: true})}
|
||||
placeholder={t("product unit", {capfirst: true})}
|
||||
radius="sm"
|
||||
data={[
|
||||
{value: "1", label: t("grams")},
|
||||
{value: "2", label: t("kilo")},
|
||||
{value: "3", label: t("piece")}
|
||||
]}
|
||||
{...form.getInputProps('unit')}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t("product price", {capfirst: true})}
|
||||
placeholder={t("product price", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('price')}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t("product priceKg", {capfirst: true})}
|
||||
placeholder={t("product priceKg", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('price_kg')}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t("product weight", {capfirst: true})}
|
||||
placeholder={t("product weight", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('weight', {capfirst: true})}
|
||||
/>
|
||||
<Select
|
||||
label={t("productor", {capfirst: true})}
|
||||
placeholder={t("productor")}
|
||||
nothingFoundMessage={t("nothing found", {capfirst: true})}
|
||||
withAsterisk
|
||||
clearable
|
||||
allowDeselect
|
||||
searchable
|
||||
data={productorsSelect || []}
|
||||
{...form.getInputProps('productor_id')}
|
||||
/>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("product name", {capfirst: true})}
|
||||
placeholder={t("product name", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Select
|
||||
label={t("product type", {capfirst: true})}
|
||||
placeholder={t("product type", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
searchable
|
||||
clearable
|
||||
data={[
|
||||
{value: "1", label: t("planned")},
|
||||
{value: "2", label: t("recurrent")}
|
||||
]}
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
</Group>
|
||||
<Select
|
||||
label={t("product unit", {capfirst: true})}
|
||||
placeholder={t("product unit", {capfirst: true})}
|
||||
description={t("the product unit will be assigned to the quantity requested in the form", { capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
searchable
|
||||
clearable
|
||||
data={Object.entries(ProductUnit).map(([key, value]) => ({value: key, label: t(value)}))}
|
||||
{...form.getInputProps('unit')}
|
||||
/>
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
label={t("product price", {capfirst: true})}
|
||||
placeholder={t("product price", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('price')}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t("product price kg", {capfirst: true})}
|
||||
placeholder={t("product price kg", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('price_kg')}
|
||||
/>
|
||||
</Group>
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
label={t("product quantity", {capfirst: true})}
|
||||
placeholder={t("product quantity", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('quantity', {capfirst: true})}
|
||||
/>
|
||||
<Select
|
||||
label={t("product quantity unit", {capfirst: true})}
|
||||
placeholder={t("product quantity unit", {capfirst: true})}
|
||||
radius="sm"
|
||||
data={Object.entries(ProductQuantityUnit).map(([key, value]) => ({value: key, label: t(value)}))}
|
||||
{...form.getInputProps('quantity_unit', {capfirst: true})}
|
||||
/>
|
||||
|
||||
</Group>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
@@ -125,7 +143,6 @@ export function ProductModal({
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
@@ -138,7 +155,6 @@ export function ProductModal({
|
||||
console.log(form.isValid(), form.getValues())
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProduct?.id)
|
||||
form.reset();
|
||||
}
|
||||
}}
|
||||
>{currentProduct ? t("edit product", {capfirst: true}) : t('create product', {capfirst: true})}</Button>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { ProductType, ProductUnit, type Product, type ProductInputs } from "@/services/resources/products";
|
||||
import { ProductModal } from "@/components/Products/Modal";
|
||||
import { deleteProduct, getProduct } from "@/services/api";
|
||||
import { useNavigate } from "react-router";
|
||||
import { ProductType, ProductUnit, type Product } from "@/services/resources/products";
|
||||
import { deleteProduct } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductRowProps = {
|
||||
product: Product;
|
||||
@@ -13,17 +12,19 @@ export type ProductRowProps = {
|
||||
export default function ProductRow({
|
||||
product,
|
||||
}: ProductRowProps) {
|
||||
const [searchParams, _] = useSearchParams();
|
||||
const deleteMutation = deleteProduct();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={product.id}>
|
||||
<Table.Td>{product.name}</Table.Td>
|
||||
<Table.Td>{ProductType[product.type]}</Table.Td>
|
||||
<Table.Td>{t(ProductType[product.type])}</Table.Td>
|
||||
<Table.Td>{product.price}</Table.Td>
|
||||
<Table.Td>{product.price_kg}</Table.Td>
|
||||
<Table.Td>{product.weight}</Table.Td>
|
||||
<Table.Td>{ProductUnit[product.unit]}</Table.Td>
|
||||
<Table.Td>{product.quantity}</Table.Td>
|
||||
<Table.Td>{product.quantity_unit}</Table.Td>
|
||||
<Table.Td>{t(ProductUnit[product.unit])}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit product", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
@@ -31,7 +32,7 @@ export default function ProductRow({
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/products/${product.id}/edit`);
|
||||
navigate(`/dashboard/products/${product.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { IconCancel } from "@tabler/icons-react";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { shipmentToShipmentInputs, type Shipment, type ShipmentInputs } from "@/services/resources/shipments";
|
||||
import { getForms, getProducts } from "@/services/api";
|
||||
import { getForms, getProductors, getProducts } from "@/services/api";
|
||||
|
||||
export type ShipmentModalProps = ModalBaseProps & {
|
||||
currentShipment?: Shipment;
|
||||
@@ -42,18 +42,28 @@ export default function ShipmentModal({
|
||||
}, [currentShipment]);
|
||||
|
||||
const { data: allForms } = getForms();
|
||||
const { data: allProducts } = getProducts();
|
||||
const { data: allProducts } = getProducts(new URLSearchParams("types=1"));
|
||||
const { data: allProductors } = getProductors()
|
||||
const formsSelect = useMemo(() => {
|
||||
return allForms?.map(form => ({value: String(form.id), label: `${form.name} ${form.season}`}))
|
||||
}, [allForms]);
|
||||
|
||||
const productsSelect = useMemo(() => {
|
||||
return allProducts?.map(product => ({value: String(product.id), label: `${product.name}`}))
|
||||
}, [allProducts]);
|
||||
if (!allProducts)
|
||||
return;
|
||||
return allProductors?.map(productor => {
|
||||
return {
|
||||
group: productor.name,
|
||||
items: allProducts
|
||||
.filter((product) => product.productor.id === productor.id)
|
||||
.map((product) => ({value: String(product.id), label: `${product.name}`}))
|
||||
}
|
||||
});
|
||||
}, [allProducts, form]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="50%"
|
||||
w={{base: "100%", md: "80%", lg: "50%"}}
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentShipment ? t("edit shipment") : t('create shipment')}
|
||||
@@ -83,8 +93,9 @@ export default function ShipmentModal({
|
||||
<MultiSelect
|
||||
label={t("shipment products", {capfirst: true})}
|
||||
placeholder={t("shipment products", {capfirst: true})}
|
||||
data={productsSelect}
|
||||
data={productsSelect || []}
|
||||
clearable
|
||||
searchable
|
||||
{...form.getInputProps('product_ids')}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
@@ -105,7 +116,6 @@ export default function ShipmentModal({
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentShipment?.id)
|
||||
// form.reset();
|
||||
}
|
||||
}}
|
||||
>{currentShipment ? t("edit shipment", {capfirst: true}) : t('create shipment', {capfirst: true})}</Button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { deleteShipment} from "@/services/api";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
@@ -12,6 +12,7 @@ export type ShipmentRowProps = {
|
||||
export default function ShipmentRow({
|
||||
shipment,
|
||||
}: ShipmentRowProps) {
|
||||
const [searchParams, _] = useSearchParams();
|
||||
const deleteMutation = deleteShipment();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -27,7 +28,7 @@ export default function ShipmentRow({
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/shipments/${shipment.id}/edit`);
|
||||
navigate(`/dashboard/shipments/${shipment.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function UserModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="50%"
|
||||
w={{base: "100%", md: "80%", lg: "50%"}}
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create user", {capfirst: true})}
|
||||
@@ -64,7 +64,6 @@ export function UserModal({
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
@@ -77,7 +76,6 @@ export function UserModal({
|
||||
console.log(form.isValid(), form.getValues())
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentUser?.id)
|
||||
form.reset();
|
||||
}
|
||||
}}
|
||||
>{currentUser ? t("edit user", {capfirst: true}) : t('create user', {capfirst: true})}</Button>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
import { UserModal } from "@/components/Users/Modal";
|
||||
import { deleteUser, getUser } from "@/services/api";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type UserRowProps = {
|
||||
user: User;
|
||||
@@ -13,6 +13,7 @@ export type UserRowProps = {
|
||||
export default function UserRow({
|
||||
user,
|
||||
}: UserRowProps) {
|
||||
const [searchParams, _] = useSearchParams();
|
||||
const deleteMutation = deleteUser();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -27,7 +28,7 @@ export default function UserRow({
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/users/${user.id}/edit`);
|
||||
navigate(`/dashboard/users/${user.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
|
||||
Reference in New Issue
Block a user