199 lines
8.2 KiB
TypeScript
199 lines
8.2 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState } from 'react';
|
||
|
|
import { Search, Filter, X, Calendar } from 'lucide-react';
|
||
|
|
|
||
|
|
interface OrderFiltersProps {
|
||
|
|
onSearchChange: (search: string) => void;
|
||
|
|
onStatusChange: (status: string) => void;
|
||
|
|
onDateRangeChange: (dateFrom: string, dateTo: string) => void;
|
||
|
|
onReset: () => void;
|
||
|
|
searchValue: string;
|
||
|
|
statusValue: string;
|
||
|
|
dateFromValue: string;
|
||
|
|
dateToValue: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function OrderFilters({
|
||
|
|
onSearchChange,
|
||
|
|
onStatusChange,
|
||
|
|
onDateRangeChange,
|
||
|
|
searchValue,
|
||
|
|
statusValue,
|
||
|
|
dateFromValue,
|
||
|
|
dateToValue,
|
||
|
|
onReset,
|
||
|
|
}: OrderFiltersProps) {
|
||
|
|
const [showDatePicker, setShowDatePicker] = useState(false);
|
||
|
|
const [localDateFrom, setLocalDateFrom] = useState(dateFromValue);
|
||
|
|
const [localDateTo, setLocalDateTo] = useState(dateToValue);
|
||
|
|
|
||
|
|
const handleApplyDateRange = () => {
|
||
|
|
onDateRangeChange(localDateFrom, localDateTo);
|
||
|
|
setShowDatePicker(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleResetDateRange = () => {
|
||
|
|
setLocalDateFrom('');
|
||
|
|
setLocalDateTo('');
|
||
|
|
onDateRangeChange('', '');
|
||
|
|
setShowDatePicker(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
const hasActiveFilters = searchValue || statusValue || dateFromValue || dateToValue;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-white border border-light-gray-border rounded-xl p-6 mb-6">
|
||
|
|
<div className="flex flex-col lg:flex-row gap-4">
|
||
|
|
{/* Search Input */}
|
||
|
|
<div className="flex-1 relative">
|
||
|
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-deep-sea-blue/40" />
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
placeholder="Search by order ID, customer name, or email..."
|
||
|
|
value={searchValue}
|
||
|
|
onChange={(e) => onSearchChange(e.target.value)}
|
||
|
|
className="w-full pl-11 pr-4 py-2.5 border border-light-gray-border rounded-lg bg-white text-deep-sea-blue placeholder-deep-sea-blue/40 focus:outline-none focus:ring-2 focus:ring-deep-sea-blue/20 focus:border-deep-sea-blue transition-all"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Status Filter */}
|
||
|
|
<div className="relative">
|
||
|
|
<Filter className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-deep-sea-blue/40" />
|
||
|
|
<select
|
||
|
|
value={statusValue}
|
||
|
|
onChange={(e) => onStatusChange(e.target.value)}
|
||
|
|
className="pl-10 pr-10 py-2.5 border border-light-gray-border rounded-lg bg-white text-deep-sea-blue focus:outline-none focus:ring-2 focus:ring-deep-sea-blue/20 focus:border-deep-sea-blue transition-all cursor-pointer appearance-none"
|
||
|
|
style={{ minWidth: '160px' }}
|
||
|
|
>
|
||
|
|
<option value="">All Status</option>
|
||
|
|
<option value="pending">Pending</option>
|
||
|
|
<option value="paid">Paid</option>
|
||
|
|
<option value="fulfilled">Fulfilled</option>
|
||
|
|
<option value="cancelled">Cancelled</option>
|
||
|
|
</select>
|
||
|
|
<div className="absolute right-3 top-1/2 transform -translate-y-1/2 pointer-events-none">
|
||
|
|
<svg className="w-4 h-4 text-deep-sea-blue/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Date Range Picker */}
|
||
|
|
<div className="relative">
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => setShowDatePicker(!showDatePicker)}
|
||
|
|
className="flex items-center space-x-2 px-4 py-2.5 border border-light-gray-border rounded-lg bg-white text-deep-sea-blue hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-deep-sea-blue/20 transition-all"
|
||
|
|
>
|
||
|
|
<Calendar className="w-4 h-4" />
|
||
|
|
<span className="text-sm font-medium">
|
||
|
|
{dateFromValue && dateToValue ? 'Date Range' : 'All Dates'}
|
||
|
|
</span>
|
||
|
|
{(dateFromValue || dateToValue) && (
|
||
|
|
<span className="px-2 py-0.5 text-xs bg-deep-sea-blue text-white rounded-full">1</span>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
|
||
|
|
{/* Date Picker Dropdown */}
|
||
|
|
{showDatePicker && (
|
||
|
|
<div className="absolute right-0 mt-2 p-4 bg-white border border-light-gray-border rounded-xl shadow-lg z-10 min-w-[300px]">
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium text-deep-sea-blue mb-2">From Date</label>
|
||
|
|
<input
|
||
|
|
type="date"
|
||
|
|
value={localDateFrom}
|
||
|
|
onChange={(e) => setLocalDateFrom(e.target.value)}
|
||
|
|
className="w-full px-3 py-2 border border-light-gray-border rounded-lg focus:outline-none focus:ring-2 focus:ring-deep-sea-blue/20"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-sm font-medium text-deep-sea-blue mb-2">To Date</label>
|
||
|
|
<input
|
||
|
|
type="date"
|
||
|
|
value={localDateTo}
|
||
|
|
onChange={(e) => setLocalDateTo(e.target.value)}
|
||
|
|
className="w-full px-3 py-2 border border-light-gray-border rounded-lg focus:outline-none focus:ring-2 focus:ring-deep-sea-blue/20"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="flex space-x-2 pt-2">
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={handleResetDateRange}
|
||
|
|
className="flex-1 px-4 py-2 text-sm font-medium text-deep-sea-blue bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
||
|
|
>
|
||
|
|
Clear
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={handleApplyDateRange}
|
||
|
|
className="flex-1 px-4 py-2 text-sm font-medium text-white bg-deep-sea-blue hover:bg-deep-sea-blue/90 rounded-lg transition-colors"
|
||
|
|
>
|
||
|
|
Apply
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Reset Filters Button */}
|
||
|
|
{hasActiveFilters && (
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={onReset}
|
||
|
|
className="flex items-center space-x-2 px-4 py-2.5 text-sm font-medium text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 rounded-lg transition-colors"
|
||
|
|
>
|
||
|
|
<X className="w-4 h-4" />
|
||
|
|
<span>Reset</span>
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Active Filters Display */}
|
||
|
|
{hasActiveFilters && (
|
||
|
|
<div className="flex flex-wrap items-center gap-2 mt-4 pt-4 border-t border-light-gray-border">
|
||
|
|
<span className="text-sm font-medium text-deep-sea-blue/70">Active filters:</span>
|
||
|
|
{searchValue && (
|
||
|
|
<span className="inline-flex items-center px-3 py-1 text-xs font-medium bg-deep-sea-blue/10 text-deep-sea-blue rounded-full">
|
||
|
|
Search: "{searchValue}"
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => onSearchChange('')}
|
||
|
|
className="ml-2 hover:text-deep-sea-blue/70"
|
||
|
|
>
|
||
|
|
<X className="w-3 h-3" />
|
||
|
|
</button>
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
{statusValue && (
|
||
|
|
<span className="inline-flex items-center px-3 py-1 text-xs font-medium bg-deep-sea-blue/10 text-deep-sea-blue rounded-full">
|
||
|
|
Status: {statusValue.charAt(0).toUpperCase() + statusValue.slice(1)}
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => onStatusChange('')}
|
||
|
|
className="ml-2 hover:text-deep-sea-blue/70"
|
||
|
|
>
|
||
|
|
<X className="w-3 h-3" />
|
||
|
|
</button>
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
{(dateFromValue || dateToValue) && (
|
||
|
|
<span className="inline-flex items-center px-3 py-1 text-xs font-medium bg-deep-sea-blue/10 text-deep-sea-blue rounded-full">
|
||
|
|
Date: {dateFromValue || '...'} to {dateToValue || '...'}
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => onDateRangeChange('', '')}
|
||
|
|
className="ml-2 hover:text-deep-sea-blue/70"
|
||
|
|
>
|
||
|
|
<X className="w-3 h-3" />
|
||
|
|
</button>
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|