homelab-dashboard/app/components/FilterBar.tsx
Bilal Teke b2d2b8b2f3 v3.1
2026-04-15 21:54:46 +02:00

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>
);
}