2026-06-13 02:20:09 +00:00
|
|
|
import React, { useState, useEffect } from 'react';
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
import { Button } from '@/components/ui/index';
|
|
|
|
|
import { RefreshCw } from 'lucide-react';
|
|
|
|
|
import { RemotesList } from '@/components/Proxmox';
|
|
|
|
|
import { AddRemoteForm } from '@/components/Proxmox';
|
|
|
|
|
import { EditRemoteForm } from '@/components/Proxmox';
|
|
|
|
|
import { RemoveRemoteDialog } from '@/components/Proxmox';
|
|
|
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/index';
|
fix: address PR review findings — race condition, real ping, atomic edit, listener cleanup
Race condition in get_proxmox_client_for_cluster: two concurrent callers
for an uncached cluster could both authenticate and insert, with the second
overwriting the first. Re-check under write lock before inserting so the
later caller returns the already-stored client instead of overwriting it.
handleConnectRemote used getProxmoxCluster (a DB-only lookup) to set status
'connected', which passed even when the Proxmox API was unreachable. Replace
with pingProxmoxCluster, a new command that authenticates and calls
GET /api2/json/version, providing a real end-to-end connectivity test.
handleEditRemote used remove-then-add, leaving a gap where the record was
absent and silently lost if addProxmoxCluster failed. Replace with
updateProxmoxCluster, a new command that issues a single SQL UPDATE (plus
in-memory pool eviction) so the record is never transiently missing.
ActionsMenu useEffect added the mousedown listener only when open=true but
the dependency array contained open, causing ambiguity about cleanup timing.
Attach the listener unconditionally on mount (empty dep array) so there is
always exactly one add and one remove with no conditional branches.
New Rust tests cover update_proxmox_cluster not-found logic and ping error
message format (420 Rust + 386 frontend, zero failures).
2026-06-20 03:26:33 +00:00
|
|
|
import { listProxmoxClusters, addProxmoxCluster, removeProxmoxCluster, updateProxmoxCluster, pingProxmoxCluster } from '@/lib/proxmoxClient';
|
2026-06-13 02:20:09 +00:00
|
|
|
import { ClusterType } from '@/lib/domain';
|
fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
import { toast } from 'sonner';
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
|
|
|
|
|
interface RemoteInfo {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
url: string;
|
|
|
|
|
username: string;
|
|
|
|
|
type: 'pve' | 'pbs';
|
|
|
|
|
status: 'connected' | 'disconnected' | 'error';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ProxmoxRemotesPage() {
|
2026-06-11 20:55:04 +00:00
|
|
|
const [remotes, setRemotes] = useState<RemoteInfo[]>([]);
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
const [showAddDialog, setShowAddDialog] = useState(false);
|
|
|
|
|
const [editingRemote, setEditingRemote] = useState<RemoteInfo | null>(null);
|
|
|
|
|
const [removingRemote, setRemovingRemote] = useState<RemoteInfo | null>(null);
|
|
|
|
|
|
2026-06-13 02:20:09 +00:00
|
|
|
const loadRemotes = async () => {
|
2026-06-11 20:55:04 +00:00
|
|
|
try {
|
2026-06-13 02:20:09 +00:00
|
|
|
const clusters = await listProxmoxClusters();
|
|
|
|
|
// TODO: Implement actual status checking via backend connection test
|
|
|
|
|
const remotesList: RemoteInfo[] = clusters.map((c) => ({
|
2026-06-11 20:55:04 +00:00
|
|
|
id: c.id,
|
|
|
|
|
name: c.name,
|
2026-06-13 02:20:09 +00:00
|
|
|
url: c.url,
|
2026-06-11 20:55:04 +00:00
|
|
|
username: c.username,
|
|
|
|
|
type: c.clusterType === 've' ? 'pve' : 'pbs',
|
2026-06-20 03:13:48 +00:00
|
|
|
status: (c.connected ? 'connected' : 'disconnected') as RemoteInfo['status'],
|
2026-06-11 20:55:04 +00:00
|
|
|
}));
|
2026-06-13 02:20:09 +00:00
|
|
|
setRemotes(remotesList);
|
2026-06-11 20:55:04 +00:00
|
|
|
} catch (err) {
|
2026-06-13 02:20:09 +00:00
|
|
|
console.error('Failed to load remotes:', err);
|
2026-06-11 20:55:04 +00:00
|
|
|
}
|
2026-06-13 02:20:09 +00:00
|
|
|
};
|
2026-06-11 20:55:04 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-06-13 02:20:09 +00:00
|
|
|
void loadRemotes();
|
|
|
|
|
}, []);
|
2026-06-11 20:55:04 +00:00
|
|
|
|
2026-06-13 02:20:09 +00:00
|
|
|
const generateId = (): string => {
|
|
|
|
|
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-14 04:49:15 +00:00
|
|
|
/**
|
|
|
|
|
* Helper function to parse a Proxmox URL and extract hostname and port.
|
|
|
|
|
* Handles URLs with or without explicit port numbers.
|
|
|
|
|
*
|
|
|
|
|
* @param url - The full URL (e.g., "https://172.0.0.18:8006" or "https://pve.example.com")
|
|
|
|
|
* @param type - The cluster type ('pve' or 'pbs') to determine default port
|
|
|
|
|
* @returns Object with hostname (stripped of protocol and port) and port number
|
|
|
|
|
*/
|
|
|
|
|
const parseRemoteUrl = (url: string, type: 'pve' | 'pbs'): { hostname: string; port: number } => {
|
|
|
|
|
let hostname = url.replace(/^https?:\/\//, '');
|
|
|
|
|
let port = type === 'pve' ? 8006 : 8007;
|
|
|
|
|
|
|
|
|
|
const portMatch = hostname.match(/:(\d+)$/);
|
|
|
|
|
if (portMatch) {
|
|
|
|
|
port = parseInt(portMatch[1], 10);
|
|
|
|
|
hostname = hostname.replace(/:\d+$/, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { hostname, port };
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-13 02:20:09 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2026-06-11 20:55:04 +00:00
|
|
|
const handleAddRemote = async (config: any) => {
|
|
|
|
|
try {
|
2026-06-13 02:20:09 +00:00
|
|
|
const clusterType = config.type === 'pve' ? 've' : 'pbs';
|
2026-06-14 04:49:15 +00:00
|
|
|
const { hostname, port } = parseRemoteUrl(config.url, config.type);
|
2026-06-14 04:27:08 +00:00
|
|
|
|
2026-06-13 02:20:09 +00:00
|
|
|
const id = config.id || generateId();
|
|
|
|
|
await addProxmoxCluster(
|
|
|
|
|
id,
|
2026-06-11 20:55:04 +00:00
|
|
|
config.name,
|
2026-06-13 02:20:09 +00:00
|
|
|
clusterType as ClusterType,
|
2026-06-14 04:27:08 +00:00
|
|
|
{ url: hostname, port },
|
2026-06-11 20:55:04 +00:00
|
|
|
config.username,
|
|
|
|
|
config.password || ''
|
|
|
|
|
);
|
2026-06-13 02:20:09 +00:00
|
|
|
await loadRemotes();
|
2026-06-11 20:55:04 +00:00
|
|
|
setShowAddDialog(false);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to add remote:', err);
|
fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
toast.error('Failed to add remote: ' + String(err));
|
|
|
|
|
throw err;
|
2026-06-11 20:55:04 +00:00
|
|
|
}
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
};
|
|
|
|
|
|
2026-06-13 02:20:09 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2026-06-11 20:55:04 +00:00
|
|
|
const handleEditRemote = async (config: any) => {
|
|
|
|
|
try {
|
2026-06-13 02:20:09 +00:00
|
|
|
const clusterType = config.type === 'pve' ? 've' : 'pbs';
|
2026-06-14 04:49:15 +00:00
|
|
|
const { hostname, port } = parseRemoteUrl(config.url, config.type);
|
2026-06-14 04:27:08 +00:00
|
|
|
|
fix: address PR review findings — race condition, real ping, atomic edit, listener cleanup
Race condition in get_proxmox_client_for_cluster: two concurrent callers
for an uncached cluster could both authenticate and insert, with the second
overwriting the first. Re-check under write lock before inserting so the
later caller returns the already-stored client instead of overwriting it.
handleConnectRemote used getProxmoxCluster (a DB-only lookup) to set status
'connected', which passed even when the Proxmox API was unreachable. Replace
with pingProxmoxCluster, a new command that authenticates and calls
GET /api2/json/version, providing a real end-to-end connectivity test.
handleEditRemote used remove-then-add, leaving a gap where the record was
absent and silently lost if addProxmoxCluster failed. Replace with
updateProxmoxCluster, a new command that issues a single SQL UPDATE (plus
in-memory pool eviction) so the record is never transiently missing.
ActionsMenu useEffect added the mousedown listener only when open=true but
the dependency array contained open, causing ambiguity about cleanup timing.
Attach the listener unconditionally on mount (empty dep array) so there is
always exactly one add and one remove with no conditional branches.
New Rust tests cover update_proxmox_cluster not-found logic and ping error
message format (420 Rust + 386 frontend, zero failures).
2026-06-20 03:26:33 +00:00
|
|
|
await updateProxmoxCluster(
|
2026-06-11 20:55:04 +00:00
|
|
|
config.id,
|
|
|
|
|
config.name,
|
2026-06-13 02:20:09 +00:00
|
|
|
clusterType as ClusterType,
|
2026-06-14 04:27:08 +00:00
|
|
|
{ url: hostname, port },
|
2026-06-11 20:55:04 +00:00
|
|
|
config.username,
|
2026-06-13 02:20:09 +00:00
|
|
|
config.password || ''
|
2026-06-11 20:55:04 +00:00
|
|
|
);
|
2026-06-13 02:20:09 +00:00
|
|
|
await loadRemotes();
|
2026-06-11 20:55:04 +00:00
|
|
|
setEditingRemote(null);
|
|
|
|
|
} catch (err) {
|
2026-06-13 02:20:09 +00:00
|
|
|
console.error('Failed to edit remote:', err);
|
fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
toast.error('Failed to edit remote: ' + String(err));
|
|
|
|
|
throw err;
|
2026-06-11 20:55:04 +00:00
|
|
|
}
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
};
|
|
|
|
|
|
2026-06-11 20:55:04 +00:00
|
|
|
const handleRemoveRemote = async () => {
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
if (removingRemote) {
|
2026-06-11 20:55:04 +00:00
|
|
|
try {
|
2026-06-13 02:20:09 +00:00
|
|
|
await removeProxmoxCluster(removingRemote.id);
|
|
|
|
|
await loadRemotes();
|
2026-06-11 20:55:04 +00:00
|
|
|
setRemovingRemote(null);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to remove remote:', err);
|
fix(proxmox): remove dummy data, fix add-remote, fix updater
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
2026-06-13 22:33:23 +00:00
|
|
|
toast.error('Failed to remove remote: ' + String(err));
|
2026-06-11 20:55:04 +00:00
|
|
|
}
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-20 03:13:48 +00:00
|
|
|
const handleConnectRemote = async (remote: RemoteInfo) => {
|
|
|
|
|
try {
|
|
|
|
|
toast.info(`Testing connection to ${remote.name}...`);
|
fix: address PR review findings — race condition, real ping, atomic edit, listener cleanup
Race condition in get_proxmox_client_for_cluster: two concurrent callers
for an uncached cluster could both authenticate and insert, with the second
overwriting the first. Re-check under write lock before inserting so the
later caller returns the already-stored client instead of overwriting it.
handleConnectRemote used getProxmoxCluster (a DB-only lookup) to set status
'connected', which passed even when the Proxmox API was unreachable. Replace
with pingProxmoxCluster, a new command that authenticates and calls
GET /api2/json/version, providing a real end-to-end connectivity test.
handleEditRemote used remove-then-add, leaving a gap where the record was
absent and silently lost if addProxmoxCluster failed. Replace with
updateProxmoxCluster, a new command that issues a single SQL UPDATE (plus
in-memory pool eviction) so the record is never transiently missing.
ActionsMenu useEffect added the mousedown listener only when open=true but
the dependency array contained open, causing ambiguity about cleanup timing.
Attach the listener unconditionally on mount (empty dep array) so there is
always exactly one add and one remove with no conditional branches.
New Rust tests cover update_proxmox_cluster not-found logic and ping error
message format (420 Rust + 386 frontend, zero failures).
2026-06-20 03:26:33 +00:00
|
|
|
await pingProxmoxCluster(remote.id);
|
|
|
|
|
toast.success(`Connected to ${remote.name}`);
|
|
|
|
|
setRemotes((prev) =>
|
|
|
|
|
prev.map((r) => (r.id === remote.id ? { ...r, status: 'connected' } : r))
|
|
|
|
|
);
|
2026-06-20 03:13:48 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to connect remote:', err);
|
|
|
|
|
toast.error('Connection failed: ' + String(err));
|
|
|
|
|
setRemotes((prev) =>
|
|
|
|
|
prev.map((r) => (r.id === remote.id ? { ...r, status: 'error' } : r))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDisconnectRemote = (remote: RemoteInfo) => {
|
|
|
|
|
setRemotes((prev) =>
|
|
|
|
|
prev.map((r) => (r.id === remote.id ? { ...r, status: 'disconnected' } : r))
|
|
|
|
|
);
|
|
|
|
|
toast.info(`Disconnected from ${remote.name}`);
|
|
|
|
|
};
|
|
|
|
|
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-2xl font-bold">Remotes</h1>
|
|
|
|
|
<p className="text-muted-foreground">Manage Proxmox VE and Backup Server connections</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex space-x-2">
|
2026-06-13 02:52:05 +00:00
|
|
|
<Button variant="outline" size="sm" onClick={() => { void loadRemotes(); }}>
|
2026-06-13 02:20:09 +00:00
|
|
|
<RefreshCw className="mr-2 h-4 w-4" />
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
Refresh
|
|
|
|
|
</Button>
|
|
|
|
|
<Button onClick={() => setShowAddDialog(true)}>
|
|
|
|
|
<span className="mr-2 h-4 w-4">+</span>
|
|
|
|
|
Add Remote
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<RemotesList
|
|
|
|
|
remotes={remotes}
|
2026-06-13 02:52:05 +00:00
|
|
|
onRefresh={() => { void loadRemotes(); }}
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
onEdit={(remote) => {
|
|
|
|
|
setEditingRemote(remote as RemoteInfo | null);
|
|
|
|
|
}}
|
|
|
|
|
onDelete={(remote) => {
|
|
|
|
|
setRemovingRemote(remote as RemoteInfo | null);
|
|
|
|
|
}}
|
2026-06-20 03:13:48 +00:00
|
|
|
onConnect={(remote) => {
|
|
|
|
|
void handleConnectRemote(remote as RemoteInfo);
|
|
|
|
|
}}
|
|
|
|
|
onDisconnect={(remote) => {
|
|
|
|
|
handleDisconnectRemote(remote as RemoteInfo);
|
|
|
|
|
}}
|
feat: implement 100% Proxmox PDM feature parity - UI components
- Add 8 new UI components: AclList, AddRemoteForm, ContainerConsole, ContainerOverview, EditRemoteForm, RemoveRemoteDialog, VMConsole, VMOverview
- Add 13 Proxmox management pages: ACLPage, BackupPage, CephPage, CertificatesPage, ContainersPage, FirewallPage, HAPage, NetworkPage, RemotesPage, SDNPage, StoragePage, TasksPage, VMsPage
- Add 13 new routes to App.tsx for Proxmox management pages
- All components use existing UI components from src/components/ui/index.tsx
- TypeScript and ESLint pass with 0 errors
- All tests pass
Files changed: 24 files, +2199 insertions
2026-06-11 18:47:09 +00:00
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{showAddDialog && (
|
|
|
|
|
<Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>
|
|
|
|
|
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Add New Remote</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<AddRemoteForm onAdd={handleAddRemote} onCancel={() => setShowAddDialog(false)} />
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{editingRemote !== null && (
|
|
|
|
|
<Dialog open={true} onOpenChange={() => setEditingRemote(null)}>
|
|
|
|
|
<DialogContent className="max-w-2xl">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Edit Remote</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<EditRemoteForm
|
|
|
|
|
remote={editingRemote}
|
|
|
|
|
onSave={handleEditRemote}
|
|
|
|
|
onCancel={() => setEditingRemote(null)}
|
|
|
|
|
/>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{removingRemote !== null && (
|
|
|
|
|
<Dialog open={true} onOpenChange={() => setRemovingRemote(null)}>
|
|
|
|
|
<DialogContent>
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Remove Remote</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<RemoveRemoteDialog
|
|
|
|
|
remote={removingRemote}
|
|
|
|
|
onConfirm={handleRemoveRemote}
|
|
|
|
|
onCancel={() => setRemovingRemote(null)}
|
|
|
|
|
/>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|