add prettier code formater
This commit is contained in:
@@ -1,268 +1,281 @@
|
||||
import { ProductForm } from "@/components/Products/Form";
|
||||
import ShipmentForm from "@/components/Shipments/Form";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateContract, useGetForm } from "@/services/api";
|
||||
import { type Product } from "@/services/resources/products";
|
||||
import { Accordion, Button, Group, List, Loader, Overlay, Stack, Text, TextInput, Title } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconMail, IconPhone, IconUser } from "@tabler/icons-react";
|
||||
import { useCallback, useMemo, useRef } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { computePrices } from "./price";
|
||||
|
||||
export function Contract() {
|
||||
const { id } = useParams();
|
||||
const { data: form } = useGetForm(Number(id), {enabled: !!id});
|
||||
const inputForm = useForm<Record<string, number | string>>({
|
||||
initialValues: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
},
|
||||
validate: {
|
||||
firstname: (value) => !value ? `${t("a firstname", {capfirst: true})} ${t("is required")}` : null,
|
||||
lastname: (value) => !value ? `${t("a lastname", {capfirst: true})} ${t("is required")}` : null,
|
||||
email: (value) => !value ? `${t("a email", {capfirst: true})} ${t("is required")}` : null,
|
||||
phone: (value) => !value ? `${t("a phone", {capfirst: true})} ${t("is required")}` : null,
|
||||
}
|
||||
});
|
||||
|
||||
const createContractMutation = useCreateContract();
|
||||
|
||||
const productsRecurent = useMemo(() => {
|
||||
return form?.productor?.products.filter((el) => el.type === "2")
|
||||
}, [form]);
|
||||
|
||||
const shipments = useMemo(() => {
|
||||
return form?.shipments;
|
||||
}, [form]);
|
||||
|
||||
const allProducts = useMemo(() => {
|
||||
return form?.productor?.products;
|
||||
}, [form])
|
||||
|
||||
const price = useMemo(() => {
|
||||
if (!allProducts) {
|
||||
return 0;
|
||||
}
|
||||
const values = Object.entries(inputForm.getValues());
|
||||
return computePrices(values, allProducts, form?.shipments.length);
|
||||
}, [inputForm, allProducts, form?.shipments]);
|
||||
|
||||
const inputRefs = useRef<Record<string, HTMLInputElement | null>>({
|
||||
firstname: null,
|
||||
lastname: null,
|
||||
email: null,
|
||||
phone: null
|
||||
});
|
||||
|
||||
const isShipmentsMinimumValue = useCallback(() => {
|
||||
if (!form)
|
||||
return false;
|
||||
const shipmentErrors = form.shipments
|
||||
.map((shipment) => {
|
||||
const total = computePrices(
|
||||
Object.entries(inputForm.getValues()),
|
||||
shipment.products
|
||||
);
|
||||
if (total < (form?.minimum_shipment_value || 0)) {
|
||||
return shipment.id;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
return shipmentErrors.length === 0;
|
||||
}, [form, inputForm]);
|
||||
|
||||
const withDefaultValues = useCallback((values: Record<string, number | string>) => {
|
||||
if (!productsRecurent || !form)
|
||||
return {};
|
||||
const result = {...values};
|
||||
|
||||
productsRecurent.forEach((product: Product) => {
|
||||
const key = `recurrent-${product.id}`;
|
||||
if (result[key] === undefined || result[key] === "") {
|
||||
result[key] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
form.shipments.forEach((shipment) => {
|
||||
shipment.products.forEach((product) => {
|
||||
const key = `planned-${shipment.id}-${product.id}`;
|
||||
if (result[key] === undefined || result[key] === "") {
|
||||
result[key] = 0;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return result;
|
||||
}, [productsRecurent, form]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
const errors = inputForm.validate();
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
if (inputForm.isValid() && isShipmentsMinimumValue()) {
|
||||
const contract = {
|
||||
form_id: form.id,
|
||||
contract: withDefaultValues(inputForm.getValues()),
|
||||
}
|
||||
await createContractMutation.mutateAsync(contract);
|
||||
} else {
|
||||
const firstErrorField = Object.keys(errors.errors)[0];
|
||||
const ref = inputRefs.current[firstErrorField];
|
||||
ref?.scrollIntoView({behavior: "smooth", block: "center"});
|
||||
}
|
||||
}, [inputForm, inputRefs, isShipmentsMinimumValue, form, createContractMutation, withDefaultValues]);
|
||||
|
||||
|
||||
if (!form || !shipments || !productsRecurent)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Stack w={{base: "100%", md: "80%", lg: "50%"}}>
|
||||
<Title order={2}>{form.name}</Title>
|
||||
<Title order={3}>{t("informations", {capfirst: true})}</Title>
|
||||
<Text size="sm">
|
||||
{t("all theses informations are for contract generation", {capfirst: true})}
|
||||
</Text>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("firstname", {capfirst: true})}
|
||||
placeholder={t("firstname", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconUser/>}
|
||||
{...inputForm.getInputProps('firstname')}
|
||||
ref={(el) => {inputRefs.current.firstname = el}}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("lastname", {capfirst: true})}
|
||||
placeholder={t("lastname", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconUser/>}
|
||||
{...inputForm.getInputProps('lastname')}
|
||||
ref={(el) => {inputRefs.current.lastname = el}}
|
||||
/>
|
||||
</Group>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("email", {capfirst: true})}
|
||||
placeholder={t("email", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconMail/>}
|
||||
{...inputForm.getInputProps('email')}
|
||||
ref={(el) => {inputRefs.current.email = el}}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("phone", {capfirst: true})}
|
||||
placeholder={t("phone", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconPhone/>}
|
||||
{...inputForm.getInputProps('phone')}
|
||||
ref={(el) => {inputRefs.current.phone = el}}
|
||||
/>
|
||||
</Group>
|
||||
<Title order={3}>{t('shipments', {capfirst: true})}</Title>
|
||||
<Text>{`${t("there is", {capfirst: true})} ${shipments.length} ${shipments.length > 1 ? t("shipments") : t("shipment")} ${t("for this contract")}`}</Text>
|
||||
<List>
|
||||
{
|
||||
shipments.map(shipment => (
|
||||
<List.Item key={shipment.id}>{`${shipment.name} :
|
||||
${
|
||||
new Date(shipment.date).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}`}
|
||||
</List.Item>
|
||||
))
|
||||
}
|
||||
</List>
|
||||
{
|
||||
productsRecurent.length > 0 ?
|
||||
<>
|
||||
<Title order={3}>{t('recurrent products', {capfirst: true})}</Title>
|
||||
<Text size="sm">{t('your selection in this category will apply for all shipments', {capfirst: true})}</Text>
|
||||
{
|
||||
productsRecurent.map((product) => (
|
||||
<ProductForm
|
||||
key={product.id}
|
||||
product={product}
|
||||
inputForm={inputForm}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</> :
|
||||
null
|
||||
}
|
||||
{
|
||||
shipments.some(shipment => shipment.products.length > 0) ?
|
||||
<>
|
||||
<Title order={3}>{t("planned products")}</Title>
|
||||
<Text>{t("select products per shipment")}</Text>
|
||||
<Accordion defaultValue={"0"}>
|
||||
{
|
||||
shipments.map((shipment, index) => (
|
||||
<ShipmentForm
|
||||
minimumPrice={form.minimum_shipment_value}
|
||||
shipment={shipment}
|
||||
index={index}
|
||||
inputForm={inputForm}
|
||||
key={shipment.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Accordion>
|
||||
</> :
|
||||
null
|
||||
}
|
||||
<Overlay
|
||||
bg={"lightGray"}
|
||||
h="10vh"
|
||||
p="sm"
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
position: "sticky",
|
||||
bottom: "0px",
|
||||
}}
|
||||
>
|
||||
<Text>{
|
||||
t("total", {capfirst: true})} : {Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(price)}
|
||||
</Text>
|
||||
<Button
|
||||
aria-label={t('submit contract')}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{t('submit contract')}
|
||||
</Button>
|
||||
</Overlay>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
import { ProductForm } from "@/components/Products/Form";
|
||||
import ShipmentForm from "@/components/Shipments/Form";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateContract, useGetForm } from "@/services/api";
|
||||
import { type Product } from "@/services/resources/products";
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
Group,
|
||||
List,
|
||||
Loader,
|
||||
Overlay,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconMail, IconPhone, IconUser } from "@tabler/icons-react";
|
||||
import { useCallback, useMemo, useRef } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { computePrices } from "./price";
|
||||
|
||||
export function Contract() {
|
||||
const { id } = useParams();
|
||||
const { data: form } = useGetForm(Number(id), { enabled: !!id });
|
||||
const inputForm = useForm<Record<string, number | string>>({
|
||||
initialValues: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
},
|
||||
validate: {
|
||||
firstname: (value) =>
|
||||
!value ? `${t("a firstname", { capfirst: true })} ${t("is required")}` : null,
|
||||
lastname: (value) =>
|
||||
!value ? `${t("a lastname", { capfirst: true })} ${t("is required")}` : null,
|
||||
email: (value) =>
|
||||
!value ? `${t("a email", { capfirst: true })} ${t("is required")}` : null,
|
||||
phone: (value) =>
|
||||
!value ? `${t("a phone", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
const createContractMutation = useCreateContract();
|
||||
|
||||
const productsRecurent = useMemo(() => {
|
||||
return form?.productor?.products.filter((el) => el.type === "2");
|
||||
}, [form]);
|
||||
|
||||
const shipments = useMemo(() => {
|
||||
return form?.shipments;
|
||||
}, [form]);
|
||||
|
||||
const allProducts = useMemo(() => {
|
||||
return form?.productor?.products;
|
||||
}, [form]);
|
||||
|
||||
const price = useMemo(() => {
|
||||
if (!allProducts) {
|
||||
return 0;
|
||||
}
|
||||
const values = Object.entries(inputForm.getValues());
|
||||
return computePrices(values, allProducts, form?.shipments.length);
|
||||
}, [inputForm, allProducts, form?.shipments]);
|
||||
|
||||
const inputRefs = useRef<Record<string, HTMLInputElement | null>>({
|
||||
firstname: null,
|
||||
lastname: null,
|
||||
email: null,
|
||||
phone: null,
|
||||
});
|
||||
|
||||
const isShipmentsMinimumValue = useCallback(() => {
|
||||
if (!form) return false;
|
||||
const shipmentErrors = form.shipments
|
||||
.map((shipment) => {
|
||||
const total = computePrices(
|
||||
Object.entries(inputForm.getValues()),
|
||||
shipment.products,
|
||||
);
|
||||
if (total < (form?.minimum_shipment_value || 0)) {
|
||||
return shipment.id;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
return shipmentErrors.length === 0;
|
||||
}, [form, inputForm]);
|
||||
|
||||
const withDefaultValues = useCallback(
|
||||
(values: Record<string, number | string>) => {
|
||||
if (!productsRecurent || !form) return {};
|
||||
const result = { ...values };
|
||||
|
||||
productsRecurent.forEach((product: Product) => {
|
||||
const key = `recurrent-${product.id}`;
|
||||
if (result[key] === undefined || result[key] === "") {
|
||||
result[key] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
form.shipments.forEach((shipment) => {
|
||||
shipment.products.forEach((product) => {
|
||||
const key = `planned-${shipment.id}-${product.id}`;
|
||||
if (result[key] === undefined || result[key] === "") {
|
||||
result[key] = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
[productsRecurent, form],
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
const errors = inputForm.validate();
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
if (inputForm.isValid() && isShipmentsMinimumValue()) {
|
||||
const contract = {
|
||||
form_id: form.id,
|
||||
contract: withDefaultValues(inputForm.getValues()),
|
||||
};
|
||||
await createContractMutation.mutateAsync(contract);
|
||||
} else {
|
||||
const firstErrorField = Object.keys(errors.errors)[0];
|
||||
const ref = inputRefs.current[firstErrorField];
|
||||
ref?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
}, [
|
||||
inputForm,
|
||||
inputRefs,
|
||||
isShipmentsMinimumValue,
|
||||
form,
|
||||
createContractMutation,
|
||||
withDefaultValues,
|
||||
]);
|
||||
|
||||
if (!form || !shipments || !productsRecurent)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack w={{ base: "100%", md: "80%", lg: "50%" }}>
|
||||
<Title order={2}>{form.name}</Title>
|
||||
<Title order={3}>{t("informations", { capfirst: true })}</Title>
|
||||
<Text size="sm">
|
||||
{t("all theses informations are for contract generation", {
|
||||
capfirst: true,
|
||||
})}
|
||||
</Text>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("firstname", { capfirst: true })}
|
||||
placeholder={t("firstname", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconUser />}
|
||||
{...inputForm.getInputProps("firstname")}
|
||||
ref={(el) => {
|
||||
inputRefs.current.firstname = el;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("lastname", { capfirst: true })}
|
||||
placeholder={t("lastname", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconUser />}
|
||||
{...inputForm.getInputProps("lastname")}
|
||||
ref={(el) => {
|
||||
inputRefs.current.lastname = el;
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("email", { capfirst: true })}
|
||||
placeholder={t("email", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconMail />}
|
||||
{...inputForm.getInputProps("email")}
|
||||
ref={(el) => {
|
||||
inputRefs.current.email = el;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("phone", { capfirst: true })}
|
||||
placeholder={t("phone", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
required
|
||||
leftSection={<IconPhone />}
|
||||
{...inputForm.getInputProps("phone")}
|
||||
ref={(el) => {
|
||||
inputRefs.current.phone = el;
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<Title order={3}>{t("shipments", { capfirst: true })}</Title>
|
||||
<Text>{`${t("there is", { capfirst: true })} ${shipments.length} ${shipments.length > 1 ? t("shipments") : t("shipment")} ${t("for this contract")}`}</Text>
|
||||
<List>
|
||||
{shipments.map((shipment) => (
|
||||
<List.Item key={shipment.id}>
|
||||
{`${shipment.name} :
|
||||
${new Date(shipment.date).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}`}
|
||||
</List.Item>
|
||||
))}
|
||||
</List>
|
||||
{productsRecurent.length > 0 ? (
|
||||
<>
|
||||
<Title order={3}>{t("recurrent products", { capfirst: true })}</Title>
|
||||
<Text size="sm">
|
||||
{t("your selection in this category will apply for all shipments", {
|
||||
capfirst: true,
|
||||
})}
|
||||
</Text>
|
||||
{productsRecurent.map((product) => (
|
||||
<ProductForm key={product.id} product={product} inputForm={inputForm} />
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
{shipments.some((shipment) => shipment.products.length > 0) ? (
|
||||
<>
|
||||
<Title order={3}>{t("planned products")}</Title>
|
||||
<Text>{t("select products per shipment")}</Text>
|
||||
<Accordion defaultValue={"0"}>
|
||||
{shipments.map((shipment, index) => (
|
||||
<ShipmentForm
|
||||
minimumPrice={form.minimum_shipment_value}
|
||||
shipment={shipment}
|
||||
index={index}
|
||||
inputForm={inputForm}
|
||||
key={shipment.id}
|
||||
/>
|
||||
))}
|
||||
</Accordion>
|
||||
</>
|
||||
) : null}
|
||||
<Overlay
|
||||
bg={"lightGray"}
|
||||
h="10vh"
|
||||
p="sm"
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
position: "sticky",
|
||||
bottom: "0px",
|
||||
}}
|
||||
>
|
||||
<Text>
|
||||
{t("total", { capfirst: true })} :{" "}
|
||||
{Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(price)}
|
||||
</Text>
|
||||
<Button aria-label={t("submit contract")} onClick={handleSubmit}>
|
||||
{t("submit contract")}
|
||||
</Button>
|
||||
</Overlay>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import type { Product } from "@/services/resources/products";
|
||||
|
||||
export function computePrices(values: [string, string | number][], products: Product[], nbShipment?: number) {
|
||||
return values.reduce((prev, [key, value]) => {
|
||||
const keyArray = key.split("-");
|
||||
const productId = Number(keyArray[keyArray.length - 1]);
|
||||
const product = products.find((product) => product.id === productId);
|
||||
if (!product) {
|
||||
return prev + 0;
|
||||
}
|
||||
const isRecurent = key.includes("recurrent") && nbShipment;
|
||||
const productPrice = Number(product.price || product.price_kg);
|
||||
const productQuantityUnit = product.unit === "2" ? 1 : 1000;
|
||||
const productQuantity = Number(product.price ? Number(value) : Number(value) / productQuantityUnit);
|
||||
return(prev + productPrice * productQuantity * (isRecurent ? nbShipment : 1));
|
||||
}, 0);
|
||||
}
|
||||
import type { Product } from "@/services/resources/products";
|
||||
|
||||
export function computePrices(
|
||||
values: [string, string | number][],
|
||||
products: Product[],
|
||||
nbShipment?: number,
|
||||
) {
|
||||
return values.reduce((prev, [key, value]) => {
|
||||
const keyArray = key.split("-");
|
||||
const productId = Number(keyArray[keyArray.length - 1]);
|
||||
const product = products.find((product) => product.id === productId);
|
||||
if (!product) {
|
||||
return prev + 0;
|
||||
}
|
||||
const isRecurent = key.includes("recurrent") && nbShipment;
|
||||
const productPrice = Number(product.price || product.price_kg);
|
||||
const productQuantityUnit = product.unit === "2" ? 1 : 1000;
|
||||
const productQuantity = Number(
|
||||
product.price ? Number(value) : Number(value) / productQuantityUnit,
|
||||
);
|
||||
return prev + productPrice * productQuantity * (isRecurent ? nbShipment : 1);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Tabs } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router";
|
||||
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
w={{base: "100%", md: "80%", lg: "60%"}}
|
||||
orientation={"horizontal"}
|
||||
value={location.pathname.split('/')[2]}
|
||||
defaultValue={"productors"}
|
||||
onChange={(value) => navigate(`/dashboard/${value}`)}
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="productors">{t("productors", {capfirst: true})}</Tabs.Tab>
|
||||
<Tabs.Tab value="products">{t("products", {capfirst: true})}</Tabs.Tab>
|
||||
<Tabs.Tab value="forms">{t("forms", {capfirst: true})}</Tabs.Tab>
|
||||
<Tabs.Tab value="shipments">{t("shipments", {capfirst: true})}</Tabs.Tab>
|
||||
{/* <Tabs.Tab value="templates">{t("templates", {capfirst: true})}</Tabs.Tab> */}
|
||||
<Tabs.Tab value="users">{t("users", {capfirst: true})}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Outlet/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
import { Tabs } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router";
|
||||
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
w={{ base: "100%", md: "80%", lg: "60%" }}
|
||||
orientation={"horizontal"}
|
||||
value={location.pathname.split("/")[2]}
|
||||
defaultValue={"productors"}
|
||||
onChange={(value) => navigate(`/dashboard/${value}`)}
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="productors">{t("productors", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab value="products">{t("products", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab value="forms">{t("forms", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab value="shipments">{t("shipments", { capfirst: true })}</Tabs.Tab>
|
||||
{/* <Tabs.Tab value="templates">{t("templates", {capfirst: true})}</Tabs.Tab> */}
|
||||
<Tabs.Tab value="users">{t("users", { capfirst: true })}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Outlet />
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,170 +1,169 @@
|
||||
import { Stack, Loader, Title, Group, ActionIcon, Tooltip, Table, ScrollArea } from "@mantine/core";
|
||||
import { useCreateForm, useEditForm, useGetForm, useGetForms } from "@/services/api";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import FormModal from "@/components/Forms/Modal";
|
||||
import FormRow from "@/components/Forms/Row";
|
||||
import type { Form, FormInputs } from "@/services/resources/forms";
|
||||
import FilterForms from "@/components/Forms/Filter";
|
||||
|
||||
export function Forms() {
|
||||
const [ searchParams, setSearchParams ] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/forms/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3]
|
||||
}
|
||||
return null
|
||||
}, [location, isEdit])
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/forms${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { isPending, data } = useGetForms(searchParams);
|
||||
const { data: currentForm } = useGetForm(Number(editId), { enabled: !!editId });
|
||||
|
||||
const { data: allForms } = useGetForms();
|
||||
|
||||
const seasons = useMemo(() => {
|
||||
return allForms?.map((form: Form) => (form.season))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allForms])
|
||||
|
||||
const productors = useMemo(() => {
|
||||
return allForms?.map((form: Form) => (form.productor.name))
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index)
|
||||
}, [allForms])
|
||||
|
||||
const createFormMutation = useCreateForm();
|
||||
const editFormMutation = useEditForm();
|
||||
|
||||
const handleCreateForm = useCallback(async (form: FormInputs) => {
|
||||
if (!form.start || !form.end)
|
||||
return;
|
||||
await createFormMutation.mutateAsync({
|
||||
...form,
|
||||
start: form?.start,
|
||||
end: form?.end,
|
||||
productor_id: Number(form.productor_id),
|
||||
referer_id: Number(form.referer_id),
|
||||
minimum_shipment_value: Number(form.minimum_shipment_value),
|
||||
});
|
||||
closeModal();
|
||||
}, [createFormMutation, closeModal]);
|
||||
|
||||
const handleEditForm = useCallback(async (form: FormInputs, id?: number) => {
|
||||
if (!id)
|
||||
return;
|
||||
await editFormMutation.mutateAsync({
|
||||
id: id,
|
||||
form: {
|
||||
...form,
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
productor_id: Number(form.productor_id),
|
||||
referer_id: Number(form.referer_id),
|
||||
minimum_shipment_value: Number(form.minimum_shipment_value),
|
||||
}
|
||||
});
|
||||
closeModal();
|
||||
}, [editFormMutation, closeModal]);
|
||||
|
||||
const onFilterChange = useCallback((
|
||||
values: string[],
|
||||
filter: string
|
||||
) => {
|
||||
setSearchParams(prev => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter)
|
||||
|
||||
values.forEach(value => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
|
||||
return params;
|
||||
});
|
||||
}, [setSearchParams])
|
||||
|
||||
|
||||
if (!data || isPending)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all forms", {capfirst: true})}</Title>
|
||||
<Tooltip label={t("create new form", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/forms/create${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconPlus/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<FormModal
|
||||
key={`${currentForm?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateForm}
|
||||
/>
|
||||
</Group>
|
||||
<FilterForms
|
||||
productors={productors || []}
|
||||
seasons={seasons || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<FormModal
|
||||
key={`${currentForm?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentForm={currentForm}
|
||||
handleSubmit={handleEditForm}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("type", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("start", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("end", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("productor", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("referer", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("actions", {capfirst: true})}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
data.map((form) => (
|
||||
<FormRow
|
||||
form={form}
|
||||
key={form.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { Stack, Loader, Title, Group, ActionIcon, Tooltip, Table, ScrollArea } from "@mantine/core";
|
||||
import { useCreateForm, useEditForm, useGetForm, useGetForms } from "@/services/api";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import FormModal from "@/components/Forms/Modal";
|
||||
import FormRow from "@/components/Forms/Row";
|
||||
import type { Form, FormInputs } from "@/services/resources/forms";
|
||||
import FilterForms from "@/components/Forms/Filter";
|
||||
|
||||
export function Forms() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/forms/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/forms${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { isPending, data } = useGetForms(searchParams);
|
||||
const { data: currentForm } = useGetForm(Number(editId), {
|
||||
enabled: !!editId,
|
||||
});
|
||||
|
||||
const { data: allForms } = useGetForms();
|
||||
|
||||
const seasons = useMemo(() => {
|
||||
return allForms
|
||||
?.map((form: Form) => form.season)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allForms]);
|
||||
|
||||
const productors = useMemo(() => {
|
||||
return allForms
|
||||
?.map((form: Form) => form.productor.name)
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index);
|
||||
}, [allForms]);
|
||||
|
||||
const createFormMutation = useCreateForm();
|
||||
const editFormMutation = useEditForm();
|
||||
|
||||
const handleCreateForm = useCallback(
|
||||
async (form: FormInputs) => {
|
||||
if (!form.start || !form.end) return;
|
||||
await createFormMutation.mutateAsync({
|
||||
...form,
|
||||
start: form?.start,
|
||||
end: form?.end,
|
||||
productor_id: Number(form.productor_id),
|
||||
referer_id: Number(form.referer_id),
|
||||
minimum_shipment_value: Number(form.minimum_shipment_value),
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[createFormMutation, closeModal],
|
||||
);
|
||||
|
||||
const handleEditForm = useCallback(
|
||||
async (form: FormInputs, id?: number) => {
|
||||
if (!id) return;
|
||||
await editFormMutation.mutateAsync({
|
||||
id: id,
|
||||
form: {
|
||||
...form,
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
productor_id: Number(form.productor_id),
|
||||
referer_id: Number(form.referer_id),
|
||||
minimum_shipment_value: Number(form.minimum_shipment_value),
|
||||
},
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[editFormMutation, closeModal],
|
||||
);
|
||||
|
||||
const onFilterChange = useCallback(
|
||||
(values: string[], filter: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach((value) => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
|
||||
return params;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
if (!data || isPending)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all forms", { capfirst: true })}</Title>
|
||||
<Tooltip label={t("create new form", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/forms/create${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconPlus />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<FormModal
|
||||
key={`${currentForm?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateForm}
|
||||
/>
|
||||
</Group>
|
||||
<FilterForms
|
||||
productors={productors || []}
|
||||
seasons={seasons || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<FormModal
|
||||
key={`${currentForm?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentForm={currentForm}
|
||||
handleSubmit={handleEditForm}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("type", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("start", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("end", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("productor", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("referer", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{data.map((form) => (
|
||||
<FormRow form={form} key={form.id} />
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ export function Home() {
|
||||
|
||||
return (
|
||||
<Flex gap="md" wrap="wrap" justify="center">
|
||||
{
|
||||
allForms && allForms?.length > 0 ?
|
||||
allForms.map((form: Form) => (
|
||||
<FormCard form={form} key={form.id}/>
|
||||
)) :
|
||||
<Text mt="lg" size="lg">{t("there is no contract for now",{capfirst: true})}</Text>
|
||||
}
|
||||
</Flex>
|
||||
{allForms && allForms?.length > 0 ? (
|
||||
allForms.map((form: Form) => <FormCard form={form} key={form.id} />)
|
||||
) : (
|
||||
<Text mt="lg" size="lg">
|
||||
{t("there is no contract for now", { capfirst: true })}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { t } from "@/config/i18n";
|
||||
import { ActionIcon, Stack, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconHome } from "@tabler/icons-react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export function NotFound() {
|
||||
const navigate = useNavigate()
|
||||
return (
|
||||
<Stack justify="center" align="center">
|
||||
<Title order={2}>{t("oops", {capfirst: true})}</Title>
|
||||
<Text>{t('this page does not exists', {capfirst: true})}</Text>
|
||||
<Tooltip label={t('back to home', {capfirst: true})}>
|
||||
<ActionIcon
|
||||
aria-label={t("back to home", {capfirst: true})}
|
||||
onClick={() => {
|
||||
navigate('/')
|
||||
}}
|
||||
>
|
||||
<IconHome/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { t } from "@/config/i18n";
|
||||
import { ActionIcon, Stack, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconHome } from "@tabler/icons-react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export function NotFound() {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Stack justify="center" align="center">
|
||||
<Title order={2}>{t("oops", { capfirst: true })}</Title>
|
||||
<Text>{t("this page does not exists", { capfirst: true })}</Text>
|
||||
<Tooltip label={t("back to home", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
aria-label={t("back to home", { capfirst: true })}
|
||||
onClick={() => {
|
||||
navigate("/");
|
||||
}}
|
||||
>
|
||||
<IconHome />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,148 +1,157 @@
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateProductor, useEditProductor, useGetProductor, useGetProductors } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ProductorRow from "@/components/Productors/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { ProductorModal } from "@/components/Productors/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import type { Productor, ProductorInputs } from "@/services/resources/productors";
|
||||
import ProductorsFilters from "@/components/Productors/Filter";
|
||||
|
||||
export default function Productors() {
|
||||
const [ searchParams, setSearchParams ] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/productors/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3]
|
||||
}
|
||||
return null
|
||||
}, [location, isEdit])
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/productors${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: productors, isPending } = useGetProductors(searchParams);
|
||||
const { data: currentProductor } = useGetProductor(Number(editId), { enabled: !!editId });
|
||||
const { data: allProductors } = useGetProductors();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allProductors?.map((productor: Productor) => (productor.name))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allProductors])
|
||||
|
||||
const types = useMemo(() => {
|
||||
return allProductors?.map((productor: Productor) => (productor.type))
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index)
|
||||
}, [allProductors])
|
||||
|
||||
const createProductorMutation = useCreateProductor();
|
||||
const editProductorMutation = useEditProductor();
|
||||
|
||||
const handleCreateProductor = useCallback(async (productor: ProductorInputs) => {
|
||||
await createProductorMutation.mutateAsync({
|
||||
...productor
|
||||
});
|
||||
closeModal();
|
||||
}, [createProductorMutation, closeModal]);
|
||||
|
||||
const handleEditProductor = useCallback(async (productor: ProductorInputs, id?: number) => {
|
||||
if (!id)
|
||||
return;
|
||||
await editProductorMutation.mutateAsync({
|
||||
id: id,
|
||||
productor: productor
|
||||
});
|
||||
closeModal();
|
||||
}, [editProductorMutation, closeModal]);
|
||||
|
||||
const onFilterChange = useCallback((values: string[], filter: string) => {
|
||||
setSearchParams(prev => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter)
|
||||
|
||||
values.forEach(value => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
}, [setSearchParams])
|
||||
|
||||
if (!productors || isPending)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all productors", {capfirst: true})}</Title>
|
||||
<Tooltip label={t("create productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/productors/create${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconPlus/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ProductorModal
|
||||
key={`${currentProductor?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateProductor}
|
||||
/>
|
||||
<ProductorModal
|
||||
key={`${currentProductor?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentProductor={currentProductor}
|
||||
handleSubmit={handleEditProductor}
|
||||
/>
|
||||
</Group>
|
||||
<ProductorsFilters
|
||||
names={names || []}
|
||||
types={types || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("type", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("address", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("payment methods", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("actions", {capfirst: true})}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
productors.map((productor) => (
|
||||
<ProductorRow
|
||||
productor={productor}
|
||||
key={productor.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import {
|
||||
useCreateProductor,
|
||||
useEditProductor,
|
||||
useGetProductor,
|
||||
useGetProductors,
|
||||
} from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ProductorRow from "@/components/Productors/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { ProductorModal } from "@/components/Productors/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import type { Productor, ProductorInputs } from "@/services/resources/productors";
|
||||
import ProductorsFilters from "@/components/Productors/Filter";
|
||||
|
||||
export default function Productors() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/productors/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/productors${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: productors, isPending } = useGetProductors(searchParams);
|
||||
const { data: currentProductor } = useGetProductor(Number(editId), {
|
||||
enabled: !!editId,
|
||||
});
|
||||
const { data: allProductors } = useGetProductors();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allProductors
|
||||
?.map((productor: Productor) => productor.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allProductors]);
|
||||
|
||||
const types = useMemo(() => {
|
||||
return allProductors
|
||||
?.map((productor: Productor) => productor.type)
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index);
|
||||
}, [allProductors]);
|
||||
|
||||
const createProductorMutation = useCreateProductor();
|
||||
const editProductorMutation = useEditProductor();
|
||||
|
||||
const handleCreateProductor = useCallback(
|
||||
async (productor: ProductorInputs) => {
|
||||
await createProductorMutation.mutateAsync({
|
||||
...productor,
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[createProductorMutation, closeModal],
|
||||
);
|
||||
|
||||
const handleEditProductor = useCallback(
|
||||
async (productor: ProductorInputs, id?: number) => {
|
||||
if (!id) return;
|
||||
await editProductorMutation.mutateAsync({
|
||||
id: id,
|
||||
productor: productor,
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[editProductorMutation, closeModal],
|
||||
);
|
||||
|
||||
const onFilterChange = useCallback(
|
||||
(values: string[], filter: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach((value) => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
if (!productors || isPending)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all productors", { capfirst: true })}</Title>
|
||||
<Tooltip label={t("create productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/productors/create${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconPlus />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ProductorModal
|
||||
key={`${currentProductor?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateProductor}
|
||||
/>
|
||||
<ProductorModal
|
||||
key={`${currentProductor?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentProductor={currentProductor}
|
||||
handleSubmit={handleEditProductor}
|
||||
/>
|
||||
</Group>
|
||||
<ProductorsFilters
|
||||
names={names || []}
|
||||
types={types || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("type", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("address", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("payment methods", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{productors.map((productor) => (
|
||||
<ProductorRow productor={productor} key={productor.id} />
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,148 +1,156 @@
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateProduct, useEditProduct, useGetProduct, useGetProducts } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ProductRow from "@/components/Products/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { ProductModal } from "@/components/Products/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { productCreateFromProductInputs, type Product, type ProductInputs } from "@/services/resources/products";
|
||||
import ProductsFilters from "@/components/Products/Filter";
|
||||
|
||||
export default function Products() {
|
||||
const [ searchParams, setSearchParams ] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/products/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3]
|
||||
}
|
||||
return null
|
||||
}, [location, isEdit])
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/products${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: products, isPending } = useGetProducts(searchParams);
|
||||
const { data: currentProduct } = useGetProduct(Number(editId), { enabled: !!editId });
|
||||
const { data: allProducts } = useGetProducts();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allProducts?.map((product: Product) => (product.name))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allProducts])
|
||||
|
||||
const productors = useMemo(() => {
|
||||
return allProducts?.map((product: Product) => (product.productor.name))
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index)
|
||||
}, [allProducts])
|
||||
|
||||
const createProductMutation = useCreateProduct();
|
||||
const editProductMutation = useEditProduct();
|
||||
|
||||
const handleCreateProduct = useCallback(async (product: ProductInputs) => {
|
||||
await createProductMutation.mutateAsync(productCreateFromProductInputs(product));
|
||||
closeModal();
|
||||
}, [createProductMutation, closeModal]);
|
||||
|
||||
const handleEditProduct = useCallback(async (product: ProductInputs, id?: number) => {
|
||||
if (!id)
|
||||
return;
|
||||
await editProductMutation.mutateAsync({
|
||||
id: id,
|
||||
product: productCreateFromProductInputs(product)
|
||||
});
|
||||
closeModal();
|
||||
}, [editProductMutation, closeModal]);
|
||||
|
||||
const onFilterChange = useCallback((values: string[], filter: string) => {
|
||||
setSearchParams(prev => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach(value => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
}, [setSearchParams])
|
||||
|
||||
if (!products || isPending)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all products", {capfirst: true})}</Title>
|
||||
<Tooltip label={t("create product", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/products/create${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconPlus/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ProductModal
|
||||
key={`${currentProduct?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateProduct}
|
||||
/>
|
||||
</Group>
|
||||
<ProductsFilters
|
||||
productors = {productors || []}
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ProductModal
|
||||
key={`${currentProduct?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentProduct={currentProduct}
|
||||
handleSubmit={handleEditProduct}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("type", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("price", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("priceKg", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("quantity", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("unit", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("actions", {capfirst: true})}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
products.map((product) => (
|
||||
<ProductRow
|
||||
product={product}
|
||||
key={product.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateProduct, useEditProduct, useGetProduct, useGetProducts } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ProductRow from "@/components/Products/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { ProductModal } from "@/components/Products/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import {
|
||||
productCreateFromProductInputs,
|
||||
type Product,
|
||||
type ProductInputs,
|
||||
} from "@/services/resources/products";
|
||||
import ProductsFilters from "@/components/Products/Filter";
|
||||
|
||||
export default function Products() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/products/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/products${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: products, isPending } = useGetProducts(searchParams);
|
||||
const { data: currentProduct } = useGetProduct(Number(editId), {
|
||||
enabled: !!editId,
|
||||
});
|
||||
const { data: allProducts } = useGetProducts();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allProducts
|
||||
?.map((product: Product) => product.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allProducts]);
|
||||
|
||||
const productors = useMemo(() => {
|
||||
return allProducts
|
||||
?.map((product: Product) => product.productor.name)
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index);
|
||||
}, [allProducts]);
|
||||
|
||||
const createProductMutation = useCreateProduct();
|
||||
const editProductMutation = useEditProduct();
|
||||
|
||||
const handleCreateProduct = useCallback(
|
||||
async (product: ProductInputs) => {
|
||||
await createProductMutation.mutateAsync(productCreateFromProductInputs(product));
|
||||
closeModal();
|
||||
},
|
||||
[createProductMutation, closeModal],
|
||||
);
|
||||
|
||||
const handleEditProduct = useCallback(
|
||||
async (product: ProductInputs, id?: number) => {
|
||||
if (!id) return;
|
||||
await editProductMutation.mutateAsync({
|
||||
id: id,
|
||||
product: productCreateFromProductInputs(product),
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[editProductMutation, closeModal],
|
||||
);
|
||||
|
||||
const onFilterChange = useCallback(
|
||||
(values: string[], filter: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach((value) => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
if (!products || isPending)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all products", { capfirst: true })}</Title>
|
||||
<Tooltip label={t("create product", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/products/create${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconPlus />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ProductModal
|
||||
key={`${currentProduct?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateProduct}
|
||||
/>
|
||||
</Group>
|
||||
<ProductsFilters
|
||||
productors={productors || []}
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ProductModal
|
||||
key={`${currentProduct?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentProduct={currentProduct}
|
||||
handleSubmit={handleEditProduct}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("type", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("price", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("priceKg", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("quantity", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("unit", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{products.map((product) => (
|
||||
<ProductRow product={product} key={product.id} />
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,145 +1,158 @@
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateShipment, useEditShipment, useGetShipment, useGetShipments } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ShipmentRow from "@/components/Shipments/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { shipmentCreateFromShipmentInputs, type Shipment, type ShipmentInputs } from "@/services/resources/shipments";
|
||||
import ShipmentModal from "@/components/Shipments/Modal";
|
||||
import ShipmentsFilters from "@/components/Shipments/Filter";
|
||||
|
||||
export default function Shipments() {
|
||||
const [ searchParams, setSearchParams ] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/shipments/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3]
|
||||
}
|
||||
return null
|
||||
}, [location, isEdit])
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/shipments${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: shipments, isPending } = useGetShipments(searchParams);
|
||||
const { data: currentShipment } = useGetShipment(Number(editId), { enabled: !!editId });
|
||||
const { data: allShipments } = useGetShipments();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allShipments?.map((shipment: Shipment) => (shipment.name))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allShipments])
|
||||
|
||||
const forms = useMemo(() => {
|
||||
return allShipments?.map((shipment: Shipment) => (shipment.form.name))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allShipments])
|
||||
|
||||
const createShipmentMutation = useCreateShipment();
|
||||
const editShipmentMutation = useEditShipment();
|
||||
|
||||
const handleCreateShipment = useCallback(async (shipment: ShipmentInputs) => {
|
||||
await createShipmentMutation.mutateAsync(shipmentCreateFromShipmentInputs(shipment));
|
||||
closeModal();
|
||||
}, [createShipmentMutation, closeModal]);
|
||||
|
||||
const handleEditShipment = useCallback(async (shipment: ShipmentInputs, id?: number) => {
|
||||
if (!id)
|
||||
return;
|
||||
await editShipmentMutation.mutateAsync({
|
||||
id: id,
|
||||
shipment: shipmentCreateFromShipmentInputs(shipment)
|
||||
});
|
||||
closeModal();
|
||||
}, [editShipmentMutation, closeModal]);
|
||||
|
||||
const onFilterChange = useCallback((values: string[], filter: string) => {
|
||||
setSearchParams(prev => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter)
|
||||
|
||||
values.forEach(value => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
}, [setSearchParams])
|
||||
|
||||
if (!shipments || isPending)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all shipments", {capfirst: true})}</Title>
|
||||
<Tooltip label={t("create shipment", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/shipments/create${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconPlus/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ShipmentModal
|
||||
key={`${currentShipment?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateShipment}
|
||||
/>
|
||||
<ShipmentModal
|
||||
key={`${currentShipment?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentShipment={currentShipment}
|
||||
handleSubmit={handleEditShipment}
|
||||
/>
|
||||
</Group>
|
||||
<ShipmentsFilters
|
||||
forms={forms || []}
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("date", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("formulare", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("actions", {capfirst: true})}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
shipments.map((shipment) => (
|
||||
<ShipmentRow
|
||||
shipment={shipment}
|
||||
key={shipment.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import {
|
||||
useCreateShipment,
|
||||
useEditShipment,
|
||||
useGetShipment,
|
||||
useGetShipments,
|
||||
} from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import ShipmentRow from "@/components/Shipments/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import {
|
||||
shipmentCreateFromShipmentInputs,
|
||||
type Shipment,
|
||||
type ShipmentInputs,
|
||||
} from "@/services/resources/shipments";
|
||||
import ShipmentModal from "@/components/Shipments/Modal";
|
||||
import ShipmentsFilters from "@/components/Shipments/Filter";
|
||||
|
||||
export default function Shipments() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/shipments/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/shipments${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: shipments, isPending } = useGetShipments(searchParams);
|
||||
const { data: currentShipment } = useGetShipment(Number(editId), {
|
||||
enabled: !!editId,
|
||||
});
|
||||
const { data: allShipments } = useGetShipments();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allShipments
|
||||
?.map((shipment: Shipment) => shipment.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allShipments]);
|
||||
|
||||
const forms = useMemo(() => {
|
||||
return allShipments
|
||||
?.map((shipment: Shipment) => shipment.form.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allShipments]);
|
||||
|
||||
const createShipmentMutation = useCreateShipment();
|
||||
const editShipmentMutation = useEditShipment();
|
||||
|
||||
const handleCreateShipment = useCallback(
|
||||
async (shipment: ShipmentInputs) => {
|
||||
await createShipmentMutation.mutateAsync(shipmentCreateFromShipmentInputs(shipment));
|
||||
closeModal();
|
||||
},
|
||||
[createShipmentMutation, closeModal],
|
||||
);
|
||||
|
||||
const handleEditShipment = useCallback(
|
||||
async (shipment: ShipmentInputs, id?: number) => {
|
||||
if (!id) return;
|
||||
await editShipmentMutation.mutateAsync({
|
||||
id: id,
|
||||
shipment: shipmentCreateFromShipmentInputs(shipment),
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[editShipmentMutation, closeModal],
|
||||
);
|
||||
|
||||
const onFilterChange = useCallback(
|
||||
(values: string[], filter: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach((value) => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
if (!shipments || isPending)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all shipments", { capfirst: true })}</Title>
|
||||
<Tooltip label={t("create shipment", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/shipments/create${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconPlus />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ShipmentModal
|
||||
key={`${currentShipment?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateShipment}
|
||||
/>
|
||||
<ShipmentModal
|
||||
key={`${currentShipment?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentShipment={currentShipment}
|
||||
handleSubmit={handleEditShipment}
|
||||
/>
|
||||
</Group>
|
||||
<ShipmentsFilters
|
||||
forms={forms || []}
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("date", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("formulare", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{shipments.map((shipment) => (
|
||||
<ShipmentRow shipment={shipment} key={shipment.id} />
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export default function Templates() {
|
||||
return (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
export default function Templates() {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -1,139 +1,142 @@
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateUser, useEditUser, useGetUser, useGetUsers } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import UserRow from "@/components/Users/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { UserModal } from "@/components/Users/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
import UsersFilters from "@/components/Users/Filter";
|
||||
|
||||
export default function Users() {
|
||||
const [ searchParams, setSearchParams ] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/users/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3]
|
||||
}
|
||||
return null
|
||||
}, [location, isEdit])
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/users${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const {data: users, isPending} = useGetUsers(searchParams);
|
||||
const { data: currentUser } = useGetUser(Number(editId), { enabled: !!editId });
|
||||
|
||||
const {data: allUsers } = useGetUsers();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allUsers?.map((user: User) => (user.name))
|
||||
.filter((season, index, array) => array.indexOf(season) === index)
|
||||
}, [allUsers])
|
||||
|
||||
const createUserMutation = useCreateUser();
|
||||
const editUserMutation = useEditUser();
|
||||
|
||||
const handleCreateUser = useCallback(async (user: UserInputs) => {
|
||||
await createUserMutation.mutateAsync(user);
|
||||
closeModal();
|
||||
}, [createUserMutation, closeModal]);
|
||||
|
||||
const handleEditUser = useCallback(async (user: UserInputs, id?: number) => {
|
||||
if (!id)
|
||||
return;
|
||||
await editUserMutation.mutateAsync({
|
||||
id: id,
|
||||
user: user
|
||||
});
|
||||
closeModal();
|
||||
}, [editUserMutation, closeModal]);
|
||||
|
||||
const onFilterChange = useCallback((values: string[], filter: string) => {
|
||||
setSearchParams(prev => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach(value => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
}, [setSearchParams])
|
||||
|
||||
if (!users || isPending)
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h="80vh"
|
||||
w="100%"
|
||||
>
|
||||
<Loader color="pink"/>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all users", {capfirst: true})}</Title>
|
||||
<Tooltip label={t("create user", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/users/create${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconPlus/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<UserModal
|
||||
key={`${currentUser?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateUser}
|
||||
/>
|
||||
<UserModal
|
||||
key={`${currentUser?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentUser={currentUser}
|
||||
handleSubmit={handleEditUser}
|
||||
/>
|
||||
</Group>
|
||||
<UsersFilters
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("email", {capfirst: true})}</Table.Th>
|
||||
<Table.Th>{t("actions", {capfirst: true})}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
users.map((user) => (
|
||||
<UserRow
|
||||
user={user}
|
||||
key={user.id}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useCreateUser, useEditUser, useGetUser, useGetUsers } from "@/services/api";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import UserRow from "@/components/Users/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { UserModal } from "@/components/Users/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
import UsersFilters from "@/components/Users/Filter";
|
||||
|
||||
export default function Users() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/users/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/users${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: users, isPending } = useGetUsers(searchParams);
|
||||
const { data: currentUser } = useGetUser(Number(editId), {
|
||||
enabled: !!editId,
|
||||
});
|
||||
|
||||
const { data: allUsers } = useGetUsers();
|
||||
|
||||
const names = useMemo(() => {
|
||||
return allUsers
|
||||
?.map((user: User) => user.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allUsers]);
|
||||
|
||||
const createUserMutation = useCreateUser();
|
||||
const editUserMutation = useEditUser();
|
||||
|
||||
const handleCreateUser = useCallback(
|
||||
async (user: UserInputs) => {
|
||||
await createUserMutation.mutateAsync(user);
|
||||
closeModal();
|
||||
},
|
||||
[createUserMutation, closeModal],
|
||||
);
|
||||
|
||||
const handleEditUser = useCallback(
|
||||
async (user: UserInputs, id?: number) => {
|
||||
if (!id) return;
|
||||
await editUserMutation.mutateAsync({
|
||||
id: id,
|
||||
user: user,
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
[editUserMutation, closeModal],
|
||||
);
|
||||
|
||||
const onFilterChange = useCallback(
|
||||
(values: string[], filter: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const params = new URLSearchParams(prev);
|
||||
params.delete(filter);
|
||||
|
||||
values.forEach((value) => {
|
||||
params.append(filter, value);
|
||||
});
|
||||
return params;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
if (!users || isPending)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>{t("all users", { capfirst: true })}</Title>
|
||||
<Tooltip label={t("create user", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/users/create${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconPlus />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<UserModal
|
||||
key={`${currentUser?.id}_create`}
|
||||
opened={isCreate}
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleCreateUser}
|
||||
/>
|
||||
<UserModal
|
||||
key={`${currentUser?.id}_edit`}
|
||||
opened={isEdit}
|
||||
onClose={closeModal}
|
||||
currentUser={currentUser}
|
||||
handleSubmit={handleEditUser}
|
||||
/>
|
||||
</Group>
|
||||
<UsersFilters
|
||||
names={names || []}
|
||||
filters={searchParams}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("email", { capfirst: true })}</Table.Th>
|
||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{users.map((user) => (
|
||||
<UserRow user={user} key={user.id} />
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user