Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import ReactDOM from 'react-dom';

function App() {
return (
<div>
<h1>Hello World!</h1>
<p>Basic React web page</p>
</div>
);
}

ReactDOM.render(<App />, document.getElementById('root'));
11 changes: 11 additions & 0 deletions src/components/loader/CustomLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Center, Loader } from "@mantine/core";

const CustomLoader = () => {
return (
<Center style={{height: "100%", width: "100%"}}>
<Loader />
</Center>
);
}

export default CustomLoader;
1 change: 1 addition & 0 deletions src/components/loader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./CustomLoader";
72 changes: 72 additions & 0 deletions src/components/notifications/errors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,76 @@ export const partErrorNotification = () => {
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const statusErrorNotification = () => {
showNotification({
title: "Unable to update status",
message: "You don't have the permission to perform this action",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const statusError2Notification = () => {
showNotification({
title: "Unable to update status",
message: "You can't update the status of this card",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const deleteErrorNotification = () => {
showNotification({
title: "Unable to delete",
message: "You don't have the permission to delete this card",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const deleteError2Notification = () => {
showNotification({
title: "Unable to delete",
message: "This card as already been approved",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const createErrorNotification = () => {
showNotification({
title: "Unable to create",
message: "An error occured, please try again",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const editErrorNotification = () => {
showNotification({
title: "Unable to edit",
message: "An error occured, please try again",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const approveErrorNotification = () => {
showNotification({
title: "Unable to approve",
message: "This card as already been approved",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};

export const rejectErrorNotification = () => {
showNotification({
title: "Unable to reject",
message: "This card as already been approved",
color: "red",
icon: <AiOutlineExclamation size={24} />,
});
};
63 changes: 63 additions & 0 deletions src/components/notifications/success.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,67 @@ export const partCreatedNotification = () => {
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const cacheRefreshedNotification = () => {
showNotification({
title: "API cache refreshed",
message: "API cache refresh with success !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const configNotification = () => {
showNotification({
title: "Config edited",
message: "New config has been saved !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const createdCardNotification = () => {
showNotification({
title: "Card created",
message: "Card successfully added to your collection !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const editCardNotification = () => {
showNotification({
title: "Card edited",
message: "Card successfully edited !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const deleteCardNotification = () => {
showNotification({
title: "Card deleted",
message: "Card successfully deleted from your collection",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const approveCardNotification = () => {
showNotification({
title: "Card approved",
message: "Card successfully approved !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};

export const rejectCardNotification = () => {
showNotification({
title: "Card rejected",
message: "Card successfully rejected !",
color: "green",
icon: <BsCheck2 size={18} />,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const LoginFields: React.FC = () => {
}
})
)
router.push("/dashboard")
router.push("/home")
}
}, [result])

Expand Down
62 changes: 62 additions & 0 deletions src/features/global/cards/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useEffect } from 'react';

import { Card, Group, Badge } from '@mantine/core';

import { Card as CardType } from "types/apiTypes";

import { statusTranslate, statusColor } from './utils/dataTranslate';

import { CardsStyle } from './style/CardsStyle';

import { useUpdateCardStatusMutation } from 'store/api/cardAPI';

import CardMenu from './components/CardMenu';

import { statusError2Notification, statusErrorNotification } from 'components/notifications/errors';

import CardBody from './components/cardParts/CardBody';
import CardTitle from './components/cardParts/CardTitle';
import CardStatusControl from './components/cardParts/CardStatusControl';
import CardManagement from './components/cardParts/CardManagement';

interface Props {
card: CardType;
refetch: any;
edition: boolean;
}

const UserCard: React.FC<Props> = ({ card, refetch, edition }) => {
const { classes } = CardsStyle();

const [updateStatus, statusResult] = useUpdateCardStatusMutation<any>();

useEffect(() => {
if (statusResult.isError) {
if (statusResult.error.status === 403)
statusErrorNotification();
if (statusResult.error.status === 400)
statusError2Notification();
} else if (statusResult.isSuccess) {
refetch();
}
}, [statusResult])

return (
<Card withBorder radius="sm" className={classes.card} p={0} shadow={"md"} mih={300}>
<Card.Section className={classes.footer}>
<Group position="apart">
<Badge variant="light" size="md" radius={"sm"} color={statusColor[`${card.status}`]}>{statusTranslate[`${card.status}`]}</Badge>
<CardMenu card={card} refetch={refetch} />
</Group>
</Card.Section>
<CardTitle card={card} />
<CardBody card={card} />
{ edition
? <CardManagement card={card} refetch={refetch} />
: <CardStatusControl card={card} updateStatus={updateStatus} />
}
</Card>
);
}

export default UserCard;
68 changes: 68 additions & 0 deletions src/features/global/cards/components/CardMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useEffect, useState } from "react";

import { useMantineTheme, ActionIcon, Menu } from "@mantine/core";

import { HiOutlineDotsHorizontal, HiOutlineTrash } from "react-icons/hi";
import { RiEditLine } from "react-icons/ri";
import { TbCheckbox } from "react-icons/tb";

import DodsModal from "./modals/DodsModal";

import { Card } from "types/apiTypes";

import EditModal from "./modals/EditCardModal";
import { useDeleteCardMutation } from "store/api/cardAPI";
import { deleteError2Notification, deleteErrorNotification } from "components/notifications/errors";
import { deleteCardNotification } from "components/notifications/success";

interface Props {
card: Card;
refetch: any;
}

const CardMenu: React.FC<Props> = ({ card, refetch }) => {
const [openDods, setOpenDods] = useState(false);
const [openEdit, setOpenEdit] = useState(false);
const theme = useMantineTheme();

const [deleteCard, resultDelete] = useDeleteCardMutation<any>();

useEffect(() => {
if (resultDelete.isError) {
if (resultDelete.error.status === 403)
deleteErrorNotification();
if (resultDelete.error.status === 400)
deleteError2Notification();
} else if (resultDelete.isSuccess) {
deleteCardNotification();
refetch();
}
}, [resultDelete])

return (
<>
<DodsModal openDods={openDods} setOpenDods={setOpenDods} card={card} />
<EditModal openEdit={openEdit} setOpenEdit={setOpenEdit} card={card} refetch={refetch} />
<Menu shadow="md" width={200} trigger="hover" openDelay={100} closeDelay={400}>
<Menu.Target>
<ActionIcon size={"lg"} color={"violet"}>
<HiOutlineDotsHorizontal size={25} color={theme.colors.violet[5]} />
</ActionIcon>
</Menu.Target>

<Menu.Dropdown>
<Menu.Label>Card</Menu.Label>
<Menu.Item icon={<TbCheckbox size={18} />} onClick={() => setOpenDods(true)}>DoDs</Menu.Item>

<Menu.Divider />

<Menu.Label>Danger zone</Menu.Label>
<Menu.Item disabled={card.status === "WAITING_APPROVAL" || card.status === "REJECTED" ? false : true} onClick={() => setOpenEdit(true)} icon={<RiEditLine size={18} />}>Edit</Menu.Item>
<Menu.Item disabled={card.status === "WAITING_APPROVAL" || card.status === "REJECTED" ? false : true} color="red" icon={<HiOutlineTrash size={18} />} onClick={() => deleteCard(card.id)}>Delete card</Menu.Item>
</Menu.Dropdown>
</Menu>
</>
);
}

export default CardMenu;
48 changes: 48 additions & 0 deletions src/features/global/cards/components/CardParts/CardBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Card, Group, Spoiler, Text } from "@mantine/core";

import { Card as CardType } from "types/apiTypes";
import { CardsStyle } from "../../style/CardsStyle";

interface Props {
card: CardType;
}

const CardBody: React.FC<Props> = ({ card }) => {
const { classes } = CardsStyle();

return (
<Card.Section className={classes.section} mt="md" pb={(card.status !== "WAITING_APPROVAL" && card.status !== "REJECTED") ? 0 : 20}>
<Spoiler maxHeight={card.status === "REJECTED" ? 63 : 50} showLabel="Show more" hideLabel="Hide" style={{fontSize: 13}}>
{ card.status === "REJECTED" &&
<>
<Text size="sm" color={"red"} className={classes.label}>Rejection reason :</Text>
<Text size="xs" color="red" pb={15}>
{card.rejectionReason}
</Text>
</>
}
<Text size="sm" color="dimmed">
<span className={classes.part}>En tant que</span> <span style={{fontSize: 12, letterSpacing: -0.25}}>{card.asWho.charAt(0).toLowerCase() + card.asWho.slice(1)}</span> <span className={classes.part}>je veux</span> <span style={{fontSize: 12, letterSpacing: -0.25}}>{card.task.charAt(0).toLowerCase() + card.task.slice(1)}</span>
</Text>
<Text size="sm" color="dimmed" className={classes.label} mt={20}>
Description
</Text>
<Text size="xs" color="dimmed">
{card.description}
</Text>
<Text size="sm" color="dimmed" mt={20} className={classes.label}>
Assignees
</Text>
<Text size="xs" color="dimmed" style={{paddingBottom: 10}}>
{card.assignees.map((person, index) =>
<Text size="xs" color="dimmed" key={index}>
- {person.firstname} {person.lastname}
</Text>
)}
</Text>
</Spoiler>
</Card.Section>
);
}

export default CardBody;
Loading