diff --git a/.vs/2024-DEV-BUT3/v17/.wsuo b/.vs/2024-DEV-BUT3/v17/.wsuo index ac4359c..e30e1c1 100644 Binary files a/.vs/2024-DEV-BUT3/v17/.wsuo and b/.vs/2024-DEV-BUT3/v17/.wsuo differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index c231ed8..dc91451 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/src/api/authentication.js b/src/api/authentication.js index b183005..2238843 100644 --- a/src/api/authentication.js +++ b/src/api/authentication.js @@ -6,7 +6,7 @@ export const isLoggedIn = async () => { return response.data; } catch (error) { - return error.response.data; + return error.responseq; } }; @@ -15,7 +15,7 @@ export const login = async (username, password) => { const response = await axios.post("/authenticate", { username, password }); return response.data; } catch (error) { - return error.response.data; + return error.response; } }; @@ -25,6 +25,6 @@ export const logout = async () => { return response.data; } catch (error) { - return error.response.data; + return error.response; } }; diff --git a/src/api/room.js b/src/api/room.js index 09ceb90..41e3b3b 100644 --- a/src/api/room.js +++ b/src/api/room.js @@ -5,7 +5,7 @@ import axios from "axios"; export const getRoom = async (_id) => { try { - const response = await axios.get("/room", {_id}); + const response = await axios.get("/room/"+{_id}); console.log(response.data) return response.data; } catch (error) { @@ -16,7 +16,7 @@ export const getRoom = async (_id) => { export const getRooms = async () => { try { - const response = await axios.get("/room", {}); + const response = await axios.get("/room"); console.log(response.data) return response.data; } catch (error) { @@ -24,3 +24,60 @@ export const getRooms = async () => { return error.response.data; } }; + +export const getRoomStats = async () => { + try { + const response = await axios.get("/room/stats"); + console.log(response.data) + return response.data; + } catch (error) { + console.log("ERROR", error.response.data) + return error.response.data; + } +}; + +export const formatRoomStats = (roomStats) => { + const global = formatRoomStatsGlobal(roomStats.global) + const rooms = Object.values(roomStats.rooms).map(formatRoom); + const years = roomStats.years; + return { + global, + rooms, + years + } +} + +function formatRoomStatsGlobal(global){ + const { rooms_count, items_count, total_price, average_price} = global; + + const most_item_room = { + name: global.most_item_room.name, + count: global.most_item_room.count + }; + + const most_expensive_room = { + name: global.most_expensive_room.name, + price: global.most_expensive_room.price + }; + + return { + rooms_count, + items_count, + total_price, + average_price, + most_item_room, + most_expensive_room + }; +} + + +function formatRoom(room) { + const { _id, name, items_count, room_price } = room; + + return { + _id, + name, + items_count, + room_price + }; +} \ No newline at end of file diff --git a/src/assets/styles/diagram.css b/src/assets/styles/diagram.css new file mode 100644 index 0000000..094e6f9 --- /dev/null +++ b/src/assets/styles/diagram.css @@ -0,0 +1,40 @@ +.diagram-container { + width: 100%; /* Utilise toute la largeur horizontale disponible */ + margin-bottom: 20px; +} + +.row-container { + display: flex; + align-items: flex-end; /* Alignement des éléments à la base */ + margin-bottom: 10px; +} + +.row { + background-color: #007bff; + height: 20px; + position: relative; + border-radius: 5px; + transition: width 0.5s ease; + +} + +.value { + position: absolute; + bottom: 50%; /* Alignement par rapport à la base de l'élément parent */ + right: 0; + transform: translate(50%, 50%); /* Ajustement de la position */ + color: white; + font-size: 12px; + padding: 5px; + border-radius: 3px; + background-color: rgba(0, 0, 0, 0.8); +} + +.label { + width: 55px; + text-align: center; + font-size: 14px; + color: #333; + position: relative; /* Permet l'alignement des labels à la base */ + bottom: 0; /* Alignement à la base */ +} \ No newline at end of file diff --git a/src/assets/styles/room-page.css b/src/assets/styles/room-page.css new file mode 100644 index 0000000..0443219 --- /dev/null +++ b/src/assets/styles/room-page.css @@ -0,0 +1,18 @@ +.manContainer { + display: flex; + flex-direction: column; + height: 100%; +} + +.topContainer { + display: flex; + flex-grow: 1; +} + +.statsContainer, .listContainer { + flex-grow: 1; +} + +.detailContainer { + height: 50%; +} diff --git a/src/assets/styles/room-stats.css b/src/assets/styles/room-stats.css new file mode 100644 index 0000000..31cac00 --- /dev/null +++ b/src/assets/styles/room-stats.css @@ -0,0 +1,47 @@ +.room-stats-container { + display: flex; + flex-direction: column; + margin: 20px; +} + +.text-stats { + background-color: #f9f9f9; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; +} + +.stats-info { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; +} + +.stat { + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; +} + + .stat p { + margin: 0; + font-weight: bold; + } + + .stat ul { + list-style: none; + padding: 0; + margin: 5px 0; + } + + .stat ul li { + margin: 5px 0; + } + +.diagram { + background-color: #fff; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} diff --git a/src/components/diagram.jsx b/src/components/diagram.jsx new file mode 100644 index 0000000..24dcb57 --- /dev/null +++ b/src/components/diagram.jsx @@ -0,0 +1,30 @@ +import React, { useState, useEffect } from "react"; +import { Col, Row } from "antd"; +import '../assets/styles/diagram.css' + + +const Diagram = ({ data }) => { + const [diagramData, setDiagramData] = useState(data); + + useEffect(() => { + setDiagramData(data); + }, [data]); + + const maxValue = Math.max(...diagramData.map(item => item.value)); + + return ( +
+

Prix total par an

+ {diagramData.map(({ name, value }) => ( +
+
{name}
+
+
{value}
+
+
+ ))} +
+ ); +}; + +export default Diagram; \ No newline at end of file diff --git a/src/components/form/formUpdateItem.jsx b/src/components/form/formUpdateItem.jsx index b2037e6..ca54b2f 100644 --- a/src/components/form/formUpdateItem.jsx +++ b/src/components/form/formUpdateItem.jsx @@ -8,34 +8,24 @@ import moment from 'moment'; const { Option } = Select; -function formatItem(itemObj) {//_id, brand, model, room, price, purchaseDate, description, categories, createdAt, updatedAt, __v, link) { - let _id = itemObj._id - let brand = itemObj.brand - let model = itemObj.model - let room = itemObj.room - let price = itemObj.price - let purchaseDate = new Date(itemObj.purchaseDate) - let description = itemObj.description - let categories = itemObj.categories - let createdAt = itemObj.createdAt - let updatedAt = itemObj.updatedAt - let __v = itemObj.__v - let link = itemObj.link - let item = { +function formatItem(itemObj) { + const {_id,brand,model,room,price,purchaseDate,description,categories,createdAt,updatedAt,__v,link} = itemObj + const formattedPurchaseDate = typeof purchaseDate === 'string' ? new Date(purchaseDate) : purchaseDate; + + return { _id, brand, model, room, price, - purchaseDate, + purchaseDate: formattedPurchaseDate, description, categories, createdAt, updatedAt, __v, link - } - return item; + }; } export const FormUpdateItem = ({ itemId }) => { diff --git a/src/components/item/ItemBox.jsx b/src/components/item/ItemBox.jsx index 1ea6ad2..3a6ad85 100644 --- a/src/components/item/ItemBox.jsx +++ b/src/components/item/ItemBox.jsx @@ -1,40 +1,10 @@ import React, { useEffect, useState } from 'react'; -import { searchAndResizeImage } from '../../api/image-request' import '../../assets/styles/modal.css' import '../../assets/styles/itembox.css' import FormUpdateItem from '../form/formUpdateItem'; -// Composant Image -const Image = ({ src, alt, request, _id }) => { - const [cacheUrl, setCacheUrl] = useState(null); - useEffect(() => { - const fetchData = async () => { - let cachedUrl = localStorage.getItem(_id); - if (!cachedUrl) { - try { - cachedUrl = await searchAndResizeImage(request); - localStorage.setItem(_id, cachedUrl); - console.log("Mise en cache de l'image avec l'ID : " + _id); - } catch (error) { - console.error("Erreur lors de la récupération de l'image : ", error); - } - } - setCacheUrl(cachedUrl); - }; - - fetchData(); - }, [request, _id]); - - if (src) { - return {alt}; - } else if (cacheUrl) { - return {alt}; - } else { - return {alt}; - } -}; // Composant Description const Description = ({ title, children }) => { diff --git a/src/components/nav/Navbar.jsx b/src/components/nav/Navbar.jsx index ea3df3f..30fbb66 100644 --- a/src/components/nav/Navbar.jsx +++ b/src/components/nav/Navbar.jsx @@ -60,7 +60,10 @@ const Navbar = () => { , {roomItems} - + , + + Voir les chambres + , ]; diff --git a/src/components/parts/image.jsx b/src/components/parts/image.jsx new file mode 100644 index 0000000..ac6c3fc --- /dev/null +++ b/src/components/parts/image.jsx @@ -0,0 +1,34 @@ + +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); + + useEffect(() => { + const fetchData = async () => { + let cachedUrl = localStorage.getItem(_id); + if (!cachedUrl) { + try { + cachedUrl = await searchAndResizeImage(request); + localStorage.setItem(_id, cachedUrl); + console.log("Mise en cache de l'image avec l'ID : " + _id); + } catch (error) { + console.error("Erreur lors de la récupération de l'image : ", error); + } + } + setCacheUrl(cachedUrl); + }; + + fetchData(); + }, [request, _id]); + + if (src) { + return {alt}; + } else if (cacheUrl) { + return {alt}; + } else { + return {alt}; + } +} \ No newline at end of file diff --git a/src/components/rooms/room-list.jsx b/src/components/rooms/room-list.jsx new file mode 100644 index 0000000..e2a1ed6 --- /dev/null +++ b/src/components/rooms/room-list.jsx @@ -0,0 +1,46 @@ +import React, { useState, useEffect } from "react"; +import axios from 'axios'; // Assurez-vous que le chemin d'importation soit correct +import { ItemBox } from './ItemBox'; +import { Space, DatePicker, Row, Col, Select, Input, InputNumber } from 'antd'; +import '../../assets/styles/item-page.css' + + + +const { RangePicker } = DatePicker; +const { Option } = Select; + +const itemsPerPage = 8; // Nombre total d'items par page +const itemsPerRow = 4; // Nombre d'items par rangée + +// Fonction pour diviser le tableau d'items en rangé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 = (roomsParam) => { + const [rooms, setRooms] = useState([]); + const [selectedRoom, setSelectedRoom] = useState('all'); + + useEffect(() => { + setRooms(roomsParam) + }, [roomsParam]); + + const handleRoomChange = (value) => { + setSelectedRoom(value); + }; + + return ( +
+
+ {rooms.forEach((room) => { + +
+ ); +}; \ No newline at end of file diff --git a/src/components/rooms/room-stats.jsx b/src/components/rooms/room-stats.jsx new file mode 100644 index 0000000..8e6742c --- /dev/null +++ b/src/components/rooms/room-stats.jsx @@ -0,0 +1,69 @@ +import React, { useEffect, useState } from 'react'; + +import { formatRoomStats } from '../../api/room' +import Diagram from '../diagram'; +import '../../assets/styles/room-stats.css' + + + +const RoomStats = ({statsParam}) => { + const [stats, setStats] = useState(); + const [diagramValues, setDiagramValues] = useState([]) + + useEffect(() => { + if (statsParam.global) { + console.log(statsParam) + let formatedStats = formatRoomStats(statsParam) + setStats(formatedStats) + } + }, [statsParam]); + + useEffect(() => { + if (stats) { + let table = Object.entries(stats.years) + .filter(([year, value]) => value !== 0) // Filtrer les entrées où Y n'est pas égal à 0 + .map(([year, value]) => ({ name: year, value: value })); + setDiagramValues(table); + } + }, [stats]) + + return ( +
+
+

Statistiques des chambres

+
+
+

Total des chambres :

+ {stats?.global?.rooms_count} +
+
+

Total des articles :

+ {stats?.global?.items_count} +
+
+

Prix total :

+ {stats?.global?.total_price} +
+
+

La chambre avec le plus d'articles :

+
    +
  • {stats?.global.most_item_room?.name}
  • +
  • Nombre d'articles : {stats?.global?.most_item_room?.count}
  • +
+
+
+

La chambre la plus chère :

+
    +
  • {stats?.global?.most_expensive_room?.name}
  • +
  • Prix total : {stats?.global?.most_expensive_room?.price}
  • +
+
+
+
+
+ +
+
+ ); +}; +export default RoomStats; \ No newline at end of file diff --git a/src/components/rooms/roomBox.jsx b/src/components/rooms/roomBox.jsx new file mode 100644 index 0000000..f5d0626 --- /dev/null +++ b/src/components/rooms/roomBox.jsx @@ -0,0 +1,23 @@ +import React, { useEffect, useState } from 'react'; +import '../../assets/styles/itembox.css' +import { } from '../item/ItemBox' + + +// Composant Détails du Produit +export const RoomBox = ({ name, itemCount, roomPrice, _id }) => { + const [roomData, setRoomData] = useState(); + + useEffect(() => { + setRoomData({name, itemCount, roomPrice, _id}) + }, [name, itemCount, roomPrice, _id]); + + + return ( +
+ + + + +
+ ); +}; \ No newline at end of file diff --git a/src/pages/rooms-stats.jsx b/src/pages/rooms-stats.jsx deleted file mode 100644 index 8087dec..0000000 --- a/src/pages/rooms-stats.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { useEffect } from 'react'; - -import { useAuth } from "../hooks"; -import { ItemPage } from "../components/item/ItemPage"; -import { usePageTitle } from '../hooks/page-title-context'; - -export const RoomsStats = () => { - const { user } = useAuth(); - const { setPageTitle } = usePageTitle(); - - - // Mettre à jour le titre de la page dans le contexte - useEffect(() => { - setPageTitle("Toutes les rooms :"); - }, [setPageTitle]); - - useEffect(() => { - setPageTitle("Toutes les rooms :"); - }, [setPageTitle]); - return ( -
-
-
- ); -}; diff --git a/src/pages/rooms.jsx b/src/pages/rooms.jsx new file mode 100644 index 0000000..7e472c9 --- /dev/null +++ b/src/pages/rooms.jsx @@ -0,0 +1,46 @@ +import React, { useEffect, useState } from 'react'; +import '../assets/styles/room-page.css' + +import { useAuth } from "../hooks"; +import { formatRoomStats, getRooms, getRoomStats } from "../api/room"; +import { usePageTitle } from '../hooks/page-title-context'; +import RoomStats from '../components/rooms/room-stats'; + +export const Rooms = () => { + const { user } = useAuth(); + const { setPageTitle } = usePageTitle(); + const [stats, setStats] = useState({}); + + + // Mettre à jour le titre de la page dans le contexte + useEffect(() => { + setPageTitle("Toutes les rooms :"); + }, [setPageTitle]); + + useEffect(() => { + const fetchData = async () => { + const roomsStatsResponse = await getRoomStats(); + setStats(roomsStatsResponse); + }; + + fetchData(); + }, []); + + + return ( +
+
+
+ + +
+
+
+
+
+ + +
+
+ ); +}; diff --git a/src/router.jsx b/src/router.jsx index 1147a9b..368f59b 100644 --- a/src/router.jsx +++ b/src/router.jsx @@ -3,6 +3,7 @@ import { Route, Routes } from "react-router-dom"; import { Home, Login, Register } from "./pages"; import { Items } from "./pages/items"; +import { Rooms } from "./pages/rooms"; export const Router = () => ( @@ -11,5 +12,6 @@ export const Router = () => ( } /> } /> } /> + } /> );