← back to Wine Finder Next

components/mobile/FilterSheet.tsx

241 lines

'use client'

import { useState } from 'react';

interface FilterSheetProps {
  isOpen: boolean;
  onClose: () => void;
  onApply: (filters: FilterState) => void;
  initialFilters?: FilterState;
}

export interface FilterState {
  priceMin?: number;
  priceMax?: number;
  ratingMin?: number;
  sources?: string[];
  type?: string[];
  inStockOnly?: boolean;
  sortBy?: 'price-asc' | 'price-desc' | 'rating' | 'name';
}

const WINE_TYPES = ['Red', 'White', 'Rosé', 'Sparkling', 'Dessert', 'Fortified'];
const SOURCES = ['Vivino', 'Wine.com', 'Total Wine', 'K&L Wines', 'Wine Access'];

export default function FilterSheet({
  isOpen,
  onClose,
  onApply,
  initialFilters = {}
}: FilterSheetProps) {
  const [filters, setFilters] = useState<FilterState>(initialFilters);

  const handleApply = () => {
    onApply(filters);
    onClose();
  };

  const handleReset = () => {
    setFilters({});
  };

  if (!isOpen) return null;

  return (
    <>
      {/* Backdrop */}
      <div
        className="fixed inset-0 bg-black/50 z-40 animate-fadeIn"
        onClick={onClose}
      />

      {/* Bottom Sheet */}
      <div className="fixed inset-x-0 bottom-0 z-50 bg-white rounded-t-3xl shadow-2xl max-h-[85vh] overflow-hidden animate-slideUp">
        {/* Handle */}
        <div className="flex justify-center pt-3 pb-2">
          <div className="w-12 h-1 bg-gray-300 rounded-full" />
        </div>

        {/* Header */}
        <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
          <h3 className="text-xl font-bold text-gray-900">Filters</h3>
          <button
            onClick={handleReset}
            className="text-purple-600 font-semibold text-sm"
          >
            Reset All
          </button>
        </div>

        {/* Scrollable Content */}
        <div className="overflow-y-auto max-h-[calc(85vh-200px)] px-6 py-4 space-y-6">
          {/* Sort By */}
          <div>
            <label className="block text-sm font-bold text-gray-900 mb-2">
              Sort By
            </label>
            <select
              value={filters.sortBy || 'price-asc'}
              onChange={(e) => setFilters({ ...filters, sortBy: e.target.value as any })}
              className="w-full px-4 py-3 border-2 border-gray-300 rounded-xl text-base focus:border-purple-500 focus:outline-none"
              style={{ fontSize: '16px' }} // Prevent iOS zoom
            >
              <option value="price-asc">Price: Low to High</option>
              <option value="price-desc">Price: High to Low</option>
              <option value="rating">Rating: High to Low</option>
              <option value="name">Name: A to Z</option>
            </select>
          </div>

          {/* Price Range */}
          <div>
            <label className="block text-sm font-bold text-gray-900 mb-2">
              Price Range
            </label>
            <div className="grid grid-cols-2 gap-3">
              <input
                type="number"
                placeholder="Min $"
                value={filters.priceMin || ''}
                onChange={(e) => setFilters({ ...filters, priceMin: parseInt(e.target.value) || undefined })}
                className="px-4 py-3 border-2 border-gray-300 rounded-xl focus:border-purple-500 focus:outline-none"
                style={{ fontSize: '16px' }}
              />
              <input
                type="number"
                placeholder="Max $"
                value={filters.priceMax || ''}
                onChange={(e) => setFilters({ ...filters, priceMax: parseInt(e.target.value) || undefined })}
                className="px-4 py-3 border-2 border-gray-300 rounded-xl focus:border-purple-500 focus:outline-none"
                style={{ fontSize: '16px' }}
              />
            </div>
          </div>

          {/* Rating */}
          <div>
            <label className="block text-sm font-bold text-gray-900 mb-2">
              Minimum Rating
            </label>
            <div className="flex gap-2">
              {[3, 3.5, 4, 4.5, 5].map((rating) => (
                <button
                  key={rating}
                  onClick={() => setFilters({ ...filters, ratingMin: rating })}
                  className={`flex-1 py-3 rounded-xl font-bold transition-all ${
                    filters.ratingMin === rating
                      ? 'bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg'
                      : 'bg-gray-100 text-gray-700'
                  }`}
                >
                  {rating}+
                </button>
              ))}
            </div>
          </div>

          {/* Wine Type */}
          <div>
            <label className="block text-sm font-bold text-gray-900 mb-2">
              Wine Type
            </label>
            <div className="flex flex-wrap gap-2">
              {WINE_TYPES.map((type) => (
                <button
                  key={type}
                  onClick={() => {
                    const current = filters.type || [];
                    const updated = current.includes(type)
                      ? current.filter(t => t !== type)
                      : [...current, type];
                    setFilters({ ...filters, type: updated.length > 0 ? updated : undefined });
                  }}
                  className={`px-4 py-2 rounded-full font-semibold text-sm transition-all ${
                    (filters.type || []).includes(type)
                      ? 'bg-purple-600 text-white'
                      : 'bg-gray-100 text-gray-700'
                  }`}
                >
                  {type}
                </button>
              ))}
            </div>
          </div>

          {/* Sources */}
          <div>
            <label className="block text-sm font-bold text-gray-900 mb-2">
              Retailers
            </label>
            <div className="space-y-2">
              {SOURCES.map((source) => (
                <label
                  key={source}
                  className="flex items-center gap-3 p-3 border-2 border-gray-200 rounded-xl cursor-pointer hover:border-purple-300 transition-colors"
                >
                  <input
                    type="checkbox"
                    checked={(filters.sources || []).includes(source)}
                    onChange={(e) => {
                      const current = filters.sources || [];
                      const updated = e.target.checked
                        ? [...current, source]
                        : current.filter(s => s !== source);
                      setFilters({ ...filters, sources: updated.length > 0 ? updated : undefined });
                    }}
                    className="w-5 h-5 text-purple-600 rounded"
                  />
                  <span className="font-medium text-gray-900">{source}</span>
                </label>
              ))}
            </div>
          </div>

          {/* In Stock Only */}
          <div>
            <label className="flex items-center gap-3 p-4 border-2 border-gray-200 rounded-xl cursor-pointer hover:border-purple-300 transition-colors">
              <input
                type="checkbox"
                checked={filters.inStockOnly || false}
                onChange={(e) => setFilters({ ...filters, inStockOnly: e.target.checked })}
                className="w-6 h-6 text-purple-600 rounded"
              />
              <div>
                <div className="font-bold text-gray-900">In Stock Only</div>
                <div className="text-sm text-gray-600">Show only available wines</div>
              </div>
            </label>
          </div>
        </div>

        {/* Footer Actions */}
        <div className="px-6 py-4 border-t border-gray-200 bg-white flex gap-3">
          <button
            onClick={onClose}
            className="flex-1 py-4 border-2 border-gray-300 text-gray-700 rounded-xl font-bold hover:bg-gray-50 transition-all"
          >
            Cancel
          </button>
          <button
            onClick={handleApply}
            className="flex-1 py-4 bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-xl font-bold hover:from-purple-700 hover:to-pink-700 transition-all shadow-lg"
          >
            Apply Filters
          </button>
        </div>
      </div>

      <style jsx>{`
        @keyframes slideUp {
          from { transform: translateY(100%); }
          to { transform: translateY(0); }
        }
        @keyframes fadeIn {
          from { opacity: 0; }
          to { opacity: 1; }
        }
      `}</style>
    </>
  );
}