import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuLabel, DropdownMenuGroup } from '@/components/ui/dropdown-menu'; import { Bookmark, Plus, Edit, Trash2, Download, Upload, RotateCcw, ChevronDown, Clock, Star, Settings, FileText, AlertCircle, Loader2 } from 'lucide-react'; import { useFilterPresets } from '@/hooks/useFilterPresets'; import { AdvancedFilters } from '@/types'; interface FilterPresetsProps { currentFilters: AdvancedFilters; onApplyPreset: (filters: AdvancedFilters) => void; onFiltersChange?: (filters: AdvancedFilters) => void; className?: string; } export function FilterPresets({ currentFilters, onApplyPreset, onFiltersChange, className = '' }: FilterPresetsProps) { const { presets, builtInPresets, customPresets, loading, error, savePreset, applyPreset, updatePreset, deletePreset, exportPresets, importPresets, resetToDefaults, clearError } = useFilterPresets(); const [showSaveDialog, setShowSaveDialog] = useState(false); const [showManageDialog, setShowManageDialog] = useState(false); const [showImportDialog, setShowImportDialog] = useState(false); const [editingPreset, setEditingPreset] = useState(null); const [newPresetName, setNewPresetName] = useState(''); const [newPresetDescription, setNewPresetDescription] = useState(''); const [newPresetIcon, setNewPresetIcon] = useState('📁'); const [importData, setImportData] = useState(''); const [importResults, setImportResults] = useState(null); const handleApplyPreset = async (presetId: string) => { try { const preset = await applyPreset(presetId); onApplyPreset(preset.filters); } catch (err) { console.error('Failed to apply preset:', err); } }; const handleSavePreset = async () => { if (!newPresetName.trim()) { return; } try { const presetData = { name: newPresetName.trim(), description: newPresetDescription.trim(), icon: newPresetIcon, filters: { ...currentFilters } }; if (editingPreset) { await updatePreset(editingPreset.id, presetData); } else { await savePreset(presetData); } setShowSaveDialog(false); setEditingPreset(null); setNewPresetName(''); setNewPresetDescription(''); setNewPresetIcon('📁'); } catch (err) { console.error('Failed to save preset:', err); } }; const handleDeletePreset = async (presetId: string) => { if (!confirm('Are you sure you want to delete this preset?')) return; try { await deletePreset(presetId); } catch (err) { console.error('Failed to delete preset:', err); } }; const handleEditPreset = (preset: any) => { setEditingPreset(preset); setNewPresetName(preset.name); setNewPresetDescription(preset.description); setNewPresetIcon(preset.icon); setShowSaveDialog(true); }; const handleExportPresets = () => { try { const exportData = exportPresets(false); // Exclude built-ins const blob = new Blob([exportData], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `filter-presets-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (err) { console.error('Failed to export presets:', err); } }; const handleImportPresets = async () => { if (!importData.trim()) { return; } try { const results = await importPresets(importData, { overwrite: false, skipDuplicates: true }); setImportResults(results); setImportData(''); } catch (err) { console.error('Failed to import presets:', err); } }; const handleResetToDefaults = async () => { if (!confirm('This will delete all custom presets and reset to built-in presets only. Are you sure?')) return; try { await resetToDefaults(); } catch (err) { console.error('Failed to reset presets:', err); } }; const getPresetPreview = (filters: AdvancedFilters): string => { const parts = []; if (filters.category) { const categories = Array.isArray(filters.category) ? filters.category : [filters.category]; parts.push(`Category: ${categories.join(', ')}`); } if (filters.priority?.length) { parts.push(`Priority: ${filters.priority.join(', ')}`); } if (filters.tags?.length) { parts.push(`Tags: ${filters.tags.slice(0, 3).join(', ')}${filters.tags.length > 3 ? '...' : ''}`); } if (filters.hasNoTags) { parts.push('No tags'); } if (filters.dateRange) { const start = new Date(filters.dateRange.start).toLocaleDateString(); const end = new Date(filters.dateRange.end).toLocaleDateString(); parts.push(`Date: ${start} - ${end}`); } if (filters.project) { parts.push(`Project: ${filters.project}`); } if (filters.contentType) { parts.push(`Type: ${filters.contentType}`); } return parts.length > 0 ? parts.join(' • ') : 'No filters'; }; const hasActiveFilters = () => { return Object.keys(currentFilters).some(key => { const value = currentFilters[key]; if (Array.isArray(value)) return value.length > 0; if (typeof value === 'object' && value !== null) return Object.keys(value).length > 0; if (typeof value === 'string') return value.trim().length > 0; return Boolean(value); }); }; // builtInPresets and customPresets are already provided by the hook return (
{/* Quick Apply Dropdown */} Built-in Presets {builtInPresets.map((preset) => ( handleApplyPreset(preset.id)} className="flex items-start gap-3 p-3 cursor-pointer" > {preset.icon}
{preset.name} {preset.usageCount > 0 && ( {preset.usageCount} )}
{preset.description}
{getPresetPreview(preset.filters)}
))} {customPresets.length > 0 && ( <> Custom Presets {customPresets.slice(0, 8).map((preset) => ( handleApplyPreset(preset.id)} className="flex items-start gap-3 p-3 cursor-pointer" > {preset.icon}
{preset.name} {preset.usageCount > 0 && ( {preset.usageCount} )}
{preset.description || 'Custom filter preset'}
{getPresetPreview(preset.filters)}
))} {customPresets.length > 8 && ( setShowManageDialog(true)} className="text-center text-muted-foreground" > +{customPresets.length - 8} more presets... )} )} setShowSaveDialog(true)} disabled={!hasActiveFilters()} className="flex items-center gap-2" > Save Current Filters setShowManageDialog(true)} className="flex items-center gap-2" > Manage Presets
{/* Save Current Filters Button */} {hasActiveFilters() && ( )} {/* Error Display */} {error && (
{error}
)} {/* Save Preset Dialog */} {editingPreset ? 'Edit Preset' : 'Save Filter Preset'} Save your current filter settings as a preset for quick access later.
setNewPresetName(e.target.value)} placeholder="e.g., Recent Work Items" className="mt-1" />