File size: 5,347 Bytes
6ff567f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
'use client';

import { useState, useEffect, useCallback, useMemo } from 'react';
import { Header } from '@/components/Header';
import { ProductCard } from '@/components/ProductCard';
import { ProductFilters } from '@/components/ProductFilters';
import { AiSuggestion } from '@/components/AiSuggestion';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Search, SlidersHorizontal, Loader2 } from 'lucide-react';
import { searchProducts } from '@/services/product-api';
import type { Product } from '@/lib/types';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';

export default function Home() {
  const [searchQuery, setSearchQuery] = useState('');
  const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');
  const [products, setProducts] = useState<Product[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  const [priceRange, setPriceRange] = useState<{ min: number | undefined, max: number | undefined }>({ min: undefined, max: undefined });
  const [minRating, setMinRating] = useState<number>(0);
  const [country, setCountry] = useState('US');
  const [language, setLanguage] = useState('en');
  
  // Debounce search query
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedSearchQuery(searchQuery);
    }, 300); // 300ms debounce delay

    return () => {
      clearTimeout(handler);
    };
  }, [searchQuery]);


  useEffect(() => {
    const fetchProducts = async () => {
      if (!debouncedSearchQuery) {
        setProducts([]);
        setIsLoading(false);
        return;
      }

      setIsLoading(true);
      try {
        const results = await searchProducts(debouncedSearchQuery, { priceRange, minRating });
        setProducts(results);
      } catch (error) {
        console.error("Failed to fetch products:", error);
        setProducts([]);
      } finally {
        setIsLoading(false);
      }
    };

    fetchProducts();
  }, [debouncedSearchQuery, priceRange, minRating]);

  const onFiltersChange = useCallback(
    (newFilters: { 
      priceRange: { min: number | undefined; max: number | undefined };
      minRating: number;
    }) => {
      setPriceRange(newFilters.priceRange);
      setMinRating(newFilters.minRating);
    },
    []
  );

  const filtersComponent = (
    <ProductFilters
      priceRange={priceRange}
      minRating={minRating}
      onFiltersChange={onFiltersChange}
    />
  );

  const aiFilters = useMemo(() => ({
    price: priceRange,
    rating: minRating,
    country,
    language,
  }), [priceRange, minRating, country, language]);

  return (
    <div className="flex min-h-screen w-full flex-col bg-background">
      <Header
        showControls={true}
        country={country}
        onCountryChange={setCountry}
        language={language}
        onLanguageChange={setLanguage}
      />
      <main className="flex-1">
        <div className="container mx-auto grid flex-1 gap-8 px-4 py-8 md:grid-cols-[280px_1fr]">
          <aside className="hidden md:block">{filtersComponent}</aside>
          <div className="flex flex-col gap-8">
            <div className="flex flex-col gap-4 sm:flex-row">
              <div className="relative flex-1">
                <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
                <Input
                  type="search"
                  placeholder="Search for products... AI will suggest as you type"
                  className="w-full rounded-full bg-white pl-10"
                  value={searchQuery}
                  onChange={(e) => setSearchQuery(e.target.value)}
                />
              </div>
              <div className="md:hidden">
                <Sheet>
                  <SheetTrigger asChild>
                    <Button variant="outline" size="icon" className="rounded-full">
                      <SlidersHorizontal className="h-5 w-5" />
                      <span className="sr-only">Open filters</span>
                    </Button>
                  </SheetTrigger>
                  <SheetContent side="left" className="w-[300px] sm:w-[400px]">
                    {filtersComponent}
                  </SheetContent>
                </Sheet>
              </div>
            </div>

            <AiSuggestion
              searchQuery={debouncedSearchQuery}
              filters={aiFilters}
              externalProductData={JSON.stringify(products)}
            />

            {isLoading ? (
               <div className="flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-muted-foreground/30 bg-muted/20 py-20 text-center">
                 <Loader2 className="h-10 w-10 animate-spin text-primary" />
                 <h3 className="mt-4 text-xl font-semibold tracking-tight text-muted-foreground">
                   Searching for products...
                 </h3>
               </div>
            ) : products.length > 0 ? (
              <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
                {products.map((product) => (
                  <ProductCard key={product.id} product={product} />
                ))}
              </div>
            ) : null}
          </div>
        </div>
      </main>
    </div>
  );
}