diff --git a/package-lock.json b/package-lock.json index da7c763..d04d5e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,19 +8,24 @@ "name": "portail-gise", "version": "0.0.0", "dependencies": { + "lucide-react": "^1.3.0", "react": "^19.2.6", "react-dom": "^19.2.6", "react-router-dom": "^7.16.0" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@tailwindcss/vite": "^4.3.1", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", "eslint": "^10.3.0", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "postcss": "^8.5.15", + "tailwindcss": "^4.3.1", "vite": "^8.0.12" } }, @@ -835,6 +840,278 @@ "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/node": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz", + "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.1" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz", + "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-x64": "4.3.1", + "@tailwindcss/oxide-freebsd-x64": "4.3.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-x64-musl": "4.3.1", + "@tailwindcss/oxide-wasm32-wasi": "4.3.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz", + "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz", + "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz", + "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz", + "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz", + "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz", + "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz", + "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz", + "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz", + "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz", + "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz", + "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz", + "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz", + "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.1", + "@tailwindcss/oxide": "4.3.1", + "tailwindcss": "4.3.1" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -953,6 +1230,43 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1128,6 +1442,20 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1423,6 +1751,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1474,6 +1816,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1541,6 +1890,16 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1906,6 +2265,25 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.3.0.tgz", + "integrity": "sha512-aKUU4jKpXe26Y3xmF3DGg+NXHteRCVT96r/+Jp7wziqvmFr0/x8ReRsES0wHgTvW7OLxiHvFLN+YeyDnK6h0dQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -2084,6 +2462,13 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2252,6 +2637,27 @@ "node": ">=0.10.0" } }, + "node_modules/tailwindcss": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", + "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", diff --git a/package.json b/package.json index 89b8b15..3a5b1da 100644 --- a/package.json +++ b/package.json @@ -10,19 +10,24 @@ "preview": "vite preview" }, "dependencies": { + "lucide-react": "^1.3.0", "react": "^19.2.6", "react-dom": "^19.2.6", "react-router-dom": "^7.16.0" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@tailwindcss/vite": "^4.3.1", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", "eslint": "^10.3.0", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "postcss": "^8.5.15", + "tailwindcss": "^4.3.1", "vite": "^8.0.12" } } diff --git a/src/App.jsx b/src/App.jsx index b15ffd9..e891607 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -16,6 +16,7 @@ import Home from './pages/public/Home'; // Import des Pages Privées (Espace Client) import Dashboard from './pages/app/Dashboard'; +import Store from './pages/app/Store'; //import Services from './pages/app/Services'; export default function App() { @@ -42,6 +43,7 @@ export default function App() { {/* Si autorisé, on charge l'interface avec la Sidebar */} }> } /> + } /> {/* } /> */} diff --git a/src/index.css b/src/index.css index 07001cd..c4b6f2c 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,5 @@ +@import "tailwindcss"; + body { margin: 0; display:block; diff --git a/src/pages/app/Dashboard.jsx b/src/pages/app/Dashboard.jsx index 6aa2e43..e468094 100644 --- a/src/pages/app/Dashboard.jsx +++ b/src/pages/app/Dashboard.jsx @@ -1,163 +1,128 @@ -// src/pages/app/Dashboard.jsx -import { Link } from 'react-router-dom'; +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { getClientOrders } from '../../services/api'; +import { Server, Database, Cloud, Globe, Plus, AlertCircle, Loader } from 'lucide-react'; export default function Dashboard() { - // --- DESIGN SYSTEM DU DASHBOARD --- - const gridStyle = { - display: 'grid', - gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', - gap: '25px', - marginTop: '30px' - }; + const navigate = useNavigate(); + const [orders, setOrders] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); - const cardStyle = { - backgroundColor: '#121212', - border: '1px solid #222', - borderTop: '4px solid #00E5FF', - padding: '25px', - position: 'relative', - overflow: 'hidden' - }; + // Chargement des données à l'ouverture du Sas + useEffect(() => { + const fetchInventory = async () => { + try { + // Appel à FOSSBilling via notre api.js + const data = await getClientOrders(); + + // FOSSBilling renvoie souvent la liste dans data.list + setOrders(data.list || []); + } catch (err) { + setError(err.message || "Impossible de récupérer la télémétrie des services."); + } finally { + setIsLoading(false); + } + }; - const statusBadgeStyle = { - position: 'absolute', - top: '20px', - right: '20px', - backgroundColor: 'rgba(0, 229, 255, 0.1)', - color: '#00E5FF', - padding: '4px 8px', - fontSize: '0.7rem', - fontWeight: 'bold', - letterSpacing: '1px' - }; + fetchInventory(); + }, []); - const btnStyle = { - display: 'inline-block', - width: '100%', - padding: '10px', - marginTop: '20px', - backgroundColor: 'transparent', - color: '#00E5FF', - border: '1px solid #00E5FF', - textAlign: 'center', - textDecoration: 'none', - textTransform: 'uppercase', - fontSize: '0.8rem', - cursor: 'pointer', - transition: 'all 0.2s', - boxSizing: 'border-box' - }; + // Fonction Radar : Détecte le type de service selon son nom pour afficher la bonne icône + const getServiceIcon = (title) => { + const t = title.toLowerCase(); + if (t.includes('vps') || t.includes('serveur')) return ; + if (t.includes('cloud') || t.includes('nextcloud')) return ; + if (t.includes('db') || t.includes('base') || t.includes('sql')) return ; + return ; // Par défaut : Web / Hestia + }; - return ( -
- - {/* --- EN-TÊTE DU TABLEAU DE BORD --- */} -
-
-

