fix: persist Proxmox settings via localStorage; fix Remotes add/refresh flow

- ProxmoxSettings: load all six settings from localStorage on mount via
  useEffect, wire Save button to write values and show a 2s confirmation,
  wire Reset button to clear keys and restore defaults
- RemotesPage: attach loadRemotes() to the header Refresh button onClick
  and replace the no-op onRefresh prop passed to RemotesList
- EditRemoteForm: add password field to RemoteConfig interface and form
  so handleEditRemote receives a complete config; use DialogFooter for
  consistent button layout
This commit is contained in:
Shaun Arman 2026-06-12 21:52:05 -05:00
parent 38eecaafcf
commit 87e21e243e
3 changed files with 73 additions and 14 deletions

View File

@ -3,12 +3,14 @@ import { Button } from '@/components/ui/index';
import { Input } from '@/components/ui/index'; import { Input } from '@/components/ui/index';
import { Label } from '@/components/ui/index'; import { Label } from '@/components/ui/index';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/index'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/index';
import { DialogFooter } from '@/components/ui/index';
interface RemoteConfig { interface RemoteConfig {
id: string; id: string;
name: string; name: string;
url: string; url: string;
username: string; username: string;
password?: string;
type: 'pve' | 'pbs'; type: 'pve' | 'pbs';
status: string; status: string;
} }
@ -25,6 +27,7 @@ export function EditRemoteForm({ remote, onSave, onCancel }: EditRemoteFormProps
name: remote.name, name: remote.name,
url: remote.url, url: remote.url,
username: remote.username, username: remote.username,
password: '',
type: remote.type, type: remote.type,
status: remote.status, status: remote.status,
}); });
@ -98,6 +101,21 @@ export function EditRemoteForm({ remote, onSave, onCancel }: EditRemoteFormProps
/> />
</div> </div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
value={config.password || ''}
onChange={(e) => setConfig({ ...config, password: e.target.value })}
placeholder="Enter new password (leave blank to keep existing)"
disabled={loading}
/>
<p className="text-xs text-muted-foreground">
Leave blank to keep the existing password
</p>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="type">Type</Label> <Label htmlFor="type">Type</Label>
<Input <Input
@ -121,14 +139,14 @@ export function EditRemoteForm({ remote, onSave, onCancel }: EditRemoteFormProps
/> />
</div> </div>
<div className="flex justify-end space-x-2 pt-4"> <DialogFooter className="flex justify-end space-x-2 pt-4">
<Button type="button" variant="outline" onClick={onCancel} disabled={loading}> <Button type="button" variant="outline" onClick={onCancel} disabled={loading}>
Cancel Cancel
</Button> </Button>
<Button type="submit" disabled={loading}> <Button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save Changes'} {loading ? 'Saving...' : 'Save Changes'}
</Button> </Button>
</div> </DialogFooter>
</div> </div>
</form> </form>
); );

View File

@ -117,7 +117,7 @@ export function ProxmoxRemotesPage() {
<p className="text-muted-foreground">Manage Proxmox VE and Backup Server connections</p> <p className="text-muted-foreground">Manage Proxmox VE and Backup Server connections</p>
</div> </div>
<div className="flex space-x-2"> <div className="flex space-x-2">
<Button variant="outline" size="sm"> <Button variant="outline" size="sm" onClick={() => { void loadRemotes(); }}>
<RefreshCw className="mr-2 h-4 w-4" /> <RefreshCw className="mr-2 h-4 w-4" />
Refresh Refresh
</Button> </Button>
@ -130,7 +130,7 @@ export function ProxmoxRemotesPage() {
<RemotesList <RemotesList
remotes={remotes} remotes={remotes}
onRefresh={() => {}} onRefresh={() => { void loadRemotes(); }}
onEdit={(remote) => { onEdit={(remote) => {
setEditingRemote(remote as RemoteInfo | null); setEditingRemote(remote as RemoteInfo | null);
}} }}

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/index'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/index';
import { Label } from '@/components/ui/index'; import { Label } from '@/components/ui/index';
import { Switch } from '@/components/ui/index'; import { Switch } from '@/components/ui/index';
@ -6,12 +6,22 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Button } from '@/components/ui/index'; import { Button } from '@/components/ui/index';
export function ProxmoxSettings() { export function ProxmoxSettings() {
const [defaultPort, setDefaultPort] = React.useState<string>('8006'); const [defaultPort, setDefaultPort] = useState<string>('8006');
const [connectionTimeout, setConnectionTimeout] = React.useState<string>('30'); const [connectionTimeout, setConnectionTimeout] = useState<string>('30');
const [retryAttempts, setRetryAttempts] = React.useState<string>('3'); const [retryAttempts, setRetryAttempts] = useState<string>('3');
const [verifyCertificates, setVerifyCertificates] = React.useState(true); const [verifyCertificates, setVerifyCertificates] = useState(true);
const [enableCaching, setEnableCaching] = React.useState(true); const [enableCaching, setEnableCaching] = useState(true);
const [enableDebug, setEnableDebug] = React.useState(false); const [enableDebug, setEnableDebug] = useState(false);
const [saved, setSaved] = useState(false);
useEffect(() => {
setDefaultPort(localStorage.getItem('proxmox_default_port') ?? '8006');
setConnectionTimeout(localStorage.getItem('proxmox_connection_timeout') ?? '30');
setRetryAttempts(localStorage.getItem('proxmox_retry_attempts') ?? '3');
setVerifyCertificates((localStorage.getItem('proxmox_verify_certificates') ?? 'true') === 'true');
setEnableCaching((localStorage.getItem('proxmox_enable_caching') ?? 'true') === 'true');
setEnableDebug((localStorage.getItem('proxmox_enable_debug') ?? 'false') === 'true');
}, []);
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@ -114,9 +124,40 @@ export function ProxmoxSettings() {
</CardContent> </CardContent>
</Card> </Card>
<div className="flex justify-end space-x-2 pt-4"> <div className="flex items-center justify-end space-x-2 pt-4">
<Button variant="outline">Reset to Defaults</Button> <Button
<Button>Save Settings</Button> variant="outline"
onClick={() => {
['proxmox_default_port', 'proxmox_connection_timeout', 'proxmox_retry_attempts',
'proxmox_verify_certificates', 'proxmox_enable_caching', 'proxmox_enable_debug']
.forEach((k) => localStorage.removeItem(k));
setDefaultPort('8006');
setConnectionTimeout('30');
setRetryAttempts('3');
setVerifyCertificates(true);
setEnableCaching(true);
setEnableDebug(false);
}}
>
Reset to Defaults
</Button>
<Button
onClick={() => {
localStorage.setItem('proxmox_default_port', defaultPort);
localStorage.setItem('proxmox_connection_timeout', connectionTimeout);
localStorage.setItem('proxmox_retry_attempts', retryAttempts);
localStorage.setItem('proxmox_verify_certificates', String(verifyCertificates));
localStorage.setItem('proxmox_enable_caching', String(enableCaching));
localStorage.setItem('proxmox_enable_debug', String(enableDebug));
setSaved(true);
setTimeout(() => setSaved(false), 2000);
}}
>
Save Settings
</Button>
{saved && (
<span className="text-sm text-green-600">Settings saved</span>
)}
</div> </div>
</div> </div>
); );