first commit

This commit is contained in:
2026-02-22 20:25:47 +01:00
commit a4f85b0b7b
31 changed files with 5870 additions and 0 deletions

122
pages/DirectoryPage.tsx Normal file
View File

@@ -0,0 +1,122 @@
import React, { useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { Search } from 'lucide-react';
import { MOCK_BUSINESSES } from '../services/mockData';
import { CATEGORIES } from '../types';
import BusinessCard from '../components/BusinessCard';
import DirectoryHero from '../components/DirectoryHero';
const DirectoryPage = () => {
const [filterCategory, setFilterCategory] = useState('All');
const [searchQuery, setSearchQuery] = useState('');
const useQuery = () => {
return new URLSearchParams(useLocation().search);
}
const query = useQuery();
useEffect(() => {
const q = query.get('q');
if (q) setSearchQuery(q);
}, [query]);
const featuredBusiness = useMemo(() => {
return MOCK_BUSINESSES.find(b => b.isFeatured);
}, []);
const filteredBusinesses = useMemo(() => {
return MOCK_BUSINESSES.filter(b => {
const matchesCategory = filterCategory === 'All' || b.category === filterCategory;
const matchesSearch = b.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
b.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
b.tags.some(t => t.toLowerCase().includes(searchQuery.toLowerCase()));
return matchesCategory && matchesSearch;
});
}, [filterCategory, searchQuery]);
return (
<div>
{/* New Hero Section */}
{featuredBusiness && <DirectoryHero featuredBusiness={featuredBusiness} />}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex flex-col md:flex-row gap-8">
{/* Filters Sidebar */}
<div className="w-full md:w-64 flex-shrink-0">
<div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100 sticky top-24">
<h3 className="font-bold text-lg mb-4 flex items-center"><Search className="w-4 h-4 mr-2"/> Filtres</h3>
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">Recherche</label>
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full border-gray-300 rounded-md shadow-sm focus:ring-brand-500 focus:border-brand-500 sm:text-sm p-2 border"
placeholder="Nom, mot-clé..."
/>
</div>
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">Catégorie</label>
<div className="space-y-2">
<div className="flex items-center">
<input
id="cat-all"
name="category"
type="radio"
checked={filterCategory === 'All'}
onChange={() => setFilterCategory('All')}
className="focus:ring-brand-500 h-4 w-4 text-brand-600 border-gray-300"
/>
<label htmlFor="cat-all" className="ml-3 text-sm text-gray-600">Toutes</label>
</div>
{CATEGORIES.map(cat => (
<div key={cat} className="flex items-center">
<input
id={`cat-${cat}`}
name="category"
type="radio"
checked={filterCategory === cat}
onChange={() => setFilterCategory(cat)}
className="focus:ring-brand-500 h-4 w-4 text-brand-600 border-gray-300"
/>
<label htmlFor={`cat-${cat}`} className="ml-3 text-sm text-gray-600 truncate" title={cat}>{cat}</label>
</div>
))}
</div>
</div>
</div>
</div>
{/* Results Grid */}
<div className="flex-1">
<div className="mb-4 flex justify-between items-center">
<h1 className="text-2xl font-bold font-serif text-gray-900">Annuaire des entreprises</h1>
<span className="text-sm text-gray-500">{filteredBusinesses.length} résultats</span>
</div>
{filteredBusinesses.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredBusinesses.map(biz => (
<BusinessCard key={biz.id} business={biz} />
))}
</div>
) : (
<div className="text-center py-20 bg-white rounded-xl border border-gray-100 border-dashed">
<div className="mx-auto h-12 w-12 text-gray-400">
<Search className="h-12 w-12" />
</div>
<h3 className="mt-2 text-sm font-medium text-gray-900">Aucun résultat</h3>
<p className="mt-1 text-sm text-gray-500">Essayez d'ajuster vos filtres de recherche.</p>
</div>
)}
</div>
</div>
</div>
</div>
);
};
export default DirectoryPage;