commit final

This commit is contained in:
Simon CATANESE 2024-05-13 01:42:15 +02:00
parent 6506d6817b
commit 1e3dcf340d
20 changed files with 103 additions and 53 deletions

Binary file not shown.

Binary file not shown.

View File

@ -11,6 +11,27 @@ Victor DESCAMPS
the tests sheet 'api/image-request.test.jsx' was commented out to avoid over-sending requests to the google API during development. Remember to uncomment the file if you want to run all the tests once, but remember to recomment the file if you want to run a series of tests. the tests sheet 'api/image-request.test.jsx' was commented out to avoid over-sending requests to the google API during development. Remember to uncomment the file if you want to run all the tests once, but remember to recomment the file if you want to run a series of tests.
An over-sending of requests could get us blocked for the day by google. An over-sending of requests could get us blocked for the day by google.
## Project summary (FR):
Nous n'avons pas réussi à mettre en œuvre toutes les fonctionnalités de l'interface utilisateur à temps. Le développement n'a pas non plus été rigoureusement test-driven.
résumé des fonctionnalités :
En se connectant, l'utilisateur peut charger la liste des articleset les filtrer par pièce, date, nom, etc. Il est possible de créer et de mettre à jour des articles.
(Il n'y a pas de confirmation de réussite ou d'échec de création/update. La page doit être actualisée pour voir les résultats.)
Une page permet de consulter les statistiques des pièces et la liste des pièces. Il est possible d'y créer de nouvelles. La mise à jour des piècesn'est pas encore implémentée.
Les icones AntDesign provoquent systématiquement une exception dans la console. Nous n'avons pas été en mesure de corriger ou éviter cette erreur. Les icones s'affichent bien, donc ça ne posera pas de problème à part la pollution de la console.
Nous avons voulu personnaliser un peu notre projet pour le différencier des autres, nous avons donc mis en place un système simple de recherche automatique d'images liées aux objets enregistrés, et une requête à l'API Inspirobot à chaque rechargement de la page sur la page d'accueil pour un accueil chaleureux.
Nous sommes conscients que ce projet ne correspond pas exactement aux attentes d'un développement full test-driven. Mais nous nous satisfaisons tout de même de nous être familiarisés avec js et React, avec les tests en général, et d'avoir pris du plaisir à développer ce petit projet.
Nous vous souhaitons une bonne correction et vous remercions pour vos cours.
Traduit avec DeepL.com (version gratuite)
## Run Locally ## Run Locally

View File

@ -1,11 +1,11 @@
.diagram-container { .diagram-container {
width: 100%; /* Utilise toute la largeur horizontale disponible */ width: 100%;
margin-bottom: 20px; margin-bottom: 20px;
} }
.row-container { .row-container {
display: flex; display: flex;
align-items: flex-end; /* Alignement des éléments à la base */ align-items: flex-end;
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -20,9 +20,9 @@
.value { .value {
position: absolute; position: absolute;
bottom: 50%; /* Alignement par rapport à la base de l'élément parent */ bottom: 50%;
right: 0; right: 0;
transform: translate(50%, 50%); /* Ajustement de la position */ transform: translate(50%, 50%);
color: white; color: white;
font-size: 12px; font-size: 12px;
padding: 5px; padding: 5px;
@ -35,6 +35,6 @@
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
color: #333; color: #333;
position: relative; /* Permet l'alignement des labels à la base */ position: relative;
bottom: 0; /* Alignement à la base */ bottom: 0;
} }

View File

@ -87,12 +87,10 @@
} }
/* Style pour les boutons de pagination */
.pagination button { .pagination button {
margin: 0 5px; margin: 0 5px;
} }
/* Style pour les ItemBox */
.item-container .item-list .ant-col { .item-container .item-list .ant-col {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -9,7 +9,7 @@
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
margin-bottom: 20px; margin-bottom: 20px;
width: 250px; /* Largeur ajustable en fonction de votre mise en page */ width: 250px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} }

View File

