homelab-dashboard/app/components/dashboard-filters.tsx
Bilal Teke 764223db6c v3.2
2026-04-16 13:56:28 +02:00

148 lines
No EOL
5.3 KiB
TypeScript

'use client';
import { ServiceStatus } from '@/src/types/service';
interface DashboardFiltersProps {
searchQuery: string;
onSearchChange: (query: string) => void;
selectedCategory: string;
onCategoryChange: (category: string) => void;
selectedStatus: ServiceStatus | 'all';
onStatusChange: (status: ServiceStatus | 'all') => void;
categories: string[];
onRefresh: () => void;
isRefreshing: boolean;
visibleCount: number;
totalCount: number;
lastUpdated: string | null;
}
export function DashboardFilters({
searchQuery,
onSearchChange,
selectedCategory,
onCategoryChange,
selectedStatus,
onStatusChange,
categories,
onRefresh,
isRefreshing,
visibleCount,
totalCount,
lastUpdated,
}: DashboardFiltersProps) {
const formatLastUpdated = (timestamp: string) => {
const date = new Date(timestamp);
return date.toLocaleTimeString('de-DE', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
};
return (
<div className="mb-8 space-y-4">
{/* Search and Refresh Row */}
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between">
<div className="flex-1 max-w-md">
<input
type="text"
placeholder="Suche nach Name oder Beschreibung..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
className="w-full px-4 py-2 border border-slate-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder-slate-500 dark:placeholder-slate-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
/>
</div>
<button
onClick={onRefresh}
disabled={isRefreshing}
className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium rounded-lg transition-colors duration-200"
>
{isRefreshing ? (
<>
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Aktualisiere...
</>
) : (
<>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
Jetzt aktualisieren
</>
)}
</button>
</div>
{/* Filters Row */}
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between">
<div className="flex flex-wrap gap-2">
{/* Category Filter */}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-700 dark:text-slate-300">Kategorie:</span>
<select
value={selectedCategory}
onChange={(e) => onCategoryChange(e.target.value)}
className="px-3 py-1 border border-slate-300 dark:border-slate-600 rounded-md bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">Alle</option>
{categories.map((category) => (
<option key={category} value={category}>
{category}
</option>
))}
</select>
</div>
{/* Status Filter */}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-700 dark:text-slate-300">Status:</span>
<select
value={selectedStatus}
onChange={(e) => onStatusChange(e.target.value as ServiceStatus | 'all')}
className="px-3 py-1 border border-slate-300 dark:border-slate-600 rounded-md bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">Alle</option>
<option value="online">Online</option>
<option value="warning">Warnung</option>
<option value="offline">Offline</option>
<option value="unknown">Unbekannt</option>
</select>
</div>
</div>
{/* Stats */}
<div className="flex items-center gap-4 text-sm text-slate-600 dark:text-slate-400">
<span>
{visibleCount} von {totalCount} Diensten
</span>
{lastUpdated && (
<span>
Letzte Aktualisierung: {formatLastUpdated(lastUpdated)}
</span>
)}
</div>
</div>
</div>
);
}