From a1c6c7053e716920812a1f4031d619d901fecc84 Mon Sep 17 00:00:00 2001 From: Tola Leng Date: Sat, 17 May 2025 21:08:44 +0800 Subject: [PATCH] feat(km): Translate SSL & Domain section Translates the SSL & Domain section, including components and pages, into Khmer. This involves updating the `km.ts` translation file with relevant Khmer translations for all UI elements and strings within the SSL & Domain management feature. --- .../ssl-domain/AddSSLCertificateForm.tsx | 32 ++-- .../ssl-domain/EditSSLCertificateForm.tsx | 35 ++--- .../ssl-domain/SSLCertificateStatusCards.tsx | 10 +- .../ssl-domain/SSLCertificatesTable.tsx | 139 +++++++++--------- .../ssl-domain/SSLDomainContent.tsx | 50 ++++--- application/src/pages/SslDomain.tsx | 11 +- 6 files changed, 146 insertions(+), 131 deletions(-) diff --git a/application/src/components/ssl-domain/AddSSLCertificateForm.tsx b/application/src/components/ssl-domain/AddSSLCertificateForm.tsx index 3c2168d..8314d36 100644 --- a/application/src/components/ssl-domain/AddSSLCertificateForm.tsx +++ b/application/src/components/ssl-domain/AddSSLCertificateForm.tsx @@ -12,6 +12,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { DialogFooter } from "@/components/ui/dialog"; import { AddSSLCertificateDto } from "@/types/ssl.types"; import { alertConfigService, AlertConfiguration } from "@/services/alertConfigService"; +import { useLanguage } from "@/contexts/LanguageContext"; const formSchema = z.object({ domain: z.string().min(1, "Domain is required"), @@ -31,6 +32,7 @@ export const AddSSLCertificateForm = ({ onCancel, isPending = false }: AddSSLCertificateFormProps) => { + const { t } = useLanguage(); const [alertConfigs, setAlertConfigs] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -71,14 +73,14 @@ export const AddSSLCertificateForm = ({ } } catch (error) { console.error("Error fetching notification channels:", error); - toast.error("Failed to load notification channels"); + toast.error(t('failedToLoadCertificates')); } finally { setIsLoading(false); } }; fetchNotificationChannels(); - }, [form]); + }, [form, t]); const handleSubmit = async (values: z.infer) => { try { @@ -94,7 +96,7 @@ export const AddSSLCertificateForm = ({ form.reset(); } catch (error) { console.error("Error adding SSL certificate:", error); - toast.error("Failed to add SSL certificate"); + toast.error(t('failedToAddCertificate')); } }; @@ -106,7 +108,7 @@ export const AddSSLCertificateForm = ({ name="domain" render={({ field }) => ( - Domain + {t('domain')} @@ -120,12 +122,12 @@ export const AddSSLCertificateForm = ({ name="warning_threshold" render={({ field }) => ( - Warning Threshold (days) + {t('warningThreshold')} - Get notified when certificates are about to expire + {t('getNotifiedExpiration')} @@ -137,12 +139,12 @@ export const AddSSLCertificateForm = ({ name="expiry_threshold" render={({ field }) => ( - Expiry Threshold (days) + {t('expiryThreshold')} - Get notified when certificates are critically close to expiring + {t('getNotifiedCritical')} @@ -154,11 +156,11 @@ export const AddSSLCertificateForm = ({ name="notification_channel" render={({ field }) => ( - Notification Channel + {t('notificationChannel')} - Choose where to receive SSL certificate alerts + {t('chooseChannel')} @@ -185,9 +187,9 @@ export const AddSSLCertificateForm = ({ /> - + diff --git a/application/src/components/ssl-domain/EditSSLCertificateForm.tsx b/application/src/components/ssl-domain/EditSSLCertificateForm.tsx index 0a6d547..1512ce4 100644 --- a/application/src/components/ssl-domain/EditSSLCertificateForm.tsx +++ b/application/src/components/ssl-domain/EditSSLCertificateForm.tsx @@ -1,4 +1,3 @@ - import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -11,6 +10,7 @@ import { SSLCertificate } from "@/types/ssl.types"; import { Loader2, Bell } from "lucide-react"; import { toast } from "sonner"; import { alertConfigService, AlertConfiguration } from "@/services/alertConfigService"; +import { useLanguage } from "@/contexts/LanguageContext"; const formSchema = z.object({ domain: z.string().min(1, "Domain is required"), @@ -29,6 +29,7 @@ interface EditSSLCertificateFormProps { } export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPending }: EditSSLCertificateFormProps) => { + const { t } = useLanguage(); const [alertConfigs, setAlertConfigs] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -61,14 +62,14 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend setAlertConfigs(enabledConfigs); } catch (error) { console.error("Error fetching notification channels:", error); - toast.error("Failed to load notification channels"); + toast.error(t('failedToLoadCertificates')); } finally { setIsLoading(false); } }; fetchNotificationChannels(); - }, []); + }, [t]); const handleSubmit = (data: FormValues) => { // Merge the updated values with the original certificate @@ -96,7 +97,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend name="domain" render={({ field }) => ( - Domain + {t('domainName')} - Domain name cannot be changed. To monitor a different domain, add a new certificate. + {t('domainCannotChange')} @@ -118,7 +119,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend name="warning_threshold" render={({ field }) => ( - Warning Threshold (Days) + {t('warningThresholdDays')} - Days before expiration to send warning + {t('daysBeforeExpiration')} @@ -140,7 +141,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend name="expiry_threshold" render={({ field }) => ( - Expiry Threshold (Days) + {t('expiryThresholdDays')} - Days before expiration to send critical alert + {t('daysBeforeCritical')} @@ -163,7 +164,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend name="notification_channel" render={({ field }) => ( - Notification Channel + {t('notificationChannel')} - Where to send notifications about this certificate + {t('whereToSend')} @@ -211,7 +212,7 @@ export const EditSSLCertificateForm = ({ certificate, onSubmit, onCancel, isPend onClick={onCancel} disabled={isPending} > - Cancel + {t('cancel')} ); -}; \ No newline at end of file +}; diff --git a/application/src/components/ssl-domain/SSLCertificateStatusCards.tsx b/application/src/components/ssl-domain/SSLCertificateStatusCards.tsx index b4b9e61..b703a44 100644 --- a/application/src/components/ssl-domain/SSLCertificateStatusCards.tsx +++ b/application/src/components/ssl-domain/SSLCertificateStatusCards.tsx @@ -1,13 +1,15 @@ - import React from "react"; import { Card } from "@/components/ui/card"; import { SSLCertificate } from "@/types/ssl.types"; +import { useLanguage } from "@/contexts/LanguageContext"; interface SSLCertificateStatusCardsProps { certificates: SSLCertificate[]; } export const SSLCertificateStatusCards = ({ certificates }: SSLCertificateStatusCardsProps) => { + const { t } = useLanguage(); + // Count certificates by status const validCount = certificates.filter(cert => cert.status === 'valid').length; const expiringCount = certificates.filter(cert => cert.status === 'expiring_soon').length; @@ -24,7 +26,7 @@ export const SSLCertificateStatusCards = ({ certificates }: SSLCertificateStatus
-

Valid Certificates

+

{t('validCertificates')}

{validCount}

@@ -38,7 +40,7 @@ export const SSLCertificateStatusCards = ({ certificates }: SSLCertificateStatus
-

Expiring Soon

+

{t('expiringSoon')}

{expiringCount}

@@ -52,7 +54,7 @@ export const SSLCertificateStatusCards = ({ certificates }: SSLCertificateStatus
-

Expired

+

{t('expired')}

{expiredCount}

diff --git a/application/src/components/ssl-domain/SSLCertificatesTable.tsx b/application/src/components/ssl-domain/SSLCertificatesTable.tsx index 06e16f0..43ec130 100644 --- a/application/src/components/ssl-domain/SSLCertificatesTable.tsx +++ b/application/src/components/ssl-domain/SSLCertificatesTable.tsx @@ -1,4 +1,3 @@ - import React, { useState } from "react"; import { format } from "date-fns"; import { @@ -21,6 +20,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { toast } from "sonner"; +import { useLanguage } from "@/contexts/LanguageContext"; interface SSLCertificatesTableProps { certificates: SSLCertificate[]; @@ -37,23 +37,24 @@ export const SSLCertificatesTable = ({ onEdit, onDelete }: SSLCertificatesTableProps) => { + const { t } = useLanguage(); const [selectedCert, setSelectedCert] = useState(null); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [certToDelete, setCertToDelete] = useState(null); const formatDate = (dateString: string | undefined) => { - if (!dateString) return 'Unknown'; + if (!dateString) return t('unknown'); try { const date = new Date(dateString); if (isNaN(date.getTime())) { console.warn("Invalid date for formatting:", dateString); - return 'Unknown'; + return t('unknown'); } return format(date, "MMM dd, yyyy"); } catch (error) { console.error("Error formatting date:", error); - return 'Unknown'; + return t('unknown'); } }; @@ -89,39 +90,39 @@ export const SSLCertificatesTable = ({
- Checking SSL certificate... + {t('checkingSSLCertificate')}
)} - Domain - Issuer - Expiration Date - Days Left - Status - Last Notified - Actions + {t('domain')} + {t('issuer')} + {t('expirationDate')} + {t('daysLeft')} + {t('status')} + {t('lastNotified')} + {t('actions')} {certificates.length === 0 ? ( - No SSL certificates found + {t('noSSLCertificates')} ) : ( certificates.map((certificate) => ( {certificate.domain} - {certificate.issuer_o || 'Unknown'} + {certificate.issuer_o || t('unknown')} {formatDate(certificate.valid_till)} - {typeof certificate.days_left === 'number' ? certificate.days_left : 'Unknown'} + {typeof certificate.days_left === 'number' ? certificate.days_left : t('unknown')} @@ -129,7 +130,7 @@ export const SSLCertificatesTable = ({ {certificate.last_notified ? formatDate(certificate.last_notified) - : "Never"} + : t('never')} @@ -144,7 +145,7 @@ export const SSLCertificatesTable = ({ onClick={() => handleViewCertificate(certificate)} className="cursor-pointer" > - View + {t('view')} { @@ -156,19 +157,19 @@ export const SSLCertificatesTable = ({ className="cursor-pointer" > - Check + {t('check')} handleEditCertificate(certificate)} className="cursor-pointer" > - Edit + {t('edit')} handleDeleteCertificate(certificate)} className="cursor-pointer text-destructive focus:text-destructive" > - Delete + {t('delete')} @@ -185,10 +186,10 @@ export const SSLCertificatesTable = ({ - SSL Certificate Details + {t('sslCertificateDetails')}

- Detailed information about the SSL certificate for {selectedCert?.domain} + {t('detailedInfo')} {selectedCert?.domain}

@@ -197,76 +198,76 @@ export const SSLCertificatesTable = ({
{/* Basic Information */}
-

Basic Information

+

{t('basicInformation')}

- Domain: + {t('domain')}: {selectedCert.domain}
- Status: + {t('status')}:
- Issued To: - {selectedCert.issued_to || 'Unknown'} + {t('issuer')}: + {selectedCert.issued_to || t('unknown')}
- Resolved IP: - {selectedCert.resolved_ip || 'Unknown'} + IP: + {selectedCert.resolved_ip || t('unknown')}
{/* Validity */}
-

Validity

+

{t('validity')}

- Valid From: - {selectedCert.valid_from ? formatDate(selectedCert.valid_from) : 'Unknown'} + {t('validFrom')}: + {selectedCert.valid_from ? formatDate(selectedCert.valid_from) : t('unknown')}
- Valid Until: + {t('validUntil')}: {formatDate(selectedCert.valid_till)}
- Days Left: + {t('daysLeft')}: {selectedCert.days_left}
- Validity Days: - {selectedCert.validity_days || 'Unknown'} + {t('validityDays')}: + {selectedCert.validity_days || t('unknown')}
{/* Issuer */}
-

Issuer

+

{t('issuerInfo')}

- Organization: - {selectedCert.issuer_o || 'Unknown'} + {t('organization')}: + {selectedCert.issuer_o || t('unknown')}
- Common Name: - {selectedCert.issuer_cn || 'Unknown'} + {t('commonName')}: + {selectedCert.issuer_cn || t('unknown')}
{/* Technical Details */}
-

Technical Details

+

{t('technicalDetails')}

- Serial Number: - {selectedCert.serial_number || 'Unknown'} + {t('serialNumber')}: + {selectedCert.serial_number || t('unknown')}
- Algorithm: - {selectedCert.cert_alg || 'Unknown'} + {t('algorithm')}: + {selectedCert.cert_alg || t('unknown')}
@@ -274,30 +275,30 @@ export const SSLCertificatesTable = ({ {/* Subject Alternative Names */}
-

Subject Alternative Names (SANs)

+

{t('subjectAltNames')}

{selectedCert.cert_sans ? (

{selectedCert.cert_sans}

) : ( -

None

+

{t('none')}

)}
{/* Monitoring Configuration */}
-

Monitoring Configuration

+

{t('monitoringConfig')}

-
Warning Threshold:
-
{selectedCert.warning_threshold} days
+
{t('warningThreshold')}:
+
{selectedCert.warning_threshold} {t('daysLeft').toLowerCase()}
-
Expiry Threshold:
-
{selectedCert.expiry_threshold} days
+
{t('expiryThreshold')}:
+
{selectedCert.expiry_threshold} {t('daysLeft').toLowerCase()}
-
Notification Channel:
+
{t('notificationChannel')}:
{selectedCert.notification_channel}
@@ -305,23 +306,23 @@ export const SSLCertificatesTable = ({ {/* Timestamps */}
-

Record Information

+

{t('recordInfo')}

-
Created:
-
{selectedCert.created ? formatDate(selectedCert.created) : 'Unknown'}
+
{t('created')}:
+
{selectedCert.created ? formatDate(selectedCert.created) : t('unknown')}
-
Last Updated:
-
{selectedCert.updated ? formatDate(selectedCert.updated) : 'Unknown'}
+
{t('lastUpdated')}:
+
{selectedCert.updated ? formatDate(selectedCert.updated) : t('unknown')}
-
Last Notification:
-
{selectedCert.last_notified ? formatDate(selectedCert.last_notified) : 'Never'}
+
{t('lastNotification')}:
+
{selectedCert.last_notified ? formatDate(selectedCert.last_notified) : t('never')}
-
Collection ID:
-
{selectedCert.collectionId || 'Unknown'}
+
{t('collectionId')}:
+
{selectedCert.collectionId || t('unknown')}
@@ -334,7 +335,7 @@ export const SSLCertificatesTable = ({ onClick={() => setSelectedCert(null)} className="w-full sm:w-auto" > - Close + {t('close')} @@ -344,15 +345,15 @@ export const SSLCertificatesTable = ({ - Delete SSL Certificate + {t('deleteSSLCertificate')}
-

Are you sure you want to delete the SSL certificate for {certToDelete?.domain}?

-

This action cannot be undone.

+

{t('deleteConfirmation')} {certToDelete?.domain}?

+

{t('deleteWarning')}

- - + +
diff --git a/application/src/components/ssl-domain/SSLDomainContent.tsx b/application/src/components/ssl-domain/SSLDomainContent.tsx index 2805b10..f4061fe 100644 --- a/application/src/components/ssl-domain/SSLDomainContent.tsx +++ b/application/src/components/ssl-domain/SSLDomainContent.tsx @@ -13,8 +13,10 @@ import { AddSSLCertificateForm } from "./AddSSLCertificateForm"; import { EditSSLCertificateForm } from "./EditSSLCertificateForm"; import type { AddSSLCertificateDto, SSLCertificate } from "@/types/ssl.types"; import { pb } from "@/lib/pocketbase"; +import { useLanguage } from "@/contexts/LanguageContext"; export const SSLDomainContent = () => { + const { t } = useLanguage(); const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [refreshingId, setRefreshingId] = useState(null); @@ -33,7 +35,7 @@ export const SSLDomainContent = () => { return result; } catch (error) { console.error("Error fetching certificates:", error); - toast.error("Failed to load SSL certificates"); + toast.error(t('failedToLoadCertificates')); throw error; } }, @@ -47,11 +49,11 @@ export const SSLDomainContent = () => { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ssl-certificates'] }); setIsAddDialogOpen(false); - toast.success("SSL certificate added successfully"); + toast.success(t('sslCertificateAdded')); }, onError: (error) => { console.error("Error adding SSL certificate:", error); - toast.error(error instanceof Error ? error.message : "Failed to add SSL certificate. Make sure the domain is valid and accessible."); + toast.error(error instanceof Error ? error.message : t('failedToAddCertificate')); } }); @@ -84,11 +86,11 @@ export const SSLDomainContent = () => { queryClient.invalidateQueries({ queryKey: ['ssl-certificates'] }); setIsEditDialogOpen(false); setSelectedCertificate(null); - toast.success("SSL certificate updated successfully"); + toast.success(t('sslCertificateUpdated')); }, onError: (error) => { console.error("Error updating SSL certificate:", error); - toast.error(error instanceof Error ? error.message : "Failed to update SSL certificate"); + toast.error(error instanceof Error ? error.message : t('failedToUpdateCertificate')); } }); @@ -97,11 +99,11 @@ export const SSLDomainContent = () => { mutationFn: deleteSSLCertificate, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ssl-certificates'] }); - toast.success("SSL certificate deleted successfully"); + toast.success(t('sslCertificateDeleted')); }, onError: (error) => { console.error("Error deleting SSL certificate:", error); - toast.error(error instanceof Error ? error.message : "Failed to delete SSL certificate"); + toast.error(error instanceof Error ? error.message : t('failedToDeleteCertificate')); } }); @@ -111,12 +113,12 @@ export const SSLDomainContent = () => { onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ['ssl-certificates'] }); setRefreshingId(null); - toast.success(`SSL certificate for ${data.domain} updated successfully`); + toast.success(t('sslCertificateRefreshed').replace('{domain}', data.domain)); }, onError: (error) => { console.error("Error refreshing SSL certificate:", error); - let errorMessage = "Failed to check SSL certificate."; + let errorMessage = t('failedToCheckCertificate'); if (error instanceof Error) { errorMessage = error.message; @@ -138,14 +140,16 @@ export const SSLDomainContent = () => { setIsRefreshingAll(false); if (result.failed === 0) { - toast.success(`Successfully refreshed all ${result.success} certificates`); + toast.success(t('allCertificatesRefreshed').replace('{count}', result.success.toString())); } else { - toast.info(`Refreshed ${result.success} certificates, ${result.failed} failed`); + toast.info(t('someCertificatesFailed') + .replace('{success}', result.success.toString()) + .replace('{failed}', result.failed.toString())); } }, onError: (error) => { console.error("Error refreshing all certificates:", error); - toast.error("Failed to refresh all certificates"); + toast.error(t('failedToCheckCertificate')); setIsRefreshingAll(false); // Still refresh the data to show any partial information @@ -179,12 +183,12 @@ export const SSLDomainContent = () => { const handleRefreshAll = async () => { if (certificates.length === 0) { - toast.info("No certificates to refresh"); + toast.info(t('noCertificatesToRefresh')); return; } setIsRefreshingAll(true); - toast.info(`Starting refresh of all ${certificates.length} certificates...`); + toast.info(t('startingRefreshAll').replace('{count}', certificates.length.toString())); refreshAllMutation.mutate(); }; @@ -195,8 +199,10 @@ export const SSLDomainContent = () => { if (error) { return (
-

Error loading SSL certificate data.

- +

{t('failedToLoadCertificates')}

+
); } @@ -206,8 +212,8 @@ export const SSLDomainContent = () => {
-

SSL & Domain Management

-

Monitor SSL certificates and their expiration dates

+

{t('sslDomainManagement')}

+

{t('monitorSSLCertificates')}

@@ -249,7 +255,7 @@ export const SSLDomainContent = () => { - Add SSL Certificate + {t('addSSLCertificate')} { }}> - Edit SSL Certificate + {t('editSSLCertificate')} { + // Get language context for translations + const { t } = useLanguage(); + // State for sidebar collapse functionality const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false); const toggleSidebar = () => setSidebarCollapsed(prev => !prev); @@ -90,15 +93,15 @@ const SslDomain = () => { toggleSidebar={toggleSidebar} />
-

Error Loading SSL Certificates

+

{t('failedToLoadCertificates')}

- {error instanceof Error ? error.message : "Unknown error occurred"} + {error instanceof Error ? error.message : t('unknown')}