114 lines
4.1 KiB
TypeScript
114 lines
4.1 KiB
TypeScript
'use client';
|
|
|
|
import { ServiceStatus } from '@/src/types/service';
|
|
|
|
interface FilterBarProps {
|
|
categories: string[];
|
|
statuses: ServiceStatus[];
|
|
selectedCategories: string[];
|
|
selectedStatuses: ServiceStatus[];
|
|
onCategoryChange: (category: string) => void;
|
|
onStatusChange: (status: ServiceStatus) => void;
|
|
onReset: () => void;
|
|
}
|
|
|
|
/**
|
|
* FilterBar - Filterleiste zum Filtern von Services nach Kategorie und Status
|
|
* @param categories - Verfügbare Kategorien
|
|
* @param statuses - Verfügbare Status
|
|
* @param selectedCategories - Aktuell ausgewählte Kategorien
|
|
* @param selectedStatuses - Aktuell ausgewählte Status
|
|
* @param onCategoryChange - Callback beim Ändern einer Kategorie
|
|
* @param onStatusChange - Callback beim Ändern eines Status
|
|
* @param onReset - Callback zum Zurücksetzen aller Filter
|
|
*/
|
|
export function FilterBar({
|
|
categories,
|
|
statuses,
|
|
selectedCategories,
|
|
selectedStatuses,
|
|
onCategoryChange,
|
|
onStatusChange,
|
|
onReset,
|
|
}: FilterBarProps) {
|
|
const hasActiveFilters =
|
|
selectedCategories.length > 0 || selectedStatuses.length > 0;
|
|
|
|
return (
|
|
<div className="mb-8 p-4 sm:p-6 bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 shadow-sm">
|
|
<div className="space-y-4">
|
|
{/* Kategorie Filter */}
|
|
<div>
|
|
<h3 className="text-sm font-semibold text-slate-900 dark:text-white mb-3">
|
|
Kategorie
|
|
</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{categories.map((category) => (
|
|
<button
|
|
key={category}
|
|
onClick={() => onCategoryChange(category)}
|
|
className={`px-3 py-1.5 rounded-full text-sm font-medium transition-colors ${
|
|
selectedCategories.includes(category)
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-600'
|
|
}`}
|
|
aria-pressed={selectedCategories.includes(category)}
|
|
>
|
|
{category}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Status Filter */}
|
|
<div>
|
|
<h3 className="text-sm font-semibold text-slate-900 dark:text-white mb-3">
|
|
Status
|
|
</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{statuses.map((status) => (
|
|
<button
|
|
key={status}
|
|
onClick={() => onStatusChange(status)}
|
|
className={`px-3 py-1.5 rounded-full text-sm font-medium transition-colors flex items-center gap-2 ${
|
|
selectedStatuses.includes(status)
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-600'
|
|
}`}
|
|
aria-pressed={selectedStatuses.includes(status)}
|
|
>
|
|
<span
|
|
className={`w-2 h-2 rounded-full ${
|
|
status === 'online'
|
|
? 'bg-green-500'
|
|
: status === 'warning'
|
|
? 'bg-amber-500'
|
|
: 'bg-red-500'
|
|
}`}
|
|
/>
|
|
{status === 'online' && 'Online'}
|
|
{status === 'warning' && 'Warnung'}
|
|
{status === 'offline' && 'Offline'}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Reset Button */}
|
|
{hasActiveFilters && (
|
|
<div className="flex gap-2 pt-2">
|
|
<button
|
|
onClick={onReset}
|
|
className="px-4 py-2 text-sm font-medium text-slate-700 dark:text-slate-300 bg-slate-100 dark:bg-slate-700 hover:bg-slate-200 dark:hover:bg-slate-600 rounded-lg transition-colors"
|
|
>
|
|
Filter zurücksetzen
|
|
</button>
|
|
<span className="text-xs text-slate-500 dark:text-slate-400 flex items-center">
|
|
{selectedCategories.length + selectedStatuses.length} Filter aktiv
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|