diff --git a/package.json b/package.json
index ac4b120..9ee48e7 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
},
"dependencies": {
"axios": "^1.6.7",
+ "clipboard-copy": "^4.0.1",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-chartjs-2": "^5.2.0",
diff --git a/playwright.config.js b/playwright.config.js
index 95de71c..5352cd4 100644
--- a/playwright.config.js
+++ b/playwright.config.js
@@ -37,36 +37,6 @@ export default defineConfig({
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
-
- // {
- // name: "firefox",
- // use: { ...devices["Desktop Firefox"] },
- // },
-
- // {
- // name: "webkit",
- // use: { ...devices["Desktop Safari"] },
- // },
-
- /* Test against mobile viewports. */
- // {
- // name: 'Mobile Chrome',
- // use: { ...devices['Pixel 5'] },
- // },
- // {
- // name: 'Mobile Safari',
- // use: { ...devices['iPhone 12'] },
- // },
-
- /* Test against branded browsers. */
- // {
- // name: 'Microsoft Edge',
- // use: { ...devices['Desktop Edge'], channel: 'msedge' },
- // },
- // {
- // name: 'Google Chrome',
- // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
- // },
],
//Run your local dev server before starting the tests /
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 899500c..5155c00 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ dependencies:
axios:
specifier: ^1.6.7
version: 1.6.8
+ clipboard-copy:
+ specifier: ^4.0.1
+ version: 4.0.1
react:
specifier: ^18.2.0
version: 18.2.0
@@ -1143,6 +1146,10 @@ packages:
string-width: 7.1.0
dev: true
+ /clipboard-copy@4.0.1:
+ resolution: {integrity: sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng==}
+ dev: false
+
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
diff --git a/src/api/authentication.js b/src/api/authentication.js
index ed86046..77cc1ae 100644
--- a/src/api/authentication.js
+++ b/src/api/authentication.js
@@ -3,7 +3,6 @@ import axios from "axios";
export const isLoggedIn = async () => {
try {
const response = await axios.get("/authenticate");
-
return response.data;
} catch (error) {
return error.response.data;
diff --git a/src/api/items.js b/src/api/items.js
index 242ba61..de8a7bc 100644
--- a/src/api/items.js
+++ b/src/api/items.js
@@ -1,41 +1,56 @@
import axios from "axios";
export const createItem = async (settings) => {
- console.log(settings);
+ try {
+ console.log(settings);
- const formData = new FormData();
- formData.append("room", settings.room);
- formData.append("brand", settings.brand);
- formData.append("model", settings.model);
- formData.append("price", settings.price);
- formData.append("purchaseDate", settings.purchaseDate);
- formData.append("link", settings.link);
- formData.append("description", settings.description);
- formData.append("image", settings.image);
- formData.append("invoice", settings.invoice);
+ const formData = new FormData();
+ formData.append("room", settings.room);
+ formData.append("brand", settings.brand);
+ formData.append("model", settings.model);
+ formData.append("price", settings.price);
+ formData.append("purchaseDate", settings.purchaseDate);
+ formData.append("link", settings.link);
+ formData.append("description", settings.description);
+ formData.append("image", settings.image);
+ formData.append("invoice", settings.invoice);
- const response = await axios.post("/item", formData);
+ const response = await axios.post("/item", formData);
- return response.data;
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
export const updateItem = async (settings) => {
- console.log(settings);
+ try {
+ console.log(settings);
- const formData = new FormData();
- formData.append("brand", settings.brand);
- formData.append("model", settings.model);
- formData.append("price", settings.price);
- formData.append("description", settings.description);
- formData.append("purchaseDate", settings.purchaseDate);
- formData.append("link", settings.link);
+ const formData = new FormData();
+ formData.append("brand", settings.brand);
+ formData.append("model", settings.model);
+ formData.append("price", settings.price);
+ formData.append("description", settings.description);
+ formData.append("purchaseDate", settings.purchaseDate);
+ formData.append("link", settings.link);
- const response = await axios.put(`/item/${settings._id}`, formData);
- return response.data;
+ const response = await axios.put(`/item/${settings._id}`, formData);
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
export const deleteItem = async (id) => {
- console.log(id);
- const response = await axios.delete(`/item/${id}`);
- return response.data;
+ try {
+ console.log(id);
+ const response = await axios.delete(`/item/${id}`);
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
diff --git a/src/api/rooms.js b/src/api/rooms.js
index 950d0b1..159962e 100644
--- a/src/api/rooms.js
+++ b/src/api/rooms.js
@@ -1,10 +1,76 @@
import axios from "axios";
+import { createItem } from "./items";
+
+export const importRooms = async (data) => {
+ try {
+ const parsed = JSON.parse(data);
+
+ if (parsed.length === 0) {
+ return false;
+ }
+
+ for (let e of parsed) {
+ const room = await createRoom(e.name);
+
+ for (let f of e.items) {
+ const settings = { ...f, room: room._id };
+ await createItem(settings);
+ }
+ }
+
+ return true;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
+};
+
+export const exportRooms = async () => {
+ try {
+ const rooms = await getRooms();
+
+ let stock = [];
+
+ for (let room of rooms) {
+ const roomData = await getRoom(room._id);
+
+ delete roomData._id;
+ delete roomData.__v;
+ delete roomData.user;
+ delete roomData.updatedAt;
+ delete roomData.createdAt;
+
+ await roomData.items.forEach((f) => {
+ delete f.createdAt;
+ delete f.image;
+ delete f.invoice;
+ delete f.updatedAt;
+ delete f.room;
+ delete f.user;
+ delete f.__v;
+ delete f._id;
+ });
+
+ stock.push(roomData);
+ }
+
+ const final = JSON.stringify(stock);
+ return final;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
+};
export const createRoom = async (name) => {
- const response = await axios.post("/room", {
- name,
- });
- return response.data;
+ try {
+ const response = await axios.post("/room", {
+ name,
+ });
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ }
};
export const getState = async () => {
@@ -18,16 +84,31 @@ export const getState = async () => {
};
export const deleteRoom = async (id) => {
- const response = await axios.delete(`/room/${id}`);
- return response.data;
+ try {
+ const response = await axios.delete(`/room/${id}`);
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
export const getRooms = async () => {
- const response = await axios.get(`/room`);
- return response.data;
+ try {
+ const response = await axios.get(`/room`);
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
export const getRoom = async (id) => {
- const response = await axios.get(`/room/${id}`);
- return response.data;
+ try {
+ const response = await axios.get(`/room/${id}`);
+ return response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
+ }
};
diff --git a/src/api/user.js b/src/api/user.js
index e0a4c62..210b173 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -18,8 +18,9 @@ export const createUser = async (username, password, confirmation) => {
confirmation,
});
return response.data;
- } catch (error) {
- return error.response.data;
+ } catch (err) {
+ console.error(err);
+ return null;
}
};
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 92c2b81..bf14591 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -3,6 +3,11 @@
* {
margin: 0;
padding: 0;
+
+ ::selection {
+ background: $primary;
+ color: white;
+ }
}
body {
a {
diff --git a/src/components/LoaderSpace/LoaderSpace.jsx b/src/components/LoaderSpace/LoaderSpace.jsx
index fea09bd..38e7630 100644
--- a/src/components/LoaderSpace/LoaderSpace.jsx
+++ b/src/components/LoaderSpace/LoaderSpace.jsx
@@ -1,9 +1,9 @@
import { MagnifyingGlass } from "react-loader-spinner";
import "./LoaderSpace.scss";
-export default function LoaderSpace({ isVisible }) {
+export default function LoaderSpace({ isVisible, isCenterLoader }) {
return (
-
+
{},
+ login: async () => {},
register: () => {},
- logout: () => {},
+ logout: async () => {},
});
export const AuthenticationProvider = ({ children }) => {
@@ -46,6 +46,7 @@ export const AuthenticationProvider = ({ children }) => {
const register = async (username, password, confirmation) => {
try {
const user = await registerApi(username, password, confirmation);
+ console.log(user);
setAuthState({ user });
redirect();
} catch (error) {
diff --git a/src/layout/Layout.jsx b/src/layout/Layout.jsx
index 1176394..9ac6f12 100644
--- a/src/layout/Layout.jsx
+++ b/src/layout/Layout.jsx
@@ -1,11 +1,11 @@
import NavBar from "../components/NavBar/NavBar";
-import "./Layout.css";
+import "./Layout.scss";
export default function Layout({ children }) {
- return (
-
-
- {children}
-
- );
+ return (
+
+
+ {children}
+
+ );
}
diff --git a/src/layout/Layout.css b/src/layout/Layout.scss
similarity index 73%
rename from src/layout/Layout.css
rename to src/layout/Layout.scss
index b15e85e..5f950c4 100644
--- a/src/layout/Layout.css
+++ b/src/layout/Layout.scss
@@ -1,4 +1,5 @@
#layout-container {
display: flex;
flex-direction: column;
+ margin-bottom: 20px;
}
diff --git a/src/pages/authenticated/authanticated.scss b/src/pages/authenticated/authanticated.scss
new file mode 100644
index 0000000..964abfa
--- /dev/null
+++ b/src/pages/authenticated/authanticated.scss
@@ -0,0 +1,38 @@
+@import "../../assets/styles/vars.scss";
+
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ gap: 20px;
+ margin-top: 30px;
+
+ .title {
+ color: $good_black;
+ }
+
+ .error {
+ color: red;
+ font-weight: bold;
+ }
+
+ .form {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ align-items: center;
+ border-radius: $my_border_rad;
+ border: 1px dashed $good_black;
+ gap: 10px;
+ padding: 10px;
+
+ .ipt {
+ width: 300px;
+ height: 35px;
+ text-align: center;
+ }
+ }
+}
diff --git a/src/pages/authenticated/login.jsx b/src/pages/authenticated/login.jsx
index b740514..ce929b3 100644
--- a/src/pages/authenticated/login.jsx
+++ b/src/pages/authenticated/login.jsx
@@ -1,6 +1,8 @@
import React, { useState } from "react";
import { useAuth } from "../../hooks";
import { useEffect } from "react";
+import "./authanticated.scss";
+import "../../components/StylizedBtn/StylizedBtn.scss";
export const Login = () => {
const { login } = useAuth();
@@ -18,32 +20,36 @@ export const Login = () => {
const response = await login(username, password);
if (response && !response.success) {
- console.log("salut");
setError(response.error);
}
};
return (
-
-
Login page
- {error &&
{error}
}
+
);
diff --git a/src/pages/authenticated/register.jsx b/src/pages/authenticated/register.jsx
index 76cbe9b..3424ef3 100644
--- a/src/pages/authenticated/register.jsx
+++ b/src/pages/authenticated/register.jsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
-
+import "../../components/StylizedBtn/StylizedBtn.scss";
import { useAuth } from "../../hooks";
export const Register = () => {
@@ -8,49 +8,69 @@ export const Register = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [confirmation, setConfirmation] = useState("");
- const [error, setError] = useState();
+ const [error, setError] = useState(null);
useEffect(() => {
document.title = "Inscription";
}, []);
const onSubmit = async (e) => {
- e.preventDefault();
- const response = await register(username, password, confirmation);
- console.log(response);
+ try {
+ e.preventDefault();
+ const response = await register(username, password, confirmation);
- if (response && !response.success) {
- setError(response.error);
+ if (!response) {
+ setError(
+ "Assurez vous que le mot de passe contient au moins 8 caracteres dont une majuscule, un symbole special et au minimum un chiffre.",
+ );
+ }
+
+ if (response && !response.success) {
+ setError(
+ "Assurez vous que le mot de passe contient au moins 8 caracteres dont une majuscule, un symbole special et au minimum un chiffre.",
+ );
+ }
+ } catch (err) {
+ console.error(err);
}
};
return (
-
-
Register page
- {error &&
{error}
}
-
)}
-
+
{dataset ? (
diff --git a/src/pages/profile/Profile.jsx b/src/pages/profile/Profile.jsx
index 54cb460..5224d8c 100644
--- a/src/pages/profile/Profile.jsx
+++ b/src/pages/profile/Profile.jsx
@@ -1,9 +1,17 @@
import { useAuth } from "../../hooks";
import { useState, useEffect } from "react";
import "./Profile.scss";
-import { updateUser, deleteUser, logout } from "../../api";
+import {
+ updateUser,
+ deleteUser,
+ logout,
+ exportRooms,
+ importRooms,
+} from "../../api";
import StylizedBtn from "../../components/StylizedBtn/StylizedBtn";
import passwordCheck from "../../services/password";
+import LoaderSpace from "./../../components/LoaderSpace/LoaderSpace";
+import clipboardCopy from "clipboard-copy";
//Bilouuuuuuu94!@@
export default function Profile() {
@@ -13,6 +21,10 @@ export default function Profile() {
const [oldPPassword, setOldPPassword] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
+ const [isIELoad, setIsIELoad] = useState(false);
+ const [exportDatas, setExportDatas] = useState(null);
+ const [importMsg, setImportMsg] = useState("");
+ const [isInAfterImport, setIsInAfterImport] = useState(false);
useEffect(() => {
document.title = `Profile`;
@@ -40,6 +52,37 @@ export default function Profile() {
setOldPPassword(e.target.value);
};
+ const importData = () => {
+ const data = prompt("Veuillez coller le code que vous avez copier");
+ if (!data) return;
+
+ setExportDatas(null);
+ setIsInAfterImport(true);
+ setIsIELoad(true);
+ setImportMsg("Chargement ...");
+
+ importRooms(data).then((res) => {
+ if (!res) {
+ setImportMsg("Une erreur est survenue");
+ } else {
+ setImportMsg("Vos depenses ont ete transfere avec sucess.");
+ }
+ setIsIELoad(false);
+ console.log(res);
+ });
+ };
+
+ const exportData = () => {
+ setIsIELoad(true);
+ setIsInAfterImport(false);
+
+ exportRooms().then((res) => {
+ setExportDatas(res);
+ setIsIELoad(false);
+ clipboardCopy(`${res}`).then();
+ });
+ };
+
const handleSubmitPassword = () => {
if (!passwordCheck(password, confirmPassword)) {
alert(
@@ -148,18 +191,36 @@ export default function Profile() {
- Fais voyager tes depenses.
+ Fais voyager tes depenses !
-
+
+
+ {exportDatas ? (
+
+
+ Vos données ont été copiées dans le presse-papier. Pour pouvoir
+ les importer, il suffira juste de cliquer sur le bouton ci-dessous{" "}
+ "Importer" , puis de coller les données dans le champ de
+ saisie qui s'ouvrira en haut de l'écran.
+
+
{exportDatas}
+
+ ) : null}
+
+ {isInAfterImport ?
{importMsg}
: null}
+
+ {!exportDatas ? (
+
+ ) : null}
diff --git a/src/pages/profile/Profile.scss b/src/pages/profile/Profile.scss
index 2d265e6..ccf62f8 100644
--- a/src/pages/profile/Profile.scss
+++ b/src/pages/profile/Profile.scss
@@ -33,11 +33,31 @@
gap: 10px;
border-radius: $my_border_rad;
+ #exported-datas-container {
+ width: 80%;
+ max-height: 300px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+
+ #exported-datas-tips {
+ border: 1px solid $good_black;
+ padding: 5px;
+ }
+
+ #exported-datas {
+ width: 100%;
+ max-height: 200px;
+ overflow: auto;
+ }
+ }
+
@media (max-width: 770px) {
width: 80%;
}
.profile-modifier-title {
+ cursor: pointer;
color: $black_text;
font-weight: bold;
}
diff --git a/src/pages/room/Room.jsx b/src/pages/room/Room.jsx
index b619b2a..f7d8ed5 100644
--- a/src/pages/room/Room.jsx
+++ b/src/pages/room/Room.jsx
@@ -5,6 +5,7 @@ import "./Room.scss";
import AddBtn from "../../components/AddBtn/AddBtn";
import { createItem, deleteItem, updateItem } from "../../api/";
import StylizedBtn from "../../components/StylizedBtn/StylizedBtn";
+import LoaderSpace from "../../components/LoaderSpace/LoaderSpace";
export default function Room() {
const { id } = useParams();
@@ -12,12 +13,16 @@ export default function Room() {
const [isInForm, setInForm] = useState(false);
const [isInFormUpdate, setIsInFormUpdate] = useState(false);
const [formUpdateData, setFormUpdateData] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
+ setIsLoading(true);
+
getRoom(id).then((res) => {
console.log(res);
document.title = `Piece - ${res.name}`;
setData(res);
+ setIsLoading(false);
});
}, []);
@@ -109,6 +114,10 @@ export default function Room() {
) : null}
+ {isLoading ? (
+
+ ) : null}
+
{isInForm || isInFormUpdate ?
: null}
{data.items && data.items.length > 0 ? (
@@ -176,11 +185,7 @@ export default function Room() {
))}
- ) : (
-
- Cette piece ne contient pas d'objet.
-
- )}
+ ) : null}
{isInForm && (
diff --git a/src/pages/room/Room.scss b/src/pages/room/Room.scss
index 52a7f0c..17b9c89 100644
--- a/src/pages/room/Room.scss
+++ b/src/pages/room/Room.scss
@@ -106,6 +106,7 @@
display: flex;
gap: 5px;
width: 100%;
+ align-items: center;
.item-description {
color: $black;
@@ -117,6 +118,7 @@
display: flex;
width: 100%;
gap: 5px;
+ align-items: center;
.item-price {
font-weight: bold;
@@ -126,6 +128,7 @@
.item-buy-date-container {
display: flex;
width: 100%;
+ align-items: center;
gap: 5px;
.item-buy-date {
diff --git a/src/pages/rooms/Rooms.jsx b/src/pages/rooms/Rooms.jsx
index 4612b6b..4896cfa 100644
--- a/src/pages/rooms/Rooms.jsx
+++ b/src/pages/rooms/Rooms.jsx
@@ -3,6 +3,7 @@ import { useState, useEffect } from "react";
import { createRoom, getRooms, deleteRoom } from "../../api";
import { Link } from "react-router-dom";
import LoaderSpace from "../../components/LoaderSpace/LoaderSpace";
+import AddBtn from "./../../components/AddBtn/AddBtn";
export default function Rooms() {
const [rooms, setRooms] = useState(null);
@@ -85,7 +86,7 @@ export default function Rooms() {
))}
) : (
-
+
)}
{isErr && !isLoad ? (
@@ -94,9 +95,7 @@ export default function Rooms() {
Creer une nouvelle piece
-
- +
-
+
);
diff --git a/sujet.pdf b/sujet.pdf
new file mode 100644
index 0000000..680b60d
Binary files /dev/null and b/sujet.pdf differ
diff --git a/tests/authenticated/login.test.js b/tests/authenticated/login.test.js
index 8e79c0d..73f832b 100644
--- a/tests/authenticated/login.test.js
+++ b/tests/authenticated/login.test.js
@@ -1,26 +1,32 @@
// @ts-check
import { test, expect } from "@playwright/test";
-import { waitFor } from "@testing-library/react";
test("Test de connexion", async ({ page }) => {
+ const account = "Bilouuuuuuu94!@@";
+
await page.goto("/login");
+ await page.waitForURL("/login");
+
await page
.locator(
"#layout-container > main > div > form > input[type=text]:nth-child(1)",
)
- .fill("Bilouuuuuuu94!@@");
-
+ .fill(account);
await page
.locator(
"#layout-container > main > div > form > input[type=password]:nth-child(2)",
)
- .fill("Bilouuuuuuu94!@@");
-
+ .fill(account);
await page.locator("#layout-container > main > div > form > button").click({
button: "left",
});
await page.waitForURL("/");
- const title = await page.title();
- console.log(title);
+
+ expect(await page.title()).toBe("Accueil");
+ expect(await page.locator("#title").innerText()).toBe(`Bonjour ${account} !`);
+
+ test.setTimeout(10000);
+
+ await page.waitForURL("/");
});
diff --git a/tests/authenticated/register.test.js b/tests/authenticated/register.test.js
new file mode 100644
index 0000000..d13ff91
--- /dev/null
+++ b/tests/authenticated/register.test.js
@@ -0,0 +1,33 @@
+// @ts-check
+import { test, expect } from "@playwright/test";
+
+test("Test de connexion", async ({ page }) => {
+ const account = "Eheheh9400$$!@@";
+
+ await page.goto("/register");
+ await page.waitForURL("/register");
+
+ await page
+ .locator(
+ "#layout-container > main > div > form > input[type=text]:nth-child(1)",
+ )
+ .fill(account);
+ await page
+ .locator(
+ "#layout-container > main > div > form > input[type=password]:nth-child(2)",
+ )
+ .fill(account);
+ await page
+ .locator(
+ "#layout-container > main > div > form > input[type=password]:nth-child(3)",
+ )
+ .fill(account);
+ await page
+ .locator("#layout-container > main > div > form > button")
+ .click({ button: "left" });
+
+ await page.waitForURL("/");
+
+ expect(await page.title()).toBe("Accueil");
+ expect(await page.locator("#title").innerText()).toBe(`Bonjour ${account} !`);
+});