feat: implement core platform features including business directory, admin dashboard, authentication, and API infrastructure
Some checks failed
Build and Push App / build (push) Failing after 16m2s
Some checks failed
Build and Push App / build (push) Failing after 16m2s
This commit is contained in:
@@ -4,26 +4,44 @@
|
||||
import React, { useState, useEffect, useMemo, Suspense } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { Search, Loader2 } from 'lucide-react';
|
||||
import { CATEGORIES, Business } from '../../types';
|
||||
import { CATEGORIES, Business, Country } from '../../types';
|
||||
import BusinessCard from '../../components/BusinessCard';
|
||||
import AnnuaireHero from '../../components/AnnuaireHero';
|
||||
|
||||
const AnnuairePageContent = () => {
|
||||
const [businesses, setBusinesses] = useState<Business[]>([]);
|
||||
const [featuredBusiness, setFeaturedBusiness] = useState<Business | null>(null);
|
||||
const [countries, setCountries] = useState<Country[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filterCategory, setFilterCategory] = useState('All');
|
||||
const [filterCountry, setFilterCountry] = useState('All');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// 1. Fetch Featured Headliner once
|
||||
// 1. Fetch Countries
|
||||
useEffect(() => {
|
||||
const fetchCountries = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/countries');
|
||||
const data = await res.json();
|
||||
if (Array.isArray(data)) setCountries(data);
|
||||
} catch (e) {}
|
||||
};
|
||||
fetchCountries();
|
||||
}, []);
|
||||
|
||||
// 2. Fetch Featured Headliner once
|
||||
useEffect(() => {
|
||||
const fetchHeadliner = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/businesses?featured=true');
|
||||
const data = await res.json();
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
setFeaturedBusiness(data[0]);
|
||||
// Stable random based on the current date (YYYYMMDD)
|
||||
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
const seed = parseInt(today);
|
||||
const randomIndex = seed % data.length;
|
||||
setFeaturedBusiness(data[randomIndex]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching headliner:', error);
|
||||
@@ -32,13 +50,14 @@ const AnnuairePageContent = () => {
|
||||
fetchHeadliner();
|
||||
}, []);
|
||||
|
||||
// 2. Fetch businesses from API
|
||||
// 3. Fetch businesses from API
|
||||
useEffect(() => {
|
||||
const fetchBusinesses = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (filterCategory !== 'All') params.append('category', filterCategory);
|
||||
if (filterCountry !== 'All') params.append('countryId', filterCountry);
|
||||
if (searchQuery) params.append('q', searchQuery);
|
||||
|
||||
const res = await fetch(`/api/businesses?${params.toString()}`);
|
||||
@@ -55,11 +74,17 @@ const AnnuairePageContent = () => {
|
||||
|
||||
const timeoutId = setTimeout(fetchBusinesses, 300); // Debounce
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [filterCategory, searchQuery]);
|
||||
}, [filterCategory, searchQuery, filterCountry]);
|
||||
|
||||
useEffect(() => {
|
||||
const q = searchParams.get('q');
|
||||
if (q) setSearchQuery(q);
|
||||
|
||||
const cat = searchParams.get('category');
|
||||
if (cat) setFilterCategory(cat);
|
||||
|
||||
const country = searchParams.get('country');
|
||||
if (country) setFilterCountry(country);
|
||||
}, [searchParams]);
|
||||
|
||||
const filteredBusinesses = businesses;
|
||||
@@ -87,9 +112,23 @@ const AnnuairePageContent = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Pays</label>
|
||||
<select
|
||||
value={filterCountry}
|
||||
onChange={(e) => setFilterCountry(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"
|
||||
>
|
||||
<option value="All">Tous les pays</option>
|
||||
{countries.map(c => (
|
||||
<option key={c.id} value={c.id}>{c.flag} {c.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</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="space-y-1 max-h-60 overflow-y-auto pr-1">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="cat-all"
|
||||
|
||||
Reference in New Issue
Block a user