- Console Opérationnelle -

- ID Réseau: usr_8472_alpha + // Fonction d'état : Formate le statut du service + const getStatusBadge = (status) => { + switch (status) { + case 'active': return ACTIF; + case 'pending_setup': return EN PRÉPARATION; + case 'suspended': return SUSPENDU; + default: return {status.toUpperCase()}; + } + }; + + return ( +
+ +
+

+ TERMINAL NEXUS +

+

Aperçu de vos accréditations réseau et infrastructures.

+
+ + {/* GESTION DES ERREURS & CHARGEMENT */} + {isLoading && ( +
+ + Synchronisation avec l'orchestrateur en cours... +
+ )} + + {error && ( +
+ + {error} +
+ )} + + {/* GRILLE DES SERVICES */} + {!isLoading && !error && ( +
+ + {/* Boucle sur les services FOSSBilling */} + {orders.map((order) => ( +
navigate(`/services/${order.id}`)} // Redirection future vers les détails + > +
+
+
+ {getServiceIcon(order.title)} +
+ {getStatusBadge(order.status)} +
+

+ {order.title} +

+

+ Facturation : {order.period} +

+
+ +
+ ID Réseau: #{order.id} + Gérer > +
+
+ ))} + + {/* LA CARTE : OBTENIR UN NOUVEAU PRODUIT */} +
navigate('/store')} // Remplace /store par l'URL de ton catalogue + className="bg-transparent border-2 border-dashed border-gray-700 hover:border-cyan-400 p-6 rounded-xl transition-colors cursor-pointer flex flex-col items-center justify-center text-center group min-h-[200px]" + > +
+ +
+

+ Demander une accréditation +

+

+ Déployer un nouveau serveur Web, VPS ou Cloud. +

+
+ +
+ )}
-
-
● SYSTÈME NOMINAL
-
Dernière connexion : Aujourd'hui
-
-
- - {/* --- GRILLE DES MODULES DE L'INFRASTRUCTURE --- */} -
- - {/* MODULE 1 : BARE-METAL (HESTIACP) */} -
-
ACTIF
-

// Serveur Web

-

- Nœud HestiaCP (panel.gise.be). Supervision des partitions web, DNS et bases de données. -

-
-
- Domaines Actifs: 1 / 5 -
-
- Bases SQL: 2 / 10 -
-
- { e.target.style.backgroundColor = '#00E5FF'; e.target.style.color = '#000'; }} - onMouseOut={(e) => { e.target.style.backgroundColor = 'transparent'; e.target.style.color = '#00E5FF'; }}> - Accéder au Panel - -
- - {/* MODULE 2 : CLOUD (NEXTCLOUD) */} -
-
ACTIF
-

// Sanctuaire Cloud

-

- Stockage chiffré Nextcloud (cloud.gise.be). Synchronisation des terminaux et partage sécurisé. -

-
-
- Espace Alloué: 50 Go -
-
- Espace Utilisé: 1.2 Go -
-
- { e.target.style.backgroundColor = '#00E5FF'; e.target.style.color = '#000'; }} - onMouseOut={(e) => { e.target.style.backgroundColor = 'transparent'; e.target.style.color = '#00E5FF'; }}> - Ouvrir le Cloud - -
- - {/* MODULE 3 : FACTURATION (FOSSBILLING) */} -
-
EN ATTENTE
-

// Facturation

-

- Centre de gestion FOSSBilling. Historique des paiements et renouvellement des baux réseau. -

