Add custom items per page dropdowns

This commit is contained in:
headlessdev
2025-04-25 23:34:52 +02:00
parent d81d8c04ad
commit 677d1c5a58
6 changed files with 470 additions and 34 deletions

View File

@@ -103,6 +103,7 @@ interface Server {
interface GetServersResponse {
servers: Server[]
maxPage: number
totalItems: number
}
interface MonitoringData {
@@ -136,6 +137,7 @@ export default function Dashboard() {
const [maxPage, setMaxPage] = useState<number>(1)
const [servers, setServers] = useState<Server[]>([])
const [loading, setLoading] = useState<boolean>(true)
const [totalItems, setTotalItems] = useState<number>(0)
const [editId, setEditId] = useState<number | null>(null)
const [editHost, setEditHost] = useState<boolean>(false)
@@ -161,11 +163,15 @@ export default function Dashboard() {
const [monitoringInterval, setMonitoringInterval] = useState<NodeJS.Timeout | null>(null);
const savedLayout = Cookies.get("layoutPreference-servers");
const savedItemsPerPage = Cookies.get("itemsPerPage-servers");
const initialIsGridLayout = savedLayout === "grid";
const initialItemsPerPage = initialIsGridLayout ? 6 : 4;
const defaultItemsPerPage = initialIsGridLayout ? 6 : 4;
const initialItemsPerPage = savedItemsPerPage ? parseInt(savedItemsPerPage) : defaultItemsPerPage;
const [isGridLayout, setIsGridLayout] = useState<boolean>(initialIsGridLayout);
const [itemsPerPage, setItemsPerPage] = useState<number>(initialItemsPerPage);
const customInputRef = useRef<HTMLInputElement>(null);
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
const toggleLayout = () => {
const newLayout = !isGridLayout;
@@ -175,7 +181,6 @@ export default function Dashboard() {
path: "/",
sameSite: "strict",
});
setItemsPerPage(newLayout ? 6 : 4); // Update itemsPerPage based on new layout
};
const add = async () => {
@@ -231,6 +236,7 @@ export default function Dashboard() {
setServers(response.data.servers)
console.log(response.data.servers)
setMaxPage(response.data.maxPage)
setTotalItems(response.data.totalItems)
setLoading(false)
} catch (error: any) {
console.log(error.response)
@@ -451,6 +457,61 @@ export default function Dashboard() {
};
}, []);
// Handler für benutzerdefinierte Zahleneingaben mit Verzögerung
const handleItemsPerPageChange = (value: string) => {
// Bestehenden Timer löschen
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
// Neuen Timer setzen
debounceTimerRef.current = setTimeout(() => {
const newItemsPerPage = parseInt(value);
// Sicherstellen, dass der Wert im gültigen Bereich liegt
if (isNaN(newItemsPerPage) || newItemsPerPage < 1) {
toast.error("Bitte eine Zahl zwischen 1 und 100 eingeben");
return;
}
const validatedValue = Math.min(Math.max(newItemsPerPage, 1), 100);
setItemsPerPage(validatedValue);
setCurrentPage(1); // Zurück zur ersten Seite
Cookies.set("itemsPerPage-servers", String(validatedValue), {
expires: 365,
path: "/",
sameSite: "strict",
});
// Daten mit neuer Paginierung abrufen
getServers();
}, 600); // 600ms Verzögerung für bessere Eingabe mehrziffriger Zahlen
};
// Handler für voreingestellte Werte aus dem Dropdown
const handlePresetItemsPerPageChange = (value: string) => {
// Für voreingestellte Werte sofort anwenden
const newItemsPerPage = parseInt(value);
// Nur Standardwerte hier verarbeiten
if ([4, 6, 10, 15, 20, 25].includes(newItemsPerPage)) {
setItemsPerPage(newItemsPerPage);
setCurrentPage(1); // Zurück zur ersten Seite
Cookies.set("itemsPerPage-servers", String(newItemsPerPage), {
expires: 365,
path: "/",
sameSite: "strict",
});
// Daten mit neuer Paginierung abrufen
getServers();
} else {
// Für benutzerdefinierte Werte den verzögerten Handler verwenden
handleItemsPerPageChange(value);
}
};
return (
<SidebarProvider>
<AppSidebar />
@@ -491,6 +552,99 @@ export default function Dashboard() {
<TooltipContent>{isGridLayout ? "Switch to list view" : "Switch to grid view"}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Select
value={String(itemsPerPage)}
onValueChange={handlePresetItemsPerPageChange}
onOpenChange={(open) => {
if (open && customInputRef.current) {
customInputRef.current.value = String(itemsPerPage);
}
}}
>
<SelectTrigger className="w-[140px]">
<SelectValue>
{itemsPerPage} {itemsPerPage === 1 ? 'item' : 'items'}
</SelectValue>
</SelectTrigger>
<SelectContent>
{![4, 6, 10, 15, 20, 25].includes(itemsPerPage) ? (
<SelectItem value={String(itemsPerPage)}>
{itemsPerPage} {itemsPerPage === 1 ? 'item' : 'items'} (custom)
</SelectItem>
) : null}
<SelectItem value="4">4 items</SelectItem>
<SelectItem value="6">6 items</SelectItem>
<SelectItem value="10">10 items</SelectItem>
<SelectItem value="15">15 items</SelectItem>
<SelectItem value="20">20 items</SelectItem>
<SelectItem value="25">25 items</SelectItem>
<div className="p-2 border-t mt-1">
<Label htmlFor="custom-items" className="text-xs font-medium">Custom (1-100)</Label>
<div className="flex items-center gap-2 mt-1">
<Input
id="custom-items"
ref={customInputRef}
type="number"
min="1"
max="100"
className="h-8"
defaultValue={itemsPerPage}
onChange={(e) => {
// Änderung nicht sofort anwenden während des Tippens
// Nur visuelles Feedback für die Validierung
const value = parseInt(e.target.value);
if (isNaN(value) || value < 1 || value > 100) {
e.target.classList.add("border-red-500");
} else {
e.target.classList.remove("border-red-500");
}
}}
onBlur={(e) => {
// Änderung anwenden, wenn das Input den Fokus verliert
const value = parseInt(e.target.value);
if (value >= 1 && value <= 100) {
handleItemsPerPageChange(e.target.value);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
// Bestehenden Debounce-Timer löschen, um sofort anzuwenden
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
debounceTimerRef.current = null;
}
const value = parseInt((e.target as HTMLInputElement).value);
if (value >= 1 && value <= 100) {
// Änderung sofort bei Enter anwenden
const validatedValue = Math.min(Math.max(value, 1), 100);
setItemsPerPage(validatedValue);
setCurrentPage(1);
Cookies.set("itemsPerPage-servers", String(validatedValue), {
expires: 365,
path: "/",
sameSite: "strict",
});
// Kurze Verzögerung hinzufügen für bessere Reaktionsfähigkeit
setTimeout(() => {
getServers();
// Dropdown schließen
document.body.click();
}, 50);
}
}
}}
onClick={(e) => e.stopPropagation()}
/>
<span className="text-xs text-muted-foreground whitespace-nowrap">items</span>
</div>
</div>
</SelectContent>
</Select>
<AlertDialog onOpenChange={setIsAddDialogOpen}>
<AlertDialogTrigger asChild>
<Button variant="outline" size="icon">
@@ -1862,6 +2016,13 @@ export default function Dashboard() {
</div>
)}
<div className="pt-4 pb-4">
<div className="flex justify-between items-center mb-2">
<div className="text-sm text-muted-foreground">
{totalItems > 0
? `Showing ${((currentPage - 1) * itemsPerPage) + 1}-${Math.min(currentPage * itemsPerPage, totalItems)} of ${totalItems} servers`
: "No servers found"}
</div>
</div>
<Pagination>
<PaginationContent>
<PaginationItem>