feat(i18n): Lokalisierung - Phase 4 abgeschlossen (HelpButton, ResyncButton, ViewToggle)
Lokalisierte Komponenten (13/alle):
- HelpButton: Hilfe-Button mit 'Need help?' und Öffnen-Action
- ResyncButton: Sync-Button mit Fortschritts-Messages und letzter Sync-Zeit
- ViewToggle: Card/List View Umschalter
Neue Translation Keys:
- helpButton.needHelp, openHelp, help
- resyncButton.syncDescription, syncing, syncJsonFiles, helpTooltip, lastSync, messages.*
- viewToggle.cardView, listView
Technische Details:
- ResyncButton: Dynamische Error-Erkennung für DE/EN ('Fehler'/'Error')
- Zeit-Formatierung mit toLocaleTimeString() für lastSync
- ViewToggle: Einfache View-Mode-Labels
This commit is contained in:
@@ -4,29 +4,31 @@ import { useState } from 'react';
|
||||
import { HelpModal } from './HelpModal';
|
||||
import { Button } from './ui/button';
|
||||
import { HelpCircle } from 'lucide-react';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
interface HelpButtonProps {
|
||||
initialSection?: string;
|
||||
}
|
||||
|
||||
export function HelpButton({ initialSection }: HelpButtonProps) {
|
||||
const { t } = useTranslation('helpButton');
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
|
||||
<div className="text-sm text-muted-foreground font-medium">
|
||||
Need help?
|
||||
{t('needHelp')}
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => setIsOpen(true)}
|
||||
variant="outline"
|
||||
size="default"
|
||||
className="inline-flex items-center"
|
||||
title="Open Help"
|
||||
title={t('openHelp')}
|
||||
>
|
||||
<HelpCircle className="w-5 h-5 mr-2" />
|
||||
Help
|
||||
{t('help')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import { useState } from 'react';
|
||||
import { api } from '~/trpc/react';
|
||||
import { Button } from './ui/button';
|
||||
import { ContextualHelpIcon } from './ContextualHelpIcon';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
export function ResyncButton() {
|
||||
const { t } = useTranslation('resyncButton');
|
||||
const [isResyncing, setIsResyncing] = useState(false);
|
||||
const [lastSync, setLastSync] = useState<Date | null>(null);
|
||||
const [syncMessage, setSyncMessage] = useState<string | null>(null);
|
||||
@@ -15,20 +17,20 @@ export function ResyncButton() {
|
||||
setIsResyncing(false);
|
||||
setLastSync(new Date());
|
||||
if (data.success) {
|
||||
setSyncMessage(data.message ?? 'Scripts synced successfully');
|
||||
setSyncMessage(data.message ?? t('messages.success'));
|
||||
// Reload the page after successful sync
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000); // Wait 2 seconds to show the success message
|
||||
} else {
|
||||
setSyncMessage(data.error ?? 'Failed to sync scripts');
|
||||
setSyncMessage(data.error ?? t('messages.failed'));
|
||||
// Clear message after 3 seconds for errors
|
||||
setTimeout(() => setSyncMessage(null), 3000);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
setIsResyncing(false);
|
||||
setSyncMessage(`Error: ${error.message}`);
|
||||
setSyncMessage(t('messages.error', { values: { message: error.message } }));
|
||||
setTimeout(() => setSyncMessage(null), 3000);
|
||||
},
|
||||
});
|
||||
@@ -42,7 +44,7 @@ export function ResyncButton() {
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
|
||||
<div className="text-sm text-muted-foreground font-medium">
|
||||
Sync scripts with ProxmoxVE repo
|
||||
{t('syncDescription')}
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -56,30 +58,30 @@ export function ResyncButton() {
|
||||
{isResyncing ? (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"></div>
|
||||
<span>Syncing...</span>
|
||||
<span>{t('syncing')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
<span>Sync Json Files</span>
|
||||
<span>{t('syncJsonFiles')}</span>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<ContextualHelpIcon section="sync-button" tooltip="Help with Sync Button" />
|
||||
<ContextualHelpIcon section="sync-button" tooltip={t('helpTooltip')} />
|
||||
</div>
|
||||
|
||||
{lastSync && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Last sync: {lastSync.toLocaleTimeString()}
|
||||
{t('lastSync', { values: { time: lastSync.toLocaleTimeString() } })}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{syncMessage && (
|
||||
<div className={`text-sm px-3 py-1 rounded-lg ${
|
||||
syncMessage.includes('Error') || syncMessage.includes('Failed')
|
||||
syncMessage.includes('Error') || syncMessage.includes('Failed') || syncMessage.includes('Fehler')
|
||||
? 'bg-error/10 text-error'
|
||||
: 'bg-success/10 text-success'
|
||||
}`}>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Grid3X3, List } from 'lucide-react';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
|
||||
interface ViewToggleProps {
|
||||
viewMode: 'card' | 'list';
|
||||
@@ -10,6 +11,8 @@ interface ViewToggleProps {
|
||||
}
|
||||
|
||||
export function ViewToggle({ viewMode, onViewModeChange }: ViewToggleProps) {
|
||||
const { t } = useTranslation('viewToggle');
|
||||
|
||||
return (
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="flex items-center space-x-1 bg-muted rounded-lg p-1">
|
||||
@@ -24,7 +27,7 @@ export function ViewToggle({ viewMode, onViewModeChange }: ViewToggleProps) {
|
||||
}`}
|
||||
>
|
||||
<Grid3X3 className="h-4 w-4" />
|
||||
<span className="text-sm">Card View</span>
|
||||
<span className="text-sm">{t('cardView')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onViewModeChange('list')}
|
||||
@@ -37,7 +40,7 @@ export function ViewToggle({ viewMode, onViewModeChange }: ViewToggleProps) {
|
||||
}`}
|
||||
>
|
||||
<List className="h-4 w-4" />
|
||||
<span className="text-sm">List View</span>
|
||||
<span className="text-sm">{t('listView')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -115,6 +115,27 @@ export const deMessages: NestedMessages = {
|
||||
settingUp: 'Wird eingerichtet...',
|
||||
},
|
||||
},
|
||||
helpButton: {
|
||||
needHelp: 'Brauchen Sie Hilfe?',
|
||||
openHelp: 'Hilfe öffnen',
|
||||
help: 'Hilfe',
|
||||
},
|
||||
resyncButton: {
|
||||
syncDescription: 'Skripte mit ProxmoxVE-Repo synchronisieren',
|
||||
syncing: 'Synchronisiere...',
|
||||
syncJsonFiles: 'JSON-Dateien synchronisieren',
|
||||
helpTooltip: 'Hilfe zum Sync-Button',
|
||||
lastSync: 'Letzte Synchronisierung: {time}',
|
||||
messages: {
|
||||
success: 'Skripte erfolgreich synchronisiert',
|
||||
failed: 'Fehler beim Synchronisieren der Skripte',
|
||||
error: 'Fehler: {message}',
|
||||
},
|
||||
},
|
||||
viewToggle: {
|
||||
cardView: 'Karten-Ansicht',
|
||||
listView: 'Listen-Ansicht',
|
||||
},
|
||||
layout: {
|
||||
title: 'PVE Skriptverwaltung',
|
||||
tagline: 'Verwalte und starte lokale Proxmox-Hilfsskripte mit Live-Ausgabe',
|
||||
|
||||
@@ -349,4 +349,25 @@ export const enMessages: NestedMessages = {
|
||||
settingUp: 'Setting Up...',
|
||||
},
|
||||
},
|
||||
helpButton: {
|
||||
needHelp: 'Need help?',
|
||||
openHelp: 'Open Help',
|
||||
help: 'Help',
|
||||
},
|
||||
resyncButton: {
|
||||
syncDescription: 'Sync scripts with ProxmoxVE repo',
|
||||
syncing: 'Syncing...',
|
||||
syncJsonFiles: 'Sync Json Files',
|
||||
helpTooltip: 'Help with Sync Button',
|
||||
lastSync: 'Last sync: {time}',
|
||||
messages: {
|
||||
success: 'Scripts synced successfully',
|
||||
failed: 'Failed to sync scripts',
|
||||
error: 'Error: {message}',
|
||||
},
|
||||
},
|
||||
viewToggle: {
|
||||
cardView: 'Card View',
|
||||
listView: 'List View',
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user