-
-
- Solde Compte: 0.00 € -
-
- Prochaine Échéance: 12/07/2026 -
-
- { e.target.style.backgroundColor = '#FFB800'; e.target.style.color = '#000'; }} - onMouseOut={(e) => { e.target.style.backgroundColor = 'transparent'; e.target.style.color = '#FFB800'; }}> - Régler la facture - -
- -
- - {/* --- TERMINAL DE LOGS (ESTHÉTIQUE SYSADMIN) --- */} -
-

>_ SYSTEM_LOGS

-
-
[ OK ] Connexion chiffrée établie via TLS v1.3
-
[ OK ] Jetons d'authentification synchronisés avec FOSSBilling API
-
[ INFO ] Vérification des quotas de stockage Nextcloud... Terminée.
-
[ INFO ] 0 ticket(s) de support en attente.
-
> En attente d'instructions..._
-
-
- - {/* Animation CSS pour le curseur clignotant du terminal */} - - -
- ); + ); } \ No newline at end of file diff --git a/src/pages/app/Store.jsx b/src/pages/app/Store.jsx new file mode 100644 index 0000000..0c72071 --- /dev/null +++ b/src/pages/app/Store.jsx @@ -0,0 +1,135 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { getProductList } from '../../services/api'; +import { Server, Database, Cloud, Globe, ShoppingCart, Loader, AlertCircle, Check } from 'lucide-react'; + +export default function Store() { + const navigate = useNavigate(); + const [products, setProducts] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + // Récupération du catalogue + useEffect(() => { + const fetchCatalog = async () => { + try { + const data = await getProductList(); + // FOSSBilling renvoie les produits dans data.list + setProducts(data.list || []); + } catch (err) { + setError("Impossible de contacter le serveur d'approvisionnement."); + } finally { + setIsLoading(false); + } + }; + + fetchCatalog(); + }, []); + + // Radar visuel : Associe une icône selon le nom de la catégorie ou du produit + const getCategoryIcon = (text) => { + const t = text.toLowerCase(); + if (t.includes('vps') || t.includes('serveur')) return ; + if (t.includes('cloud') || t.includes('nextcloud')) return ; + if (t.includes('db') || t.includes('base')) return ; + return ; + }; + + // Extracteur de prix : L'API FOSSBilling a une structure de prix assez profonde + const extractMonthlyPrice = (pricing) => { + if (pricing?.type === 'free') return 'Gratuit'; + if (pricing?.type === 'once') return `${pricing.once.price} € (Une fois)`; + + // CORRECTION ICI : Utilisation des crochets pour la clé numérique '1' + if (pricing?.type === 'recurrent' && pricing.recurrent['1']) { + return `${pricing.recurrent['1'].price} € / mois`; + } + + return 'Prix sur demande'; +}; + + return ( +
+ +
+

+ CATALOGUE NEXUS +

+

+ Déployez de nouvelles instances et étendez votre infrastructure en quelques secondes. Provisionnement automatisé 24/7. +

+
+ + {/* GESTION DES ERREURS & CHARGEMENT */} + {isLoading && ( +
+ + TÉLÉCHARGEMENT DU CATALOGUE... +
+ )} + + {error && ( +
+
+ + {error} +
+
+ )} + + {/* GRILLE DU CATALOGUE */} + {!isLoading && !error && ( +
+ + {products.map((product) => ( +
+ {/* En-tête de la carte */} +
+
+ {getCategoryIcon(product.category || product.title)} +
+

+ {product.title} +

+
+ {extractMonthlyPrice(product.pricing)} +
+
+ + {/* Description du produit */} +
+ {/* FOSSBilling permet de mettre du HTML dans la description. + Ici, on triche un peu en simulant des puces pour le design, + mais tu pourras utiliser dangerouslySetInnerHTML si tu as mis du vrai HTML. */} +
+ {product.description ? ( +

{product.description}

+ ) : ( + <> +
Provisionnement instantané
+
Support technique 24/7
+
SLA 99.9%
+ + )} +
+ + {/* Bouton d'action */} + +
+
+ ))} + +
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/services/api.js b/src/services/api.js index 8f868f4..90a66e8 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -42,6 +42,10 @@ export const getClientOrders = () => export const getOrderService = (order_id) => apiCall(`${BASE_URL}/api/client/order/service`, { id: order_id }); +// Récupère le catalogue public des produits FOSSBilling +export const getProductList = () => + apiCall(`${BASE_URL}/api/guest/product/get_list`); + // ========================================== // ROUTES PERSONNALISÉES (CUSTOM API) // ========================================== diff --git a/vite.config.js b/vite.config.js index f2d425d..0cc4800 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,8 +1,9 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' export default defineConfig({ - plugins: [react()], + plugins: [react(), tailwindcss()], server: { proxy: { // Toutes les requêtes qui commencent par /api iront vers ton FOSSBilling