heymenn's picture
initial commit
4b1a31e
import React, { useState, useEffect } from 'react';
import { MeetingDoc } from '../types';
import { Filter, X } from 'lucide-react';
interface DocFilterBarProps {
docs: MeetingDoc[];
onFilterChange: (filteredDocs: MeetingDoc[]) => void;
}
const DocFilterBar: React.FC<DocFilterBarProps> = ({ docs, onFilterChange }) => {
// Extract unique values
const allTypes = Array.from(new Set<string>(docs.map(d => d.Type))).sort();
const allStatuses = Array.from(new Set<string>(docs.map(d => d['TDoc Status']))).sort();
const allAgendas = Array.from(new Set<string>(docs.map(d => d['Agenda item description']))).sort();
// Default selections
const defaultTypes = ["LS out", "LS in", "pCR", "CR"];
const defaultStatuses = ["noted", "revised", "approved", "agreed"];
// State
const [selectedTypes, setSelectedTypes] = useState<string[]>([]);
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
const [selectedAgendas, setSelectedAgendas] = useState<string[]>([]);
// Initialize defaults when docs change
useEffect(() => {
if (docs.length > 0) {
const initialTypes = allTypes.filter(t => defaultTypes.includes(t));
// If no default types match, maybe select all or none? Let's stick to defaults.
// If the default list is empty (none found), user can select manually.
setSelectedTypes(initialTypes.length > 0 ? initialTypes : []);
const initialStatuses = allStatuses.filter(s => defaultStatuses.includes(s));
setSelectedStatuses(initialStatuses.length > 0 ? initialStatuses : []);
// Select all agendas by default
setSelectedAgendas(allAgendas);
}
}, [docs]); // Re-run when docs source changes
// Filter logic
useEffect(() => {
const filtered = docs.filter(doc => {
const typeMatch = selectedTypes.length === 0 || selectedTypes.includes(doc.Type);
const statusMatch = selectedStatuses.length === 0 || selectedStatuses.includes(doc['TDoc Status']);
const agendaMatch = selectedAgendas.length === 0 || selectedAgendas.includes(doc['Agenda item description']);
return typeMatch && statusMatch && agendaMatch;
});
onFilterChange(filtered);
}, [selectedTypes, selectedStatuses, selectedAgendas, docs]);
const toggleSelection = (item: string, current: string[], setter: (val: string[]) => void) => {
if (current.includes(item)) {
setter(current.filter(i => i !== item));
} else {
setter([...current, item]);
}
};
const toggleAll = (all: string[], current: string[], setter: (val: string[]) => void) => {
if (current.length === all.length) {
setter([]);
} else {
setter(all);
}
};
if (docs.length === 0) return null;
return (
<div className="bg-white p-4 rounded-xl shadow-sm border border-slate-200 mb-6 animate-fade-in">
<div className="flex items-center mb-3">
<Filter className="w-4 h-4 text-slate-500 mr-2" />
<h3 className="text-sm font-semibold text-slate-800">Filter Documents</h3>
<span className="ml-auto text-xs text-slate-500">{docs.length} total docs</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* Type Filter */}
<div>
<div className="flex justify-between items-center mb-2">
<label className="text-xs font-medium text-slate-600 uppercase tracking-wider">Type</label>
<button
onClick={() => toggleAll(allTypes, selectedTypes, setSelectedTypes)}
className="text-[10px] text-blue-600 hover:text-blue-800"
>
{selectedTypes.length === allTypes.length ? 'Clear' : 'All'}
</button>
</div>
<div className="max-h-32 overflow-y-auto custom-scrollbar space-y-1">
{allTypes.map(type => (
<label key={type} className="flex items-center space-x-2 cursor-pointer hover:bg-slate-50 p-1 rounded">
<input
type="checkbox"
checked={selectedTypes.includes(type)}
onChange={() => toggleSelection(type, selectedTypes, setSelectedTypes)}
className="rounded border-slate-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5"
/>
<span className="text-xs text-slate-700 truncate" title={type}>{type}</span>
</label>
))}
</div>
</div>
{/* Status Filter */}
<div>
<div className="flex justify-between items-center mb-2">
<label className="text-xs font-medium text-slate-600 uppercase tracking-wider">Status</label>
<button
onClick={() => toggleAll(allStatuses, selectedStatuses, setSelectedStatuses)}
className="text-[10px] text-blue-600 hover:text-blue-800"
>
{selectedStatuses.length === allStatuses.length ? 'Clear' : 'All'}
</button>
</div>
<div className="max-h-32 overflow-y-auto custom-scrollbar space-y-1">
{allStatuses.map(status => (
<label key={status} className="flex items-center space-x-2 cursor-pointer hover:bg-slate-50 p-1 rounded">
<input
type="checkbox"
checked={selectedStatuses.includes(status)}
onChange={() => toggleSelection(status, selectedStatuses, setSelectedStatuses)}
className="rounded border-slate-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5"
/>
<span className="text-xs text-slate-700 truncate" title={status}>{status}</span>
</label>
))}
</div>
</div>
{/* Agenda Filter */}
<div>
<div className="flex justify-between items-center mb-2">
<label className="text-xs font-medium text-slate-600 uppercase tracking-wider">Agenda Item</label>
<button
onClick={() => toggleAll(allAgendas, selectedAgendas, setSelectedAgendas)}
className="text-[10px] text-blue-600 hover:text-blue-800"
>
{selectedAgendas.length === allAgendas.length ? 'Clear' : 'All'}
</button>
</div>
<div className="max-h-32 overflow-y-auto custom-scrollbar space-y-1">
{allAgendas.map(agenda => (
<label key={agenda} className="flex items-center space-x-2 cursor-pointer hover:bg-slate-50 p-1 rounded">
<input
type="checkbox"
checked={selectedAgendas.includes(agenda)}
onChange={() => toggleSelection(agenda, selectedAgendas, setSelectedAgendas)}
className="rounded border-slate-300 text-blue-600 focus:ring-blue-500 h-3.5 w-3.5"
/>
<span className="text-xs text-slate-700 truncate" title={agenda}>{agenda}</span>
</label>
))}
</div>
</div>
</div>
</div>
);
};
export default DocFilterBar;