Add authentification
This commit is contained in:
20
frontend/src/components/Auth/index.tsx
Normal file
20
frontend/src/components/Auth/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useCurrentUser } from "@/services/api";
|
||||
import { Group, Loader } from "@mantine/core";
|
||||
import { Navigate, Outlet } from "react-router";
|
||||
|
||||
export function Auth() {
|
||||
const { data: userLogged, isLoading } = useCurrentUser();
|
||||
|
||||
if (!userLogged && isLoading)
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
|
||||
if (!userLogged?.logged) {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
@@ -1,50 +1,57 @@
|
||||
import { Button, Group, Modal, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import { Button, Group, Modal, Select, type ModalBaseProps } from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel, IconEdit, IconPlus } from "@tabler/icons-react";
|
||||
import { type Contract, type ContractInputs } from "@/services/resources/contracts";
|
||||
import { IconCancel, IconDownload } from "@tabler/icons-react";
|
||||
import { useGetForms } from "@/services/api";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export type ContractModalProps = ModalBaseProps & {
|
||||
currentContract?: Contract;
|
||||
handleSubmit: (contract: ContractInputs, id?: number) => void;
|
||||
handleSubmit: (id: number) => void;
|
||||
};
|
||||
|
||||
export function ContractModal({
|
||||
opened,
|
||||
onClose,
|
||||
currentContract,
|
||||
handleSubmit,
|
||||
}: ContractModalProps) {
|
||||
const form = useForm<ContractInputs>({
|
||||
// initialValues: {
|
||||
// firstname: currentContract?.firstname ?? "",
|
||||
// lastname: currentContract?.lastname ?? "",
|
||||
// email: currentContract?.email ?? "",
|
||||
// },
|
||||
// validate: {
|
||||
// firstname: (value) =>
|
||||
// !value ? `${t("name", { capfirst: true })} ${t("is required")}` : null,
|
||||
// email: (value) =>
|
||||
// !value ? `${t("email", { capfirst: true })} ${t("is required")}` : null,
|
||||
// },
|
||||
export type ContractDownloadInputs = {
|
||||
form_id: number;
|
||||
};
|
||||
|
||||
export function ContractModal({ opened, onClose, handleSubmit }: ContractModalProps) {
|
||||
const { data: allForms } = useGetForms();
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
form_id: null,
|
||||
},
|
||||
validate: {
|
||||
form_id: (value) =>
|
||||
!value ? `${t("a form", { capfirst: true })} ${t("is required")}` : null,
|
||||
},
|
||||
});
|
||||
|
||||
const formSelect = useMemo(() => {
|
||||
return allForms?.map((form) => ({
|
||||
value: String(form.id),
|
||||
label: `${form.season} ${form.name}`,
|
||||
}));
|
||||
}, [allForms]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create contract", { capfirst: true })}>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
<TextInput
|
||||
label={t("contract name", { capfirst: true })}
|
||||
placeholder={t("contract name", { capfirst: true })}
|
||||
radius="sm"
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={t("download contracts", { capfirst: true })}
|
||||
>
|
||||
<Select
|
||||
label={t("form", { capfirst: true })}
|
||||
placeholder={t("select a form", { capfirst: true })}
|
||||
description={t(
|
||||
"by selecting a form here you can download all contracts of your form",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
nothingFoundMessage={t("nothing found", { capfirst: true })}
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("contract email", { capfirst: true })}
|
||||
placeholder={t("contract email", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
{...form.getInputProps("email")}
|
||||
clearable
|
||||
allowDeselect
|
||||
searchable
|
||||
data={formSelect || []}
|
||||
{...form.getInputProps("form_id")}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
@@ -61,22 +68,16 @@ export function ContractModal({
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
aria-label={
|
||||
currentContract
|
||||
? t("edit contract", { capfirst: true })
|
||||
: t("create contract", { capfirst: true })
|
||||
}
|
||||
leftSection={currentContract ? <IconEdit /> : <IconPlus />}
|
||||
aria-label={t("download contracts")}
|
||||
leftSection={<IconDownload />}
|
||||
onClick={() => {
|
||||
form.validate();
|
||||
if (form.isValid()) {
|
||||
handleSubmit(form.getValues(), currentContract?.id);
|
||||
handleSubmit(Number(form.values.form_id));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentContract
|
||||
? t("edit contract", { capfirst: true })
|
||||
: t("create contract", { capfirst: true })}
|
||||
{t("download contracts", { capfirst: true })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
|
||||
@@ -10,24 +10,17 @@ export type ContractRowProps = {
|
||||
};
|
||||
|
||||
export default function ContractRow({ contract }: ContractRowProps) {
|
||||
// const [searchParams] = useSearchParams();
|
||||
const deleteMutation = useDeleteContract();
|
||||
// const navigate = useNavigate();
|
||||
const {refetch, isFetching} = useGetContractFile(contract.id)
|
||||
const handleDownload = useCallback(async () => {
|
||||
const { data } = await refetch();
|
||||
if (!data)
|
||||
return;
|
||||
const getContractMutation = useGetContractFile();
|
||||
|
||||
const url = URL.createObjectURL(data.blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = data.filename;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}, [useGetContractFile])
|
||||
const handleDownload = useCallback(async () => {
|
||||
getContractMutation.mutateAsync(contract.id);
|
||||
}, [useGetContractFile, contract, contract.id]);
|
||||
return (
|
||||
<Table.Tr key={contract.id}>
|
||||
<Table.Td>
|
||||
{contract.form.name} {contract.form.season}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{contract.firstname} {contract.lastname}
|
||||
</Table.Td>
|
||||
@@ -36,32 +29,16 @@ export default function ContractRow({ contract }: ContractRowProps) {
|
||||
{contract.cheque_quantity > 0 && contract.cheque_quantity} {contract.payment_method}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{/* <Tooltip label={t("edit contract", { capfirst: true })}>
|
||||
<Tooltip label={t("download contract", { capfirst: true })}>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/dashboard/contracts/${contract.id}/edit${searchParams ? `?${searchParams.toString()}` : ""}`,
|
||||
);
|
||||
handleDownload();
|
||||
}}
|
||||
>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip> */}
|
||||
<Tooltip
|
||||
label={t("download contract")}
|
||||
>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
mr="5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload()
|
||||
}}
|
||||
>
|
||||
<IconDownload/>
|
||||
<IconDownload />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("remove contract", { capfirst: true })}>
|
||||
|
||||
@@ -1,31 +1,52 @@
|
||||
import { NavLink } from "react-router";
|
||||
import { t } from "@/config/i18n";
|
||||
import "./index.css";
|
||||
import { Group } from "@mantine/core";
|
||||
import { Button, Group, Loader } from "@mantine/core";
|
||||
import { Config } from "@/config/config";
|
||||
import { useCurrentUser, useLogoutUser } from "@/services/api";
|
||||
|
||||
export function Navbar() {
|
||||
const { data: user, isLoading } = useCurrentUser();
|
||||
const logout = useLogoutUser();
|
||||
if (!user && isLoading) {
|
||||
return (
|
||||
<Group align="center" justify="center" h="80vh" w="100%">
|
||||
<Loader color="pink" />
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<Group>
|
||||
<NavLink className={"navLink"} aria-label={t("home")} to="/">
|
||||
{t("home", { capfirst: true })}
|
||||
</NavLink>
|
||||
{user?.logged ? (
|
||||
<NavLink className={"navLink"} aria-label={t("dashboard")} to="/dashboard/help">
|
||||
{t("dashboard", { capfirst: true })}
|
||||
</NavLink>
|
||||
) : null}
|
||||
</Group>
|
||||
{!user?.logged ? (
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t("dashboard")}
|
||||
to="/dashboard/help"
|
||||
aria-label={t("login with keycloak", { capfirst: true })}
|
||||
to={`${Config.backend_uri}/auth/login`}
|
||||
>
|
||||
{t("dashboard", { capfirst: true })}
|
||||
{t("login with keycloak", { capfirst: true })}
|
||||
</NavLink>
|
||||
</Group>
|
||||
<NavLink
|
||||
className={"navLink"}
|
||||
aria-label={t("login with keycloak", { capfirst: true })}
|
||||
to={`${Config.backend_uri}/auth/login`}
|
||||
>
|
||||
{t("login with keycloak", { capfirst: true })}
|
||||
</NavLink>
|
||||
) : (
|
||||
<Button
|
||||
className={"navLink"}
|
||||
aria-label={t("logout", { capfirst: true })}
|
||||
onClick={() => {
|
||||
logout.mutate();
|
||||
}}
|
||||
>
|
||||
{t("logout", { capfirst: true })}
|
||||
</Button>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Group,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
Select,
|
||||
TextInput,
|
||||
Title,
|
||||
type ModalBaseProps,
|
||||
@@ -15,6 +16,8 @@ import {
|
||||
type Productor,
|
||||
type ProductorInputs,
|
||||
} from "@/services/resources/productors";
|
||||
import { useMemo } from "react";
|
||||
import { useGetRoles } from "@/services/api";
|
||||
|
||||
export type ProductorModalProps = ModalBaseProps & {
|
||||
currentProductor?: Productor;
|
||||
@@ -27,6 +30,8 @@ export function ProductorModal({
|
||||
currentProductor,
|
||||
handleSubmit,
|
||||
}: ProductorModalProps) {
|
||||
const { data: allRoles } = useGetRoles();
|
||||
|
||||
const form = useForm<ProductorInputs>({
|
||||
initialValues: {
|
||||
name: currentProductor?.name ?? "",
|
||||
@@ -44,6 +49,10 @@ export function ProductorModal({
|
||||
},
|
||||
});
|
||||
|
||||
const roleSelect = useMemo(() => {
|
||||
return allRoles?.map((role) => ({ value: String(role.name), label: role.name }));
|
||||
}, [allRoles]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create productor", { capfirst: true })}>
|
||||
<Title order={4}>{t("Informations", { capfirst: true })}</Title>
|
||||
@@ -54,11 +63,14 @@ export function ProductorModal({
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
<Select
|
||||
label={t("productor type", { capfirst: true })}
|
||||
placeholder={t("productor type", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
clearable
|
||||
searchable
|
||||
data={roleSelect || []}
|
||||
{...form.getInputProps("type")}
|
||||
/>
|
||||
<TextInput
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
} from "@/services/resources/products";
|
||||
import { useMemo } from "react";
|
||||
import { useGetProductors } from "@/services/api";
|
||||
import { InputLabel } from "@/components/Label";
|
||||
|
||||
export type ProductModalProps = ModalBaseProps & {
|
||||
currentProduct?: Product;
|
||||
@@ -90,7 +89,10 @@ export function ProductModal({ opened, onClose, currentProduct, handleSubmit }:
|
||||
label={t("product type", { capfirst: true })}
|
||||
placeholder={t("product type", { capfirst: true })}
|
||||
radius="sm"
|
||||
description={t("a product type define the way it will be organized on the final contract form (showed to users) it can be reccurent or occassional. Recurrent products will be set for all shipments if selected by user, Occasional products can be choosen for each shipments", {capfirst: true})}
|
||||
description={t(
|
||||
"a product type define the way it will be organized on the final contract form (showed to users) it can be reccurent or occassional. Recurrent products will be set for all shipments if selected by user, Occasional products can be choosen for each shipments",
|
||||
{ capfirst: true },
|
||||
)}
|
||||
searchable
|
||||
clearable
|
||||
withAsterisk
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { Button, Group, Modal, TextInput, Title, type ModalBaseProps } from "@mantine/core";
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
Select,
|
||||
TextInput,
|
||||
Title,
|
||||
type ModalBaseProps,
|
||||
} from "@mantine/core";
|
||||
import { t } from "@/config/i18n";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconCancel, IconEdit, IconPlus } from "@tabler/icons-react";
|
||||
import { type User, type UserInputs } from "@/services/resources/users";
|
||||
import { useGetRoles } from "@/services/api";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export type UserModalProps = ModalBaseProps & {
|
||||
currentUser?: User;
|
||||
@@ -10,10 +21,12 @@ export type UserModalProps = ModalBaseProps & {
|
||||
};
|
||||
|
||||
export function UserModal({ opened, onClose, currentUser, handleSubmit }: UserModalProps) {
|
||||
const { data: allRoles } = useGetRoles();
|
||||
const form = useForm<UserInputs>({
|
||||
initialValues: {
|
||||
name: currentUser?.name ?? "",
|
||||
email: currentUser?.email ?? "",
|
||||
role_names: currentUser?.roles.map((role) => role.name) ?? [],
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
@@ -23,6 +36,10 @@ export function UserModal({ opened, onClose, currentUser, handleSubmit }: UserMo
|
||||
},
|
||||
});
|
||||
|
||||
const roleSelect = useMemo(() => {
|
||||
return allRoles?.map((role) => ({ value: String(role.name), label: role.name }));
|
||||
}, [allRoles]);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={t("create user", { capfirst: true })}>
|
||||
<Title order={4}>{t("informations", { capfirst: true })}</Title>
|
||||
@@ -40,6 +57,16 @@ export function UserModal({ opened, onClose, currentUser, handleSubmit }: UserMo
|
||||
withAsterisk
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
<MultiSelect
|
||||
label={t("user roles", { capfirst: true })}
|
||||
placeholder={t("user roles", { capfirst: true })}
|
||||
radius="sm"
|
||||
withAsterisk
|
||||
clearable
|
||||
searchable
|
||||
data={roleSelect || []}
|
||||
{...form.getInputProps("role_names")}
|
||||
/>
|
||||
<Group mt="sm" justify="space-between">
|
||||
<Button
|
||||
variant="filled"
|
||||
|
||||
Reference in New Issue
Block a user