Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 11 additions & 71 deletions client/src/features/home/FeaturedItems.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import React, { useEffect, useMemo, useState } from 'react';
import MenuCard from '../../components/ui/MenuCard';
import api from '../../lib/axios';

const FeaturedItems = ({ outletId, showAll = false }) => {
const FeaturedItems = ({ outletId, showAll = false, searchQuery = '', categoryFilter = 'all', dietFilter = 'all' }) => {
const [items, setItems] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [category, setCategory] = useState('all');
const [diet, setDiet] = useState('all'); // 'all' | 'veg' | 'non-veg'
const [tag, setTag] = useState('all');
const [tag] = useState('all');

// Use external filters if provided
const activeSearch = searchQuery;
const activeCategory = categoryFilter;
const activeDiet = dietFilter;

useEffect(() => {
const fetchMenu = async () => {
Expand All @@ -34,85 +36,23 @@ const FeaturedItems = ({ outletId, showAll = false }) => {

const filteredItems = useMemo(() => {
if (!outletId) return [];
const term = searchTerm.trim().toLowerCase();
const term = activeSearch.trim().toLowerCase();
return items.filter((item) => {
const matchesCategory = category === 'all' || item.category === category;
const matchesDiet = diet === 'all' || (diet === 'veg' ? item.isVeg === true : item.isVeg === false);
const matchesCategory = activeCategory === 'all' || item.category === activeCategory;
const matchesDiet = activeDiet === 'all' || (activeDiet === 'veg' ? item.isVeg === true : item.isVeg === false);
const matchesTag = tag === 'all' || (Array.isArray(item.tags) && item.tags.includes(tag));
const matchesSearch =
!term || item.name.toLowerCase().includes(term) || (item.description || '').toLowerCase().includes(term);
return matchesCategory && matchesDiet && matchesTag && matchesSearch;
});
}, [items, searchTerm, category, diet, tag, outletId]);

const categories = useMemo(() => {
const unique = Array.from(new Set(items.map((item) => item.category).filter(Boolean)));
return ['all', ...unique];
}, [items]);

const tags = useMemo(() => {
const unique = new Set();
items.forEach((item) => {
if (Array.isArray(item.tags)) {
item.tags.forEach((t) => t && unique.add(t));
}
});
return ['all', ...Array.from(unique)];
}, [items]);
}, [items, activeSearch, activeCategory, activeDiet, tag, outletId]);

const displayItems = showAll ? filteredItems : filteredItems.slice(0, 8);

if (!outletId) return null;

return (
<div className="space-y-4">
<div className="flex flex-col gap-3 bg-zinc-900/60 border border-white/5 rounded-xl p-3">
<div className="flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between">
<input
type="search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search dishes..."
className="w-full sm:w-1/2 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 border border-white/10 focus:border-amber-500 focus:outline-none"
/>
<div className="flex gap-3 w-full sm:w-auto">
<select
value={category}
onChange={(e) => setCategory(e.target.value)}
className="w-1/2 sm:w-44 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 border border-white/10 focus:border-amber-500 focus:outline-none"
>
{categories.map((cat) => (
<option key={cat} value={cat}>
{cat === 'all' ? 'All Categories' : cat}
</option>
))}
</select>
<select
value={diet}
onChange={(e) => setDiet(e.target.value)}
className="w-1/2 sm:w-36 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 border border-white/10 focus:border-amber-500 focus:outline-none"
>
<option value="all">Veg & Non-Veg</option>
<option value="veg">Veg</option>
<option value="non-veg">Non-Veg</option>
</select>
</div>
</div>
<div className="flex gap-3">
<select
value={tag}
onChange={(e) => setTag(e.target.value)}
className="w-full sm:w-60 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 border border-white/10 focus:border-amber-500 focus:outline-none"
>
{tags.map((t) => (
<option key={t} value={t}>
{t === 'all' ? 'All Tags' : t}
</option>
))}
</select>
</div>
</div>

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{displayItems.map((item) => (
<MenuCard key={item._id} item={item} />
Expand Down
19 changes: 15 additions & 4 deletions client/src/features/home/TrendingItems.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import api from '../../lib/axios';
import { Flame } from 'lucide-react';
import MenuCard from '../../components/ui/MenuCard';

const TrendingItems = ({ outletId }) => {
const TrendingItems = ({ outletId, searchQuery = '', categoryFilter = 'all', dietFilter = 'all' }) => {
const { addToCart } = useCart();
const [trendingItems, setTrendingItems] = useState([]);

Expand All @@ -26,8 +26,19 @@ const TrendingItems = ({ outletId }) => {
fetchTrendingItems();
}, [outletId]);

// Don't show section if no trending items found
if (trendingItems.length === 0) {
// Filter trending items based on search and filters
const filteredTrendingItems = trendingItems.filter((item) => {
const matchesSearch =
!searchQuery ||
item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
(item.description || '').toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = categoryFilter === 'all' || item.category === categoryFilter;
const matchesDiet = dietFilter === 'all' || (dietFilter === 'veg' ? item.isVeg === true : item.isVeg === false);
return matchesSearch && matchesCategory && matchesDiet;
});

// Don't show section if no trending items found after filtering
if (filteredTrendingItems.length === 0) {
return null;
}

Expand All @@ -40,7 +51,7 @@ const TrendingItems = ({ outletId }) => {
</div>

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{trendingItems.map((item) => (
{filteredTrendingItems.map((item) => (
<MenuCard
key={item._id}
item={item}
Expand Down
23 changes: 23 additions & 0 deletions client/src/hooks/useDebounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useState, useEffect } from 'react';

/**
* Custom hook for debouncing values
* @param {*} value - The value to debounce
* @param {number} delay - Delay in milliseconds (default: 300ms)
* @returns {*} - Debounced value
*/
export function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(timer);
};
}, [value, delay]);

return debouncedValue;
}
Loading
Loading