80 lines
3.4 KiB
React
80 lines
3.4 KiB
React
// src/pages/app/Store.jsx
|
|
import { useState, useEffect } from 'react';
|
|
import { Server, Database, Cloud, Globe, Loader, AlertCircle } from 'lucide-react';
|
|
import { getProductList } from '../../services/billing_api';
|
|
import CategorySection from '../../components/store/CategorySection';
|
|
|
|
export default function Store() {
|
|
const [groupedProducts, setGroupedProducts] = useState({});
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
|
|
const CATEGORY_ORDER = ["Web Hosting", "VPS", "Database", "Cloud"];
|
|
const CATEGORY_MAP = { 1: "Web Hosting", 4: "VPS", 3: "Database", 2: "Cloud" };
|
|
|
|
useEffect(() => {
|
|
const fetchCatalog = async () => {
|
|
try {
|
|
const data = await getProductList();
|
|
const products = data.list || [];
|
|
|
|
const grid = products.reduce((acc, product) => {
|
|
const catId = product.product_category_id;
|
|
const catName = CATEGORY_MAP[catId] || 'Autres Services';
|
|
|
|
if (!acc[catName]) acc[catName] = [];
|
|
acc[catName].push(product);
|
|
return acc;
|
|
}, {});
|
|
|
|
setGroupedProducts(grid);
|
|
} catch (err) {
|
|
setError("Impossible de contacter le serveur d'approvisionnement.");
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
fetchCatalog();
|
|
}, []);
|
|
|
|
const getCategoryIcon = (text) => {
|
|
const t = text.toLowerCase();
|
|
if (t.includes('vps') || t.includes('serveur')) return <Server className="w-8 h-8 text-cyan-400" />;
|
|
if (t.includes('cloud') || t.includes('nextcloud')) return <Cloud className="w-8 h-8 text-blue-400" />;
|
|
if (t.includes('data') || t.includes('db') || t.includes('base')) return <Database className="w-8 h-8 text-purple-400" />;
|
|
return <Globe className="w-8 h-8 text-emerald-400" />;
|
|
};
|
|
|
|
const sortedCategoryNames = Object.keys(groupedProducts).sort((a, b) => {
|
|
let indexA = CATEGORY_ORDER.indexOf(a);
|
|
let indexB = CATEGORY_ORDER.indexOf(b);
|
|
if (indexA === -1) indexA = 999;
|
|
if (indexB === -1) indexB = 999;
|
|
return indexA - indexB;
|
|
});
|
|
|
|
return (
|
|
<div className="w-full max-w-7xl p-6 mx-auto">
|
|
<header className="mb-12 text-center pt-8">
|
|
<h1 className="text-4xl md:text-5xl font-black text-white tracking-widest mb-4">
|
|
CATALOGUE <span className="text-cyan-400">NEXUS</span>
|
|
</h1>
|
|
<p className="text-gray-400 max-w-2xl mx-auto text-lg">
|
|
Déployez de nouvelles instances et étendez votre infrastructure. Facturation flexible.
|
|
</p>
|
|
</header>
|
|
|
|
{isLoading && <div className="flex justify-center text-cyan-400 my-20"><Loader className="w-10 h-10 animate-spin" /></div>}
|
|
{error && <div className="flex justify-center text-red-400 my-20"><AlertCircle className="w-10 h-10 mr-2" /> {error}</div>}
|
|
|
|
{!isLoading && !error && sortedCategoryNames.map((categoryName) => (
|
|
<CategorySection
|
|
key={categoryName}
|
|
categoryName={categoryName}
|
|
products={groupedProducts[categoryName]}
|
|
getCategoryIcon={getCategoryIcon}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
} |