This commit is contained in:
@@ -39,6 +39,10 @@
|
||||
"create productor": "create producer",
|
||||
"edit productor": "edit producer",
|
||||
"remove productor": "remove producer",
|
||||
"remove form": "remove form",
|
||||
"edit shipment": "edit shipment",
|
||||
"create shipment": "create shipment",
|
||||
"create user": "create user",
|
||||
"home": "home",
|
||||
"dashboard": "dashboard",
|
||||
"filter by name": "filter by name",
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
"create productor": "créer le/la producteur·trice",
|
||||
"edit productor": "modifier le/la producteur·trice",
|
||||
"remove productor": "supprimer le/la producteur·trice",
|
||||
"remove form": "supprimer un formulaire de contrat",
|
||||
"edit shipment": "modifier la livraison",
|
||||
"create shipment": "créer la livraison",
|
||||
"create user": "créer l'utilisateur·trice",
|
||||
"home": "accueil",
|
||||
"dashboard": "tableau de bord",
|
||||
"filter by name": "filtrer par nom",
|
||||
@@ -183,7 +187,7 @@
|
||||
"liter": "litres (L)",
|
||||
"are you sure you want to delete": "êtes vous sûr de vouloir supprimer",
|
||||
"this will also delete": "cette action supprimera aussi",
|
||||
"delete entity": "supprimer un {{entity}}",
|
||||
"delete entity": "supprimer le/la {{entity}}",
|
||||
"delete": "supprimer",
|
||||
"success": "succès",
|
||||
"success edit": "{{entity}} correctement édité",
|
||||
|
||||
@@ -2,16 +2,17 @@ import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
||||
import { type Contract } from "@/services/resources/contracts";
|
||||
import { IconDownload, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useDeleteContract, useGetContractFile } from "@/services/api";
|
||||
import { useGetContractFile } from "@/services/api";
|
||||
import { useCallback } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export type ContractRowProps = {
|
||||
contract: Contract;
|
||||
};
|
||||
|
||||
export default function ContractRow({ contract }: ContractRowProps) {
|
||||
const deleteMutation = useDeleteContract();
|
||||
const getContractMutation = useGetContractFile();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleDownload = useCallback(async () => {
|
||||
getContractMutation.mutateAsync(contract.id);
|
||||
@@ -29,12 +30,10 @@ export default function ContractRow({ contract }: ContractRowProps) {
|
||||
{contract.cheque_quantity > 0 && contract.cheque_quantity} {contract.payment_method}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{
|
||||
`${Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(contract.total_price)}`
|
||||
}
|
||||
{`${Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(contract.total_price)}`}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip label={t("download contract", { capfirst: true })}>
|
||||
@@ -54,8 +53,9 @@ export default function ContractRow({ contract }: ContractRowProps) {
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(contract.id);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/dashboard/contracts/${contract.id}/delete`);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
|
||||
@@ -7,50 +7,48 @@ import { Link } from "react-router";
|
||||
export type DeleteModalProps = ModalBaseProps & {
|
||||
handleSubmit: (id: number) => void;
|
||||
entityType: string;
|
||||
entity?: {name: string, id: number} | undefined;
|
||||
}
|
||||
entity?: { name: string; id: number } | undefined;
|
||||
};
|
||||
|
||||
export function DeleteModal({
|
||||
opened,
|
||||
onClose,
|
||||
handleSubmit,
|
||||
opened,
|
||||
onClose,
|
||||
handleSubmit,
|
||||
entityType,
|
||||
entity,
|
||||
}: DeleteModalProps) {
|
||||
if (!entity) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
const {data: deleteDependencies} = useGetDeleteDependencies(entityType, entity.id);
|
||||
const { data: deleteDependencies } = useGetDeleteDependencies(entityType, entity.id);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("delete entity", { capfirst: true, entity: t(entityType)})}
|
||||
title={t("delete entity", { capfirst: true, entity: t(entityType) })}
|
||||
>
|
||||
<Text>{`${t("are you sure you want to delete", {capfirst: true})} : "${entity.name}"`}</Text>
|
||||
{deleteDependencies && deleteDependencies.length > 0 ? <Text>{`${t("this will also delete", {capfirst: true})} :`}</Text> : null}
|
||||
<Text>{`${t("are you sure you want to delete", { capfirst: true })} : "${entity.name}"`}</Text>
|
||||
{deleteDependencies && deleteDependencies.length > 0 ? (
|
||||
<Text>{`${t("this will also delete", { capfirst: true })} :`}</Text>
|
||||
) : null}
|
||||
{
|
||||
<List>
|
||||
{
|
||||
deleteDependencies?.map((dependency) => (
|
||||
<List.Item
|
||||
key={dependency.id}
|
||||
>
|
||||
{
|
||||
dependency.type === 'contract' ? `${t(dependency.type, {capfirst: true})} - ${dependency.name}` :
|
||||
<Link
|
||||
to={`/dashboard/${dependency.type}s/${dependency.id}/edit`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`${t(dependency.type, {capfirst: true})} - ${dependency.name}`}
|
||||
</Link>
|
||||
|
||||
}
|
||||
</List.Item>
|
||||
))
|
||||
}
|
||||
{deleteDependencies?.map((dependency) => (
|
||||
<List.Item key={dependency.id}>
|
||||
{dependency.type === "contract" ? (
|
||||
`${t(dependency.type, { capfirst: true })} - ${dependency.name}`
|
||||
) : (
|
||||
<Link
|
||||
to={`/dashboard/${dependency.type}s/${dependency.id}/edit`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{`${t(dependency.type, { capfirst: true })} - ${dependency.name}`}
|
||||
</Link>
|
||||
)}
|
||||
</List.Item>
|
||||
))}
|
||||
</List>
|
||||
}
|
||||
<Group mt="sm" justify="space-between">
|
||||
@@ -65,16 +63,16 @@ export function DeleteModal({
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={t("delete entity", { capfirst: true, entity: t(entityType)})}
|
||||
aria-label={t("delete entity", { capfirst: true, entity: t(entityType) })}
|
||||
leftSection={<IconCheck />}
|
||||
onClick={() => {
|
||||
handleSubmit(entity.id);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("delete", { capfirst: true})}
|
||||
{t("delete", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,37 +10,33 @@ export type FormCardProps = {
|
||||
};
|
||||
|
||||
export function FormCard({ form }: FormCardProps) {
|
||||
const contractBaseTemplate = useGetContractFileTemplate()
|
||||
const contractBaseTemplate = useGetContractFileTemplate();
|
||||
return (
|
||||
<Paper shadow="xl" p="xl" miw={{ base: "100vw", md: "25vw", lg: "20vw" }}>
|
||||
<Group justify="start" mb="md">
|
||||
<Tooltip
|
||||
label={t("download base template to print")}
|
||||
>
|
||||
<ActionIcon
|
||||
variant={"outline"}
|
||||
aria-label={t("download base template to print")}
|
||||
onClick={async () => {
|
||||
await contractBaseTemplate.mutateAsync(form.id)
|
||||
}}
|
||||
>
|
||||
<IconDownload/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={t("fill contract online")}
|
||||
>
|
||||
<ActionIcon
|
||||
variant={"outline"}
|
||||
aria-label={t("fill contract online")}
|
||||
component={Link}
|
||||
to={`/form/${form.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<IconExternalLink/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("download base template to print")}>
|
||||
<ActionIcon
|
||||
variant={"outline"}
|
||||
aria-label={t("download base template to print")}
|
||||
onClick={async () => {
|
||||
await contractBaseTemplate.mutateAsync(form.id);
|
||||
}}
|
||||
>
|
||||
<IconDownload />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("fill contract online")}>
|
||||
<ActionIcon
|
||||
variant={"outline"}
|
||||
aria-label={t("fill contract online")}
|
||||
component={Link}
|
||||
to={`/form/${form.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<IconExternalLink />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
|
||||
<Box
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit }
|
||||
productor_id: currentForm?.productor?.id.toString() ?? "",
|
||||
referer_id: currentForm?.referer?.id.toString() ?? "",
|
||||
minimum_shipment_value: currentForm?.minimum_shipment_value ?? null,
|
||||
visible: currentForm?.visible ?? false
|
||||
visible: currentForm?.visible ?? false,
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
@@ -53,8 +53,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit }
|
||||
});
|
||||
|
||||
const usersSelect = useMemo(() => {
|
||||
if (!users)
|
||||
return [];
|
||||
if (!users) return [];
|
||||
return users?.map((user) => ({
|
||||
value: String(user.id),
|
||||
label: `${user.name}`,
|
||||
@@ -62,8 +61,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit }
|
||||
}, [users]);
|
||||
|
||||
const productorsSelect = useMemo(() => {
|
||||
if (!productors)
|
||||
return [];
|
||||
if (!productors) return [];
|
||||
return productors?.map((prod) => ({
|
||||
value: String(prod.id),
|
||||
label: `${prod.name}`,
|
||||
@@ -142,10 +140,14 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit }
|
||||
radius="sm"
|
||||
{...form.getInputProps("minimum_shipment_value")}
|
||||
/>
|
||||
<Checkbox mt="lg"
|
||||
label={t("visible", {capfirst: true})}
|
||||
description={t("by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form", {capfirst: true})}
|
||||
{...form.getInputProps("visible", {type: "checkbox"})}
|
||||
<Checkbox
|
||||
mt="lg"
|
||||
label={t("visible", { capfirst: true })}
|
||||
description={t(
|
||||
"by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
{...form.getInputProps("visible", { type: "checkbox" })}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IconEdit, IconX } from "@tabler/icons-react";
|
||||
import { t } from "@/config/i18n";
|
||||
import type { Form } from "@/services/resources/forms";
|
||||
|
||||
|
||||
export type FormRowProps = {
|
||||
form: Form;
|
||||
};
|
||||
@@ -15,10 +14,11 @@ export default function FormRow({ form }: FormRowProps) {
|
||||
return (
|
||||
<Table.Tr key={form.id}>
|
||||
<Table.Td>
|
||||
{form.visible ?
|
||||
<Badge color="green">{t("visible", {capfirst: true})}</Badge> :
|
||||
<Badge color="red">{t("hidden", {capfirst: true})}</Badge>
|
||||
}
|
||||
{form.visible ? (
|
||||
<Badge color="green">{t("visible", { capfirst: true })}</Badge>
|
||||
) : (
|
||||
<Badge color="red">{t("hidden", { capfirst: true })}</Badge>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>{form.name}</Table.Td>
|
||||
<Table.Td>{form.season}</Table.Td>
|
||||
|
||||
@@ -23,11 +23,11 @@ export function Navbar() {
|
||||
<nav>
|
||||
<Group>
|
||||
<NavLink className={"navLink"} aria-label={t("home")} to="/">
|
||||
{isPhone ? <IconHome/> : t("home", { capfirst: true })}
|
||||
{isPhone ? <IconHome /> : t("home", { capfirst: true })}
|
||||
</NavLink>
|
||||
{user?.logged ? (
|
||||
<NavLink className={"navLink"} aria-label={t("dashboard")} to="/dashboard/help">
|
||||
{isPhone ? <IconSettings/> : t("dashboard", { capfirst: true })}
|
||||
{isPhone ? <IconSettings /> : t("dashboard", { capfirst: true })}
|
||||
</NavLink>
|
||||
) : null}
|
||||
</Group>
|
||||
@@ -37,7 +37,7 @@ export function Navbar() {
|
||||
className={"navLink"}
|
||||
aria-label={t("login with keycloak", { capfirst: true })}
|
||||
>
|
||||
{isPhone ? <IconLogin/> : t("login with keycloak", { capfirst: true })}
|
||||
{isPhone ? <IconLogin /> : t("login with keycloak", { capfirst: true })}
|
||||
</a>
|
||||
) : (
|
||||
<a
|
||||
@@ -45,7 +45,7 @@ export function Navbar() {
|
||||
className={"navLink"}
|
||||
aria-label={t("logout", { capfirst: true })}
|
||||
>
|
||||
{isPhone ? <IconLogout/> : t("logout", { capfirst: true })}
|
||||
{isPhone ? <IconLogout /> : t("logout", { capfirst: true })}
|
||||
</a>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
@@ -1,88 +1,86 @@
|
||||
import { t } from "@/config/i18n";
|
||||
import type { ContractInputs } from "@/services/resources/contracts";
|
||||
import type { Productor } from "@/services/resources/productors";
|
||||
import { Group, NumberInput, Stack, TextInput, Title } from "@mantine/core";
|
||||
import type { UseFormReturnType } from "@mantine/form";
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
export type ContractChequeProps = {
|
||||
inputForm: UseFormReturnType<ContractInputs>;
|
||||
price: number;
|
||||
productor: Productor;
|
||||
};
|
||||
|
||||
export type Cheque = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export function ContractCheque({ inputForm, price, productor }: ContractChequeProps) {
|
||||
useEffect(() => {
|
||||
if (!inputForm.values.payment_method.includes("cheque")) {
|
||||
return;
|
||||
}
|
||||
const quantity = Number(inputForm.values.cheque_quantity);
|
||||
if (!quantity || quantity <= 0) return;
|
||||
const cheques = inputForm.values.cheques || [];
|
||||
if (cheques.length !== quantity) {
|
||||
const newCheques = Array.from({ length: quantity }, (_, i) => ({
|
||||
name: cheques[i]?.name ?? "",
|
||||
value: cheques[i]?.value ?? 0,
|
||||
}));
|
||||
inputForm.setFieldValue("cheques", newCheques);
|
||||
}
|
||||
|
||||
const totalCents = Math.round(price * 100);
|
||||
const base = Math.floor(totalCents / quantity);
|
||||
const rest = totalCents - base * quantity;
|
||||
for (let i = 0; i < quantity; i++) {
|
||||
const val = (i === quantity - 1 ? base + rest : base) / 100;
|
||||
inputForm.setFieldValue(`cheques.${i}.value`, val.toFixed(2));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputForm.values.cheque_quantity, price, inputForm.values.cheques]);
|
||||
|
||||
const paymentMethod = useMemo(() => {
|
||||
return productor?.payment_methods.find((el) => el.name === "cheque")
|
||||
}, [productor]);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={4}>{`${t("order name")} : ${paymentMethod?.details}`}</Title>
|
||||
<NumberInput
|
||||
label={t("cheque quantity", { capfirst: true })}
|
||||
placeholder={t("enter cheque quantity", { capfirst: true })}
|
||||
description={t(
|
||||
"number of cheques between 1 and 3 cheques also enter your cheques identifiers, value is calculated automatically",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
min={1}
|
||||
max={paymentMethod?.max && paymentMethod?.max !== "" ? Number(paymentMethod?.max) : 3}
|
||||
{...inputForm.getInputProps(`cheque_quantity`)}
|
||||
/>
|
||||
<Group grow>
|
||||
{inputForm.values.cheques.map((cheque, index) => (
|
||||
<Stack key={`${index}`}>
|
||||
<TextInput
|
||||
label={t("cheque id", { capfirst: true })}
|
||||
placeholder={t("cheque id", { capfirst: true })}
|
||||
{...inputForm.getInputProps(`cheques.${index}.name`)}
|
||||
error={
|
||||
cheque.name == "" ?
|
||||
inputForm?.errors.cheques :
|
||||
null
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
readOnly
|
||||
label={t("cheque value", { capfirst: true })}
|
||||
suffix={"€"}
|
||||
placeholder={t("enter cheque value", { capfirst: true })}
|
||||
{...inputForm.getInputProps(`cheques.${index}.value`)}
|
||||
/>
|
||||
</Stack>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
import { t } from "@/config/i18n";
|
||||
import type { ContractInputs } from "@/services/resources/contracts";
|
||||
import type { Productor } from "@/services/resources/productors";
|
||||
import { Group, NumberInput, Stack, TextInput, Title } from "@mantine/core";
|
||||
import type { UseFormReturnType } from "@mantine/form";
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
export type ContractChequeProps = {
|
||||
inputForm: UseFormReturnType<ContractInputs>;
|
||||
price: number;
|
||||
productor: Productor;
|
||||
};
|
||||
|
||||
export type Cheque = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export function ContractCheque({ inputForm, price, productor }: ContractChequeProps) {
|
||||
useEffect(() => {
|
||||
if (!inputForm.values.payment_method.includes("cheque")) {
|
||||
return;
|
||||
}
|
||||
const quantity = Number(inputForm.values.cheque_quantity);
|
||||
if (!quantity || quantity <= 0) return;
|
||||
const cheques = inputForm.values.cheques || [];
|
||||
if (cheques.length !== quantity) {
|
||||
const newCheques = Array.from({ length: quantity }, (_, i) => ({
|
||||
name: cheques[i]?.name ?? "",
|
||||
value: cheques[i]?.value ?? 0,
|
||||
}));
|
||||
inputForm.setFieldValue("cheques", newCheques);
|
||||
}
|
||||
|
||||
const totalCents = Math.round(price * 100);
|
||||
const base = Math.floor(totalCents / quantity);
|
||||
const rest = totalCents - base * quantity;
|
||||
for (let i = 0; i < quantity; i++) {
|
||||
const val = (i === quantity - 1 ? base + rest : base) / 100;
|
||||
inputForm.setFieldValue(`cheques.${i}.value`, val.toFixed(2));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputForm.values.cheque_quantity, price, inputForm.values.cheques]);
|
||||
|
||||
const paymentMethod = useMemo(() => {
|
||||
return productor?.payment_methods.find((el) => el.name === "cheque");
|
||||
}, [productor]);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={4}>{`${t("order name")} : ${paymentMethod?.details}`}</Title>
|
||||
<NumberInput
|
||||
label={t("cheque quantity", { capfirst: true })}
|
||||
placeholder={t("enter cheque quantity", { capfirst: true })}
|
||||
description={t(
|
||||
"number of cheques between 1 and 3 cheques also enter your cheques identifiers, value is calculated automatically",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
min={1}
|
||||
max={
|
||||
paymentMethod?.max && paymentMethod?.max !== "" ? Number(paymentMethod?.max) : 3
|
||||
}
|
||||
{...inputForm.getInputProps(`cheque_quantity`)}
|
||||
/>
|
||||
<Group grow>
|
||||
{inputForm.values.cheques.map((cheque, index) => (
|
||||
<Stack key={`${index}`}>
|
||||
<TextInput
|
||||
label={t("cheque id", { capfirst: true })}
|
||||
placeholder={t("cheque id", { capfirst: true })}
|
||||
{...inputForm.getInputProps(`cheques.${index}.name`)}
|
||||
error={cheque.name == "" ? inputForm?.errors.cheques : null}
|
||||
/>
|
||||
<NumberInput
|
||||
readOnly
|
||||
label={t("cheque value", { capfirst: true })}
|
||||
suffix={"€"}
|
||||
placeholder={t("enter cheque value", { capfirst: true })}
|
||||
{...inputForm.getInputProps(`cheques.${index}.value`)}
|
||||
/>
|
||||
</Stack>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,21 +48,31 @@ export function ProductorModal({
|
||||
!value ? `${t("address", { capfirst: true })} ${t("is required")}` : null,
|
||||
type: (value) =>
|
||||
!value ? `${t("type", { capfirst: true })} ${t("is required")}` : null,
|
||||
payment_methods: (value) =>
|
||||
value.length === 0 || value.some(
|
||||
(payment) =>
|
||||
payment.name === "cheque" &&
|
||||
payment.details === "") ?
|
||||
`${t("a payment method", { capfirst: true })} ${t("is required")}` : null,
|
||||
payment_methods: (value) =>
|
||||
value.length === 0 ||
|
||||
value.some((payment) => payment.name === "cheque" && payment.details === "")
|
||||
? `${t("a payment method", { capfirst: true })} ${t("is required")}`
|
||||
: null,
|
||||
},
|
||||
});
|
||||
|
||||
const roleSelect = useMemo(() => {
|
||||
return loggedUser?.user?.roles?.map((role) => ({ value: String(role.name), label: role.name }));
|
||||
return loggedUser?.user?.roles?.map((role) => ({
|
||||
value: String(role.name),
|
||||
label: role.name,
|
||||
}));
|
||||
}, [loggedUser?.user?.roles]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create productor", { capfirst: true })}>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={
|
||||
currentProductor
|
||||
? t("edit productor", { capfirst: true })
|
||||
: t("create productor", { capfirst: true })
|
||||
}
|
||||
>
|
||||
<Title order={4}>{t("Informations", { capfirst: true })}</Title>
|
||||
<TextInput
|
||||
label={t("productor name", { capfirst: true })}
|
||||
@@ -127,7 +137,7 @@ export function ProductorModal({
|
||||
<NumberInput
|
||||
label={t("max cheque number", { capfirst: true })}
|
||||
placeholder={t("max cheque number", { capfirst: true })}
|
||||
description={t("can be empty default to 3", {capfirst: true})}
|
||||
description={t("can be empty default to 3", { capfirst: true })}
|
||||
{...form.getInputProps(`payment_methods.${index}.max`)}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -2,7 +2,6 @@ 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 = {
|
||||
@@ -11,7 +10,6 @@ export type ProductorRowProps = {
|
||||
|
||||
export default function ProductorRow({ productor }: ProductorRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProductor();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -46,8 +44,11 @@ export default function ProductorRow({ productor }: ProductorRowProps) {
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(productor.id);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/productors/${productor.id}/delete${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
|
||||
@@ -59,8 +59,7 @@ export function ProductModal({ opened, onClose, currentProduct, handleSubmit }:
|
||||
});
|
||||
|
||||
const productorsSelect = useMemo(() => {
|
||||
if (!productors)
|
||||
return [];
|
||||
if (!productors) return [];
|
||||
return productors?.map((productor) => ({
|
||||
value: String(productor.id),
|
||||
label: `${productor.name}`,
|
||||
@@ -68,7 +67,15 @@ export function ProductModal({ opened, onClose, currentProduct, handleSubmit }:
|
||||
}, [productors]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create product", { capfirst: true })}>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={
|
||||
currentProduct
|
||||
? t("edit product", { capfirst: true })
|
||||
: t("create product", { capfirst: true })
|
||||
}
|
||||
>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
<Select
|
||||
label={t("productor", { capfirst: true })}
|
||||
|
||||
@@ -2,7 +2,6 @@ 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 = {
|
||||
@@ -11,7 +10,6 @@ export type ProductRowProps = {
|
||||
|
||||
export default function ProductRow({ product }: ProductRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteProduct();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -59,8 +57,11 @@ export default function ProductRow({ product }: ProductRowProps) {
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(product.id);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/products/${product.id}/delete${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
|
||||
@@ -48,8 +48,7 @@ export default function ShipmentModal({
|
||||
const { data: allProductors } = useGetProductors();
|
||||
|
||||
const formsSelect = useMemo(() => {
|
||||
if (!allForms)
|
||||
return [];
|
||||
if (!allForms) return [];
|
||||
return allForms?.map((currentForm) => ({
|
||||
value: String(currentForm.id),
|
||||
label: `${currentForm.name} ${currentForm.season}`,
|
||||
@@ -75,7 +74,11 @@ export default function ShipmentModal({
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={currentShipment ? t("edit shipment") : t("create shipment")}
|
||||
title={
|
||||
currentShipment
|
||||
? t("edit shipment", { capfirst: true })
|
||||
: t("create shipment", { capfirst: true })
|
||||
}
|
||||
>
|
||||
<TextInput
|
||||
label={t("shipment name", { capfirst: true })}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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";
|
||||
@@ -11,7 +10,6 @@ export type ShipmentRowProps = {
|
||||
|
||||
export default function ShipmentRow({ shipment }: ShipmentRowProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteShipment();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -20,7 +18,7 @@ export default function ShipmentRow({ shipment }: ShipmentRowProps) {
|
||||
<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 })}>
|
||||
<Tooltip label={t("edit shipment", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
@@ -34,13 +32,16 @@ export default function ShipmentRow({ shipment }: ShipmentRowProps) {
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove productor", { capfirst: true })}>
|
||||
<Tooltip label={t("remove shipment", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(shipment.id);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/shipments/${shipment.id}/delete${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
|
||||
@@ -36,13 +36,20 @@ export function UserModal({ opened, onClose, currentUser, handleSubmit }: UserMo
|
||||
});
|
||||
|
||||
const roleSelect = useMemo(() => {
|
||||
if (!allRoles)
|
||||
return [];
|
||||
if (!allRoles) return [];
|
||||
return allRoles?.map((role) => ({ value: String(role.name), label: role.name }));
|
||||
}, [allRoles]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create user", { capfirst: true })}>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={
|
||||
currentUser
|
||||
? t("edit user", { capfirst: true })
|
||||
: t("create user", { capfirst: true })
|
||||
}
|
||||
>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
<TextInput
|
||||
label={t("user name", { capfirst: true })}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ActionIcon, Badge, Box, 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 = {
|
||||
@@ -11,36 +10,31 @@ export type UserRowProps = {
|
||||
|
||||
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 style={{maxWidth: 200}}>
|
||||
<Table.Td style={{ maxWidth: 200 }}>
|
||||
<Box
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: 4
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{user.roles.slice(0, 3).map((value) => (
|
||||
<Badge key={value.id} size="xs">
|
||||
{t(value.name, { capfirst: true })}
|
||||
</Badge>
|
||||
))}
|
||||
{
|
||||
user.roles.length > 3 && (
|
||||
<Tooltip
|
||||
label={user.roles.slice(3).map(role=>`${role.name} `)}
|
||||
>
|
||||
{user.roles.slice(0, 3).map((value) => (
|
||||
<Badge key={value.id} size="xs">
|
||||
{t(value.name, { capfirst: true })}
|
||||
</Badge>
|
||||
))}
|
||||
{user.roles.length > 3 && (
|
||||
<Tooltip label={user.roles.slice(3).map((role) => `${role.name} `)}>
|
||||
<Badge size="xs" variant="light">
|
||||
+{user.roles.length - 3}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</Box>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
@@ -63,8 +57,11 @@ export default function UserRow({ user }: UserRowProps) {
|
||||
color="red"
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={() => {
|
||||
deleteMutation.mutate(user.id);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/users/${user.id}/delete${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
|
||||
@@ -50,8 +50,10 @@ export function Contract() {
|
||||
!value ? `${t("a phone", { capfirst: true })} ${t("is required")}` : null,
|
||||
payment_method: (value) =>
|
||||
!value ? `${t("a payment method", { capfirst: true })} ${t("is required")}` : null,
|
||||
cheques: (value, values) =>
|
||||
values.payment_method === "cheque" && value.some((val) => val.name == "") ? `${t("cheque id", {capfirst: true})} ${t("is required")}` : null,
|
||||
cheques: (value, values) =>
|
||||
values.payment_method === "cheque" && value.some((val) => val.name == "")
|
||||
? `${t("cheque id", { capfirst: true })} ${t("is required")}`
|
||||
: null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -137,12 +139,13 @@ export function Contract() {
|
||||
const formValues = inputForm.getValues();
|
||||
const contract = {
|
||||
...formValues,
|
||||
cheque_quantity: formValues.payment_method === "cheque" ? formValues.cheque_quantity : 0,
|
||||
cheque_quantity:
|
||||
formValues.payment_method === "cheque" ? formValues.cheque_quantity : 0,
|
||||
form_id: form.id,
|
||||
products: tranformProducts(withDefaultValues(formValues.products)),
|
||||
};
|
||||
await createContractMutation.mutateAsync(contract);
|
||||
window.location.href = '/';
|
||||
window.location.href = "/";
|
||||
} else {
|
||||
const firstErrorField = Object.keys(errors.errors)[0];
|
||||
const ref = inputRefs.current[firstErrorField];
|
||||
@@ -165,7 +168,7 @@ export function Contract() {
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack w={{ base: "100%", md: "80%", lg: "50%" }} p={{base: 'xs'}}>
|
||||
<Stack w={{ base: "100%", md: "80%", lg: "50%" }} p={{ base: "xs" }}>
|
||||
<Title order={2}>{form.name}</Title>
|
||||
<Title order={3}>{t("informations", { capfirst: true })}</Title>
|
||||
<Text size="sm">
|
||||
@@ -283,17 +286,13 @@ export function Contract() {
|
||||
ref={(el) => {
|
||||
inputRefs.current.payment_method = el;
|
||||
}}
|
||||
comboboxProps={{
|
||||
comboboxProps={{
|
||||
withinPortal: false,
|
||||
position: "bottom-start",
|
||||
}}
|
||||
/>
|
||||
{inputForm.values.payment_method === "cheque" ? (
|
||||
<ContractCheque
|
||||
productor={form?.productor}
|
||||
price={price}
|
||||
inputForm={inputForm}
|
||||
/>
|
||||
<ContractCheque productor={form?.productor} price={price} inputForm={inputForm} />
|
||||
) : null}
|
||||
{inputForm.values.payment_method === "transfer" ? (
|
||||
<Text>
|
||||
@@ -320,10 +319,12 @@ export function Contract() {
|
||||
currency: "EUR",
|
||||
}).format(price)}
|
||||
</Text>
|
||||
<Button
|
||||
leftSection={<IconDownload/>}
|
||||
aria-label={t("submit contracts")} onClick={handleSubmit}>
|
||||
{t("submit", {capfirst: true})}
|
||||
<Button
|
||||
leftSection={<IconDownload />}
|
||||
aria-label={t("submit contracts")}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{t("submit", { capfirst: true })}
|
||||
</Button>
|
||||
</Overlay>
|
||||
</Stack>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useGetAllContractFile, useGetContracts, useGetRecap } from "@/services/api";
|
||||
import {
|
||||
useDeleteContract,
|
||||
useGetAllContractFile,
|
||||
useGetContract,
|
||||
useGetContracts,
|
||||
useGetRecap,
|
||||
} from "@/services/api";
|
||||
import { IconDownload, IconTableExport } from "@tabler/icons-react";
|
||||
import ContractRow from "@/components/Contracts/Row";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
@@ -8,6 +14,7 @@ import { ContractModal } from "@/components/Contracts/Modal";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { type Contract } from "@/services/resources/contracts";
|
||||
import ContractsFilters from "@/components/Contracts/Filter";
|
||||
import { DeleteModal } from "@/components/DeleteModal";
|
||||
|
||||
export default function Contracts() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -17,18 +24,29 @@ export default function Contracts() {
|
||||
const getRecapMutation = useGetRecap();
|
||||
const isdownload = location.pathname.includes("/download");
|
||||
const isrecap = location.pathname.includes("/export");
|
||||
const isDelete = location.pathname.includes("/delete");
|
||||
|
||||
const deleteId = useMemo(() => {
|
||||
if (isDelete) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
}, [location]);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/contracts${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const { data: contracts, isPending } = useGetContracts(searchParams);
|
||||
const { data: currentContract } = useGetContract(Number(deleteId), {
|
||||
enabled: !!deleteId,
|
||||
});
|
||||
|
||||
const { data: allContracts } = useGetContracts();
|
||||
const deleteContractMutation = useDeleteContract();
|
||||
|
||||
const forms = useMemo(() => {
|
||||
if (!allContracts)
|
||||
return [];
|
||||
if (!allContracts) return [];
|
||||
return allContracts
|
||||
?.map((contract: Contract) => contract.form.name)
|
||||
.filter((contract, index, array) => array.indexOf(contract) === index);
|
||||
@@ -57,11 +75,11 @@ export default function Contracts() {
|
||||
);
|
||||
|
||||
const handleDownloadRecap = useCallback(
|
||||
async (id: number) => {
|
||||
async (id: number) => {
|
||||
await getRecapMutation.mutateAsync(id);
|
||||
},
|
||||
[getAllContractFilesMutation],
|
||||
)
|
||||
);
|
||||
|
||||
if (!contracts || isPending)
|
||||
return (
|
||||
@@ -87,9 +105,7 @@ export default function Contracts() {
|
||||
<IconDownload />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={t("download recap", { capfirst: true })}
|
||||
>
|
||||
<Tooltip label={t("download recap", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
disabled={false}
|
||||
onClick={(e) => {
|
||||
@@ -103,7 +119,7 @@ export default function Contracts() {
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
|
||||
|
||||
<ContractModal
|
||||
opened={isdownload}
|
||||
onClose={closeModal}
|
||||
@@ -114,6 +130,18 @@ export default function Contracts() {
|
||||
onClose={closeModal}
|
||||
handleSubmit={handleDownloadRecap}
|
||||
/>
|
||||
<DeleteModal
|
||||
opened={isDelete}
|
||||
onClose={closeModal}
|
||||
handleSubmit={(id: number) => {
|
||||
deleteContractMutation.mutate(id);
|
||||
}}
|
||||
entityType={"contract"}
|
||||
entity={{
|
||||
name: `${currentContract?.firstname} ${currentContract?.lastname}`,
|
||||
id: currentContract?.id || 0,
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<ContractsFilters
|
||||
forms={forms || []}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useAuth } from "@/services/auth/AuthProvider";
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const {loggedUser} = useAuth();
|
||||
const { loggedUser } = useAuth();
|
||||
return (
|
||||
<Tabs
|
||||
w={{ base: "100%", md: "80%", lg: "60%" }}
|
||||
@@ -16,17 +16,50 @@ export default function Dashboard() {
|
||||
onChange={(value) => navigate(`/dashboard/${value}`)}
|
||||
>
|
||||
<Tabs.List mb="md">
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/help" {...props}></Link>)} value="help">{t("help", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/productors" {...props}></Link>)} value="productors">{t("productors", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/products" {...props}></Link>)} value="products">{t("products", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/forms" {...props}></Link>)} value="forms">{t("forms", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/shipments" {...props}></Link>)} value="shipments">{t("shipments", { capfirst: true })}</Tabs.Tab>
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/contracts" {...props}></Link>)} value="contracts">{t("contracts", { capfirst: true })}</Tabs.Tab>
|
||||
{
|
||||
loggedUser?.user?.roles && loggedUser?.user?.roles?.length > 5 ?
|
||||
<Tabs.Tab renderRoot={(props) => (<Link to="/dashboard/users" {...props}></Link>)} value="users">{t("users", { capfirst: true })}</Tabs.Tab> :
|
||||
null
|
||||
}
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/help" {...props}></Link>}
|
||||
value="help"
|
||||
>
|
||||
{t("help", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/productors" {...props}></Link>}
|
||||
value="productors"
|
||||
>
|
||||
{t("productors", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/products" {...props}></Link>}
|
||||
value="products"
|
||||
>
|
||||
{t("products", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/forms" {...props}></Link>}
|
||||
value="forms"
|
||||
>
|
||||
{t("forms", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/shipments" {...props}></Link>}
|
||||
value="shipments"
|
||||
>
|
||||
{t("shipments", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/contracts" {...props}></Link>}
|
||||
value="contracts"
|
||||
>
|
||||
{t("contracts", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
{loggedUser?.user?.roles && loggedUser?.user?.roles?.length > 5 ? (
|
||||
<Tabs.Tab
|
||||
renderRoot={(props) => <Link to="/dashboard/users" {...props}></Link>}
|
||||
value="users"
|
||||
>
|
||||
{t("users", { capfirst: true })}
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
</Tabs.List>
|
||||
<Outlet />
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { Stack, Loader, Title, Group, ActionIcon, Tooltip, Table, ScrollArea } from "@mantine/core";
|
||||
import { useCreateForm, useDeleteForm, useEditForm, useGetForm, useGetReferentForms } from "@/services/api";
|
||||
import {
|
||||
useCreateForm,
|
||||
useDeleteForm,
|
||||
useEditForm,
|
||||
useGetForm,
|
||||
useGetReferentForms,
|
||||
} from "@/services/api";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
@@ -25,7 +31,7 @@ export function Forms() {
|
||||
}
|
||||
return null;
|
||||
}, [location, isEdit]);
|
||||
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
navigate(`/dashboard/forms${searchParams ? `?${searchParams.toString()}` : ""}`);
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
@@ -242,10 +242,13 @@ export function Help() {
|
||||
</Text>
|
||||
</Blockquote>
|
||||
</Stack>
|
||||
<Title order={3}>{t("export contracts", {capfirst: true})}</Title>
|
||||
<Title order={3}>{t("export contracts", { capfirst: true })}</Title>
|
||||
<Stack>
|
||||
<Text>
|
||||
{t("to export contracts submissions before sending to the productor go to the contracts section", {capfirst: true})}
|
||||
{t(
|
||||
"to export contracts submissions before sending to the productor go to the contracts section",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
<ActionIcon
|
||||
ml="4"
|
||||
size="xs"
|
||||
@@ -260,21 +263,32 @@ export function Help() {
|
||||
<IconLink />
|
||||
</ActionIcon>
|
||||
</Text>
|
||||
<Text>{t("in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract", {capfirst: true})}</Text>
|
||||
<Text>
|
||||
{t("you can download all contracts for your form using the export all", {capfirst: true})}{" "}
|
||||
<ActionIcon size="sm">
|
||||
<IconDownload/>
|
||||
</ActionIcon>{" "}
|
||||
{t("button in top right of the page", { section: t("contracts") })}{" "}
|
||||
{t("in the same corner you can download a recap by clicking on the button", {capfirst: true})}{" "}
|
||||
<ActionIcon size="sm">
|
||||
<IconTableExport/>
|
||||
</ActionIcon>{" "}
|
||||
|
||||
{t(
|
||||
"in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
</Text>
|
||||
<Text>
|
||||
{t("once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page", {capfirst: true})}
|
||||
{t("you can download all contracts for your form using the export all", {
|
||||
capfirst: true,
|
||||
})}{" "}
|
||||
<ActionIcon size="sm">
|
||||
<IconDownload />
|
||||
</ActionIcon>{" "}
|
||||
{t("button in top right of the page", { section: t("contracts") })}{" "}
|
||||
{t("in the same corner you can download a recap by clicking on the button", {
|
||||
capfirst: true,
|
||||
})}{" "}
|
||||
<ActionIcon size="sm">
|
||||
<IconTableExport />
|
||||
</ActionIcon>{" "}
|
||||
</Text>
|
||||
<Text>
|
||||
{t(
|
||||
"once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Title order={3}>{t("glossary", { capfirst: true })}</Title>
|
||||
|
||||
@@ -14,21 +14,23 @@ export function Home() {
|
||||
useEffect(() => {
|
||||
if (searchParams.get("sessionExpired")) {
|
||||
showNotification({
|
||||
title: t("session expired", {capfirst: true}),
|
||||
message: t("your session has expired please log in again", {capfirst: true}),
|
||||
title: t("session expired", { capfirst: true }),
|
||||
message: t("your session has expired please log in again", { capfirst: true }),
|
||||
color: "red",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
if (searchParams.get("userNotAllowed")) {
|
||||
showNotification({
|
||||
title: t("user not allowed", {capfirst: true}),
|
||||
message: t("your keycloak user has no roles, please contact your administrator", {capfirst: true}),
|
||||
title: t("user not allowed", { capfirst: true }),
|
||||
message: t("your keycloak user has no roles, please contact your administrator", {
|
||||
capfirst: true,
|
||||
}),
|
||||
color: "red",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
}, [searchParams])
|
||||
}, [searchParams]);
|
||||
|
||||
return (
|
||||
<Stack mt="lg">
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { Loader } from "@mantine/core";
|
||||
import { useEffect } from "react";
|
||||
import { useSearchParams } from "react-router";
|
||||
|
||||
export function Login() {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
const accessToken = searchParams.get("access_token");
|
||||
const idToken = searchParams.get("id_token");
|
||||
const refreshToken = searchParams.get("refresh_token");
|
||||
|
||||
if (accessToken && idToken) {
|
||||
localStorage.setItem("access_token", accessToken);
|
||||
localStorage.setItem("id_token", idToken);
|
||||
localStorage.setItem("refresh_token", refreshToken || "");
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
return <Loader />;
|
||||
}
|
||||
import { Loader } from "@mantine/core";
|
||||
import { useEffect } from "react";
|
||||
import { useSearchParams } from "react-router";
|
||||
|
||||
export function Login() {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
const accessToken = searchParams.get("access_token");
|
||||
const idToken = searchParams.get("id_token");
|
||||
const refreshToken = searchParams.get("refresh_token");
|
||||
|
||||
if (accessToken && idToken) {
|
||||
localStorage.setItem("access_token", accessToken);
|
||||
localStorage.setItem("id_token", idToken);
|
||||
localStorage.setItem("refresh_token", refreshToken || "");
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } f
|
||||
import { t } from "@/config/i18n";
|
||||
import {
|
||||
useCreateProductor,
|
||||
useDeleteProductor,
|
||||
useEditProductor,
|
||||
useGetProductor,
|
||||
useGetProductors,
|
||||
@@ -13,19 +14,21 @@ 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";
|
||||
import { DeleteModal } from "@/components/DeleteModal";
|
||||
|
||||
export default function Productors() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { data: productors, isPending } = useGetProductors(searchParams);
|
||||
|
||||
|
||||
const { data: allProductors } = useGetProductors();
|
||||
const isCreate = location.pathname === "/dashboard/productors/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
const isDelete = location.pathname.includes("/delete");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
if (isEdit || isDelete) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
@@ -40,16 +43,14 @@ export default function Productors() {
|
||||
}, [navigate, searchParams]);
|
||||
|
||||
const names = useMemo(() => {
|
||||
if (!allProductors)
|
||||
return [];
|
||||
if (!allProductors) return [];
|
||||
return allProductors
|
||||
?.map((productor: Productor) => productor.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allProductors]);
|
||||
|
||||
const types = useMemo(() => {
|
||||
if (!allProductors)
|
||||
return [];
|
||||
if (!allProductors) return [];
|
||||
return allProductors
|
||||
?.map((productor: Productor) => productor.type)
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index);
|
||||
@@ -57,16 +58,17 @@ export default function Productors() {
|
||||
|
||||
const createProductorMutation = useCreateProductor();
|
||||
const editProductorMutation = useEditProductor();
|
||||
const deleteProductorMutation = useDeleteProductor();
|
||||
|
||||
const handleCreateProductor = useCallback(
|
||||
async (productor: ProductorInputs) => {
|
||||
await createProductorMutation.mutateAsync({
|
||||
...productor,
|
||||
payment_methods: productor.payment_methods.map((payment) =>( {
|
||||
payment_methods: productor.payment_methods.map((payment) => ({
|
||||
name: payment.name,
|
||||
details: payment.details,
|
||||
max: payment.max === "" ? null : payment.max
|
||||
}))
|
||||
max: payment.max === "" ? null : payment.max,
|
||||
})),
|
||||
});
|
||||
closeModal();
|
||||
},
|
||||
@@ -80,11 +82,11 @@ export default function Productors() {
|
||||
id: id,
|
||||
productor: {
|
||||
...productor,
|
||||
payment_methods: productor.payment_methods.map((payment) =>( {
|
||||
name: payment.name,
|
||||
details: payment.details,
|
||||
max: payment.max === "" ? null : payment.max
|
||||
}))
|
||||
payment_methods: productor.payment_methods.map((payment) => ({
|
||||
name: payment.name,
|
||||
details: payment.details,
|
||||
max: payment.max === "" ? null : payment.max,
|
||||
})),
|
||||
},
|
||||
});
|
||||
closeModal();
|
||||
@@ -143,6 +145,15 @@ export default function Productors() {
|
||||
currentProductor={currentProductor}
|
||||
handleSubmit={handleEditProductor}
|
||||
/>
|
||||
<DeleteModal
|
||||
opened={isDelete}
|
||||
onClose={closeModal}
|
||||
handleSubmit={(id: number) => {
|
||||
deleteProductorMutation.mutate(id);
|
||||
}}
|
||||
entityType={"productor"}
|
||||
entity={currentProductor}
|
||||
/>
|
||||
</Group>
|
||||
<ProductorsFilters
|
||||
names={names || []}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
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 {
|
||||
useCreateProduct,
|
||||
useDeleteProduct,
|
||||
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";
|
||||
@@ -12,6 +18,7 @@ import {
|
||||
type ProductInputs,
|
||||
} from "@/services/resources/products";
|
||||
import ProductsFilters from "@/components/Products/Filter";
|
||||
import { DeleteModal } from "@/components/DeleteModal";
|
||||
|
||||
export default function Products() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -19,9 +26,10 @@ export default function Products() {
|
||||
const navigate = useNavigate();
|
||||
const isCreate = location.pathname === "/dashboard/products/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
const isDelete = location.pathname.includes("/delete");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
if (isEdit || isDelete) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
@@ -38,16 +46,14 @@ export default function Products() {
|
||||
const { data: allProducts } = useGetProducts();
|
||||
|
||||
const names = useMemo(() => {
|
||||
if (!allProducts)
|
||||
return [];
|
||||
if (!allProducts) return [];
|
||||
return allProducts
|
||||
?.map((product: Product) => product.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allProducts]);
|
||||
|
||||
const productors = useMemo(() => {
|
||||
if (!allProducts)
|
||||
return [];
|
||||
if (!allProducts) return [];
|
||||
return allProducts
|
||||
?.map((product: Product) => product.productor.name)
|
||||
.filter((productor, index, array) => array.indexOf(productor) === index);
|
||||
@@ -55,6 +61,7 @@ export default function Products() {
|
||||
|
||||
const createProductMutation = useCreateProduct();
|
||||
const editProductMutation = useEditProduct();
|
||||
const deleteProductMutation = useDeleteProduct();
|
||||
|
||||
const handleCreateProduct = useCallback(
|
||||
async (product: ProductInputs) => {
|
||||
@@ -134,6 +141,15 @@ export default function Products() {
|
||||
currentProduct={currentProduct}
|
||||
handleSubmit={handleEditProduct}
|
||||
/>
|
||||
<DeleteModal
|
||||
opened={isDelete}
|
||||
onClose={closeModal}
|
||||
handleSubmit={(id: number) => {
|
||||
deleteProductMutation.mutate(id);
|
||||
}}
|
||||
entityType={"product"}
|
||||
entity={currentProduct}
|
||||
/>
|
||||
<ScrollArea type="auto">
|
||||
<Table striped>
|
||||
<Table.Thead>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ActionIcon, Group, Loader, ScrollArea, Stack, Table, Title, Tooltip } f
|
||||
import { t } from "@/config/i18n";
|
||||
import {
|
||||
useCreateShipment,
|
||||
useDeleteShipment,
|
||||
useEditShipment,
|
||||
useGetShipment,
|
||||
useGetShipments,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
} from "@/services/resources/shipments";
|
||||
import ShipmentModal from "@/components/Shipments/Modal";
|
||||
import ShipmentsFilters from "@/components/Shipments/Filter";
|
||||
import { DeleteModal } from "@/components/DeleteModal";
|
||||
|
||||
export default function Shipments() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -25,9 +27,10 @@ export default function Shipments() {
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/shipments/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
const isDelete = location.pathname.includes("/delete");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
if (isEdit || isDelete) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
@@ -44,16 +47,14 @@ export default function Shipments() {
|
||||
const { data: allShipments } = useGetShipments();
|
||||
|
||||
const names = useMemo(() => {
|
||||
if (!allShipments)
|
||||
return [];
|
||||
if (!allShipments) return [];
|
||||
return allShipments
|
||||
?.map((shipment: Shipment) => shipment.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
}, [allShipments]);
|
||||
|
||||
const forms = useMemo(() => {
|
||||
if (!allShipments)
|
||||
return [];
|
||||
if (!allShipments) return [];
|
||||
return allShipments
|
||||
?.map((shipment: Shipment) => shipment.form.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
@@ -61,6 +62,7 @@ export default function Shipments() {
|
||||
|
||||
const createShipmentMutation = useCreateShipment();
|
||||
const editShipmentMutation = useEditShipment();
|
||||
const deleteShipmentMutation = useDeleteShipment();
|
||||
|
||||
const handleCreateShipment = useCallback(
|
||||
async (shipment: ShipmentInputs) => {
|
||||
@@ -133,6 +135,15 @@ export default function Shipments() {
|
||||
currentShipment={currentShipment}
|
||||
handleSubmit={handleEditShipment}
|
||||
/>
|
||||
<DeleteModal
|
||||
opened={isDelete}
|
||||
onClose={closeModal}
|
||||
handleSubmit={(id: number) => {
|
||||
deleteShipmentMutation.mutate(id);
|
||||
}}
|
||||
entityType={"shipment"}
|
||||
entity={currentShipment}
|
||||
/>
|
||||
</Group>
|
||||
<ShipmentsFilters
|
||||
forms={forms || []}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { useCreateUser, useDeleteUser, 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";
|
||||
@@ -8,6 +8,7 @@ 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";
|
||||
import { DeleteModal } from "@/components/DeleteModal";
|
||||
|
||||
export default function Users() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -16,9 +17,10 @@ export default function Users() {
|
||||
|
||||
const isCreate = location.pathname === "/dashboard/users/create";
|
||||
const isEdit = location.pathname.includes("/edit");
|
||||
const isDelete = location.pathname.includes("/delete");
|
||||
|
||||
const editId = useMemo(() => {
|
||||
if (isEdit) {
|
||||
if (isEdit || isDelete) {
|
||||
return location.pathname.split("/")[3];
|
||||
}
|
||||
return null;
|
||||
@@ -36,8 +38,7 @@ export default function Users() {
|
||||
const { data: allUsers } = useGetUsers();
|
||||
|
||||
const names = useMemo(() => {
|
||||
if (!allUsers)
|
||||
return [];
|
||||
if (!allUsers) return [];
|
||||
return allUsers
|
||||
?.map((user: User) => user.name)
|
||||
.filter((season, index, array) => array.indexOf(season) === index);
|
||||
@@ -45,6 +46,7 @@ export default function Users() {
|
||||
|
||||
const createUserMutation = useCreateUser();
|
||||
const editUserMutation = useEditUser();
|
||||
const deleteUserMutation = useDeleteUser();
|
||||
|
||||
const handleCreateUser = useCallback(
|
||||
async (user: UserInputs) => {
|
||||
@@ -117,6 +119,15 @@ export default function Users() {
|
||||
currentUser={currentUser}
|
||||
handleSubmit={handleEditUser}
|
||||
/>
|
||||
<DeleteModal
|
||||
opened={isDelete}
|
||||
onClose={closeModal}
|
||||
handleSubmit={(id: number) => {
|
||||
deleteUserMutation.mutate(id);
|
||||
}}
|
||||
entityType={"user"}
|
||||
entity={currentUser}
|
||||
/>
|
||||
</Group>
|
||||
<UsersFilters
|
||||
names={names || []}
|
||||
|
||||
@@ -34,21 +34,26 @@ export const router = createBrowserRouter([
|
||||
{ path: "productors", Component: Productors },
|
||||
{ path: "productors/create", Component: Productors },
|
||||
{ path: "productors/:id/edit", Component: Productors },
|
||||
{ path: "productors/:id/delete", Component: Productors },
|
||||
{ path: "products", Component: Products },
|
||||
{ path: "products/create", Component: Products },
|
||||
{ path: "products/:id/edit", Component: Products },
|
||||
{ path: "products/:id/delete", Component: Products },
|
||||
{ path: "contracts", Component: Contracts },
|
||||
{ path: "contracts/download", Component: Contracts },
|
||||
{ path: "contracts/export", Component: Contracts },
|
||||
{ path: "contracts/:id/delete", Component: Contracts },
|
||||
{ path: "users", Component: Users },
|
||||
{ path: "users/create", Component: Users },
|
||||
{ path: "users/:id/edit", Component: Users },
|
||||
{ path: "users/:id/delete", Component: Users },
|
||||
{ path: "forms", Component: Forms },
|
||||
{ path: "forms/:id/edit", Component: Forms },
|
||||
{ path: "forms/:id/delete", Component: Forms },
|
||||
{ path: "forms/create", Component: Forms },
|
||||
{ path: "shipments", Component: Shipments },
|
||||
{ path: "shipments/:id/edit", Component: Shipments },
|
||||
{ path: "shipments/:id/delete", Component: Shipments },
|
||||
{ path: "shipments/create", Component: Shipments },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -5,23 +5,19 @@ import type { UserLogged } from "../resources/users";
|
||||
export type Auth = {
|
||||
loggedUser: UserLogged | null;
|
||||
isLoading: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
const AuthContext = createContext<Auth | undefined>(undefined)
|
||||
const AuthContext = createContext<Auth | undefined>(undefined);
|
||||
|
||||
export function AuthProvider({ children }: {children: React.ReactNode}) {
|
||||
const {data: loggedUser, isLoading} = useCurrentUser();
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const { data: loggedUser, isLoading } = useCurrentUser();
|
||||
|
||||
const value: Auth = {
|
||||
loggedUser: loggedUser ?? null,
|
||||
isLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={value}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAuth(): Auth {
|
||||
@@ -30,4 +26,4 @@ export function useAuth(): Auth {
|
||||
throw new Error("useAuth must be used inside AuthProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Group, Loader } from "@mantine/core";
|
||||
import { Navigate, Outlet } from "react-router";
|
||||
import { useAuth } from "../AuthProvider";
|
||||
|
||||
export function ProtectedRoute() {
|
||||
const { loggedUser, isLoading } = useAuth();
|
||||
|
||||
if (!loggedUser && isLoading)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
if (!loggedUser?.logged) {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
import { Group, Loader } from "@mantine/core";
|
||||
import { Navigate, Outlet } from "react-router";
|
||||
import { useAuth } from "../AuthProvider";
|
||||
|
||||
export function ProtectedRoute() {
|
||||
const { loggedUser, isLoading } = useAuth();
|
||||
|
||||
if (!loggedUser && isLoading)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
if (!loggedUser?.logged) {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user