diff --git a/src/app/_components/ExecutionModeModal.tsx b/src/app/_components/ExecutionModeModal.tsx index bc715c1..10c452c 100644 --- a/src/app/_components/ExecutionModeModal.tsx +++ b/src/app/_components/ExecutionModeModal.tsx @@ -6,6 +6,7 @@ import { Button } from './ui/button'; import { ColorCodedDropdown } from './ColorCodedDropdown'; import { SettingsModal } from './SettingsModal'; import { useRegisterModal } from './modal/ModalStackProvider'; +import { useTranslation } from '@/lib/i18n/useTranslation'; interface ExecutionModeModalProps { @@ -16,6 +17,7 @@ interface ExecutionModeModalProps { } export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: ExecutionModeModalProps) { + const { t } = useTranslation('executionModeModal'); useRegisterModal(isOpen, { id: 'execution-mode-modal', allowEscape: true, onClose }); const [servers, setServers] = useState([]); const [loading, setLoading] = useState(false); @@ -23,19 +25,6 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E const [selectedServer, setSelectedServer] = useState(null); const [settingsModalOpen, setSettingsModalOpen] = useState(false); - useEffect(() => { - if (isOpen) { - void fetchServers(); - } - }, [isOpen]); - - // Auto-select server when exactly one server is available - useEffect(() => { - if (isOpen && !loading && servers.length === 1) { - setSelectedServer(servers[0] ?? null); - } - }, [isOpen, loading, servers]); - // Refresh servers when settings modal closes const handleSettingsModalClose = () => { setSettingsModalOpen(false); @@ -49,7 +38,7 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E try { const response = await fetch('/api/servers'); if (!response.ok) { - throw new Error('Failed to fetch servers'); + throw new Error(t('errors.fetchFailed')); } const data = await response.json(); // Sort servers by name alphabetically @@ -58,15 +47,29 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E ); setServers(sortedServers); } catch (err) { - setError(err instanceof Error ? err.message : 'An error occurred'); + setError(err instanceof Error ? err.message : t('errors.fetchFailed')); } finally { setLoading(false); } }; + useEffect(() => { + if (isOpen) { + void fetchServers(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen]); + + // Auto-select server when exactly one server is available + useEffect(() => { + if (isOpen && !loading && servers.length === 1) { + setSelectedServer(servers[0] ?? null); + } + }, [isOpen, loading, servers]); + const handleExecute = () => { if (!selectedServer) { - setError('Please select a server for SSH execution'); + setError(t('errors.noServerSelected')); return; } @@ -88,7 +91,7 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
{/* Header */}
-

Select Server

+

{t('title')}

) : servers.length === 0 ? (
-

No servers configured

-

Add servers in Settings to execute scripts

+

{t('noServersConfigured')}

+

{t('addServersHint')}

) : servers.length === 1 ? ( @@ -141,10 +144,10 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E

- Install Script Confirmation + {t('installConfirmation.title')}

- Do you want to install "{scriptName}" on the following server? + {t('installConfirmation.description', { values: { scriptName } })}

@@ -155,7 +158,7 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E

- {selectedServer?.name ?? 'Unnamed Server'} + {selectedServer?.name ?? t('unnamedServer')}

{selectedServer?.ip} @@ -171,14 +174,14 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E variant="outline" size="default" > - Cancel + {t('actions.cancel')}

@@ -187,20 +190,20 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E

- Select server to execute "{scriptName}" + {t('multipleServers.title', { values: { scriptName } })}

{/* Server Selection */}
@@ -211,7 +214,7 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E variant="outline" size="default" > - Cancel + {t('actions.cancel')}
diff --git a/src/app/_components/PublicKeyModal.tsx b/src/app/_components/PublicKeyModal.tsx index 91065a5..70e37d4 100644 --- a/src/app/_components/PublicKeyModal.tsx +++ b/src/app/_components/PublicKeyModal.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { X, Copy, Check, Server, Globe } from 'lucide-react'; import { Button } from './ui/button'; import { useRegisterModal } from './modal/ModalStackProvider'; +import { useTranslation } from '@/lib/i18n/useTranslation'; interface PublicKeyModalProps { isOpen: boolean; @@ -14,6 +15,7 @@ interface PublicKeyModalProps { } export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverIp }: PublicKeyModalProps) { + const { t } = useTranslation('publicKeyModal'); useRegisterModal(isOpen, { id: 'public-key-modal', allowEscape: true, onClose }); const [copied, setCopied] = useState(false); const [commandCopied, setCommandCopied] = useState(false); @@ -45,7 +47,7 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI } catch (fallbackError) { console.error('Fallback copy failed:', fallbackError); // If all else fails, show the key in an alert - alert('Please manually copy this key:\n\n' + publicKey); + alert(t('copyFallback') + publicKey); } document.body.removeChild(textArea); @@ -53,7 +55,7 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI } catch (error) { console.error('Failed to copy to clipboard:', error); // Fallback: show the key in an alert - alert('Please manually copy this key:\n\n' + publicKey); + alert(t('copyFallback') + publicKey); } }; @@ -82,14 +84,14 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI setTimeout(() => setCommandCopied(false), 2000); } catch (fallbackError) { console.error('Fallback copy failed:', fallbackError); - alert('Please manually copy this command:\n\n' + command); + alert(t('copyCommandFallback') + command); } document.body.removeChild(textArea); } } catch (error) { console.error('Failed to copy command to clipboard:', error); - alert('Please manually copy this command:\n\n' + command); + alert(t('copyCommandFallback') + command); } }; @@ -103,8 +105,8 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI
-

SSH Public Key

-

Add this key to your server's authorized_keys

+

{t('title')}

+

{t('subtitle')}

@@ -169,14 +171,14 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI value={publicKey} readOnly className="w-full px-3 py-2 border rounded-md shadow-sm bg-card text-foreground font-mono text-xs min-h-[60px] resize-none border-border focus:outline-none focus:ring-2 focus:ring-ring focus:border-ring" - placeholder="Public key will appear here..." + placeholder={t('placeholder')} /> {/* Quick Command */}
- + @@ -202,14 +204,14 @@ export function PublicKeyModal({ isOpen, onClose, publicKey, serverName, serverI

- Copy and paste this command directly into your server terminal to add the key to authorized_keys + {t('quickCommandHint')}

{/* Footer */}
diff --git a/src/lib/i18n/messages/de.ts b/src/lib/i18n/messages/de.ts index ecc45d9..9e5c096 100644 --- a/src/lib/i18n/messages/de.ts +++ b/src/lib/i18n/messages/de.ts @@ -157,6 +157,55 @@ export const deMessages: NestedMessages = { buttonTitle: 'Einstellungen öffnen', buttonLabel: 'Einstellungen', }, + executionModeModal: { + title: 'Server auswählen', + loadingServers: 'Lade Server...', + noServersConfigured: 'Keine Server konfiguriert', + addServersHint: 'Fügen Sie Server in den Einstellungen hinzu, um Skripte auszuführen', + openServerSettings: 'Servereinstellungen öffnen', + installConfirmation: { + title: 'Skript-Installation bestätigen', + description: 'Möchten Sie "{scriptName}" auf folgendem Server installieren?', + }, + unnamedServer: 'Unbenannter Server', + multipleServers: { + title: 'Server für "{scriptName}" auswählen', + selectServerLabel: 'Server auswählen', + placeholder: 'Wählen Sie einen Server...', + }, + actions: { + cancel: 'Abbrechen', + install: 'Installieren', + runOnServer: 'Auf Server ausführen', + }, + errors: { + noServerSelected: 'Bitte wählen Sie einen Server für die SSH-Ausführung', + fetchFailed: 'Fehler beim Abrufen der Server', + }, + }, + publicKeyModal: { + title: 'SSH Public Key', + subtitle: 'Fügen Sie diesen Schlüssel zu den authorized_keys Ihres Servers hinzu', + instructions: { + title: 'Anleitung:', + step1: 'Kopieren Sie den unten stehenden öffentlichen Schlüssel', + step2: 'SSH-Verbindung zu Ihrem Server herstellen:', + step3: 'Schlüssel zu authorized_keys hinzufügen:', + step4: 'Korrekte Berechtigungen setzen:', + }, + publicKeyLabel: 'Öffentlicher Schlüssel:', + quickCommandLabel: 'Schnell-Hinzufügen-Befehl:', + quickCommandHint: 'Kopieren Sie diesen Befehl und fügen Sie ihn direkt in Ihr Server-Terminal ein, um den Schlüssel zu authorized_keys hinzuzufügen', + placeholder: 'Öffentlicher Schlüssel wird hier angezeigt...', + actions: { + copy: 'Kopieren', + copied: 'Kopiert!', + copyCommand: 'Befehl kopieren', + close: 'Schließen', + }, + copyFallback: 'Bitte kopieren Sie diesen Schlüssel manuell:\n\n', + copyCommandFallback: 'Bitte kopieren Sie diesen Befehl manuell:\n\n', + }, layout: { title: 'PVE Skriptverwaltung', tagline: 'Verwalte und starte lokale Proxmox-Hilfsskripte mit Live-Ausgabe', diff --git a/src/lib/i18n/messages/en.ts b/src/lib/i18n/messages/en.ts index 2b2f108..fe3bd8d 100644 --- a/src/lib/i18n/messages/en.ts +++ b/src/lib/i18n/messages/en.ts @@ -391,4 +391,53 @@ export const enMessages: NestedMessages = { buttonTitle: 'Open Settings', buttonLabel: 'Settings', }, + executionModeModal: { + title: 'Select Server', + loadingServers: 'Loading servers...', + noServersConfigured: 'No servers configured', + addServersHint: 'Add servers in Settings to execute scripts', + openServerSettings: 'Open Server Settings', + installConfirmation: { + title: 'Install Script Confirmation', + description: 'Do you want to install "{scriptName}" on the following server?', + }, + unnamedServer: 'Unnamed Server', + multipleServers: { + title: 'Select server to execute "{scriptName}"', + selectServerLabel: 'Select Server', + placeholder: 'Select a server...', + }, + actions: { + cancel: 'Cancel', + install: 'Install', + runOnServer: 'Run on Server', + }, + errors: { + noServerSelected: 'Please select a server for SSH execution', + fetchFailed: 'Failed to fetch servers', + }, + }, + publicKeyModal: { + title: 'SSH Public Key', + subtitle: 'Add this key to your server\'s authorized_keys', + instructions: { + title: 'Instructions:', + step1: 'Copy the public key below', + step2: 'SSH into your server:', + step3: 'Add the key to authorized_keys:', + step4: 'Set proper permissions:', + }, + publicKeyLabel: 'Public Key:', + quickCommandLabel: 'Quick Add Command:', + quickCommandHint: 'Copy and paste this command directly into your server terminal to add the key to authorized_keys', + placeholder: 'Public key will appear here...', + actions: { + copy: 'Copy', + copied: 'Copied!', + copyCommand: 'Copy Command', + close: 'Close', + }, + copyFallback: 'Please manually copy this key:\n\n', + copyCommandFallback: 'Please manually copy this command:\n\n', + }, };