* Add comprehensive Chinese internationalization support - Implemented i18n framework with react-i18next for multi-language support - Added Chinese (zh) and English (en) translation files with comprehensive coverage - Localized Admin interface, authentication flows, and error messages - Translated FileManager operations and UI elements - Updated HomepageAuth component with localized authentication messages - Localized LeftSidebar navigation and host management - Added language switcher component (shown after login only) - Configured default language as English with Chinese as secondary option - Localized TOTPSetup two-factor authentication interface - Updated Docker build to include translation files - Achieved 95%+ UI localization coverage across core components Co-Authored-By: Claude <noreply@anthropic.com> * Extend Chinese localization coverage to Host Manager components - Added comprehensive translations for HostManagerHostViewer component - Localized all host management UI text including import/export features - Translated error messages and confirmation dialogs for host operations - Added translations for HostManagerHostEditor validation messages - Localized connection details, organization settings, and form labels - Fixed syntax error in FileManagerOperations component - Achieved near-complete localization of SSH host management interface - Updated placeholders and tooltips for better user guidance Co-Authored-By: Claude <noreply@anthropic.com> * Complete comprehensive Chinese localization for Termix - Added full localization support for Tunnel components (connected/disconnected states, retry messages) - Localized all tunnel status messages and connection errors - Added translations for port forwarding UI elements - Verified Server, TopNavbar, and Tab components already have complete i18n support - Achieved 99%+ localization coverage across entire application - All core UI components now fully support Chinese and English languages This completes the comprehensive internationalization effort for the Termix SSH management platform. Co-Authored-By: Claude <noreply@anthropic.com> * Localize additional Host Manager components and authentication settings - Added translations for all authentication options (Password, Key, SSH Private Key) - Localized form labels in HostManagerHostEditor (Pin Connection, Enable Terminal/Tunnel/FileManager) - Translated Upload/Update Key button states - Localized Host Viewer and Add/Edit Host tab labels - Added Chinese translations for all host management settings - Fixed duplicate translation keys in JSON files Co-Authored-By: Claude <noreply@anthropic.com> * Extend localization coverage to UI components and common strings - Added comprehensive common translations (online/offline, success/error, etc.) - Localized status indicator component with all status states - Updated FileManagerLeftSidebar toast messages for rename/delete operations - Added translations for UI elements (close, toggle sidebar, etc.) - Expanded placeholder translations for form inputs - Added Chinese translations for all new common strings - Improved consistency across component status messages Co-Authored-By: Claude <noreply@anthropic.com> * Complete Chinese localization for remaining UI components - Add comprehensive Chinese translations for Host Manager component - Translate all form labels, buttons, and descriptions - Add translations for SSH configuration warnings and instructions - Localize tunnel connection settings and port forwarding options - Localize SSH Tools panel - Translate key recording functionality - Add translations for settings and configuration options - Translate homepage welcome messages and navigation elements - Add Chinese translations for login success messages - Localize "Updates & Releases" section title - Translate sidebar "Host Manager" button - Fix translation key display issues - Remove duplicate translation keys in both language files - Ensure all components properly reference translation keys - Fix hosts.tunnelConnections key mapping This completes the full Chinese localization of the Termix application, achieving near 100% UI translation coverage while maintaining English as the default language. * Complete final Chinese localization for Host Manager tunnel configuration - Add Chinese translations for authentication UI elements - Translate "Authentication", "Password", and "Key" tab labels - Localize SSH private key and key password fields - Add translations for key type selector - Localize tunnel connection configuration descriptions - Translate retry attempts and retry interval descriptions - Add dynamic tunnel forwarding description with port parameters - Localize endpoint SSH configuration labels - Fix missing translation keys - Add "upload" translation for file upload button - Ensure all FormLabel and FormDescription elements use translation keys This completes the comprehensive Chinese localization of the entire Termix application, achieving 100% UI translation coverage. * Fix OIDC errors for "Failed to get user information" * Fix OIDC errors for "Failed to get user information" * Fix spelling error * Fix PR feedback: Improve Profile section translations and UX - Fixed password reset translations in Profile section - Moved language selector from TopNavbar to Profile page - Added profile.selectPreferredLanguage translation key - Improved user experience for language preferences * Migrate everything to alert system, update user.ts for OIDC updates. * Update env * Fix OIDC errors for "Failed to get user information" * Fix OIDC errors for "Failed to get user information" * Fix spelling error * Migrate everything to alert system, update user.ts for OIDC updates. * Translation update * Translation update * Translation update * Translate tunnels * Comment update * Update build workflow naming * Add more translations, fix user delete failing * Fix config editor erorrs causing user delete failure --------- Co-authored-by: ZacharyZcR <PayasoNorahC@protonmail.com> Co-authored-by: Claude <noreply@anthropic.com>
154 lines
5.1 KiB
TypeScript
154 lines
5.1 KiB
TypeScript
import React from "react";
|
|
import {Card, CardContent, CardFooter, CardHeader, CardTitle} from "@/components/ui/card.tsx";
|
|
import {Button} from "@/components/ui/button.tsx";
|
|
import {Badge} from "@/components/ui/badge.tsx";
|
|
import {X, ExternalLink, AlertTriangle, Info, CheckCircle, AlertCircle} from "lucide-react";
|
|
import {useTranslation} from "react-i18next";
|
|
|
|
interface TermixAlert {
|
|
id: string;
|
|
title: string;
|
|
message: string;
|
|
expiresAt: string;
|
|
priority?: 'low' | 'medium' | 'high' | 'critical';
|
|
type?: 'info' | 'warning' | 'error' | 'success';
|
|
actionUrl?: string;
|
|
actionText?: string;
|
|
}
|
|
|
|
interface AlertCardProps {
|
|
alert: TermixAlert;
|
|
onDismiss: (alertId: string) => void;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const getAlertIcon = (type?: string) => {
|
|
switch (type) {
|
|
case 'warning':
|
|
return <AlertTriangle className="h-5 w-5 text-yellow-500"/>;
|
|
case 'error':
|
|
return <AlertCircle className="h-5 w-5 text-red-500"/>;
|
|
case 'success':
|
|
return <CheckCircle className="h-5 w-5 text-green-500"/>;
|
|
case 'info':
|
|
default:
|
|
return <Info className="h-5 w-5 text-blue-500"/>;
|
|
}
|
|
};
|
|
|
|
const getPriorityBadgeVariant = (priority?: string) => {
|
|
switch (priority) {
|
|
case 'critical':
|
|
return 'destructive';
|
|
case 'high':
|
|
return 'destructive';
|
|
case 'medium':
|
|
return 'secondary';
|
|
case 'low':
|
|
default:
|
|
return 'outline';
|
|
}
|
|
};
|
|
|
|
const getTypeBadgeVariant = (type?: string) => {
|
|
switch (type) {
|
|
case 'warning':
|
|
return 'secondary';
|
|
case 'error':
|
|
return 'destructive';
|
|
case 'success':
|
|
return 'default';
|
|
case 'info':
|
|
default:
|
|
return 'outline';
|
|
}
|
|
};
|
|
|
|
export function HomepageAlertCard({alert, onDismiss, onClose}: AlertCardProps): React.ReactElement {
|
|
const {t} = useTranslation();
|
|
|
|
if (!alert) {
|
|
return null;
|
|
}
|
|
|
|
const handleDismiss = () => {
|
|
onDismiss(alert.id);
|
|
onClose();
|
|
};
|
|
|
|
const formatExpiryDate = (expiryString: string) => {
|
|
const expiryDate = new Date(expiryString);
|
|
const now = new Date();
|
|
const diffTime = expiryDate.getTime() - now.getTime();
|
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
|
|
if (diffDays < 0) return t('common.expired');
|
|
if (diffDays === 0) return t('common.expiresToday');
|
|
if (diffDays === 1) return t('common.expiresTomorrow');
|
|
return t('common.expiresInDays', {days: diffDays});
|
|
};
|
|
|
|
return (
|
|
<Card className="w-full max-w-2xl mx-auto">
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
{getAlertIcon(alert.type)}
|
|
<CardTitle className="text-xl font-bold">
|
|
{alert.title}
|
|
</CardTitle>
|
|
</div>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={onClose}
|
|
className="h-8 w-8 p-0"
|
|
>
|
|
<X className="h-4 w-4"/>
|
|
</Button>
|
|
</div>
|
|
<div className="flex items-center gap-2 mt-2">
|
|
{alert.priority && (
|
|
<Badge variant={getPriorityBadgeVariant(alert.priority)}>
|
|
{alert.priority.toUpperCase()}
|
|
</Badge>
|
|
)}
|
|
{alert.type && (
|
|
<Badge variant={getTypeBadgeVariant(alert.type)}>
|
|
{alert.type}
|
|
</Badge>
|
|
)}
|
|
<span className="text-sm text-muted-foreground">
|
|
{formatExpiryDate(alert.expiresAt)}
|
|
</span>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="pb-4">
|
|
<p className="text-muted-foreground leading-relaxed whitespace-pre-wrap">
|
|
{alert.message}
|
|
</p>
|
|
</CardContent>
|
|
<CardFooter className="flex items-center justify-between pt-0">
|
|
<div className="flex gap-2">
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleDismiss}
|
|
>
|
|
Dismiss
|
|
</Button>
|
|
{alert.actionUrl && alert.actionText && (
|
|
<Button
|
|
variant="default"
|
|
onClick={() => window.open(alert.actionUrl, '_blank', 'noopener,noreferrer')}
|
|
className="gap-2"
|
|
>
|
|
{alert.actionText}
|
|
<ExternalLink className="h-4 w-4"/>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
);
|
|
}
|