148 lines
No EOL
5.3 KiB
TypeScript
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>
|
|
);
|
|
} |