commit final
This commit is contained in:
Binary file not shown.
BIN
.vs/slnx.sqlite
BIN
.vs/slnx.sqlite
Binary file not shown.
21
README.md
21
README.md
@@ -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.
|
||||
An over-sending of requests could get us blocked for the day by google.
|
||||
|
||||
## Project summary (FR):
|
||||
|
||||
Nous n'avons pas r<>ussi <20> mettre en <20>uvre toutes les fonctionnalit<69>s de l'interface utilisateur <20> temps. Le d<>veloppement n'a pas non plus <20>t<EFBFBD> rigoureusement test-driven.
|
||||
|
||||
r<EFBFBD>sum<EFBFBD> des fonctionnalit<69>s :
|
||||
|
||||
En se connectant, l'utilisateur peut charger la liste des articleset les filtrer par pi<70>ce, date, nom, etc. Il est possible de cr<63>er et de mettre <20> jour des articles.
|
||||
(Il n'y a pas de confirmation de r<>ussite ou d'<27>chec de cr<63>ation/update. La page doit <20>tre actualis<69>e pour voir les r<>sultats.)
|
||||
Une page permet de consulter les statistiques des pi<70>ces et la liste des pi<70>ces. Il est possible d'y cr<63>er de nouvelles. La mise <20> jour des pi<70>cesn'est pas encore impl<70>ment<6E>e.
|
||||
|
||||
Les icones AntDesign provoquent syst<73>matiquement une exception dans la console. Nous n'avons pas <20>t<EFBFBD> en mesure de corriger ou <20>viter cette erreur. Les icones s'affichent bien, donc <20>a ne posera pas de probl<62>me <20> part la pollution de la console.
|
||||
|
||||
Nous avons voulu personnaliser un peu notre projet pour le diff<66>rencier des autres, nous avons donc mis en place un syst<73>me simple de recherche automatique d'images li<6C>es aux objets enregistr<74>s, et une requ<71>te <20> l'API Inspirobot <20> 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 <20>tre familiaris<69>s avec js et React, avec les tests en g<>n<EFBFBD>ral, et d'avoir pris du plaisir <20> 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
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
.diagram-container {
|
||||
width: 100%; /* Utilise toute la largeur horizontale disponible */
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.row-container {
|
||||
display: flex;
|
||||
align-items: flex-end; /* Alignement des <20>l<EFBFBD>ments <20> la base */
|
||||
align-items: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
.value {
|
||||
position: absolute;
|
||||
bottom: 50%; /* Alignement par rapport <20> la base de l'<27>l<EFBFBD>ment parent */
|
||||
bottom: 50%;
|
||||
right: 0;
|
||||
transform: translate(50%, 50%); /* Ajustement de la position */
|
||||
transform: translate(50%, 50%);
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
@@ -35,6 +35,6 @@
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
position: relative; /* Permet l'alignement des labels <20> la base */
|
||||
bottom: 0; /* Alignement <20> la base */
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
@@ -87,12 +87,10 @@
|
||||
}
|
||||
|
||||
|
||||
/* Style pour les boutons de pagination */
|
||||
.pagination button {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
/* Style pour les ItemBox */
|
||||
.item-container .item-list .ant-col {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@@ -9,7 +9,7 @@
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* CSS pour la fen<65>tre modale */
|
||||
.modal {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@@ -1,22 +1,22 @@
|
||||
.list-container {
|
||||
width: 100%;
|
||||
max-height:500px;
|
||||
overflow-y: auto; /* Activer le d<>filement vertical si n<>cessaire */
|
||||
padding: 20px; /* Espace int<6E>rieur de la liste */
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.room-details {
|
||||
width: 100%; /* Largeur de chaque bo<62>te */
|
||||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px; /* Espace entre chaque bo<62>te */
|
||||
margin-bottom: 20px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.room-details:hover {
|
||||
transform: translateY(-5px); /* Effet d'<27>l<EFBFBD>vation au survol */
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.room-details .title {
|
||||
|
@@ -15,8 +15,8 @@ const Diagram = ({ data }) => {
|
||||
<div className="diagram-container">
|
||||
<h2>Prix total par an</h2>
|
||||
{diagramData.map(({ name, value }) => {
|
||||
// Appliquer l'<27>chelle logarithmique <EFBFBD> la valeur
|
||||
const scaledValue = Math.log(value + 1); // Ajouter 1 pour <20>viter le logarithme de z<>ro
|
||||
// Appliquer l'<27>chelle logarithmique sur la valeur
|
||||
const scaledValue = Math.log(value + 1);
|
||||
const scaledMaxValue = Math.log(maxValue + 1);
|
||||
return (
|
||||
<div key={name} className="row-container">
|
||||
|
@@ -32,8 +32,7 @@ export const FormCreateItem = ({ onClose }) => {
|
||||
values,
|
||||
);
|
||||
console.log(response.data);
|
||||
// Fermer la fenêtre modale après soumission réussie
|
||||
onClose();
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
@@ -75,8 +75,7 @@ export const FormUpdateItem = ({ itemId, onClose }) => {
|
||||
values,
|
||||
);
|
||||
console.log(response.data);
|
||||
// Fermer la fenêtre modale après soumission réussie
|
||||
onClose();
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -87,7 +86,7 @@ export const FormUpdateItem = ({ itemId, onClose }) => {
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
initialValues={item} // Initialise le formulaire avec les valeurs de l'élément
|
||||
initialValues={item}
|
||||
>
|
||||
<h1>Update Item</h1>
|
||||
<Form.Item label="Brand" name="brand">
|
||||
|
58
src/components/form/formUpdateRoom.jsx
Normal file
58
src/components/form/formUpdateRoom.jsx
Normal 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;
|
@@ -6,16 +6,13 @@ import { Image } from "../parts/image";
|
||||
import { Description } from "../parts/description";
|
||||
import { Characteristic } from "../parts/characteristic";
|
||||
|
||||
// Composant Détails du Produit
|
||||
export const ItemBox = ({ model, brand, purchaseDate, price, _id }) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
// Fonction pour ouvrir la fenêtre modale
|
||||
const openModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
// Fonction pour fermer la fenêtre modale
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
@@ -25,9 +25,7 @@ function getItem(key, label) {
|
||||
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
// Component
|
||||
const Navbar = () => {
|
||||
//Hook calls
|
||||
const [rooms, setRooms] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -56,7 +54,6 @@ const Navbar = () => {
|
||||
</Menu.Item>,
|
||||
];
|
||||
|
||||
// Rendu du composant Navbar
|
||||
try {
|
||||
return (
|
||||
<Menu
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
// Composant Caract<63>ristique
|
||||
export const Characteristic = ({ label, value }) => {
|
||||
return (
|
||||
<div className="characteristic">
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
// Composant Description
|
||||
export const Description = ({ title, children }) => {
|
||||
return (
|
||||
<div className="description">
|
||||
|
@@ -2,7 +2,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { searchAndResizeImage } from '../../api/image-request'
|
||||
|
||||
// Composant Image
|
||||
export const Image = ({ src, alt, request, _id }) => {
|
||||
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' />;
|
||||
} else if (cacheUrl) {
|
||||
return <img src={cacheUrl} alt={alt} style={{ display: 'block', margin: 'auto' }} width='150px' height='150px' />;
|
||||
} else {
|
||||
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' />;
|
||||
} else {//default image
|
||||
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' />;
|
||||
}
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FormCreateRoom } from "../../components/form/formCreateRoom";
|
||||
//import { RoomUpdateForm } from './RoomUpdateForm';
|
||||
import { FormUpdateRoom } from "../../components/form/formUpdateRoom";
|
||||
|
||||
export const RoomDetail = ({
|
||||
selectedRoom,
|
||||
onCreateFormSubmit,
|
||||
onUpdateFormSubmit,
|
||||
onBack,
|
||||
}) => {
|
||||
const [isUpdateFormVisible, setIsUpdateFormVisible] = useState(false);
|
||||
@@ -16,12 +15,6 @@ export const RoomDetail = ({
|
||||
}
|
||||
}, [selectedRoom]);
|
||||
|
||||
// Afficher le formulaire de mise <20> jour lorsqu'une chambre est s<>lectionn<6E>e
|
||||
const handleRoomClick = () => {
|
||||
setIsUpdateFormVisible(true);
|
||||
};
|
||||
|
||||
// Afficher le formulaire de cr<63>ation lorsqu'on revient en arri<72>re
|
||||
const handleBackClick = () => {
|
||||
setIsUpdateFormVisible(false);
|
||||
onBack(null);
|
||||
@@ -31,9 +24,9 @@ export const RoomDetail = ({
|
||||
<div className="room-detail">
|
||||
{isUpdateFormVisible ? (
|
||||
<div>
|
||||
<h2>Modifier une chambre</h2>
|
||||
<h2>Modifier une chambre (non fonctionnel)</h2>
|
||||
|
||||
<FormCreateRoom onSubmit={onCreateFormSubmit} />
|
||||
<FormUpdateRoom />
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
|
@@ -3,16 +3,8 @@ import "../../assets/styles/room-list.css";
|
||||
import { RoomBox } from "../../components/rooms/roomBox";
|
||||
import { formatRoomStats } from "../../api/room";
|
||||
|
||||
// Fonction pour diviser le tableau d'items en rang<6E>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 }) => {
|
||||
const [rooms, setRooms] = useState([]);
|
||||
const [selectedRoom, setSelectedRoom] = useState("all");
|
||||
|
@@ -21,7 +21,7 @@ const RoomStats = ({statsParam}) => {
|
||||
useEffect(() => {
|
||||
if (stats) {
|
||||
let table = Object.entries(stats.years)
|
||||
.filter(([year, value]) => value !== 0) // Filtrer les entr<74>es o<> Y n'est pas <20>gal <20> 0
|
||||
.filter(([year, value]) => value !== 0)
|
||||
.map(([year, value]) => ({ name: year, value: value }));
|
||||
setDiagramValues(table);
|
||||
}
|
||||
|
Reference in New Issue
Block a user