add prettier code formater
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer>
|
||||
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
return <footer></footer>;
|
||||
}
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
import { Badge, Box, Group, Paper, Text, Title } from "@mantine/core";
|
||||
import { Link } from "react-router";
|
||||
import type { Form } from "@/services/resources/forms";
|
||||
|
||||
export type FormCardProps = {
|
||||
form: Form;
|
||||
}
|
||||
|
||||
export function FormCard({form}: FormCardProps) {
|
||||
return (
|
||||
<Paper
|
||||
shadow="xl"
|
||||
p="xl"
|
||||
miw={{base: "100vw", md: "25vw", lg:"20vw"}}
|
||||
>
|
||||
<Box
|
||||
component={Link}
|
||||
to={`/form/${form.id}`}
|
||||
style={{textDecoration: "none", color: "black"}}
|
||||
>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Title
|
||||
order={3}
|
||||
textWrap="wrap"
|
||||
lineClamp={1}
|
||||
>
|
||||
{form.name}
|
||||
</Title>
|
||||
<Badge>{form.season}</Badge>
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Text>{form.productor.name}</Text>
|
||||
<Text>{form.referer.name}</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
);
|
||||
}
|
||||
import { Badge, Box, Group, Paper, Text, Title } from "@mantine/core";
|
||||
import { Link } from "react-router";
|
||||
import type { Form } from "@/services/resources/forms";
|
||||
|
||||
export type FormCardProps = {
|
||||
form: Form;
|
||||
};
|
||||
|
||||
export function FormCard({ form }: FormCardProps) {
|
||||
return (
|
||||
<Paper shadow="xl" p="xl" miw={{ base: "100vw", md: "25vw", lg: "20vw" }}>
|
||||
<Box
|
||||
component={Link}
|
||||
to={`/form/${form.id}`}
|
||||
style={{ textDecoration: "none", color: "black" }}
|
||||
>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Title order={3} textWrap="wrap" lineClamp={1}>
|
||||
{form.name}
|
||||
</Title>
|
||||
<Badge>{form.season}</Badge>
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Text>{form.productor.name}</Text>
|
||||
<Text>{form.referer.name}</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export type FilterFormsProps = {
|
||||
seasons: string[];
|
||||
productors: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
}
|
||||
|
||||
export default function FilterForms({
|
||||
seasons,
|
||||
productors,
|
||||
filters,
|
||||
onFilterChange
|
||||
}: FilterFormsProps) {
|
||||
const defaultProductors = useMemo(() => {
|
||||
return filters.getAll("productors")
|
||||
}, [filters]);
|
||||
const defaultSeasons = useMemo(() => {
|
||||
return filters.getAll("seasons")
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by season", {capfirst: true})}
|
||||
placeholder={t("filter by season", {capfirst: true})}
|
||||
data={seasons}
|
||||
defaultValue={defaultSeasons}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'seasons')
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by productor", {capfirst: true})}
|
||||
placeholder={t("filter by productor", {capfirst: true})}
|
||||
data={productors}
|
||||
defaultValue={defaultProductors}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'productors')
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export type FilterFormsProps = {
|
||||
seasons: string[];
|
||||
productors: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
};
|
||||
|
||||
export default function FilterForms({
|
||||
seasons,
|
||||
productors,
|
||||
filters,
|
||||
onFilterChange,
|
||||
}: FilterFormsProps) {
|
||||
const defaultProductors = useMemo(() => {
|
||||
return filters.getAll("productors");
|
||||
}, [filters]);
|
||||
const defaultSeasons = useMemo(() => {
|
||||
return filters.getAll("seasons");
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by season", { capfirst: true })}
|
||||
placeholder={t("filter by season", { capfirst: true })}
|
||||
data={seasons}
|
||||
defaultValue={defaultSeasons}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "seasons");
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by productor", { capfirst: true })}
|
||||
placeholder={t("filter by productor", { capfirst: true })}
|
||||
data={productors}
|
||||
defaultValue={defaultProductors}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "productors");
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Button, Group, Modal, NumberInput, Select, TextInput, type ModalBaseProps } from "@mantine/core";
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Modal,
|
||||
NumberInput,
|
||||
Select,
|
||||
TextInput,
|
||||
type ModalBaseProps,
|
||||
} from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { IconCancel, IconEdit, IconPlus } from "@tabler/icons-react";
|
||||
@@ -10,16 +18,11 @@ import type { Form, FormInputs } from "@/services/resources/forms";
|
||||
export type FormModalProps = ModalBaseProps & {
|
||||
currentForm?: Form;
|
||||
handleSubmit: (form: FormInputs, id?: number) => void;
|
||||
}
|
||||
};
|
||||
|
||||
export default function FormModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentForm,
|
||||
handleSubmit
|
||||
}: FormModalProps) {
|
||||
const {data: productors} = useGetProductors();
|
||||
const {data: users} = useGetUsers();
|
||||
export default function FormModal({ opened, onClose, currentForm, handleSubmit }: FormModalProps) {
|
||||
const { data: productors } = useGetProductors();
|
||||
const { data: users } = useGetUsers();
|
||||
|
||||
const form = useForm<FormInputs>({
|
||||
initialValues: {
|
||||
@@ -33,115 +36,138 @@ export default function FormModal({
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("a name", {capfirst: true})} ${t('is required')}` : null,
|
||||
!value ? `${t("a name", { capfirst: true })} ${t("is required")}` : null,
|
||||
season: (value) =>
|
||||
!value ? `${t("a season", {capfirst: true})} ${t('is required')}` : null,
|
||||
!value ? `${t("a season", { capfirst: true })} ${t("is required")}` : null,
|
||||
start: (value) =>
|
||||
!value ? `${t("a start date", {capfirst: true})} ${t('is required')}` : null,
|
||||
!value ? `${t("a start date", { capfirst: true })} ${t("is required")}` : null,
|
||||
end: (value) =>
|
||||
!value ? `${t("a end date", {capfirst: true})} ${t('is required')}` : null,
|
||||
!value ? `${t("a end date", { capfirst: true })} ${t("is required")}` : null,
|
||||
productor_id: (value) =>
|
||||
!value ? `${t("a productor", {capfirst: true})} ${t('is required')}` : null,
|
||||
!value ? `${t("a productor", { capfirst: true })} ${t("is required")}` : null,
|
||||
referer_id: (value) =>
|
||||
!value ? `${t("a referer", {capfirst: true})} ${t('is required')}` : null
|
||||
}
|
||||
!value ? `${t("a referer", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
const usersSelect = useMemo(() => {
|
||||
return users?.map(user => ({value: String(user.id), label: `${user.name}`}))
|
||||
return users?.map((user) => ({
|
||||
value: String(user.id),
|
||||
label: `${user.name}`,
|
||||
}));
|
||||
}, [users]);
|
||||
|
||||
const productorsSelect = useMemo(() => {
|
||||
return productors?.map(prod => ({value: String(prod.id), label: `${prod.name}`}))
|
||||
return productors?.map((prod) => ({
|
||||
value: String(prod.id),
|
||||
label: `${prod.name}`,
|
||||
}));
|
||||
}, [productors]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentForm ? t("edit form", {capfirst: true}) : t('create form', {capfirst: true})}
|
||||
title={
|
||||
currentForm
|
||||
? t("edit form", { capfirst: true })
|
||||
: t("create form", { capfirst: true })
|
||||
}
|
||||
>
|
||||
<TextInput
|
||||
label={t("form name", {capfirst: true})}
|
||||
placeholder={t("form name", {capfirst: true})}
|
||||
label={t("form name", { capfirst: true })}
|
||||
placeholder={t("form name", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('name')}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("contract season", {capfirst: true})}
|
||||
placeholder={t("contract season", {capfirst: true})}
|
||||
description={t("contract season recommandation", {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')}
|
||||
{...form.getInputProps("season")}
|
||||
/>
|
||||
<Group grow>
|
||||
<DatePickerInput
|
||||
label={t("start date", {capfirst: true})}
|
||||
placeholder={t("start date", {capfirst: true})}
|
||||
label={t("start date", { capfirst: true })}
|
||||
placeholder={t("start date", { capfirst: true })}
|
||||
withAsterisk
|
||||
{...form.getInputProps('start')}
|
||||
{...form.getInputProps("start")}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("end date", {capfirst: true})}
|
||||
placeholder={t("end date", {capfirst: true})}
|
||||
label={t("end date", { capfirst: true })}
|
||||
placeholder={t("end date", { capfirst: true })}
|
||||
withAsterisk
|
||||
{...form.getInputProps('end')}
|
||||
{...form.getInputProps("end")}
|
||||
/>
|
||||
</Group>
|
||||
<Select
|
||||
label={t("referer", {capfirst: true})}
|
||||
placeholder={t("referer", {capfirst: true})}
|
||||
nothingFoundMessage={t("nothing found", {capfirst: true})}
|
||||
label={t("referer", { capfirst: true })}
|
||||
placeholder={t("referer", { capfirst: true })}
|
||||
nothingFoundMessage={t("nothing found", { capfirst: true })}
|
||||
withAsterisk
|
||||
clearable
|
||||
allowDeselect
|
||||
searchable
|
||||
data={usersSelect || []}
|
||||
{...form.getInputProps('referer_id')}
|
||||
{...form.getInputProps("referer_id")}
|
||||
/>
|
||||
<Select
|
||||
label={t("productor", {capfirst: true})}
|
||||
placeholder={t("productor", {capfirst: true})}
|
||||
nothingFoundMessage={t("nothing found", {capfirst: true})}
|
||||
label={t("productor", { capfirst: true })}
|
||||
placeholder={t("productor", { capfirst: true })}
|
||||
nothingFoundMessage={t("nothing found", { capfirst: true })}
|
||||
withAsterisk
|
||||
clearable
|
||||
allowDeselect
|
||||
searchable
|
||||
data={productorsSelect || []}
|
||||
{...form.getInputProps('productor_id')}
|
||||
{...form.getInputProps("productor_id")}
|
||||
/>
|
||||
<NumberInput
|
||||
label={t("minimum shipment value", {capfirst: true})}
|
||||
placeholder={t("minimum shipment value", {capfirst: true})}
|
||||
description={t("some contracts require a minimum value per shipment, ignore this field if it's not the case", {capfirst: true})}
|
||||
label={t("minimum shipment value", { capfirst: true })}
|
||||
placeholder={t("minimum shipment value", { capfirst: true })}
|
||||
description={t(
|
||||
"some contracts require a minimum value per shipment, ignore this field if it's not the case",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
radius="sm"
|
||||
{...form.getInputProps('minimum_shipment_value')}
|
||||
{...form.getInputProps("minimum_shipment_value")}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
aria-label={t("cancel", { capfirst: true })}
|
||||
leftSection={<IconCancel />}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>{t("cancel", {capfirst: true})}</Button>
|
||||
>
|
||||
{t("cancel", { capfirst: true })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={currentForm ? t("edit form", {capfirst: true}) : t('create form', {capfirst: true})}
|
||||
leftSection={currentForm ? <IconEdit/> : <IconPlus/>}
|
||||
aria-label={
|
||||
currentForm
|
||||
? t("edit form", { capfirst: true })
|
||||
: t("create form", { capfirst: true })
|
||||
}
|
||||
leftSection={currentForm ? <IconEdit /> : <IconPlus />}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentForm?.id)
|
||||
handleSubmit(form.getValues(), currentForm?.id);
|
||||
}
|
||||
}}
|
||||
>{currentForm ? t("edit form", {capfirst: true}) : t('create form', {capfirst: true})}</Button>
|
||||
>
|
||||
{currentForm
|
||||
? t("edit form", { capfirst: true })
|
||||
: t("create form", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { useDeleteForm} from "@/services/api";
|
||||
import { useDeleteForm } from "@/services/api";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
import type { Form } from "@/services/resources/forms";
|
||||
|
||||
export type FormRowProps = {
|
||||
form: Form;
|
||||
}
|
||||
};
|
||||
|
||||
export default function FormRow({
|
||||
form,
|
||||
}: FormRowProps) {
|
||||
export default function FormRow({ form }: FormRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteForm();
|
||||
const navigate = useNavigate();
|
||||
@@ -25,31 +23,33 @@ export default function FormRow({
|
||||
<Table.Td>{form.productor.name}</Table.Td>
|
||||
<Table.Td>{form.referer.name}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
<Tooltip label={t("edit productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/forms/${form.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
navigate(
|
||||
`/dashboard/forms/${form.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
<Tooltip label={t("remove productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(form.id);
|
||||
}}
|
||||
>
|
||||
<IconX/>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
|
||||
export type InputLabelProps = {
|
||||
label: string;
|
||||
info: string;
|
||||
isRequired?: boolean;
|
||||
}
|
||||
|
||||
export function InputLabel({label, info, isRequired}: InputLabelProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Tooltip label={info}>
|
||||
<ActionIcon variant="transparent" size="xs" color="gray">
|
||||
<IconInfoCircle size={16}/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<span>
|
||||
{label}
|
||||
{
|
||||
isRequired ?
|
||||
<span style={{ color: 'red' }}> *</span> : null
|
||||
}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
|
||||
export type InputLabelProps = {
|
||||
label: string;
|
||||
info: string;
|
||||
isRequired?: boolean;
|
||||
};
|
||||
|
||||
export function InputLabel({ label, info, isRequired }: InputLabelProps) {
|
||||
return (
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||||
<Tooltip label={info}>
|
||||
<ActionIcon variant="transparent" size="xs" color="gray">
|
||||
<IconInfoCircle size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<span>
|
||||
{label}
|
||||
{isRequired ? <span style={{ color: "red" }}> *</span> : null}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ nav {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navLink {
|
||||
.navLink {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,24 @@ export function Navbar() {
|
||||
return (
|
||||
<nav>
|
||||
<Group>
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t('home')}
|
||||
to="/"
|
||||
>
|
||||
{t("home", {capfirst: true})}
|
||||
<NavLink className={"navLink"} aria-label={t("home")} to="/">
|
||||
{t("home", { capfirst: true })}
|
||||
</NavLink>
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t('dashboard')}
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t("dashboard")}
|
||||
to="/dashboard/productors"
|
||||
>
|
||||
{t("dashboard", {capfirst: true})}
|
||||
{t("dashboard", { capfirst: true })}
|
||||
</NavLink>
|
||||
</Group>
|
||||
<NavLink
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t("login with keycloak")}
|
||||
to={`${Config.backend_uri}/auth/login`}
|
||||
>
|
||||
{t("login with keycloak", {capfirst: true})}
|
||||
{t("login with keycloak", { capfirst: true })}
|
||||
</NavLink>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ProductorsFiltersProps = {
|
||||
names: string[];
|
||||
types: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
}
|
||||
|
||||
export default function ProductorsFilter({
|
||||
names,
|
||||
types,
|
||||
filters,
|
||||
onFilterChange
|
||||
}: ProductorsFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names")
|
||||
}, [filters]);
|
||||
const defaultTypes = useMemo(() => {
|
||||
return filters.getAll("types")
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", {capfirst: true})}
|
||||
placeholder={t("filter by name", {capfirst: true})}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by type", {capfirst: true})}
|
||||
placeholder={t("filter by type", {capfirst: true})}
|
||||
data={types}
|
||||
defaultValue={defaultTypes}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'types')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ProductorsFiltersProps = {
|
||||
names: string[];
|
||||
types: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
};
|
||||
|
||||
export default function ProductorsFilter({
|
||||
names,
|
||||
types,
|
||||
filters,
|
||||
onFilterChange,
|
||||
}: ProductorsFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names");
|
||||
}, [filters]);
|
||||
const defaultTypes = useMemo(() => {
|
||||
return filters.getAll("types");
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", { capfirst: true })}
|
||||
placeholder={t("filter by name", { capfirst: true })}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "names");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by type", { capfirst: true })}
|
||||
placeholder={t("filter by type", { capfirst: true })}
|
||||
data={types}
|
||||
defaultValue={defaultTypes}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "types");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,130 +1,151 @@
|
||||
import { Button, Group, Modal, MultiSelect, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { PaymentMethods, type Productor, type ProductorInputs } from "@/services/resources/productors";
|
||||
|
||||
export type ProductorModalProps = ModalBaseProps & {
|
||||
currentProductor?: Productor;
|
||||
handleSubmit: (productor: ProductorInputs, id?: number) => void;
|
||||
}
|
||||
|
||||
export function ProductorModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentProductor,
|
||||
handleSubmit
|
||||
}: ProductorModalProps) {
|
||||
const form = useForm<ProductorInputs>({
|
||||
initialValues: {
|
||||
name: currentProductor?.name ?? "",
|
||||
address: currentProductor?.address ?? "",
|
||||
payment_methods: currentProductor?.payment_methods ?? [],
|
||||
type: currentProductor?.type ?? "",
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", {capfirst: true})} ${t("is required")}` : null,
|
||||
address: (value) =>
|
||||
!value ? `${t("address", {capfirst: true})} ${t("is required")}` : null,
|
||||
type: (value) =>
|
||||
!value ? `${t("type", {capfirst: true})} ${t("is required")}` : null
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create productor", {capfirst: true})}
|
||||
>
|
||||
<Title order={4}>{t("Informations", {capfirst: true})}</Title>
|
||||
<TextInput
|
||||
label={t("productor name", {capfirst: true})}
|
||||
placeholder={t("productor name", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("productor type", {capfirst: true})}
|
||||
placeholder={t("productor type", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("type")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("productor address", {capfirst: true})}
|
||||
placeholder={t("productor address", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("address")}
|
||||
/>
|
||||
<MultiSelect
|
||||
label={t("payment methods", {capfirst: true})}
|
||||
placeholder={t("payment methods", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
data={PaymentMethods}
|
||||
clearable
|
||||
searchable
|
||||
value={form.values.payment_methods.map(p => p.name)}
|
||||
onChange={(names) => {
|
||||
form.setFieldValue("payment_methods", names.map(name => {
|
||||
const existing = form.values.payment_methods.find(p => p.name === name);
|
||||
return existing ?? {
|
||||
name,
|
||||
details: ""
|
||||
};
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
{
|
||||
form.values.payment_methods.map((method, index) => (
|
||||
<TextInput
|
||||
key={index}
|
||||
label={
|
||||
method.name === "cheque" ?
|
||||
t("order name", {capfirst: true}) :
|
||||
method.name === "transfer" ?
|
||||
t("IBAN") :
|
||||
t("details", {capfirst: true})
|
||||
}
|
||||
placeholder={
|
||||
method.name === "cheque" ?
|
||||
t("order name", {capfirst: true}) :
|
||||
method.name === "transfer" ?
|
||||
t("IBAN") :
|
||||
t("details", {capfirst: true})
|
||||
}
|
||||
{...form.getInputProps(
|
||||
`payment_methods.${index}.details`
|
||||
)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>{t("cancel", {capfirst: true})}</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={currentProductor ? t("edit productor", {capfirst: true}) : t("create productor", {capfirst: true})}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProductor?.id)
|
||||
}
|
||||
}}
|
||||
>{currentProductor ? t("edit productor", {capfirst: true}) : t("create productor", {capfirst: true})}</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
TextInput,
|
||||
Title,
|
||||
type ModalBaseProps,
|
||||
} from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import {
|
||||
PaymentMethods,
|
||||
type Productor,
|
||||
type ProductorInputs,
|
||||
} from "@/services/resources/productors";
|
||||
|
||||
export type ProductorModalProps = ModalBaseProps & {
|
||||
currentProductor?: Productor;
|
||||
handleSubmit: (productor: ProductorInputs, id?: number) => void;
|
||||
};
|
||||
|
||||
export function ProductorModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentProductor,
|
||||
handleSubmit,
|
||||
}: ProductorModalProps) {
|
||||
const form = useForm<ProductorInputs>({
|
||||
initialValues: {
|
||||
name: currentProductor?.name ?? "",
|
||||
address: currentProductor?.address ?? "",
|
||||
payment_methods: currentProductor?.payment_methods ?? [],
|
||||
type: currentProductor?.type ?? "",
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", { capfirst: true })} ${t("is required")}` : null,
|
||||
address: (value) =>
|
||||
!value ? `${t("address", { capfirst: true })} ${t("is required")}` : null,
|
||||
type: (value) =>
|
||||
!value ? `${t("type", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create productor", { capfirst: true })}>
|
||||
<Title order={4}>{t("Informations", { capfirst: true })}</Title>
|
||||
<TextInput
|
||||
label={t("productor name", { capfirst: true })}
|
||||
placeholder={t("productor name", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("productor type", { capfirst: true })}
|
||||
placeholder={t("productor type", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("type")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("productor address", { capfirst: true })}
|
||||
placeholder={t("productor address", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("address")}
|
||||
/>
|
||||
<MultiSelect
|
||||
label={t("payment methods", { capfirst: true })}
|
||||
placeholder={t("payment methods", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
data={PaymentMethods}
|
||||
clearable
|
||||
searchable
|
||||
value={form.values.payment_methods.map((p) => p.name)}
|
||||
onChange={(names) => {
|
||||
form.setFieldValue(
|
||||
"payment_methods",
|
||||
names.map((name) => {
|
||||
const existing = form.values.payment_methods.find(
|
||||
(p) => p.name === name,
|
||||
);
|
||||
return (
|
||||
existing ?? {
|
||||
name,
|
||||
details: "",
|
||||
}
|
||||
);
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{form.values.payment_methods.map((method, index) => (
|
||||
<TextInput
|
||||
key={index}
|
||||
label={
|
||||
method.name === "cheque"
|
||||
? t("order name", { capfirst: true })
|
||||
: method.name === "transfer"
|
||||
? t("IBAN")
|
||||
: t("details", { capfirst: true })
|
||||
}
|
||||
placeholder={
|
||||
method.name === "cheque"
|
||||
? t("order name", { capfirst: true })
|
||||
: method.name === "transfer"
|
||||
? t("IBAN")
|
||||
: t("details", { capfirst: true })
|
||||
}
|
||||
{...form.getInputProps(`payment_methods.${index}.details`)}
|
||||
/>
|
||||
))}
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", { capfirst: true })}
|
||||
leftSection={<IconCancel />}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("cancel", { capfirst: true })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={
|
||||
currentProductor
|
||||
? t("edit productor", { capfirst: true })
|
||||
: t("create productor", { capfirst: true })
|
||||
}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProductor?.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentProductor
|
||||
? t("edit productor", { capfirst: true })
|
||||
: t("create productor", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,62 +1,59 @@
|
||||
import { ActionIcon, Badge, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import type { Productor } from "@/services/resources/productors";
|
||||
import { useDeleteProductor } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductorRowProps = {
|
||||
productor: Productor;
|
||||
}
|
||||
|
||||
export default function ProductorRow({
|
||||
productor,
|
||||
}: ProductorRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProductor();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={productor.id}>
|
||||
<Table.Td>{productor.name}</Table.Td>
|
||||
<Table.Td>{productor.type}</Table.Td>
|
||||
<Table.Td>{productor.address}</Table.Td>
|
||||
<Table.Td>
|
||||
{
|
||||
productor.payment_methods.map((value) =>(
|
||||
<Badge key={value.name} ml="xs">
|
||||
{t(value.name, {capfirst: true})}
|
||||
</Badge>
|
||||
))
|
||||
}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/productors/${productor.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(productor.id);
|
||||
}}
|
||||
>
|
||||
<IconX/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Badge, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import type { Productor } from "@/services/resources/productors";
|
||||
import { useDeleteProductor } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductorRowProps = {
|
||||
productor: Productor;
|
||||
};
|
||||
|
||||
export default function ProductorRow({ productor }: ProductorRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProductor();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={productor.id}>
|
||||
<Table.Td>{productor.name}</Table.Td>
|
||||
<Table.Td>{productor.type}</Table.Td>
|
||||
<Table.Td>{productor.address}</Table.Td>
|
||||
<Table.Td>
|
||||
{productor.payment_methods.map((value) => (
|
||||
<Badge key={value.name} ml="xs">
|
||||
{t(value.name, { capfirst: true })}
|
||||
</Badge>
|
||||
))}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/productors/${productor.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(productor.id);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ProductsFiltersProps = {
|
||||
names: string[];
|
||||
productors: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
}
|
||||
|
||||
export default function ProductsFilters({
|
||||
names,
|
||||
productors,
|
||||
filters,
|
||||
onFilterChange
|
||||
}: ProductsFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names")
|
||||
}, [filters]);
|
||||
const defaultProductors = useMemo(() => {
|
||||
return filters.getAll("productors")
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", {capfirst: true})}
|
||||
placeholder={t("filter by name", {capfirst: true})}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by productor", {capfirst: true})}
|
||||
placeholder={t("filter by productor", {capfirst: true})}
|
||||
data={productors}
|
||||
defaultValue={defaultProductors}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'productors')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ProductsFiltersProps = {
|
||||
names: string[];
|
||||
productors: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
};
|
||||
|
||||
export default function ProductsFilters({
|
||||
names,
|
||||
productors,
|
||||
filters,
|
||||
onFilterChange,
|
||||
}: ProductsFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names");
|
||||
}, [filters]);
|
||||
const defaultProductors = useMemo(() => {
|
||||
return filters.getAll("productors");
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", { capfirst: true })}
|
||||
placeholder={t("filter by name", { capfirst: true })}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "names");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by productor", { capfirst: true })}
|
||||
placeholder={t("filter by productor", { capfirst: true })}
|
||||
data={productors}
|
||||
defaultValue={defaultProductors}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "productors");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,37 +8,34 @@ export type ProductFormProps = {
|
||||
inputForm: UseFormReturnType<Record<string, string | number>>;
|
||||
product: Product;
|
||||
shipment?: Shipment;
|
||||
}
|
||||
};
|
||||
|
||||
export function ProductForm({
|
||||
inputForm,
|
||||
product,
|
||||
shipment,
|
||||
}: ProductFormProps) {
|
||||
export function ProductForm({ inputForm, product, shipment }: ProductFormProps) {
|
||||
return (
|
||||
<Group mb="sm" grow>
|
||||
<NumberInput
|
||||
label={
|
||||
`${product.name}
|
||||
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
|
||||
? 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])}`}
|
||||
${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}`)}
|
||||
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,163 +1,196 @@
|
||||
import { Button, Group, Modal, NumberInput, Select, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { ProductQuantityUnit, ProductUnit, type Product, type ProductInputs } from "@/services/resources/products";
|
||||
import { useMemo } from "react";
|
||||
import { useGetProductors } from "@/services/api";
|
||||
import { InputLabel } from "@/components/Label";
|
||||
|
||||
export type ProductModalProps = ModalBaseProps & {
|
||||
currentProduct?: Product;
|
||||
handleSubmit: (product: ProductInputs, id?: number) => void;
|
||||
}
|
||||
|
||||
export function ProductModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentProduct,
|
||||
handleSubmit
|
||||
}: ProductModalProps) {
|
||||
const {data: productors} = useGetProductors();
|
||||
const form = useForm<ProductInputs>({
|
||||
initialValues: {
|
||||
name: currentProduct?.name ?? "",
|
||||
unit: currentProduct?.unit ?? null,
|
||||
price: currentProduct?.price ?? null,
|
||||
price_kg: currentProduct?.price_kg ?? null,
|
||||
quantity: currentProduct?.quantity ?? null,
|
||||
quantity_unit: currentProduct?.quantity_unit ?? null,
|
||||
type: currentProduct?.type ?? null,
|
||||
productor_id: currentProduct ? String(currentProduct.productor.id) : null,
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", {capfirst: true})} ${t('is required')}` : null,
|
||||
unit: (value) =>
|
||||
!value ? `${t("unit", {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) =>
|
||||
!value ? `${t("productor", {capfirst: true})} ${t('is required')}` : null
|
||||
},
|
||||
});
|
||||
|
||||
const productorsSelect = useMemo(() => {
|
||||
return productors?.map(productor => ({value: String(productor.id), label: `${productor.name}`}))
|
||||
}, [productors]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create product", {capfirst: true})}
|
||||
>
|
||||
<Title order={4}>{t("informations", {capfirst: true})}</Title>
|
||||
<Select
|
||||
label={t("productor", {capfirst: true})}
|
||||
placeholder={t("productor")}
|
||||
nothingFoundMessage={t("nothing found", {capfirst: true})}
|
||||
withAsterisk
|
||||
clearable
|
||||
searchable
|
||||
data={productorsSelect || []}
|
||||
{...form.getInputProps('productor_id')}
|
||||
/>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("product name", {capfirst: true})}
|
||||
placeholder={t("product name", {capfirst: true})}
|
||||
radius="sm"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Select
|
||||
label={
|
||||
<InputLabel
|
||||
label={t("product type", {capfirst: true})}
|
||||
info={t("recurrent product is for all shipments, planned product is for a specific shipment (see shipment form)", {capfirst: true})}
|
||||
isRequired
|
||||
/>
|
||||
}
|
||||
placeholder={t("product type", {capfirst: true})}
|
||||
radius="sm"
|
||||
searchable
|
||||
clearable
|
||||
data={[
|
||||
{value: "1", label: t("planned", {capfirst: true})},
|
||||
{value: "2", label: t("recurrent", {capfirst: true})}
|
||||
]}
|
||||
{...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, {capfirst: true})}))}
|
||||
{...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"
|
||||
clearable
|
||||
searchable
|
||||
data={Object.entries(ProductQuantityUnit).map(([key, value]) => ({value: key, label: t(value, {capfirst: true})}))}
|
||||
{...form.getInputProps('quantity_unit', {capfirst: true})}
|
||||
/>
|
||||
|
||||
</Group>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>{t("cancel", {capfirst: true})}</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={currentProduct ? t("edit product", {capfirst: true}) : t('create product', {capfirst: true})}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProduct?.id)
|
||||
}
|
||||
}}
|
||||
>{currentProduct ? t("edit product", {capfirst: true}) : t('create product', {capfirst: true})}</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Modal,
|
||||
NumberInput,
|
||||
Select,
|
||||
TextInput,
|
||||
Title,
|
||||
type ModalBaseProps,
|
||||
} from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import {
|
||||
ProductQuantityUnit,
|
||||
ProductUnit,
|
||||
type Product,
|
||||
type ProductInputs,
|
||||
} from "@/services/resources/products";
|
||||
import { useMemo } from "react";
|
||||
import { useGetProductors } from "@/services/api";
|
||||
import { InputLabel } from "@/components/Label";
|
||||
|
||||
export type ProductModalProps = ModalBaseProps & {
|
||||
currentProduct?: Product;
|
||||
handleSubmit: (product: ProductInputs, id?: number) => void;
|
||||
};
|
||||
|
||||
export function ProductModal({ opened, onClose, currentProduct, handleSubmit }: ProductModalProps) {
|
||||
const { data: productors } = useGetProductors();
|
||||
const form = useForm<ProductInputs>({
|
||||
initialValues: {
|
||||
name: currentProduct?.name ?? "",
|
||||
unit: currentProduct?.unit ?? null,
|
||||
price: currentProduct?.price ?? null,
|
||||
price_kg: currentProduct?.price_kg ?? null,
|
||||
quantity: currentProduct?.quantity ?? null,
|
||||
quantity_unit: currentProduct?.quantity_unit ?? null,
|
||||
type: currentProduct?.type ?? null,
|
||||
productor_id: currentProduct ? String(currentProduct.productor.id) : null,
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", { capfirst: true })} ${t("is required")}` : null,
|
||||
unit: (value) =>
|
||||
!value ? `${t("unit", { 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) =>
|
||||
!value ? `${t("productor", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
const productorsSelect = useMemo(() => {
|
||||
return productors?.map((productor) => ({
|
||||
value: String(productor.id),
|
||||
label: `${productor.name}`,
|
||||
}));
|
||||
}, [productors]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create product", { capfirst: true })}>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
<Select
|
||||
label={t("productor", { capfirst: true })}
|
||||
placeholder={t("productor")}
|
||||
nothingFoundMessage={t("nothing found", { capfirst: true })}
|
||||
withAsterisk
|
||||
clearable
|
||||
searchable
|
||||
data={productorsSelect || []}
|
||||
{...form.getInputProps("productor_id")}
|
||||
/>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
label={t("product name", { capfirst: true })}
|
||||
placeholder={t("product name", { capfirst: true })}
|
||||
radius="sm"
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<Select
|
||||
label={
|
||||
<InputLabel
|
||||
label={t("product type", { capfirst: true })}
|
||||
info={t(
|
||||
"recurrent product is for all shipments, planned product is for a specific shipment (see shipment form)",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
isRequired
|
||||
/>
|
||||
}
|
||||
placeholder={t("product type", { capfirst: true })}
|
||||
radius="sm"
|
||||
searchable
|
||||
clearable
|
||||
data={[
|
||||
{ value: "1", label: t("planned", { capfirst: true }) },
|
||||
{ value: "2", label: t("recurrent", { capfirst: true }) },
|
||||
]}
|
||||
{...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, { capfirst: true }),
|
||||
}))}
|
||||
{...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"
|
||||
clearable
|
||||
searchable
|
||||
data={Object.entries(ProductQuantityUnit).map(([key, value]) => ({
|
||||
value: key,
|
||||
label: t(value, { capfirst: true }),
|
||||
}))}
|
||||
{...form.getInputProps("quantity_unit", { capfirst: true })}
|
||||
/>
|
||||
</Group>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", { capfirst: true })}
|
||||
leftSection={<IconCancel />}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("cancel", { capfirst: true })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={
|
||||
currentProduct
|
||||
? t("edit product", { capfirst: true })
|
||||
: t("create product", { capfirst: true })
|
||||
}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentProduct?.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentProduct
|
||||
? t("edit product", { capfirst: true })
|
||||
: t("create product", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,73 +1,72 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { ProductType, ProductUnit, type Product } from "@/services/resources/products";
|
||||
import { useDeleteProduct } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductRowProps = {
|
||||
product: Product;
|
||||
}
|
||||
|
||||
export default function ProductRow({
|
||||
product,
|
||||
}: ProductRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProduct();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={product.id}>
|
||||
<Table.Td>{product.name}</Table.Td>
|
||||
<Table.Td>{t(ProductType[product.type])}</Table.Td>
|
||||
<Table.Td>
|
||||
{
|
||||
product.price ?
|
||||
Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(product.price) : null
|
||||
}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{
|
||||
product.price_kg ?
|
||||
`${Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(product.price_kg)}/kg` : null
|
||||
|
||||
}
|
||||
</Table.Td>
|
||||
<Table.Td>{product.quantity}{product.quantity_unit}</Table.Td>
|
||||
<Table.Td>{t(ProductUnit[product.unit], {capfirst: true})}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit product", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/products/${product.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove product", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(product.id);
|
||||
}}
|
||||
>
|
||||
<IconX/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { ProductType, ProductUnit, type Product } from "@/services/resources/products";
|
||||
import { useDeleteProduct } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type ProductRowProps = {
|
||||
product: Product;
|
||||
};
|
||||
|
||||
export default function ProductRow({ product }: ProductRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProduct();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={product.id}>
|
||||
<Table.Td>{product.name}</Table.Td>
|
||||
<Table.Td>{t(ProductType[product.type])}</Table.Td>
|
||||
<Table.Td>
|
||||
{product.price
|
||||
? Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(product.price)
|
||||
: null}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{product.price_kg
|
||||
? `${Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(product.price_kg)}/kg`
|
||||
: null}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{product.quantity}
|
||||
{product.quantity_unit}
|
||||
</Table.Td>
|
||||
<Table.Td>{t(ProductUnit[product.unit], { capfirst: true })}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit product", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/products/${product.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove product", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(product.id);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ShipmentFiltersProps = {
|
||||
names: string[];
|
||||
forms: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
}
|
||||
|
||||
export default function ShipmentsFilters({
|
||||
names,
|
||||
forms,
|
||||
filters,
|
||||
onFilterChange
|
||||
}: ShipmentFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names")
|
||||
}, [filters]);
|
||||
const defaultForms = useMemo(() => {
|
||||
return filters.getAll("forms")
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", {capfirst: true})}
|
||||
placeholder={t("filter by name", {capfirst: true})}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by form", {capfirst: true})}
|
||||
placeholder={t("filter by form", {capfirst: true})}
|
||||
data={forms}
|
||||
defaultValue={defaultForms}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'forms')
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type ShipmentFiltersProps = {
|
||||
names: string[];
|
||||
forms: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
};
|
||||
|
||||
export default function ShipmentsFilters({
|
||||
names,
|
||||
forms,
|
||||
filters,
|
||||
onFilterChange,
|
||||
}: ShipmentFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names");
|
||||
}, [filters]);
|
||||
const defaultForms = useMemo(() => {
|
||||
return filters.getAll("forms");
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", { capfirst: true })}
|
||||
placeholder={t("filter by name", { capfirst: true })}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "names");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by form", { capfirst: true })}
|
||||
placeholder={t("filter by form", { capfirst: true })}
|
||||
data={forms}
|
||||
defaultValue={defaultForms}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "forms");
|
||||
}}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export type ShipmentFormProps = {
|
||||
shipment: Shipment;
|
||||
minimumPrice?: number | null;
|
||||
index: number;
|
||||
}
|
||||
};
|
||||
|
||||
export default function ShipmentForm({
|
||||
shipment,
|
||||
@@ -20,20 +20,16 @@ export default function ShipmentForm({
|
||||
minimumPrice,
|
||||
}: ShipmentFormProps) {
|
||||
const shipmentPrice = useMemo(() => {
|
||||
const values = Object
|
||||
.entries(inputForm.getValues())
|
||||
.filter(([key]) =>
|
||||
key.includes("planned") &&
|
||||
key.split("-")[1] === String(shipment.id)
|
||||
);
|
||||
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, shipment.id]);
|
||||
|
||||
const priceRequirement = useMemo(() => {
|
||||
if (!minimumPrice)
|
||||
return false;
|
||||
return minimumPrice ? shipmentPrice < minimumPrice : true
|
||||
}, [shipmentPrice, minimumPrice])
|
||||
if (!minimumPrice) return false;
|
||||
return minimumPrice ? shipmentPrice < minimumPrice : true;
|
||||
}, [shipmentPrice, minimumPrice]);
|
||||
|
||||
return (
|
||||
<Accordion.Item value={String(index)}>
|
||||
@@ -41,44 +37,38 @@ export default function ShipmentForm({
|
||||
<Group justify="space-between">
|
||||
<Text>{shipment.name}</Text>
|
||||
<Stack gap={0}>
|
||||
<Text c={priceRequirement ? "red" : "green"}>{
|
||||
Intl.NumberFormat(
|
||||
"fr-FR",
|
||||
{style: "currency", currency: "EUR"}
|
||||
).format(shipmentPrice)
|
||||
}</Text>
|
||||
{
|
||||
priceRequirement ?
|
||||
<Text c="red"size="sm">
|
||||
{`${t("minimum price for this shipment should be at least", {capfirst: true})} ${minimumPrice}€`}
|
||||
</Text> :
|
||||
null
|
||||
}
|
||||
<Text c={priceRequirement ? "red" : "green"}>
|
||||
{Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(shipmentPrice)}
|
||||
</Text>
|
||||
{priceRequirement ? (
|
||||
<Text c="red" size="sm">
|
||||
{`${t("minimum price for this shipment should be at least", { capfirst: true })} ${minimumPrice}€`}
|
||||
</Text>
|
||||
) : null}
|
||||
</Stack>
|
||||
<Text mr="lg">
|
||||
{`${
|
||||
new Date(shipment.date).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}`}
|
||||
{`${new Date(shipment.date).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}`}
|
||||
</Text>
|
||||
</Group>
|
||||
</Accordion.Control>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{
|
||||
shipment?.products.map((product) => (
|
||||
{shipment?.products.map((product) => (
|
||||
<ProductForm
|
||||
key={product.id}
|
||||
product={product}
|
||||
shipment={shipment}
|
||||
inputForm={inputForm}
|
||||
/>
|
||||
))
|
||||
}
|
||||
))}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +1,146 @@
|
||||
import { Button, Group, Modal, MultiSelect, Select, TextInput, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useMemo } from "react";
|
||||
import { type Shipment, type ShipmentInputs } from "@/services/resources/shipments";
|
||||
import { useGetForms, useGetProductors, useGetProducts } from "@/services/api";
|
||||
|
||||
export type ShipmentModalProps = ModalBaseProps & {
|
||||
currentShipment?: Shipment;
|
||||
handleSubmit: (shipment: ShipmentInputs, id?: number) => void;
|
||||
}
|
||||
|
||||
export default function ShipmentModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentShipment,
|
||||
handleSubmit
|
||||
}: ShipmentModalProps) {
|
||||
const form = useForm<ShipmentInputs>({
|
||||
initialValues: {
|
||||
name: currentShipment?.name ?? "",
|
||||
date: currentShipment?.date ?? null,
|
||||
form_id: currentShipment ? String(currentShipment?.form_id) : "",
|
||||
product_ids: currentShipment?.products.map((el) => (String(el.id))) ?? []
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("a name", {capfirst: true})} ${t('is required')}` : null,
|
||||
date: (value) =>
|
||||
!value ? `${t("a shipment date", {capfirst: true})} ${t('is required')}` : null,
|
||||
form_id: (value) =>
|
||||
!value ? `${t("a form", {capfirst: true})} ${t('is required')}` : null,
|
||||
}
|
||||
});
|
||||
|
||||
const { data: allForms } = useGetForms();
|
||||
const { data: allProducts } = useGetProducts(new URLSearchParams("types=1"));
|
||||
const { data: allProductors } = useGetProductors()
|
||||
|
||||
const formsSelect = useMemo(() => {
|
||||
return allForms?.map(currentForm => ({value: String(currentForm.id), label: `${currentForm.name} ${currentForm.season}`}))
|
||||
}, [allForms]);
|
||||
|
||||
const productsSelect = useMemo(() => {
|
||||
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}`}))
|
||||
}
|
||||
});
|
||||
}, [allProductors, allProducts]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentShipment ? t("edit shipment") : t('create shipment')}
|
||||
>
|
||||
<TextInput
|
||||
label={t("shipment name", {capfirst: true})}
|
||||
placeholder={t("shipment name", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("shipment date", {capfirst: true})}
|
||||
placeholder={t("shipment date", {capfirst: true})}
|
||||
withAsterisk
|
||||
{...form.getInputProps('date')}
|
||||
/>
|
||||
<Select
|
||||
label={t("shipment form", {capfirst: true})}
|
||||
placeholder={t("shipment form", {capfirst: true})}
|
||||
radius="sm"
|
||||
data={formsSelect || []}
|
||||
clearable
|
||||
withAsterisk
|
||||
{...form.getInputProps('form_id')}
|
||||
/>
|
||||
<MultiSelect
|
||||
label={t("shipment products", {capfirst: true})}
|
||||
placeholder={t("shipment products", {capfirst: true})}
|
||||
description={t("shipment products is necessary only for planned products (if all products are recurrent leave empty)", {capfirst: true})}
|
||||
data={productsSelect || []}
|
||||
clearable
|
||||
searchable
|
||||
{...form.getInputProps('product_ids')}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>{t("cancel", {capfirst: true})}</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={currentShipment ? t("edit shipment", {capfirst: true}) : t('create shipment', {capfirst: true})}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentShipment?.id)
|
||||
}
|
||||
}}
|
||||
>{currentShipment ? t("edit shipment", {capfirst: true}) : t('create shipment', {capfirst: true})}</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
Select,
|
||||
TextInput,
|
||||
type ModalBaseProps,
|
||||
} from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useMemo } from "react";
|
||||
import { type Shipment, type ShipmentInputs } from "@/services/resources/shipments";
|
||||
import { useGetForms, useGetProductors, useGetProducts } from "@/services/api";
|
||||
|
||||
export type ShipmentModalProps = ModalBaseProps & {
|
||||
currentShipment?: Shipment;
|
||||
handleSubmit: (shipment: ShipmentInputs, id?: number) => void;
|
||||
};
|
||||
|
||||
export default function ShipmentModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentShipment,
|
||||
handleSubmit,
|
||||
}: ShipmentModalProps) {
|
||||
const form = useForm<ShipmentInputs>({
|
||||
initialValues: {
|
||||
name: currentShipment?.name ?? "",
|
||||
date: currentShipment?.date ?? null,
|
||||
form_id: currentShipment ? String(currentShipment?.form_id) : "",
|
||||
product_ids: currentShipment?.products.map((el) => String(el.id)) ?? [],
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("a name", { capfirst: true })} ${t("is required")}` : null,
|
||||
date: (value) =>
|
||||
!value ? `${t("a shipment date", { capfirst: true })} ${t("is required")}` : null,
|
||||
form_id: (value) =>
|
||||
!value ? `${t("a form", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
const { data: allForms } = useGetForms();
|
||||
const { data: allProducts } = useGetProducts(new URLSearchParams("types=1"));
|
||||
const { data: allProductors } = useGetProductors();
|
||||
|
||||
const formsSelect = useMemo(() => {
|
||||
return allForms?.map((currentForm) => ({
|
||||
value: String(currentForm.id),
|
||||
label: `${currentForm.name} ${currentForm.season}`,
|
||||
}));
|
||||
}, [allForms]);
|
||||
|
||||
const productsSelect = useMemo(() => {
|
||||
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}`,
|
||||
})),
|
||||
};
|
||||
});
|
||||
}, [allProductors, allProducts]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentShipment ? t("edit shipment") : t("create shipment")}
|
||||
>
|
||||
<TextInput
|
||||
label={t("shipment name", { capfirst: true })}
|
||||
placeholder={t("shipment name", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label={t("shipment date", { capfirst: true })}
|
||||
placeholder={t("shipment date", { capfirst: true })}
|
||||
withAsterisk
|
||||
{...form.getInputProps("date")}
|
||||
/>
|
||||
<Select
|
||||
label={t("shipment form", { capfirst: true })}
|
||||
placeholder={t("shipment form", { capfirst: true })}
|
||||
radius="sm"
|
||||
data={formsSelect || []}
|
||||
clearable
|
||||
withAsterisk
|
||||
{...form.getInputProps("form_id")}
|
||||
/>
|
||||
<MultiSelect
|
||||
label={t("shipment products", { capfirst: true })}
|
||||
placeholder={t("shipment products", { capfirst: true })}
|
||||
description={t(
|
||||
"shipment products is necessary only for planned products (if all products are recurrent leave empty)",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
data={productsSelect || []}
|
||||
clearable
|
||||
searchable
|
||||
{...form.getInputProps("product_ids")}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", { capfirst: true })}
|
||||
leftSection={<IconCancel />}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("cancel", { capfirst: true })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={
|
||||
currentShipment
|
||||
? t("edit shipment", { capfirst: true })
|
||||
: t("create shipment", { capfirst: true })
|
||||
}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentShipment?.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentShipment
|
||||
? t("edit shipment", { capfirst: true })
|
||||
: t("create shipment", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { useDeleteShipment} from "@/services/api";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
import type { Shipment } from "@/services/resources/shipments";
|
||||
|
||||
export type ShipmentRowProps = {
|
||||
shipment: Shipment;
|
||||
}
|
||||
|
||||
export default function ShipmentRow({
|
||||
shipment,
|
||||
}: ShipmentRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteShipment();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={shipment.id}>
|
||||
<Table.Td>{shipment.name}</Table.Td>
|
||||
<Table.Td>{shipment.date}</Table.Td>
|
||||
<Table.Td>{`${shipment.form.name} ${shipment.form.season}`}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/shipments/${shipment.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(shipment.id);
|
||||
}}
|
||||
>
|
||||
<IconX/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { useDeleteShipment } from "@/services/api";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
import type { Shipment } from "@/services/resources/shipments";
|
||||
|
||||
export type ShipmentRowProps = {
|
||||
shipment: Shipment;
|
||||
};
|
||||
|
||||
export default function ShipmentRow({ shipment }: ShipmentRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteShipment();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={shipment.id}>
|
||||
<Table.Td>{shipment.name}</Table.Td>
|
||||
<Table.Td>{shipment.date}</Table.Td>
|
||||
<Table.Td>{`${shipment.form.name} ${shipment.form.season}`}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/shipments/${shipment.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(shipment.id);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type UserFiltersProps = {
|
||||
names: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
}
|
||||
|
||||
export default function UserFilters({
|
||||
names,
|
||||
filters,
|
||||
onFilterChange
|
||||
}: UserFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names")
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", {capfirst: true})}
|
||||
placeholder={t("filter by name", {capfirst: true})}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, 'names')
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
import { Group, MultiSelect } from "@mantine/core";
|
||||
import { useMemo } from "react";
|
||||
import { t } from "@/config/i18n";
|
||||
|
||||
export type UserFiltersProps = {
|
||||
names: string[];
|
||||
filters: URLSearchParams;
|
||||
onFilterChange: (values: string[], filter: string) => void;
|
||||
};
|
||||
|
||||
export default function UserFilters({ names, filters, onFilterChange }: UserFiltersProps) {
|
||||
const defaultNames = useMemo(() => {
|
||||
return filters.getAll("names");
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<MultiSelect
|
||||
aria-label={t("filter by name", { capfirst: true })}
|
||||
placeholder={t("filter by name", { capfirst: true })}
|
||||
data={names}
|
||||
defaultValue={defaultNames}
|
||||
onChange={(values: string[]) => {
|
||||
onFilterChange(values, "names");
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,76 +1,77 @@
|
||||
import { Button, Group, Modal, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
|
||||
export type UserModalProps = ModalBaseProps & {
|
||||
currentUser?: User;
|
||||
handleSubmit: (user: UserInputs, id?: number) => void;
|
||||
}
|
||||
|
||||
export function UserModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentUser,
|
||||
handleSubmit
|
||||
}: UserModalProps) {
|
||||
const form = useForm<UserInputs>({
|
||||
initialValues: {
|
||||
name: currentUser?.name ?? "",
|
||||
email: currentUser?.email ?? ""
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", {capfirst: true})} ${t('is required')}` : null,
|
||||
email: (value) =>
|
||||
!value ? `${t("email", {capfirst: true})} ${t('is required')}` : null,
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("create user", {capfirst: true})}
|
||||
>
|
||||
<Title order={4}>{t("informations", {capfirst: true})}</Title>
|
||||
<TextInput
|
||||
label={t("user name", {capfirst: true})}
|
||||
placeholder={t("user name", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("user email", {capfirst: true})}
|
||||
placeholder={t("user email", {capfirst: true})}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps('email')}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", {capfirst: true})}
|
||||
leftSection={<IconCancel/>}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>{t("cancel", {capfirst: true})}</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={currentUser ? t("edit user", {capfirst: true}) : t('create user', {capfirst: true})}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentUser?.id)
|
||||
}
|
||||
}}
|
||||
>{currentUser ? t("edit user", {capfirst: true}) : t('create user', {capfirst: true})}</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
import { Button, Group, Modal, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel } from "@tabler/icons-react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
|
||||
export type UserModalProps = ModalBaseProps & {
|
||||
currentUser?: User;
|
||||
handleSubmit: (user: UserInputs, id?: number) => void;
|
||||
};
|
||||
|
||||
export function UserModal({ opened, onClose, currentUser, handleSubmit }: UserModalProps) {
|
||||
const form = useForm<UserInputs>({
|
||||
initialValues: {
|
||||
name: currentUser?.name ?? "",
|
||||
email: currentUser?.email ?? "",
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
!value ? `${t("name", { capfirst: true })} ${t("is required")}` : null,
|
||||
email: (value) =>
|
||||
!value ? `${t("email", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create user", { capfirst: true })}>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
<TextInput
|
||||
label={t("user name", { capfirst: true })}
|
||||
placeholder={t("user name", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("user email", { capfirst: true })}
|
||||
placeholder={t("user email", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
aria-label={t("cancel", { capfirst: true })}
|
||||
leftSection={<IconCancel />}
|
||||
onClick={() => {
|
||||
form.clearErrors();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("cancel", { capfirst: true })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={
|
||||
currentUser
|
||||
? t("edit user", { capfirst: true })
|
||||
: t("create user", { capfirst: true })
|
||||
}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentUser?.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentUser
|
||||
? t("edit user", { capfirst: true })
|
||||
: t("create user", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { type User } from "@/services/resources/users";
|
||||
import { useDeleteUser } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type UserRowProps = {
|
||||
user: User;
|
||||
}
|
||||
|
||||
export default function UserRow({
|
||||
user,
|
||||
}: UserRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteUser();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={user.id}>
|
||||
<Table.Td>{user.name}</Table.Td>
|
||||
<Table.Td>{user.email}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit user", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/users/${user.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}}
|
||||
>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove user", {capfirst: true})}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(user.id);
|
||||
}}
|
||||
>
|
||||
<IconX/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
|
||||
);
|
||||
}
|
||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { type User } from "@/services/resources/users";
|
||||
import { useDeleteUser } from "@/services/api";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export type UserRowProps = {
|
||||
user: User;
|
||||
};
|
||||
|
||||
export default function UserRow({ user }: UserRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteUser();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Table.Tr key={user.id}>
|
||||
<Table.Td>{user.name}</Table.Td>
|
||||
<Table.Td>{user.email}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("edit user", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/users/${user.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove user", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(user.id);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user