mirror of
https://github.com/crocofied/CoreControl.git
synced 2025-12-29 16:14:43 +00:00
Add custom items per page dropdowns
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user