Fix server delete function and Implement pause/resume functionality

- Implement the server delete functionality in the server table and dropdown menu.
- Implement pause and resume functionality for server monitoring in the server table dropdown menu.
This commit is contained in:
Tola Leng
2025-07-17 23:03:40 +07:00
parent 6906a65952
commit 0cad374616
2 changed files with 310 additions and 190 deletions
@@ -8,11 +8,14 @@ import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
import { RefreshCw, Search, Eye, Activity, MoreHorizontal, Pause, Play, Edit, Trash2 } from "lucide-react"; import { RefreshCw, Search, Eye, Activity, MoreHorizontal, Pause, Play, Edit, Trash2 } from "lucide-react";
import { Server } from "@/types/server.types"; import { Server } from "@/types/server.types";
import { ServerStatusBadge } from "./ServerStatusBadge"; import { ServerStatusBadge } from "./ServerStatusBadge";
import { OSTypeIcon } from "./OSTypeIcon"; import { OSTypeIcon } from "./OSTypeIcon";
import { serverService } from "@/services/serverService"; import { serverService } from "@/services/serverService";
import { useToast } from "@/hooks/use-toast";
import { pb } from "@/lib/pocketbase";
interface ServerTableProps { interface ServerTableProps {
servers: Server[]; servers: Server[];
@@ -22,8 +25,12 @@ interface ServerTableProps {
export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps) => { export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps) => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [pausedServers, setPausedServers] = useState<Set<string>>(new Set()); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [selectedServer, setSelectedServer] = useState<Server | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const [pausingServers, setPausingServers] = useState<Set<string>>(new Set());
const navigate = useNavigate(); const navigate = useNavigate();
const { toast } = useToast();
const filteredServers = servers.filter(server => const filteredServers = servers.filter(server =>
server.name.toLowerCase().includes(searchTerm.toLowerCase()) || server.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
@@ -39,29 +46,105 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
navigate(`/container-monitoring/${serverId}`); navigate(`/container-monitoring/${serverId}`);
}; };
const handlePauseResume = (serverId: string) => { const handlePauseResume = async (server: Server) => {
const isPaused = pausedServers.has(serverId); const serverId = server.id;
const isPaused = server.status === "paused";
if (pausingServers.has(serverId)) {
return; // Already processing this server
}
try {
setPausingServers(prev => new Set(prev).add(serverId));
if (isPaused) { if (isPaused) {
setPausedServers(prev => { // Resume monitoring
await pb.collection('servers').update(serverId, {
status: "up",
last_checked: new Date().toISOString()
});
toast({
title: "Server resumed",
description: `Monitoring resumed for ${server.name}`,
});
console.log(`Resume server monitoring: ${serverId}`);
} else {
// Pause monitoring
await pb.collection('servers').update(serverId, {
status: "paused",
last_checked: new Date().toISOString()
});
toast({
title: "Server paused",
description: `Monitoring paused for ${server.name}`,
});
console.log(`Pause server monitoring: ${serverId}`);
}
// Refresh the server list to show updated status
onRefresh();
} catch (error) {
console.error('Error updating server status:', error);
toast({
variant: "destructive",
title: "Error",
description: `Failed to ${isPaused ? 'resume' : 'pause'} server monitoring. Please try again.`,
});
} finally {
setPausingServers(prev => {
const newSet = new Set(prev); const newSet = new Set(prev);
newSet.delete(serverId); newSet.delete(serverId);
return newSet; return newSet;
}); });
// console.log('Resume server monitoring:', serverId);
} else {
setPausedServers(prev => new Set(prev).add(serverId));
// console.log('Pause server monitoring:', serverId);
} }
}; };
const handleEdit = (serverId: string) => { const handleEdit = (serverId: string) => {
// TODO: Implement edit functionality // TODO: Implement edit functionality
// console.log('Edit server:', serverId); console.log('Edit server:', serverId);
}; };
const handleDelete = (serverId: string) => { const handleDelete = (server: Server) => {
// TODO: Implement delete functionality setSelectedServer(server);
// console.log('Delete server:', serverId); setDeleteDialogOpen(true);
};
const confirmDelete = async () => {
if (!selectedServer || isDeleting) return;
try {
setIsDeleting(true);
// Delete the server from the database
await pb.collection('servers').delete(selectedServer.id);
toast({
title: "Server deleted",
description: `${selectedServer.name} has been deleted successfully.`,
});
// Refresh the server list
onRefresh();
// Close the dialog
setDeleteDialogOpen(false);
setSelectedServer(null);
} catch (error) {
console.error('Error deleting server:', error);
toast({
variant: "destructive",
title: "Error",
description: "Failed to delete server. Please try again.",
});
} finally {
setIsDeleting(false);
}
}; };
if (isLoading) { if (isLoading) {
@@ -81,6 +164,7 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
} }
return ( return (
<>
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
@@ -128,14 +212,14 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
const cpuUsage = server.cpu_usage || 0; const cpuUsage = server.cpu_usage || 0;
const memoryUsage = server.ram_total > 0 ? (server.ram_used / server.ram_total) * 100 : 0; const memoryUsage = server.ram_total > 0 ? (server.ram_used / server.ram_total) * 100 : 0;
const diskUsage = server.disk_total > 0 ? (server.disk_used / server.disk_total) * 100 : 0; const diskUsage = server.disk_total > 0 ? (server.disk_used / server.disk_total) * 100 : 0;
const isPaused = pausedServers.has(server.id); const isPaused = server.status === "paused";
const isProcessing = pausingServers.has(server.id);
return ( return (
<TableRow key={server.id} className="hover:bg-muted/50"> <TableRow key={server.id} className="hover:bg-muted/50">
<TableCell> <TableCell>
<div> <div>
<div className="font-medium">{server.name}</div> <div className="font-medium">{server.name}</div>
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>
@@ -160,7 +244,7 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
value={cpuUsage} value={cpuUsage}
className="h-2" className="h-2"
indicatorClassName={ indicatorClassName={
cpuUsage > 90 ? "bg-red-00" : cpuUsage > 90 ? "bg-red-500" :
cpuUsage > 75 ? "bg-orange-500" : cpuUsage > 75 ? "bg-orange-500" :
cpuUsage > 60 ? "bg-yellow-500" : "bg-green-500" cpuUsage > 60 ? "bg-yellow-500" : "bg-green-500"
} }
@@ -211,9 +295,13 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
<TableCell className="text-right"> <TableCell className="text-right">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0"> <Button variant="ghost" className="h-8 w-8 p-0" disabled={isProcessing}>
<span className="sr-only">Open menu</span> <span className="sr-only">Open menu</span>
{isProcessing ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<MoreHorizontal className="h-4 w-4" /> <MoreHorizontal className="h-4 w-4" />
)}
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[200px]"> <DropdownMenuContent align="end" className="w-[200px]">
@@ -228,7 +316,10 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
</DropdownMenuItem> </DropdownMenuItem>
)} )}
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => handlePauseResume(server.id)}> <DropdownMenuItem
onClick={() => handlePauseResume(server)}
disabled={isProcessing}
>
{isPaused ? ( {isPaused ? (
<> <>
<Play className="mr-2 h-4 w-4" /> <Play className="mr-2 h-4 w-4" />
@@ -247,7 +338,7 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
Edit Server Edit Server
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => handleDelete(server.id)} onClick={() => handleDelete(server)}
className="text-red-600 focus:text-red-600" className="text-red-600 focus:text-red-600"
> >
<Trash2 className="mr-2 h-4 w-4" /> <Trash2 className="mr-2 h-4 w-4" />
@@ -265,5 +356,34 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps)
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* Delete Confirmation Dialog */}
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure you want to delete this server?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete{' '}
<span className="font-semibold text-foreground">
{selectedServer?.name}
</span>{' '}
and all of its monitoring data.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeleting}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
onClick={confirmDelete}
disabled={isDeleting}
className="bg-red-600 text-white hover:bg-red-700"
>
{isDeleting ? "Deleting..." : "Delete"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
); );
}; };
+1 -1
View File
@@ -8,7 +8,7 @@ export interface Server {
hostname: string; hostname: string;
ip_address: string; ip_address: string;
os_type: string; os_type: string;
status: 'up' | 'down' | 'warning'; status: 'up' | 'down' | 'warning' | 'paused';
uptime: string; uptime: string;
ram_total: number; ram_total: number;
ram_used: number; ram_used: number;