@ -1,4 +1,3 @@
/* CSS pour la fenêtre modale */
.modal { .modal {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -1,22 +1,22 @@
.list-container { .list-container {
width: 100%; width: 100%;
max-height:500px; max-height:500px;
overflow-y: auto; /* Activer le défilement vertical si nécessaire */ overflow-y: auto;
padding: 20px; /* Espace intérieur de la liste */ padding: 20px;
} }
.room-details { .room-details {
width: 100%; /* Largeur de chaque boîte */ width: 100%;
background-color: #f9f9f9; background-color: #f9f9f9;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px; padding: 20px;
margin-bottom: 20px; /* Espace entre chaque boîte */ margin-bottom: 20px;
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.room-details:hover { .room-details:hover {
transform: translateY(-5px); /* Effet d'élévation au survol */ transform: translateY(-5px);
} }
.room-details .title { .room-details .title {

View File

@ -15,8 +15,8 @@ const Diagram = ({ data }) => {
<div className="diagram-container"> <div className="diagram-container">
<h2>Prix total par an</h2> <h2>Prix total par an</h2>
{diagramData.map(({ name, value }) => { {diagramData.map(({ name, value }) => {
// Appliquer l'<EFBFBD>chelle logarithmique <EFBFBD> la valeur // Appliquer l'<EFBFBD>chelle logarithmique sur la valeur
const scaledValue = Math.log(value + 1); // Ajouter 1 pour <EFBFBD>viter le logarithme de z<EFBFBD>ro const scaledValue = Math.log(value + 1);
const scaledMaxValue = Math.log(maxValue + 1); const scaledMaxValue = Math.log(maxValue + 1);
return ( return (
<div key={name} className="row-container"> <div key={name} className="row-container">

View File

@ -32,8 +32,7 @@ export const FormCreateItem = ({ onClose }) => {
values, values,
); );
console.log(response.data); console.log(response.data);
// Fermer la fenêtre modale après soumission réussie
onClose();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -75,8 +75,7 @@ export const FormUpdateItem = ({ itemId, onClose }) => {
values, values,
); );
console.log(response.data); console.log(response.data);
// Fermer la fenêtre modale après soumission réussie
onClose();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@ -87,7 +86,7 @@ export const FormUpdateItem = ({ itemId, onClose }) => {
<Form <Form
form={form} form={form}
onFinish={onFinish} onFinish={onFinish}
initialValues={item} // Initialise le formulaire avec les valeurs de l'élément initialValues={item}
> >
<h1>Update Item</h1> <h1>Update Item</h1>
<Form.Item label="Brand" name="brand"> <Form.Item label="Brand" name="brand">

View File

@ -0,0 +1,58 @@
import React, { useState, useEffect } from "react";
import {getRoom } from '../../api/room'
import { Form, Input, Button } from "antd";
import axios from "axios";
export const FormUpdateRoom = ({_id}) => {
const [form] = Form.useForm();
const [room, setRoom] = useState();
//useEffect(() => {
// const fetchData = async () => {
// const roomsResponse = await getRoom(_id);
// setRoom(roomsResponse);
// console.log(roomsResponse);
// };
// fetchData();
//}, [_id]);
//useEffect(() => {
// if (room) {
// form.setFieldsValue({
// ...room
// });
// }
//}, [room, form]);
const onFinish = async (values) => {
try {
const response = await axios.post(
`${import.meta.env.VITE_API_URL}/room`,
values,
);
console.log(response.data);
} catch (error) {
console.error(error);
}
};
return (
<Form form={form} onFinish={onFinish}>
<Form.Item
label="Room Name"
name="name"
rules={[{ required: true, message: "Please input the room name!" }]}
>
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
export default FormUpdateRoom;

View File

@ -6,16 +6,13 @@ import { Image } from "../parts/image";
import { Description } from "../parts/description"; import { Description } from "../parts/description";
import { Characteristic } from "../parts/characteristic"; import { Characteristic } from "../parts/characteristic";
// Composant Détails du Produit
export const ItemBox = ({ model, brand, purchaseDate, price, _id }) => { export const ItemBox = ({ model, brand, purchaseDate, price, _id }) => {
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
// Fonction pour ouvrir la fenêtre modale
const openModal = () => { const openModal = () => {
setIsModalOpen(true); setIsModalOpen(true);
}; };
// Fonction pour fermer la fenêtre modale
const closeModal = () => { const closeModal = () => {
setIsModalOpen(false); setIsModalOpen(false);
}; };

View File

@ -25,9 +25,7 @@ function getItem(key, label) {
const { SubMenu } = Menu; const { SubMenu } = Menu;
// Component
const Navbar = () => { const Navbar = () => {
//Hook calls
const [rooms, setRooms] = useState([]); const [rooms, setRooms] = useState([]);
useEffect(() => { useEffect(() => {
@ -56,7 +54,6 @@ const Navbar = () => {
</Menu.Item>, </Menu.Item>,
]; ];
// Rendu du composant Navbar
try { try {
return ( return (
<Menu <Menu

View File

@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
// Composant Caract<EFBFBD>ristique
export const Characteristic = ({ label, value }) => { export const Characteristic = ({ label, value }) => {
return ( return (
<div className="characteristic"> <div className="characteristic">

View File

@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
// Composant Description
export const Description = ({ title, children }) => { export const Description = ({ title, children }) => {
return ( return (
<div className="description"> <div className="description">

View File

@ -2,7 +2,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { searchAndResizeImage } from '../../api/image-request' import { searchAndResizeImage } from '../../api/image-request'
// Composant Image
export const Image = ({ src, alt, request, _id }) => { export const Image = ({ src, alt, request, _id }) => {
const [cacheUrl, setCacheUrl] = useState(null); const [cacheUrl, setCacheUrl] = useState(null);
@ -28,7 +27,7 @@ export const Image = ({ src, alt, request, _id }) => {
return <img src={src} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />; return <img src={src} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />;
} else if (cacheUrl) { } else if (cacheUrl) {
return <img src={cacheUrl} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />; return <img src={cacheUrl} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />;
} else { } else {//default image
return <img src={"https://media.discordapp.net/attachments/1164176196930637956/1167746303820840990/IMG_20231028_104620.jpg?ex=663ddefe&is=663c8d7e&hm=0985ce123fd1751f65388f7fefde5db6ce817e514e30f1d3c81eb28b15e78453&=&"} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />; return <img src={"https://cdn.discordapp.com/attachments/1164176196930637956/1213857681362784266/dinorundiscord.gif?ex=6642258f&is=6640d40f&hm=cf0ca54df3e002a15049618a6654b22c5d0c7943dc420e84936635725aceb90f&"} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />;
} }
} }

View File

@ -1,11 +1,10 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { FormCreateRoom } from "../../components/form/formCreateRoom"; import { FormCreateRoom } from "../../components/form/formCreateRoom";
//import { RoomUpdateForm } from './RoomUpdateForm'; import { FormUpdateRoom } from "../../components/form/formUpdateRoom";
export const RoomDetail = ({ export const RoomDetail = ({
selectedRoom, selectedRoom,
onCreateFormSubmit, onCreateFormSubmit,
onUpdateFormSubmit,
onBack, onBack,
}) => { }) => {
const [isUpdateFormVisible, setIsUpdateFormVisible] = useState(false); const [isUpdateFormVisible, setIsUpdateFormVisible] = useState(false);
@ -16,12 +15,6 @@ export const RoomDetail = ({
} }
}, [selectedRoom]); }, [selectedRoom]);
// Afficher le formulaire de mise <EFBFBD> jour lorsqu'une chambre est s<EFBFBD>lectionn<EFBFBD>e
const handleRoomClick = () => {
setIsUpdateFormVisible(true);
};
// Afficher le formulaire de cr<EFBFBD>ation lorsqu'on revient en arri<EFBFBD>re
const handleBackClick = () => { const handleBackClick = () => {
setIsUpdateFormVisible(false); setIsUpdateFormVisible(false);
onBack(null); onBack(null);
@ -31,9 +24,9 @@ export const RoomDetail = ({
<div className="room-detail"> <div className="room-detail">
{isUpdateFormVisible ? ( {isUpdateFormVisible ? (
<div> <div>
<h2>Modifier une chambre</h2> <h2>Modifier une chambre (non fonctionnel)</h2>
<FormCreateRoom onSubmit={onCreateFormSubmit} /> <FormUpdateRoom />
</div> </div>
) : ( ) : (
<div> <div>

View File

@ -3,16 +3,8 @@ import "../../assets/styles/room-list.css";
import { RoomBox } from "../../components/rooms/roomBox"; import { RoomBox } from "../../components/rooms/roomBox";
import { formatRoomStats } from "../../api/room"; import { formatRoomStats } from "../../api/room";
// Fonction pour diviser le tableau d'items en rang<EFBFBD>es
const chunkArray = (arr, size) => {
const chunkedArr = [];
for (let i = 0; i < arr.length; i += size) {
chunkedArr.push(arr.slice(i, i + size));
}
return chunkedArr;
};
// Composant d'affichage de la page
export const RoomList = ({ statsParam, onRoomClick }) => { export const RoomList = ({ statsParam, onRoomClick }) => {
const [rooms, setRooms] = useState([]); const [rooms, setRooms] = useState([]);
const [selectedRoom, setSelectedRoom] = useState("all"); const [selectedRoom, setSelectedRoom] = useState("all");

View File

@ -21,7 +21,7 @@ const RoomStats = ({statsParam}) => {
useEffect(() => { useEffect(() => {
if (stats) { if (stats) {
let table = Object.entries(stats.years) let table = Object.entries(stats.years)
.filter(([year, value]) => value !== 0) // Filtrer les entrées où Y n'est pas égal à 0 .filter(([year, value]) => value !== 0)
.map(([year, value]) => ({ name: year, value: value })); .map(([year, value]) => ({ name: year, value: value }));
setDiagramValues(table); setDiagramValues(table);
} }