mirror of
https://github.com/kyantech/Palmr.git
synced 2025-11-02 13:03:15 +00:00
feat: enhance sharing features with security and expiration settings
- Introduced ShareSecurityModal and ShareExpirationModal components to manage share security and expiration settings. - Updated SharesModals and SharesTable components to integrate new modals for managing security and expiration. - Enhanced ShareManager to handle updates for security and expiration settings. - Improved localization files to include new strings related to share security and expiration across multiple languages. - Refactored existing components to support the new functionality, improving user experience in managing shared items.
This commit is contained in:
@@ -10,7 +10,10 @@
|
||||
"yes": "نعم",
|
||||
"no": "لا",
|
||||
"dashboard": "لوحة القيادة",
|
||||
"back": "رجوع"
|
||||
"back": "رجوع",
|
||||
"updating": "جاري التحديث...",
|
||||
"saving": "جاري الحفظ...",
|
||||
"update": "تحديث"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "إنشاء مشاركة",
|
||||
@@ -18,7 +21,7 @@
|
||||
"descriptionLabel": "الوصف",
|
||||
"descriptionPlaceholder": "أدخل وصفًا (اختياري)",
|
||||
"expirationLabel": "تاريخ الانتهاء",
|
||||
"expirationPlaceholder": "MM/DD/YYYY HH:MM",
|
||||
"expirationPlaceholder": "شهر/يوم/سنة ساعة:دقيقة",
|
||||
"maxViewsLabel": "الحد الأقصى للمشاهدات",
|
||||
"maxViewsPlaceholder": "اتركه فارغًا للحصول على عدد غير محدود",
|
||||
"passwordProtection": "محمي بكلمة مرور",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "الرفع الأخير",
|
||||
"viewAll": "عرض الكل",
|
||||
"uploadFile": "رفع ملف",
|
||||
"noFiles": "لم يتم رفع أي ملفات بعد"
|
||||
"noFiles": "لم يتم رفع أي ملفات بعد",
|
||||
"upload": "رفع"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "المشاركات الأخيرة",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "فشل في إزالة المستلم",
|
||||
"sendingNotifications": "جاري إرسال الإشعارات...",
|
||||
"notifySuccess": "تم إعلام المستلمين بنجاح",
|
||||
"notifyError": "فشل في إعلام المستلمين"
|
||||
"notifyError": "فشل في إعلام المستلمين",
|
||||
"bulkNotifySuccess": "تم إرسال إشعارات إلى {count} مستلم",
|
||||
"selectAll": "تحديد الكل",
|
||||
"singleNotifySuccess": "تم إرسال إشعار إلى {email}",
|
||||
"removeSingle": "إزالة هذا المستلم",
|
||||
"selectRecipient": "تحديد {email}",
|
||||
"bulkRemoveSuccess": "تم إزالة {count} مستلم بنجاح",
|
||||
"notifySingle": "إشعار هذا المستلم",
|
||||
"notifySelected": "إشعار المحددين",
|
||||
"invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح",
|
||||
"noRecipientsDescription": "إضافة مستلمين لمشاركة هذا المحتوى عبر البريد الإلكتروني",
|
||||
"singleNotifyError": "فشل في إشعار المستلم",
|
||||
"bulkRemoveError": "فشل في إزالة المستلمين المحددين",
|
||||
"modalDescription": "إضافة وإدارة المستلمين لهذه المشاركة. يمكنك إشعار جميع أو مستلمين محددين عند تكوين SMTP.",
|
||||
"duplicateEmail": "تم إضافة هذا المستلم بالفعل",
|
||||
"removeSelected": "إزالة المحددين",
|
||||
"selectedCount": "{count} محدد",
|
||||
"addRecipient": "إضافة مستلم",
|
||||
"bulkNotifyError": "فشل في إشعار المستلمين المحددين"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "المستقبلون",
|
||||
"notAvailable": "غير متاح",
|
||||
"invalidDate": "تاريخ غير صحيح",
|
||||
"loadError": "فشل في تحميل تفاصيل المشاركة"
|
||||
"loadError": "فشل في تحميل تفاصيل المشاركة",
|
||||
"editSecurity": "تحرير الأمان",
|
||||
"editExpiration": "تحرير انتهاء الصلاحية"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "تم حذف المشاركة بنجاح",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "فشل في إعلام المستلمين",
|
||||
"bulkDeleteError": "فشل في حذف المشاركات",
|
||||
"bulkDeleteLoading": "جارٍ حذف {count, plural, =1 {مشاركة واحدة} other {# مشاركات}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {تم حذف مشاركة واحدة بنجاح} other {تم حذف # مشاركات بنجاح}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {تم حذف مشاركة واحدة بنجاح} other {تم حذف # مشاركات بنجاح}}",
|
||||
"securityUpdateError": "فشل في تحديث إعدادات الأمان",
|
||||
"expirationUpdateError": "فشل في تحديث إعدادات انتهاء الصلاحية",
|
||||
"securityUpdateSuccess": "تم تحديث إعدادات الأمان بنجاح",
|
||||
"expirationUpdateSuccess": "تم تحديث إعدادات انتهاء الصلاحية بنجاح"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "الحجم الإجمالي",
|
||||
"creating": "جاري الإنشاء...",
|
||||
"create": "إنشاء مشاركة"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "تكوين حماية كلمة المرور وخيارات الأمان لهذه المشاركة",
|
||||
"info": {
|
||||
"title": "كيف يعمل:",
|
||||
"withoutPassword": "يمكن لأي شخص لديه الرابط الوصول إلى هذه المشاركة بدون كلمة مرور.",
|
||||
"withPassword": "سيحتاج المستخدمون إلى إدخال كلمة المرور للوصول إلى هذه المشاركة."
|
||||
},
|
||||
"existingPasswordMessage": "هذه المشاركة لديها بالفعل كلمة مرور. إذا كنت تريد تحديثها، أدخل كلمة المرور الجديدة في الحقل أدناه واحفظ.",
|
||||
"passwordProtection": "حماية كلمة المرور",
|
||||
"error": {
|
||||
"updateFailed": "فشل في تحديث إعدادات الأمان"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "متطلبات كلمة المرور:",
|
||||
"minLength": "على الأقل حرفان"
|
||||
},
|
||||
"newPassword": "كلمة مرور جديدة",
|
||||
"success": {
|
||||
"passwordUpdated": "تم تحديث كلمة المرور بنجاح",
|
||||
"passwordRemoved": "تم إزالة حماية كلمة المرور بنجاح",
|
||||
"passwordSet": "تم تمكين حماية كلمة المرور بنجاح"
|
||||
},
|
||||
"password": "كلمة المرور",
|
||||
"validation": {
|
||||
"passwordRequired": "كلمة المرور مطلوبة",
|
||||
"passwordTooShort": "يجب أن تكون كلمة المرور حرفين على الأقل"
|
||||
},
|
||||
"currentStatus": "الحالة الحالية",
|
||||
"passwordPlaceholder": "أدخل كلمة مرور آمنة",
|
||||
"title": "إعدادات أمان المشاركة"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "لا تنتهي صلاحيته أبداً",
|
||||
"success": {
|
||||
"expirationUpdated": "تم تحديث تاريخ انتهاء الصلاحية بنجاح",
|
||||
"expirationRemoved": "تم إزالة انتهاء الصلاحية بنجاح - المشاركة الآن دائمة",
|
||||
"expirationSet": "تم تعيين تاريخ انتهاء الصلاحية بنجاح"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "يمكنك تغيير أو إزالة تاريخ انتهاء الصلاحية في أي وقت",
|
||||
"willBeInaccessible": "ستصبح المشاركة غير قابلة للوصول بعد هذا التاريخ",
|
||||
"noExpiration": "هذه المشاركة لن تنتهي صلاحيتها أبداً وستبقى قابلة للوصول إلى أجل غير مسمى.",
|
||||
"title": "حول انتهاء الصلاحية:"
|
||||
},
|
||||
"enableExpiration": "تمكين انتهاء الصلاحية",
|
||||
"title": "إعدادات انتهاء صلاحية المشاركة",
|
||||
"subtitle": "تكوين متى ستنتهي صلاحية هذه المشاركة",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "يجب أن يكون تاريخ انتهاء الصلاحية في المستقبل",
|
||||
"dateRequired": "يرجى تحديد تاريخ انتهاء صلاحية"
|
||||
},
|
||||
"currentStatus": "الحالة الحالية",
|
||||
"error": {
|
||||
"updateFailed": "فشل في تحديث إعدادات انتهاء الصلاحية"
|
||||
},
|
||||
"expires": "تنتهي صلاحيته:",
|
||||
"expirationDate": "تاريخ انتهاء الصلاحية"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"dashboard": "Dashboard",
|
||||
"back": "Zurück"
|
||||
"back": "Zurück",
|
||||
"updating": "Aktualisierung läuft...",
|
||||
"saving": "Speichere...",
|
||||
"update": "Aktualisieren"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Freigabe Erstellen",
|
||||
@@ -140,7 +143,7 @@
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"poweredBy": "Powered by",
|
||||
"poweredBy": "Angetrieben von",
|
||||
"kyanHomepage": "Kyantech Homepage"
|
||||
},
|
||||
"forgotPassword": {
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Kürzlich hochgeladen",
|
||||
"viewAll": "Alle anzeigen",
|
||||
"uploadFile": "Datei hochladen",
|
||||
"noFiles": "Noch keine Dateien hochgeladen"
|
||||
"noFiles": "Noch keine Dateien hochgeladen",
|
||||
"upload": "Hochladen"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Kürzlich geteilte",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Fehler beim Entfernen des Empfängers",
|
||||
"sendingNotifications": "Benachrichtigungen werden gesendet...",
|
||||
"notifySuccess": "Empfänger erfolgreich benachrichtigt",
|
||||
"notifyError": "Fehler beim Benachrichtigen der Empfänger"
|
||||
"notifyError": "Fehler beim Benachrichtigen der Empfänger",
|
||||
"bulkNotifySuccess": "Benachrichtigungen an {count} Empfänger gesendet",
|
||||
"selectAll": "Alle auswählen",
|
||||
"singleNotifySuccess": "Benachrichtigung an {email} gesendet",
|
||||
"removeSingle": "Diesen Empfänger entfernen",
|
||||
"selectRecipient": "{email} auswählen",
|
||||
"bulkRemoveSuccess": "{count} Empfänger erfolgreich entfernt",
|
||||
"notifySingle": "Diesen Empfänger benachrichtigen",
|
||||
"notifySelected": "Ausgewählte benachrichtigen",
|
||||
"invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
|
||||
"noRecipientsDescription": "Empfänger hinzufügen, um diesen Inhalt per E-Mail zu teilen",
|
||||
"singleNotifyError": "Empfänger konnte nicht benachrichtigt werden",
|
||||
"bulkRemoveError": "Ausgewählte Empfänger konnten nicht entfernt werden",
|
||||
"modalDescription": "Empfänger für diese Freigabe hinzufügen und verwalten. Sie können alle oder bestimmte Empfänger benachrichtigen, wenn SMTP konfiguriert ist.",
|
||||
"duplicateEmail": "Dieser Empfänger wurde bereits hinzugefügt",
|
||||
"removeSelected": "Ausgewählte entfernen",
|
||||
"selectedCount": "{count} ausgewählt",
|
||||
"addRecipient": "Empfänger hinzufügen",
|
||||
"bulkNotifyError": "Ausgewählte Empfänger konnten nicht benachrichtigt werden"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "Empfänger",
|
||||
"notAvailable": "N/V",
|
||||
"invalidDate": "Ungültiges Datum",
|
||||
"loadError": "Fehler beim Laden der Freigabe-Details"
|
||||
"loadError": "Fehler beim Laden der Freigabe-Details",
|
||||
"editSecurity": "Sicherheit bearbeiten",
|
||||
"editExpiration": "Ablauf bearbeiten"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Freigabe erfolgreich gelöscht",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Fehler beim Benachrichtigen der Empfänger",
|
||||
"bulkDeleteError": "Fehler beim Löschen der Freigaben",
|
||||
"bulkDeleteLoading": "Lösche {count, plural, =1 {1 Freigabe} other {# Freigaben}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 Freigabe erfolgreich gelöscht} other {# Freigaben erfolgreich gelöscht}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 Freigabe erfolgreich gelöscht} other {# Freigaben erfolgreich gelöscht}}",
|
||||
"securityUpdateError": "Sicherheitseinstellungen konnten nicht aktualisiert werden",
|
||||
"expirationUpdateError": "Ablaufeinstellungen konnten nicht aktualisiert werden",
|
||||
"securityUpdateSuccess": "Sicherheitseinstellungen erfolgreich aktualisiert",
|
||||
"expirationUpdateSuccess": "Ablaufeinstellungen erfolgreich aktualisiert"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Gesamtgröße",
|
||||
"creating": "Erstellen...",
|
||||
"create": "Freigabe Erstellen"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Passwortschutz und Sicherheitsoptionen für diese Freigabe konfigurieren",
|
||||
"info": {
|
||||
"title": "So funktioniert es:",
|
||||
"withoutPassword": "Jeder mit dem Link kann auf diese Freigabe ohne Passwort zugreifen.",
|
||||
"withPassword": "Benutzer müssen das Passwort eingeben, um auf diese Freigabe zuzugreifen."
|
||||
},
|
||||
"existingPasswordMessage": "Diese Freigabe hat bereits ein Passwort. Wenn Sie es aktualisieren möchten, geben Sie das neue Passwort in das Feld unten ein und speichern Sie.",
|
||||
"passwordProtection": "Passwortschutz",
|
||||
"error": {
|
||||
"updateFailed": "Sicherheitseinstellungen konnten nicht aktualisiert werden"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Passwort-Anforderungen:",
|
||||
"minLength": "Mindestens 2 Zeichen"
|
||||
},
|
||||
"newPassword": "Neues Passwort",
|
||||
"success": {
|
||||
"passwordUpdated": "Passwort erfolgreich aktualisiert",
|
||||
"passwordRemoved": "Passwortschutz erfolgreich entfernt",
|
||||
"passwordSet": "Passwortschutz erfolgreich aktiviert"
|
||||
},
|
||||
"password": "Passwort",
|
||||
"validation": {
|
||||
"passwordRequired": "Passwort ist erforderlich",
|
||||
"passwordTooShort": "Passwort muss mindestens 2 Zeichen haben"
|
||||
},
|
||||
"currentStatus": "Aktueller Status",
|
||||
"passwordPlaceholder": "Sicheres Passwort eingeben",
|
||||
"title": "Freigabe-Sicherheitseinstellungen"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Läuft nie ab",
|
||||
"success": {
|
||||
"expirationUpdated": "Ablaufdatum erfolgreich aktualisiert",
|
||||
"expirationRemoved": "Ablauf erfolgreich entfernt - Freigabe ist jetzt dauerhaft",
|
||||
"expirationSet": "Ablaufdatum erfolgreich festgelegt"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Sie können das Ablaufdatum jederzeit ändern oder entfernen",
|
||||
"willBeInaccessible": "Die Freigabe wird nach diesem Datum unzugänglich",
|
||||
"noExpiration": "Diese Freigabe läuft nie ab und bleibt unbegrenzt zugänglich.",
|
||||
"title": "Über Ablauf:"
|
||||
},
|
||||
"enableExpiration": "Ablauf aktivieren",
|
||||
"title": "Freigabe-Ablaufeinstellungen",
|
||||
"subtitle": "Konfigurieren, wann diese Freigabe abläuft",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "Ablaufdatum muss in der Zukunft liegen",
|
||||
"dateRequired": "Bitte wählen Sie ein Ablaufdatum"
|
||||
},
|
||||
"currentStatus": "Aktueller Status",
|
||||
"error": {
|
||||
"updateFailed": "Ablaufeinstellungen konnten nicht aktualisiert werden"
|
||||
},
|
||||
"expires": "Läuft ab:",
|
||||
"expirationDate": "Ablaufdatum"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
"loading": "Loading, please wait...",
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"saving": "Saving...",
|
||||
"update": "Update",
|
||||
"updating": "Updating...",
|
||||
"delete": "Delete",
|
||||
"close": "Close",
|
||||
"download": "Download",
|
||||
@@ -274,6 +277,7 @@
|
||||
"recentFiles": {
|
||||
"title": "Recent Uploads",
|
||||
"viewAll": "View All",
|
||||
"upload": "Upload",
|
||||
"uploadFile": "Upload File",
|
||||
"noFiles": "No files uploaded yet"
|
||||
},
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Failed to remove recipient",
|
||||
"sendingNotifications": "Sending notifications...",
|
||||
"notifySuccess": "Recipients notified successfully",
|
||||
"notifyError": "Failed to notify recipients"
|
||||
"notifyError": "Failed to notify recipients",
|
||||
"selectAll": "Select all",
|
||||
"selectedCount": "{count} selected",
|
||||
"selectRecipient": "Select {email}",
|
||||
"notifySelected": "Notify Selected",
|
||||
"removeSelected": "Remove Selected",
|
||||
"notifySingle": "Notify this recipient",
|
||||
"removeSingle": "Remove this recipient",
|
||||
"bulkRemoveSuccess": "{count} recipients removed successfully",
|
||||
"bulkRemoveError": "Failed to remove selected recipients",
|
||||
"bulkNotifySuccess": "Notifications sent to {count} recipients",
|
||||
"bulkNotifyError": "Failed to notify selected recipients",
|
||||
"singleNotifySuccess": "Notification sent to {email}",
|
||||
"singleNotifyError": "Failed to notify recipient",
|
||||
"modalDescription": "Add and manage recipients for this share. You can notify all or specific recipients when SMTP is configured.",
|
||||
"addRecipient": "Add Recipient",
|
||||
"invalidEmail": "Please enter a valid email address",
|
||||
"duplicateEmail": "This recipient has already been added",
|
||||
"noRecipientsDescription": "Add recipients to share this content via email"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -529,6 +551,8 @@
|
||||
"expires": "Expires",
|
||||
"never": "Never",
|
||||
"security": "Security",
|
||||
"editSecurity": "Edit Security",
|
||||
"editExpiration": "Edit Expiration",
|
||||
"passwordProtected": "Password Protected",
|
||||
"publicAccess": "Public Access",
|
||||
"maxViews": "Max Views:",
|
||||
@@ -538,11 +562,73 @@
|
||||
"invalidDate": "Invalid date",
|
||||
"loadError": "Failed to load share details"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"title": "Share Security Settings",
|
||||
"subtitle": "Configure password protection and security options for this share",
|
||||
"currentStatus": "Current Status",
|
||||
"passwordProtection": "Password Protection",
|
||||
"password": "Password",
|
||||
"newPassword": "New Password",
|
||||
"passwordPlaceholder": "Enter a secure password",
|
||||
"existingPasswordMessage": "This share already has a password. If you want to update it, enter the new password in the field below and save.",
|
||||
"passwordRequirements": {
|
||||
"title": "Password requirements:",
|
||||
"minLength": "At least 2 characters"
|
||||
},
|
||||
"info": {
|
||||
"title": "How it works:",
|
||||
"withPassword": "Users will need to enter the password to access this share.",
|
||||
"withoutPassword": "Anyone with the link can access this share without a password."
|
||||
},
|
||||
"validation": {
|
||||
"passwordRequired": "Password is required",
|
||||
"passwordTooShort": "Password must be at least 2 characters"
|
||||
},
|
||||
"success": {
|
||||
"passwordSet": "Password protection enabled successfully",
|
||||
"passwordUpdated": "Password updated successfully",
|
||||
"passwordRemoved": "Password protection removed successfully"
|
||||
},
|
||||
"error": {
|
||||
"updateFailed": "Failed to update security settings"
|
||||
}
|
||||
},
|
||||
"shareExpiration": {
|
||||
"title": "Share Expiration Settings",
|
||||
"subtitle": "Configure when this share will expire",
|
||||
"currentStatus": "Current Status",
|
||||
"expires": "Expires:",
|
||||
"neverExpires": "Never Expires",
|
||||
"enableExpiration": "Enable Expiration",
|
||||
"expirationDate": "Expiration Date",
|
||||
"validation": {
|
||||
"dateRequired": "Please select an expiration date",
|
||||
"dateMustBeFuture": "Expiration date must be in the future"
|
||||
},
|
||||
"success": {
|
||||
"expirationSet": "Expiration date set successfully",
|
||||
"expirationUpdated": "Expiration date updated successfully",
|
||||
"expirationRemoved": "Expiration removed successfully - share is now permanent"
|
||||
},
|
||||
"error": {
|
||||
"updateFailed": "Failed to update expiration settings"
|
||||
},
|
||||
"info": {
|
||||
"title": "About expiration:",
|
||||
"willBeInaccessible": "The share will become inaccessible after this date",
|
||||
"canBeChanged": "You can change or remove the expiration date anytime",
|
||||
"noExpiration": "This share will never expire and will remain accessible indefinitely."
|
||||
}
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Share deleted successfully",
|
||||
"deleteError": "Failed to delete share",
|
||||
"updateSuccess": "Share updated successfully",
|
||||
"updateError": "Failed to update share",
|
||||
"securityUpdateSuccess": "Security settings updated successfully",
|
||||
"securityUpdateError": "Failed to update security settings",
|
||||
"expirationUpdateSuccess": "Expiration settings updated successfully",
|
||||
"expirationUpdateError": "Failed to update expiration settings",
|
||||
"filesUpdateSuccess": "Files updated successfully",
|
||||
"filesUpdateError": "Failed to update files",
|
||||
"recipientsUpdateSuccess": "Recipients updated successfully",
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Sí",
|
||||
"no": "No",
|
||||
"dashboard": "Panel",
|
||||
"back": "Volver"
|
||||
"back": "Volver",
|
||||
"updating": "Actualizando...",
|
||||
"saving": "Guardando...",
|
||||
"update": "Actualizar"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Crear Compartir",
|
||||
@@ -272,9 +275,10 @@
|
||||
}
|
||||
},
|
||||
"recentFiles": {
|
||||
"title": "Subidas recientes",
|
||||
"viewAll": "Ver todo",
|
||||
"uploadFile": "Subir archivo",
|
||||
"title": "Cargas Recientes",
|
||||
"viewAll": "Ver Todo",
|
||||
"upload": "Subir",
|
||||
"uploadFile": "Subir Archivo",
|
||||
"noFiles": "Aún no se han subido archivos"
|
||||
},
|
||||
"recentShares": {
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Error al eliminar el destinatario",
|
||||
"sendingNotifications": "Enviando notificaciones...",
|
||||
"notifySuccess": "Destinatarios notificados exitosamente",
|
||||
"notifyError": "Error al notificar a los destinatarios"
|
||||
"notifyError": "Error al notificar a los destinatarios",
|
||||
"bulkNotifySuccess": "Notificaciones enviadas a {count} destinatarios",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"singleNotifySuccess": "Notificación enviada a {email}",
|
||||
"removeSingle": "Eliminar este destinatario",
|
||||
"selectRecipient": "Seleccionar {email}",
|
||||
"bulkRemoveSuccess": "{count} destinatarios eliminados exitosamente",
|
||||
"notifySingle": "Notificar este destinatario",
|
||||
"notifySelected": "Notificar Seleccionados",
|
||||
"invalidEmail": "Por favor ingresa una dirección de correo válida",
|
||||
"noRecipientsDescription": "Agregar destinatarios para compartir este contenido por correo",
|
||||
"singleNotifyError": "Error al notificar destinatario",
|
||||
"bulkRemoveError": "Error al eliminar destinatarios seleccionados",
|
||||
"modalDescription": "Agregar y gestionar destinatarios para este compartir. Puedes notificar a todos o destinatarios específicos cuando SMTP esté configurado.",
|
||||
"duplicateEmail": "Este destinatario ya ha sido agregado",
|
||||
"removeSelected": "Eliminar Seleccionados",
|
||||
"selectedCount": "{count} seleccionados",
|
||||
"addRecipient": "Agregar Destinatario",
|
||||
"bulkNotifyError": "Error al notificar destinatarios seleccionados"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -534,9 +556,11 @@
|
||||
"maxViews": "Vistas Máx.:",
|
||||
"files": "Archivos",
|
||||
"recipients": "Destinatarios",
|
||||
"notAvailable": "N/A",
|
||||
"notAvailable": "N/D",
|
||||
"invalidDate": "Fecha inválida",
|
||||
"loadError": "Error al cargar detalles del compartir"
|
||||
"loadError": "Error al cargar detalles del compartir",
|
||||
"editSecurity": "Editar Seguridad",
|
||||
"editExpiration": "Editar Expiración"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Compartición eliminada exitosamente",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Error al notificar a los destinatarios",
|
||||
"bulkDeleteError": "Error al eliminar compartidos",
|
||||
"bulkDeleteLoading": "Eliminando {count, plural, =1 {1 compartido} other {# compartidos}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 compartido eliminado con éxito} other {# compartidos eliminados con éxito}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 compartido eliminado con éxito} other {# compartidos eliminados con éxito}}",
|
||||
"securityUpdateError": "Error al actualizar configuración de seguridad",
|
||||
"expirationUpdateError": "Error al actualizar configuración de expiración",
|
||||
"securityUpdateSuccess": "Configuración de seguridad actualizada exitosamente",
|
||||
"expirationUpdateSuccess": "Configuración de expiración actualizada exitosamente"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Tamaño total",
|
||||
"creating": "Creando...",
|
||||
"create": "Crear Compartir"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Configurar protección por contraseña y opciones de seguridad para este compartir",
|
||||
"info": {
|
||||
"title": "Cómo funciona:",
|
||||
"withoutPassword": "Cualquiera con el enlace puede acceder a este compartir sin contraseña.",
|
||||
"withPassword": "Los usuarios necesitarán ingresar la contraseña para acceder a este compartir."
|
||||
},
|
||||
"existingPasswordMessage": "Este compartir ya tiene una contraseña. Si quieres actualizarla, ingresa la nueva contraseña en el campo de abajo y guarda.",
|
||||
"passwordProtection": "Protección por Contraseña",
|
||||
"error": {
|
||||
"updateFailed": "Error al actualizar configuración de seguridad"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Requisitos de contraseña:",
|
||||
"minLength": "Al menos 2 caracteres"
|
||||
},
|
||||
"newPassword": "Nueva Contraseña",
|
||||
"success": {
|
||||
"passwordUpdated": "Contraseña actualizada exitosamente",
|
||||
"passwordRemoved": "Protección por contraseña eliminada exitosamente",
|
||||
"passwordSet": "Protección por contraseña habilitada exitosamente"
|
||||
},
|
||||
"password": "Contraseña",
|
||||
"validation": {
|
||||
"passwordRequired": "La contraseña es requerida",
|
||||
"passwordTooShort": "La contraseña debe tener al menos 2 caracteres"
|
||||
},
|
||||
"currentStatus": "Estado Actual",
|
||||
"passwordPlaceholder": "Ingresa una contraseña segura",
|
||||
"title": "Configuración de Seguridad del Compartir"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Nunca Expira",
|
||||
"success": {
|
||||
"expirationUpdated": "Fecha de expiración actualizada exitosamente",
|
||||
"expirationRemoved": "Expiración eliminada exitosamente - el compartir es ahora permanente",
|
||||
"expirationSet": "Fecha de expiración establecida exitosamente"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Puedes cambiar o eliminar la fecha de expiración en cualquier momento",
|
||||
"willBeInaccessible": "El compartir será inaccesible después de esta fecha",
|
||||
"noExpiration": "Este compartir nunca expirará y permanecerá accesible indefinidamente.",
|
||||
"title": "Acerca de la expiración:"
|
||||
},
|
||||
"enableExpiration": "Habilitar Expiración",
|
||||
"title": "Configuración de Expiración del Compartir",
|
||||
"subtitle": "Configurar cuándo expirará este compartir",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "La fecha de expiración debe estar en el futuro",
|
||||
"dateRequired": "Por favor selecciona una fecha de expiración"
|
||||
},
|
||||
"currentStatus": "Estado Actual",
|
||||
"error": {
|
||||
"updateFailed": "Error al actualizar configuración de expiración"
|
||||
},
|
||||
"expires": "Expira:",
|
||||
"expirationDate": "Fecha de Expiración"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"dashboard": "Tableau de bord",
|
||||
"back": "Retour"
|
||||
"back": "Retour",
|
||||
"updating": "Mise à jour...",
|
||||
"saving": "Enregistrement...",
|
||||
"update": "Mettre à jour"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Créer un Partage",
|
||||
@@ -231,7 +234,7 @@
|
||||
"firstName": "Prénom",
|
||||
"lastName": "Nom",
|
||||
"username": "Nom d'Utilisateur",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"updateButton": "Mettre à Jour le Profil"
|
||||
},
|
||||
"header": {
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Téléchargements Récents",
|
||||
"viewAll": "Voir Tout",
|
||||
"uploadFile": "Envoyer un Fichier",
|
||||
"noFiles": "Aucun fichier téléchargé pour le moment"
|
||||
"noFiles": "Aucun fichier téléchargé pour le moment",
|
||||
"upload": "Télécharger"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Partages Récents",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Échec de la suppression du destinataire",
|
||||
"sendingNotifications": "Envoi des notifications...",
|
||||
"notifySuccess": "Destinataires notifiés avec succès",
|
||||
"notifyError": "Échec de la notification des destinataires"
|
||||
"notifyError": "Échec de la notification des destinataires",
|
||||
"bulkNotifySuccess": "Notifications envoyées à {count} destinataires",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"singleNotifySuccess": "Notification envoyée à {email}",
|
||||
"removeSingle": "Supprimer ce destinataire",
|
||||
"selectRecipient": "Sélectionner {email}",
|
||||
"bulkRemoveSuccess": "{count} destinataires supprimés avec succès",
|
||||
"notifySingle": "Notifier ce destinataire",
|
||||
"notifySelected": "Notifier les Sélectionnés",
|
||||
"invalidEmail": "Veuillez entrer une adresse email valide",
|
||||
"noRecipientsDescription": "Ajouter des destinataires pour partager ce contenu par email",
|
||||
"singleNotifyError": "Échec de notification du destinataire",
|
||||
"bulkRemoveError": "Échec de suppression des destinataires sélectionnés",
|
||||
"modalDescription": "Ajouter et gérer les destinataires pour ce partage. Vous pouvez notifier tous ou des destinataires spécifiques lorsque SMTP est configuré.",
|
||||
"duplicateEmail": "Ce destinataire a déjà été ajouté",
|
||||
"removeSelected": "Supprimer les Sélectionnés",
|
||||
"selectedCount": "{count} sélectionnés",
|
||||
"addRecipient": "Ajouter Destinataire",
|
||||
"bulkNotifyError": "Échec de notification des destinataires sélectionnés"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -312,7 +334,7 @@
|
||||
"firstName": "Prénom",
|
||||
"lastName": "Nom",
|
||||
"username": "Nom d'utilisateur",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"password": "Mot de passe"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -358,7 +380,7 @@
|
||||
"description": "Paramètres de base de l'application"
|
||||
},
|
||||
"email": {
|
||||
"title": "Email",
|
||||
"title": "E-mail",
|
||||
"description": "Configuration du serveur de messagerie"
|
||||
},
|
||||
"security": {
|
||||
@@ -534,9 +556,11 @@
|
||||
"maxViews": "Vues Max.:",
|
||||
"files": "Fichiers",
|
||||
"recipients": "Destinataires",
|
||||
"notAvailable": "N/A",
|
||||
"notAvailable": "N/D",
|
||||
"invalidDate": "Date invalide",
|
||||
"loadError": "Échec du chargement des détails du partage"
|
||||
"loadError": "Échec du chargement des détails du partage",
|
||||
"editSecurity": "Modifier la Sécurité",
|
||||
"editExpiration": "Modifier l'Expiration"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Partage supprimé avec succès",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Échec de la notification des destinataires",
|
||||
"bulkDeleteError": "Échec de la suppression des partages",
|
||||
"bulkDeleteLoading": "Suppression de {count, plural, =1 {1 partage} other {# partages}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 partage supprimé avec succès} other {# partages supprimés avec succès}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 partage supprimé avec succès} other {# partages supprimés avec succès}}",
|
||||
"securityUpdateError": "Échec de mise à jour des paramètres de sécurité",
|
||||
"expirationUpdateError": "Échec de mise à jour des paramètres d'expiration",
|
||||
"securityUpdateSuccess": "Paramètres de sécurité mis à jour avec succès",
|
||||
"expirationUpdateSuccess": "Paramètres d'expiration mis à jour avec succès"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -700,7 +728,7 @@
|
||||
"firstName": "Prénom",
|
||||
"lastName": "Nom",
|
||||
"username": "Nom d'Utilisateur",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"password": "Mot de Passe",
|
||||
"newPassword": "Nouveau Mot de Passe (optionnel)",
|
||||
"passwordPlaceholder": "Laisser vide pour garder le mot de passe actuel",
|
||||
@@ -730,7 +758,7 @@
|
||||
"actions": "ACTIONS",
|
||||
"active": "Actif",
|
||||
"inactive": "Inactif",
|
||||
"admin": "Admin",
|
||||
"admin": "Administrateur",
|
||||
"userr": "Utilisateur"
|
||||
}
|
||||
},
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Taille totale",
|
||||
"creating": "Création...",
|
||||
"create": "Créer un Partage"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Configurer la protection par mot de passe et les options de sécurité pour ce partage",
|
||||
"info": {
|
||||
"title": "Comment ça marche:",
|
||||
"withoutPassword": "Toute personne ayant le lien peut accéder à ce partage sans mot de passe.",
|
||||
"withPassword": "Les utilisateurs devront entrer le mot de passe pour accéder à ce partage."
|
||||
},
|
||||
"existingPasswordMessage": "Ce partage a déjà un mot de passe. Si vous voulez le mettre à jour, entrez le nouveau mot de passe dans le champ ci-dessous et enregistrez.",
|
||||
"passwordProtection": "Protection par Mot de Passe",
|
||||
"error": {
|
||||
"updateFailed": "Échec de mise à jour des paramètres de sécurité"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Exigences du mot de passe:",
|
||||
"minLength": "Au moins 2 caractères"
|
||||
},
|
||||
"newPassword": "Nouveau Mot de Passe",
|
||||
"success": {
|
||||
"passwordUpdated": "Mot de passe mis à jour avec succès",
|
||||
"passwordRemoved": "Protection par mot de passe supprimée avec succès",
|
||||
"passwordSet": "Protection par mot de passe activée avec succès"
|
||||
},
|
||||
"password": "Mot de Passe",
|
||||
"validation": {
|
||||
"passwordRequired": "Le mot de passe est requis",
|
||||
"passwordTooShort": "Le mot de passe doit contenir au moins 2 caractères"
|
||||
},
|
||||
"currentStatus": "Statut Actuel",
|
||||
"passwordPlaceholder": "Entrez un mot de passe sécurisé",
|
||||
"title": "Paramètres de Sécurité du Partage"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "N'expire Jamais",
|
||||
"success": {
|
||||
"expirationUpdated": "Date d'expiration mise à jour avec succès",
|
||||
"expirationRemoved": "Expiration supprimée avec succès - le partage est maintenant permanent",
|
||||
"expirationSet": "Date d'expiration définie avec succès"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Vous pouvez changer ou supprimer la date d'expiration à tout moment",
|
||||
"willBeInaccessible": "Le partage deviendra inaccessible après cette date",
|
||||
"noExpiration": "Ce partage n'expirera jamais et restera accessible indéfiniment.",
|
||||
"title": "À propos de l'expiration:"
|
||||
},
|
||||
"enableExpiration": "Activer l'Expiration",
|
||||
"title": "Paramètres d'Expiration du Partage",
|
||||
"subtitle": "Configurer quand ce partage expirera",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "La date d'expiration doit être dans le futur",
|
||||
"dateRequired": "Veuillez sélectionner une date d'expiration"
|
||||
},
|
||||
"currentStatus": "Statut Actuel",
|
||||
"error": {
|
||||
"updateFailed": "Échec de mise à jour des paramètres d'expiration"
|
||||
},
|
||||
"expires": "Expire:",
|
||||
"expirationDate": "Date d'Expiration"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "हाँ",
|
||||
"no": "नहीं",
|
||||
"dashboard": "डैशबोर्ड",
|
||||
"back": "वापस"
|
||||
"back": "वापस",
|
||||
"updating": "अपडेट हो रहा है...",
|
||||
"saving": "सहेज रहा है...",
|
||||
"update": "अपडेट करें"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "साझाकरण बनाएं",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "हाल की अपलोड्स",
|
||||
"viewAll": "सभी देखें",
|
||||
"uploadFile": "फाइल अपलोड करें",
|
||||
"noFiles": "अभी तक कोई फाइल अपलोड नहीं हुई"
|
||||
"noFiles": "अभी तक कोई फाइल अपलोड नहीं हुई",
|
||||
"upload": "अपलोड"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "हाल के साझाकरण",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "प्राप्तकर्ता हटाने में विफल",
|
||||
"sendingNotifications": "सूचनाएँ भेजी जा रही हैं...",
|
||||
"notifySuccess": "प्राप्तकर्ताओं को सफलतापूर्वक सूचित किया गया",
|
||||
"notifyError": "प्राप्तकर्ताओं को सूचित करने में विफल"
|
||||
"notifyError": "प्राप्तकर्ताओं को सूचित करने में विफल",
|
||||
"bulkNotifySuccess": "{count} प्राप्तकर्ताओं को सूचनाएं भेजी गईं",
|
||||
"selectAll": "सभी चुनें",
|
||||
"singleNotifySuccess": "{email} को सूचना भेजी गई",
|
||||
"removeSingle": "इस प्राप्तकर्ता को हटाएं",
|
||||
"selectRecipient": "{email} चुनें",
|
||||
"bulkRemoveSuccess": "{count} प्राप्तकर्ता सफलतापूर्वक हटाए गए",
|
||||
"notifySingle": "इस प्राप्तकर्ता को सूचित करें",
|
||||
"notifySelected": "चयनित को सूचित करें",
|
||||
"invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें",
|
||||
"noRecipientsDescription": "ईमेल के माध्यम से इस सामग्री को साझा करने के लिए प्राप्तकर्ता जोड़ें",
|
||||
"singleNotifyError": "प्राप्तकर्ता को सूचित करने में विफल",
|
||||
"bulkRemoveError": "चयनित प्राप्तकर्ताओं को हटाने में विफल",
|
||||
"modalDescription": "इस साझाकरण के लिए प्राप्तकर्ता जोड़ें और प्रबंधित करें। जब SMTP कॉन्फ़िगर किया गया हो तो आप सभी या विशिष्ट प्राप्तकर्ताओं को सूचित कर सकते हैं।",
|
||||
"duplicateEmail": "यह प्राप्तकर्ता पहले से ही जोड़ा गया है",
|
||||
"removeSelected": "चयनित हटाएं",
|
||||
"selectedCount": "{count} चयनित",
|
||||
"addRecipient": "प्राप्तकर्ता जोड़ें",
|
||||
"bulkNotifyError": "चयनित प्राप्तकर्ताओं को सूचित करने में विफल"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "प्राप्तकर्ता",
|
||||
"notAvailable": "उप/नहीं",
|
||||
"invalidDate": "अमान्य तिथि",
|
||||
"loadError": "साझाकरण विवरण लोड करने में विफल"
|
||||
"loadError": "साझाकरण विवरण लोड करने में विफल",
|
||||
"editSecurity": "सुरक्षा संपादित करें",
|
||||
"editExpiration": "समाप्ति संपादित करें"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "साझाकरण सफलतापूर्वक हटाया गया",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "प्राप्तकर्ताओं को सूचित करने में त्रुटि",
|
||||
"bulkDeleteError": "साझाकरण हटाने में विफल",
|
||||
"bulkDeleteLoading": "{count, plural, =1 {1 साझाकरण} other {# साझाकरण}} हटाया जा रहा है...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 साझाकरण सफलतापूर्वक हटाया गया} other {# साझाकरण सफलतापूर्वक हटाए गए}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 साझाकरण सफलतापूर्वक हटाया गया} other {# साझाकरण सफलतापूर्वक हटाए गए}}",
|
||||
"securityUpdateError": "सुरक्षा सेटिंग्स अपडेट करने में विफल",
|
||||
"expirationUpdateError": "समाप्ति सेटिंग्स अपडेट करने में विफल",
|
||||
"securityUpdateSuccess": "सुरक्षा सेटिंग्स सफलतापूर्वक अपडेट हुईं",
|
||||
"expirationUpdateSuccess": "समाप्ति सेटिंग्स सफलतापूर्वक अपडेट हुईं"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "कुल आकार",
|
||||
"creating": "बनाया जा रहा है...",
|
||||
"create": "साझाकरण बनाएं"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "इस साझाकरण के लिए पासवर्ड सुरक्षा और सुरक्षा विकल्प कॉन्फ़िगर करें",
|
||||
"info": {
|
||||
"title": "यह कैसे काम करता है:",
|
||||
"withoutPassword": "लिंक वाला कोई भी व्यक्ति बिना पासवर्ड के इस साझाकरण तक पहुंच सकता है।",
|
||||
"withPassword": "उपयोगकर्ताओं को इस साझाकरण तक पहुंचने के लिए पासवर्ड दर्ज करना होगा।"
|
||||
},
|
||||
"existingPasswordMessage": "इस साझाकरण में पहले से ही एक पासवर्ड है। यदि आप इसे अपडेट करना चाहते हैं, तो नीचे दिए गए फील्ड में नया पासवर्ड दर्ज करें और सहेजें।",
|
||||
"passwordProtection": "पासवर्ड सुरक्षा",
|
||||
"error": {
|
||||
"updateFailed": "सुरक्षा सेटिंग्स अपडेट करने में विफल"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "पासवर्ड आवश्यकताएं:",
|
||||
"minLength": "कम से कम 2 अक्षर"
|
||||
},
|
||||
"newPassword": "नया पासवर्ड",
|
||||
"success": {
|
||||
"passwordUpdated": "पासवर्ड सफलतापूर्वक अपडेट हुआ",
|
||||
"passwordRemoved": "पासवर्ड सुरक्षा सफलतापूर्वक हटाई गई",
|
||||
"passwordSet": "पासवर्ड सुरक्षा सफलतापूर्वक सक्षम की गई"
|
||||
},
|
||||
"password": "पासवर्ड",
|
||||
"validation": {
|
||||
"passwordRequired": "पासवर्ड आवश्यक है",
|
||||
"passwordTooShort": "पासवर्ड कम से कम 2 अक्षर का होना चाहिए"
|
||||
},
|
||||
"currentStatus": "वर्तमान स्थिति",
|
||||
"passwordPlaceholder": "एक सुरक्षित पासवर्ड दर्ज करें",
|
||||
"title": "साझाकरण सुरक्षा सेटिंग्स"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "कभी समाप्त नहीं होता",
|
||||
"success": {
|
||||
"expirationUpdated": "समाप्ति तिथि सफलतापूर्वक अपडेट हुई",
|
||||
"expirationRemoved": "समाप्ति सफलतापूर्वक हटाई गई - साझाकरण अब स्थायी है",
|
||||
"expirationSet": "समाप्ति तिथि सफलतापूर्वक सेट की गई"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "आप समाप्ति तिथि को कभी भी बदल या हटा सकते हैं",
|
||||
"willBeInaccessible": "इस तिथि के बाद साझाकरण अगम्य हो जाएगा",
|
||||
"noExpiration": "यह साझाकरण कभी समाप्त नहीं होगा और अनिश्चित काल तक पहुंच योग्य रहेगा।",
|
||||
"title": "समाप्ति के बारे में:"
|
||||
},
|
||||
"enableExpiration": "समाप्ति सक्षम करें",
|
||||
"title": "साझाकरण समाप्ति सेटिंग्स",
|
||||
"subtitle": "कॉन्फ़िगर करें कि यह साझाकरण कब समाप्त होगा",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "समाप्ति तिथि भविष्य में होनी चाहिए",
|
||||
"dateRequired": "कृपया एक समाप्ति तिथि चुनें"
|
||||
},
|
||||
"currentStatus": "वर्तमान स्थिति",
|
||||
"error": {
|
||||
"updateFailed": "समाप्ति सेटिंग्स अपडेट करने में विफल"
|
||||
},
|
||||
"expires": "समाप्त होता है:",
|
||||
"expirationDate": "समाप्ति तिथि"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Sì",
|
||||
"no": "No",
|
||||
"dashboard": "Dashboard",
|
||||
"back": "Indietro"
|
||||
"back": "Indietro",
|
||||
"updating": "Aggiornamento...",
|
||||
"saving": "Salvataggio...",
|
||||
"update": "Aggiorna"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Crea Condivisione",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Caricamenti Recenti",
|
||||
"viewAll": "Visualizza Tutti",
|
||||
"uploadFile": "Carica File",
|
||||
"noFiles": "Nessun file caricato ancora"
|
||||
"noFiles": "Nessun file caricato ancora",
|
||||
"upload": "Carica"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Condivisioni Recenti",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Errore durante la rimozione del destinatario",
|
||||
"sendingNotifications": "Invio notifiche in corso...",
|
||||
"notifySuccess": "Destinatari notificati con successo",
|
||||
"notifyError": "Errore durante la notifica dei destinatari"
|
||||
"notifyError": "Errore durante la notifica dei destinatari",
|
||||
"bulkNotifySuccess": "Notifiche inviate a {count} destinatari",
|
||||
"selectAll": "Seleziona tutto",
|
||||
"singleNotifySuccess": "Notifica inviata a {email}",
|
||||
"removeSingle": "Rimuovi questo destinatario",
|
||||
"selectRecipient": "Seleziona {email}",
|
||||
"bulkRemoveSuccess": "{count} destinatari rimossi con successo",
|
||||
"notifySingle": "Notifica questo destinatario",
|
||||
"notifySelected": "Notifica Selezionati",
|
||||
"invalidEmail": "Inserisci un indirizzo email valido",
|
||||
"noRecipientsDescription": "Aggiungi destinatari per condividere questo contenuto via email",
|
||||
"singleNotifyError": "Impossibile notificare il destinatario",
|
||||
"bulkRemoveError": "Impossibile rimuovere i destinatari selezionati",
|
||||
"modalDescription": "Aggiungi e gestisci destinatari per questa condivisione. Puoi notificare tutti o destinatari specifici quando SMTP è configurato.",
|
||||
"duplicateEmail": "Questo destinatario è già stato aggiunto",
|
||||
"removeSelected": "Rimuovi Selezionati",
|
||||
"selectedCount": "{count} selezionati",
|
||||
"addRecipient": "Aggiungi Destinatario",
|
||||
"bulkNotifyError": "Impossibile notificare i destinatari selezionati"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "Destinatari",
|
||||
"notAvailable": "N/D",
|
||||
"invalidDate": "Data non valida",
|
||||
"loadError": "Errore nel caricamento dei dettagli della condivisione"
|
||||
"loadError": "Errore nel caricamento dei dettagli della condivisione",
|
||||
"editSecurity": "Modifica Sicurezza",
|
||||
"editExpiration": "Modifica Scadenza"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Condivisione eliminata con successo",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Errore durante la notifica dei destinatari",
|
||||
"bulkDeleteError": "Errore nell'eliminazione delle condivisioni",
|
||||
"bulkDeleteLoading": "Eliminazione di {count, plural, =1 {1 condivisione} other {# condivisioni}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 condivisione eliminata con successo} other {# condivisioni eliminate con successo}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 condivisione eliminata con successo} other {# condivisioni eliminate con successo}}",
|
||||
"securityUpdateError": "Impossibile aggiornare le impostazioni di sicurezza",
|
||||
"expirationUpdateError": "Impossibile aggiornare le impostazioni di scadenza",
|
||||
"securityUpdateSuccess": "Impostazioni di sicurezza aggiornate con successo",
|
||||
"expirationUpdateSuccess": "Impostazioni di scadenza aggiornate con successo"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Dimensione totale",
|
||||
"creating": "Creazione...",
|
||||
"create": "Crea Condivisione"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Configura protezione password e opzioni di sicurezza per questa condivisione",
|
||||
"info": {
|
||||
"title": "Come funziona:",
|
||||
"withoutPassword": "Chiunque abbia il link può accedere a questa condivisione senza password.",
|
||||
"withPassword": "Gli utenti dovranno inserire la password per accedere a questa condivisione."
|
||||
},
|
||||
"existingPasswordMessage": "Questa condivisione ha già una password. Se vuoi aggiornarla, inserisci la nuova password nel campo sottostante e salva.",
|
||||
"passwordProtection": "Protezione Password",
|
||||
"error": {
|
||||
"updateFailed": "Impossibile aggiornare le impostazioni di sicurezza"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Requisiti password:",
|
||||
"minLength": "Almeno 2 caratteri"
|
||||
},
|
||||
"newPassword": "Nuova Password",
|
||||
"success": {
|
||||
"passwordUpdated": "Password aggiornata con successo",
|
||||
"passwordRemoved": "Protezione password rimossa con successo",
|
||||
"passwordSet": "Protezione password abilitata con successo"
|
||||
},
|
||||
"password": "Password",
|
||||
"validation": {
|
||||
"passwordRequired": "La password è obbligatoria",
|
||||
"passwordTooShort": "La password deve essere di almeno 2 caratteri"
|
||||
},
|
||||
"currentStatus": "Stato Attuale",
|
||||
"passwordPlaceholder": "Inserisci una password sicura",
|
||||
"title": "Impostazioni Sicurezza Condivisione"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Non Scade Mai",
|
||||
"success": {
|
||||
"expirationUpdated": "Data di scadenza aggiornata con successo",
|
||||
"expirationRemoved": "Scadenza rimossa con successo - la condivisione è ora permanente",
|
||||
"expirationSet": "Data di scadenza impostata con successo"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Puoi cambiare o rimuovere la data di scadenza in qualsiasi momento",
|
||||
"willBeInaccessible": "La condivisione diventerà inaccessibile dopo questa data",
|
||||
"noExpiration": "Questa condivisione non scadrà mai e rimarrà accessibile indefinitamente.",
|
||||
"title": "Informazioni sulla scadenza:"
|
||||
},
|
||||
"enableExpiration": "Abilita Scadenza",
|
||||
"title": "Impostazioni Scadenza Condivisione",
|
||||
"subtitle": "Configura quando questa condivisione scadrà",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "La data di scadenza deve essere nel futuro",
|
||||
"dateRequired": "Seleziona una data di scadenza"
|
||||
},
|
||||
"currentStatus": "Stato Attuale",
|
||||
"error": {
|
||||
"updateFailed": "Impossibile aggiornare le impostazioni di scadenza"
|
||||
},
|
||||
"expires": "Scade:",
|
||||
"expirationDate": "Data di Scadenza"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "はい",
|
||||
"no": "いいえ",
|
||||
"dashboard": "ダッシュボード",
|
||||
"back": "戻る"
|
||||
"back": "戻る",
|
||||
"updating": "更新中...",
|
||||
"saving": "保存中...",
|
||||
"update": "更新"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "共有を作成",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "最近のアップロード",
|
||||
"viewAll": "すべて表示",
|
||||
"uploadFile": "ファイルをアップロード",
|
||||
"noFiles": "まだファイルがアップロードされていません"
|
||||
"noFiles": "まだファイルがアップロードされていません",
|
||||
"upload": "アップロード"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "最近の共有",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "受信者の削除に失敗しました",
|
||||
"sendingNotifications": "通知を送信中...",
|
||||
"notifySuccess": "受信者に正常に通知しました",
|
||||
"notifyError": "受信者への通知に失敗しました"
|
||||
"notifyError": "受信者への通知に失敗しました",
|
||||
"bulkNotifySuccess": "{count}人の受信者に通知を送信しました",
|
||||
"selectAll": "すべて選択",
|
||||
"singleNotifySuccess": "{email}に通知を送信しました",
|
||||
"removeSingle": "この受信者を削除",
|
||||
"selectRecipient": "{email}を選択",
|
||||
"bulkRemoveSuccess": "{count}人の受信者が正常に削除されました",
|
||||
"notifySingle": "この受信者に通知",
|
||||
"notifySelected": "選択済みに通知",
|
||||
"invalidEmail": "有効なメールアドレスを入力してください",
|
||||
"noRecipientsDescription": "メールでこのコンテンツを共有するために受信者を追加してください",
|
||||
"singleNotifyError": "受信者への通知に失敗しました",
|
||||
"bulkRemoveError": "選択された受信者の削除に失敗しました",
|
||||
"modalDescription": "この共有の受信者を追加・管理します。SMTPが設定されている場合、すべての受信者または特定の受信者に通知できます。",
|
||||
"duplicateEmail": "この受信者は既に追加されています",
|
||||
"removeSelected": "選択済みを削除",
|
||||
"selectedCount": "{count}件選択済み",
|
||||
"addRecipient": "受信者を追加",
|
||||
"bulkNotifyError": "選択された受信者への通知に失敗しました"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -534,9 +556,11 @@
|
||||
"maxViews": "最大表示回数:",
|
||||
"files": "ファイル",
|
||||
"recipients": "受信者",
|
||||
"notAvailable": "N/A",
|
||||
"notAvailable": "該当なし",
|
||||
"invalidDate": "無効な日付",
|
||||
"loadError": "共有詳細の読み込みに失敗しました"
|
||||
"loadError": "共有詳細の読み込みに失敗しました",
|
||||
"editSecurity": "セキュリティを編集",
|
||||
"editExpiration": "期限を編集"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "共有が正常に削除されました",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "受信者への通知に失敗しました",
|
||||
"bulkDeleteError": "共有の削除に失敗しました",
|
||||
"bulkDeleteLoading": "{count, plural, =1 {1つの共有} other {#つの共有}}を削除中...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1つの共有が正常に削除されました} other {#つの共有が正常に削除されました}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1つの共有が正常に削除されました} other {#つの共有が正常に削除されました}}",
|
||||
"securityUpdateError": "セキュリティ設定の更新に失敗しました",
|
||||
"expirationUpdateError": "有効期限設定の更新に失敗しました",
|
||||
"securityUpdateSuccess": "セキュリティ設定が正常に更新されました",
|
||||
"expirationUpdateSuccess": "有効期限設定が正常に更新されました"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -650,17 +678,17 @@
|
||||
"insufficientStorage": "ストレージ容量が不足しています。利用可能な容量は {availablespace}MB です。",
|
||||
"unauthorized": "権限がありません: このリソースにアクセスするには有効なトークンが必要です。",
|
||||
"selectMultipleFiles": "複数のファイルを選択するにはクリック",
|
||||
"finish": "Concluir",
|
||||
"finish": "完了",
|
||||
"confirmCancel": {
|
||||
"warning": "Se você fechar agora, os uploads serão cancelados e qualquer progresso será perdido.",
|
||||
"continue": "Continuar Uploads",
|
||||
"title": "Cancelar Uploads",
|
||||
"messageMultiple": "Há {count} uploads em andamento.",
|
||||
"messageSingle": "Há um upload em andamento.",
|
||||
"cancel": "Cancelar Uploads"
|
||||
"warning": "今閉じるとアップロードがキャンセルされ、進行状況が失われます。",
|
||||
"continue": "アップロードを続行",
|
||||
"title": "アップロードをキャンセル",
|
||||
"messageMultiple": "{count}件のアップロードが進行中です。",
|
||||
"messageSingle": "1件のアップロードが進行中です。",
|
||||
"cancel": "アップロードをキャンセル"
|
||||
},
|
||||
"multipleTitle": "Enviar Múltiplos Arquivos",
|
||||
"startUploads": "Iniciar Uploads",
|
||||
"multipleTitle": "複数ファイルをアップロード",
|
||||
"startUploads": "アップロードを開始",
|
||||
"allSuccess": "{count, plural, =1 {ファイルがアップロードされました} other {#個のファイルがアップロードされました}}",
|
||||
"partialSuccess": "{success}個のファイルがアップロードされ、{error}個が失敗しました",
|
||||
"dragAndDrop": "またはここにファイルをドラッグ&ドロップ"
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "合計サイズ",
|
||||
"creating": "作成中...",
|
||||
"create": "共有を作成"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "この共有のパスワード保護とセキュリティオプションを設定",
|
||||
"info": {
|
||||
"title": "仕組み:",
|
||||
"withoutPassword": "リンクを持つ誰でもパスワードなしでこの共有にアクセスできます。",
|
||||
"withPassword": "ユーザーはこの共有にアクセスするためにパスワードを入力する必要があります。"
|
||||
},
|
||||
"existingPasswordMessage": "この共有には既にパスワードが設定されています。更新したい場合は、下のフィールドに新しいパスワードを入力して保存してください。",
|
||||
"passwordProtection": "パスワード保護",
|
||||
"error": {
|
||||
"updateFailed": "セキュリティ設定の更新に失敗しました"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "パスワード要件:",
|
||||
"minLength": "2文字以上"
|
||||
},
|
||||
"newPassword": "新しいパスワード",
|
||||
"success": {
|
||||
"passwordUpdated": "パスワードが正常に更新されました",
|
||||
"passwordRemoved": "パスワード保護が正常に削除されました",
|
||||
"passwordSet": "パスワード保護が正常に有効化されました"
|
||||
},
|
||||
"password": "パスワード",
|
||||
"validation": {
|
||||
"passwordRequired": "パスワードが必要です",
|
||||
"passwordTooShort": "パスワードは2文字以上である必要があります"
|
||||
},
|
||||
"currentStatus": "現在のステータス",
|
||||
"passwordPlaceholder": "安全なパスワードを入力してください",
|
||||
"title": "共有セキュリティ設定"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "期限なし",
|
||||
"success": {
|
||||
"expirationUpdated": "有効期限が正常に更新されました",
|
||||
"expirationRemoved": "有効期限が正常に削除されました - 共有は永続的になりました",
|
||||
"expirationSet": "有効期限が正常に設定されました"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "有効期限はいつでも変更または削除できます",
|
||||
"willBeInaccessible": "この日時以降、共有にアクセスできなくなります",
|
||||
"noExpiration": "この共有は期限がなく、無期限でアクセス可能です。",
|
||||
"title": "有効期限について:"
|
||||
},
|
||||
"enableExpiration": "有効期限を有効にする",
|
||||
"title": "共有有効期限設定",
|
||||
"subtitle": "この共有の期限を設定",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "有効期限は未来の日時である必要があります",
|
||||
"dateRequired": "有効期限を選択してください"
|
||||
},
|
||||
"currentStatus": "現在のステータス",
|
||||
"error": {
|
||||
"updateFailed": "有効期限設定の更新に失敗しました"
|
||||
},
|
||||
"expires": "期限:",
|
||||
"expirationDate": "有効期限"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "예",
|
||||
"no": "아니오",
|
||||
"dashboard": "대시보드",
|
||||
"back": "뒤로"
|
||||
"back": "뒤로",
|
||||
"updating": "업데이트 중...",
|
||||
"saving": "저장 중...",
|
||||
"update": "업데이트"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "공유 생성",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "최근 업로드",
|
||||
"viewAll": "전체 보기",
|
||||
"uploadFile": "파일 업로드",
|
||||
"noFiles": "아직 파일이 업로드되지 않았습니다"
|
||||
"noFiles": "아직 파일이 업로드되지 않았습니다",
|
||||
"upload": "업로드"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "최근 공유",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "수신자 제거에 실패했습니다",
|
||||
"sendingNotifications": "알림 전송 중...",
|
||||
"notifySuccess": "수신자에게 성공적으로 알렸습니다",
|
||||
"notifyError": "수신자 알림 전송에 실패했습니다"
|
||||
"notifyError": "수신자 알림 전송에 실패했습니다",
|
||||
"bulkNotifySuccess": "{count}명의 수신자에게 알림을 전송했습니다",
|
||||
"selectAll": "모두 선택",
|
||||
"singleNotifySuccess": "{email}에게 알림을 전송했습니다",
|
||||
"removeSingle": "이 수신자 제거",
|
||||
"selectRecipient": "{email} 선택",
|
||||
"bulkRemoveSuccess": "{count}명의 수신자가 성공적으로 제거되었습니다",
|
||||
"notifySingle": "이 수신자에게 알림",
|
||||
"notifySelected": "선택된 항목에 알림",
|
||||
"invalidEmail": "유효한 이메일 주소를 입력해주세요",
|
||||
"noRecipientsDescription": "이메일을 통해 이 콘텐츠를 공유하려면 수신자를 추가하세요",
|
||||
"singleNotifyError": "수신자 알림 전송에 실패했습니다",
|
||||
"bulkRemoveError": "선택된 수신자 제거에 실패했습니다",
|
||||
"modalDescription": "이 공유의 수신자를 추가하고 관리합니다. SMTP가 설정되어 있을 때 모든 수신자 또는 특정 수신자에게 알림을 보낼 수 있습니다.",
|
||||
"duplicateEmail": "이 수신자는 이미 추가되었습니다",
|
||||
"removeSelected": "선택된 항목 제거",
|
||||
"selectedCount": "{count}개 선택됨",
|
||||
"addRecipient": "수신자 추가",
|
||||
"bulkNotifyError": "선택된 수신자들에게 알림 전송에 실패했습니다"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -534,9 +556,11 @@
|
||||
"maxViews": "최대 조회수:",
|
||||
"files": "파일",
|
||||
"recipients": "받는 사람",
|
||||
"notAvailable": "N/A",
|
||||
"notAvailable": "해당없음",
|
||||
"invalidDate": "잘못된 날짜",
|
||||
"loadError": "공유 세부 정보 로드에 실패했습니다"
|
||||
"loadError": "공유 세부 정보 로드에 실패했습니다",
|
||||
"editSecurity": "보안 편집",
|
||||
"editExpiration": "만료 편집"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "공유가 성공적으로 삭제되었습니다",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "수신자 알림 전송에 실패했습니다",
|
||||
"bulkDeleteError": "공유 삭제 실패",
|
||||
"bulkDeleteLoading": "{count, plural, =1 {1개의 공유} other {#개의 공유}} 삭제 중...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1개의 공유가 성공적으로 삭제되었습니다} other {#개의 공유가 성공적으로 삭제되었습니다}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1개의 공유가 성공적으로 삭제되었습니다} other {#개의 공유가 성공적으로 삭제되었습니다}}",
|
||||
"securityUpdateError": "보안 설정 업데이트에 실패했습니다",
|
||||
"expirationUpdateError": "만료 설정 업데이트에 실패했습니다",
|
||||
"securityUpdateSuccess": "보안 설정이 성공적으로 업데이트되었습니다",
|
||||
"expirationUpdateSuccess": "만료 설정이 성공적으로 업데이트되었습니다"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "전체 크기",
|
||||
"creating": "생성 중...",
|
||||
"create": "공유 생성"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "이 공유의 비밀번호 보호 및 보안 옵션을 구성하세요",
|
||||
"info": {
|
||||
"title": "작동 방식:",
|
||||
"withoutPassword": "링크를 가진 누구나 비밀번호 없이 이 공유에 액세스할 수 있습니다.",
|
||||
"withPassword": "사용자는 이 공유에 액세스하려면 비밀번호를 입력해야 합니다."
|
||||
},
|
||||
"existingPasswordMessage": "이 공유에는 이미 비밀번호가 설정되어 있습니다. 업데이트하려면 아래 필드에 새 비밀번호를 입력하고 저장하세요.",
|
||||
"passwordProtection": "비밀번호 보호",
|
||||
"error": {
|
||||
"updateFailed": "보안 설정 업데이트에 실패했습니다"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "비밀번호 요구사항:",
|
||||
"minLength": "최소 2자 이상"
|
||||
},
|
||||
"newPassword": "새 비밀번호",
|
||||
"success": {
|
||||
"passwordUpdated": "비밀번호가 성공적으로 업데이트되었습니다",
|
||||
"passwordRemoved": "비밀번호 보호가 성공적으로 제거되었습니다",
|
||||
"passwordSet": "비밀번호 보호가 성공적으로 활성화되었습니다"
|
||||
},
|
||||
"password": "비밀번호",
|
||||
"validation": {
|
||||
"passwordRequired": "비밀번호가 필요합니다",
|
||||
"passwordTooShort": "비밀번호는 최소 2자 이상이어야 합니다"
|
||||
},
|
||||
"currentStatus": "현재 상태",
|
||||
"passwordPlaceholder": "안전한 비밀번호를 입력하세요",
|
||||
"title": "공유 보안 설정"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "만료되지 않음",
|
||||
"success": {
|
||||
"expirationUpdated": "만료 날짜가 성공적으로 업데이트되었습니다",
|
||||
"expirationRemoved": "만료가 성공적으로 제거되었습니다 - 공유가 이제 영구적입니다",
|
||||
"expirationSet": "만료 날짜가 성공적으로 설정되었습니다"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "언제든지 만료 날짜를 변경하거나 제거할 수 있습니다",
|
||||
"willBeInaccessible": "이 날짜 이후에는 공유에 액세스할 수 없게 됩니다",
|
||||
"noExpiration": "이 공유는 만료되지 않으며 무기한 액세스 가능합니다.",
|
||||
"title": "만료에 대해:"
|
||||
},
|
||||
"enableExpiration": "만료 활성화",
|
||||
"title": "공유 만료 설정",
|
||||
"subtitle": "이 공유가 언제 만료될지 구성하세요",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "만료 날짜는 미래여야 합니다",
|
||||
"dateRequired": "만료 날짜를 선택해주세요"
|
||||
},
|
||||
"currentStatus": "현재 상태",
|
||||
"error": {
|
||||
"updateFailed": "만료 설정 업데이트에 실패했습니다"
|
||||
},
|
||||
"expires": "만료:",
|
||||
"expirationDate": "만료 날짜"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Ja",
|
||||
"no": "Nee",
|
||||
"dashboard": "Dashboard",
|
||||
"back": "Terug"
|
||||
"back": "Terug",
|
||||
"updating": "Bijwerken...",
|
||||
"saving": "Opslaan...",
|
||||
"update": "Bijwerken"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Delen Maken",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Recente Uploads",
|
||||
"viewAll": "Alles Bekijken",
|
||||
"uploadFile": "Bestand Uploaden",
|
||||
"noFiles": "Nog geen bestanden geüpload"
|
||||
"noFiles": "Nog geen bestanden geüpload",
|
||||
"upload": "Uploaden"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Recente Delingen",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Fout bij het verwijderen van ontvanger",
|
||||
"sendingNotifications": "Meldingen verzenden...",
|
||||
"notifySuccess": "Ontvangers succesvol gewaarschuwd",
|
||||
"notifyError": "Fout bij het waarschuwen van ontvangers"
|
||||
"notifyError": "Fout bij het waarschuwen van ontvangers",
|
||||
"bulkNotifySuccess": "Notificaties verzonden naar {count} ontvangers",
|
||||
"selectAll": "Alles selecteren",
|
||||
"singleNotifySuccess": "Notificatie verzonden naar {email}",
|
||||
"removeSingle": "Deze ontvanger verwijderen",
|
||||
"selectRecipient": "{email} selecteren",
|
||||
"bulkRemoveSuccess": "{count} ontvangers succesvol verwijderd",
|
||||
"notifySingle": "Deze ontvanger waarschuwen",
|
||||
"notifySelected": "Geselecteerde waarschuwen",
|
||||
"invalidEmail": "Voer een geldig e-mailadres in",
|
||||
"noRecipientsDescription": "Voeg ontvangers toe om deze inhoud via e-mail te delen",
|
||||
"singleNotifyError": "Fout bij het waarschuwen van ontvanger",
|
||||
"bulkRemoveError": "Fout bij het verwijderen van geselecteerde ontvangers",
|
||||
"modalDescription": "Voeg ontvangers toe en beheer ze voor dit delen. Je kunt alle of specifieke ontvangers waarschuwen wanneer SMTP is geconfigureerd.",
|
||||
"duplicateEmail": "Deze ontvanger is al toegevoegd",
|
||||
"removeSelected": "Geselecteerde verwijderen",
|
||||
"selectedCount": "{count} geselecteerd",
|
||||
"addRecipient": "Ontvanger Toevoegen",
|
||||
"bulkNotifyError": "Fout bij het waarschuwen van geselecteerde ontvangers"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "Ontvangers",
|
||||
"notAvailable": "N/B",
|
||||
"invalidDate": "Ongeldige datum",
|
||||
"loadError": "Fout bij laden van delen details"
|
||||
"loadError": "Fout bij laden van delen details",
|
||||
"editSecurity": "Beveiliging Bewerken",
|
||||
"editExpiration": "Vervaldatum Bewerken"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Delen succesvol verwijderd",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Fout bij het waarschuwen van ontvangers",
|
||||
"bulkDeleteError": "Delen verwijderen mislukt",
|
||||
"bulkDeleteLoading": "{count, plural, =1 {1 deel} other {# delen}} verwijderen...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 deel succesvol verwijderd} other {# delen succesvol verwijderd}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 deel succesvol verwijderd} other {# delen succesvol verwijderd}}",
|
||||
"securityUpdateError": "Fout bij het bijwerken van beveiligingsinstellingen",
|
||||
"expirationUpdateError": "Fout bij het bijwerken van verloop instellingen",
|
||||
"securityUpdateSuccess": "Beveiligingsinstellingen succesvol bijgewerkt",
|
||||
"expirationUpdateSuccess": "Verloop instellingen succesvol bijgewerkt"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Totale grootte",
|
||||
"creating": "Aanmaken...",
|
||||
"create": "Delen Maken"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Configureer wachtwoordbeveiliging en beveiligingsopties voor dit delen",
|
||||
"info": {
|
||||
"title": "Hoe het werkt:",
|
||||
"withoutPassword": "Iedereen met de link kan dit delen benaderen zonder wachtwoord.",
|
||||
"withPassword": "Gebruikers moeten het wachtwoord invoeren om dit delen te benaderen."
|
||||
},
|
||||
"existingPasswordMessage": "Dit delen heeft al een wachtwoord. Als je het wilt bijwerken, voer dan het nieuwe wachtwoord in het onderstaande veld in en sla op.",
|
||||
"passwordProtection": "Wachtwoordbeveiliging",
|
||||
"error": {
|
||||
"updateFailed": "Fout bij het bijwerken van beveiligingsinstellingen"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Wachtwoordvereisten:",
|
||||
"minLength": "Minimaal 2 tekens"
|
||||
},
|
||||
"newPassword": "Nieuw Wachtwoord",
|
||||
"success": {
|
||||
"passwordUpdated": "Wachtwoord succesvol bijgewerkt",
|
||||
"passwordRemoved": "Wachtwoordbeveiliging succesvol verwijderd",
|
||||
"passwordSet": "Wachtwoordbeveiliging succesvol ingeschakeld"
|
||||
},
|
||||
"password": "Wachtwoord",
|
||||
"validation": {
|
||||
"passwordRequired": "Wachtwoord is vereist",
|
||||
"passwordTooShort": "Wachtwoord moet minimaal 2 tekens bevatten"
|
||||
},
|
||||
"currentStatus": "Huidige Status",
|
||||
"passwordPlaceholder": "Voer een veilig wachtwoord in",
|
||||
"title": "Delen Beveiligingsinstellingen"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Verloopt Nooit",
|
||||
"success": {
|
||||
"expirationUpdated": "Vervaldatum succesvol bijgewerkt",
|
||||
"expirationRemoved": "Verloop succesvol verwijderd - delen is nu permanent",
|
||||
"expirationSet": "Vervaldatum succesvol ingesteld"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Je kunt de vervaldatum op elk moment wijzigen of verwijderen",
|
||||
"willBeInaccessible": "Het delen wordt na deze datum ontoegankelijk",
|
||||
"noExpiration": "Dit delen verloopt nooit en blijft voor onbepaalde tijd toegankelijk.",
|
||||
"title": "Over verloop:"
|
||||
},
|
||||
"enableExpiration": "Vervaldatum Inschakelen",
|
||||
"title": "Delen Verloop Instellingen",
|
||||
"subtitle": "Configureer wanneer dit delen verloopt",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "Vervaldatum moet in de toekomst liggen",
|
||||
"dateRequired": "Selecteer een vervaldatum"
|
||||
},
|
||||
"currentStatus": "Huidige Status",
|
||||
"error": {
|
||||
"updateFailed": "Fout bij het bijwerken van verloop instellingen"
|
||||
},
|
||||
"expires": "Verloopt:",
|
||||
"expirationDate": "Vervaldatum"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Sim",
|
||||
"no": "Não",
|
||||
"dashboard": "Painel",
|
||||
"back": "Voltar"
|
||||
"back": "Voltar",
|
||||
"updating": "Atualizando...",
|
||||
"saving": "Salvando...",
|
||||
"update": "Atualizar"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Criar Compartilhamento",
|
||||
@@ -253,7 +256,7 @@
|
||||
"firstName": "Nome",
|
||||
"lastName": "Sobrenome",
|
||||
"username": "Nome de Usuário",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"updateButton": "Atualizar Perfil"
|
||||
},
|
||||
"header": {
|
||||
@@ -297,7 +300,8 @@
|
||||
"title": "Uploads Recentes",
|
||||
"viewAll": "Ver Todos",
|
||||
"uploadFile": "Enviar Arquivo",
|
||||
"noFiles": "Nenhum arquivo enviado ainda"
|
||||
"noFiles": "Nenhum arquivo enviado ainda",
|
||||
"upload": "Carregar"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Compartilhamentos Recentes",
|
||||
@@ -318,7 +322,25 @@
|
||||
"removeError": "Falha ao remover destinatário",
|
||||
"sendingNotifications": "Enviando notificações...",
|
||||
"notifySuccess": "Destinatários notificados com sucesso",
|
||||
"notifyError": "Falha ao notificar destinatários"
|
||||
"notifyError": "Falha ao notificar destinatários",
|
||||
"selectAll": "Selecionar todos",
|
||||
"selectedCount": "{count} selecionados",
|
||||
"selectRecipient": "Selecionar {email}",
|
||||
"notifySelected": "Notificar Selecionados",
|
||||
"removeSelected": "Remover Selecionados",
|
||||
"notifySingle": "Notificar este destinatário",
|
||||
"removeSingle": "Remover este destinatário",
|
||||
"bulkRemoveSuccess": "{count} destinatários removidos com sucesso",
|
||||
"bulkRemoveError": "Falha ao remover destinatários selecionados",
|
||||
"bulkNotifySuccess": "Notificações enviadas para {count} destinatários",
|
||||
"bulkNotifyError": "Falha ao notificar destinatários selecionados",
|
||||
"singleNotifySuccess": "Notificação enviada para {email}",
|
||||
"singleNotifyError": "Falha ao notificar destinatário",
|
||||
"modalDescription": "Adicione e gerencie destinatários para este compartilhamento. Você pode notificar todos ou destinatários específicos quando o SMTP estiver configurado.",
|
||||
"addRecipient": "Adicionar Destinatário",
|
||||
"invalidEmail": "Por favor, digite um endereço de e-mail válido",
|
||||
"duplicateEmail": "Este destinatário já foi adicionado",
|
||||
"noRecipientsDescription": "Adicione destinatários para compartilhar este conteúdo via e-mail"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -334,7 +356,7 @@
|
||||
"firstName": "Nome",
|
||||
"lastName": "Sobrenome",
|
||||
"username": "Nome de Usuário",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"password": "Senha"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -377,7 +399,7 @@
|
||||
"description": "Configurações básicas da aplicação"
|
||||
},
|
||||
"email": {
|
||||
"title": "Email",
|
||||
"title": "E-mail",
|
||||
"description": "Configuração do servidor de email"
|
||||
},
|
||||
"security": {
|
||||
@@ -556,9 +578,11 @@
|
||||
"maxViews": "Máx. Visualizações:",
|
||||
"files": "Arquivos",
|
||||
"recipients": "Destinatários",
|
||||
"notAvailable": "N/A",
|
||||
"notAvailable": "N/D",
|
||||
"invalidDate": "Data inválida",
|
||||
"loadError": "Falha ao carregar detalhes do compartilhamento"
|
||||
"loadError": "Falha ao carregar detalhes do compartilhamento",
|
||||
"editSecurity": "Editar Segurança",
|
||||
"editExpiration": "Editar Expiração"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Compartilhamento excluído com sucesso",
|
||||
@@ -576,7 +600,11 @@
|
||||
"linkGenerateError": "Falha ao gerar link de compartilhamento",
|
||||
"notifyLoading": "Enviando notificações...",
|
||||
"notifySuccess": "Destinatários notificados com sucesso",
|
||||
"notifyError": "Falha ao notificar destinatários"
|
||||
"notifyError": "Falha ao notificar destinatários",
|
||||
"securityUpdateError": "Falha ao atualizar configurações de segurança",
|
||||
"expirationUpdateError": "Falha ao atualizar configurações de expiração",
|
||||
"securityUpdateSuccess": "Configurações de segurança atualizadas com sucesso",
|
||||
"expirationUpdateSuccess": "Configurações de expiração atualizadas com sucesso"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -722,7 +750,7 @@
|
||||
"firstName": "Nome",
|
||||
"lastName": "Sobrenome",
|
||||
"username": "Nome de Usuário",
|
||||
"email": "Email",
|
||||
"email": "E-mail",
|
||||
"password": "Senha",
|
||||
"newPassword": "Nova Senha (opcional)",
|
||||
"passwordPlaceholder": "Deixe em branco para manter a senha atual",
|
||||
@@ -752,7 +780,7 @@
|
||||
"actions": "AÇÕES",
|
||||
"active": "Ativo",
|
||||
"inactive": "Inativo",
|
||||
"admin": "Admin",
|
||||
"admin": "Administrador",
|
||||
"userr": "Usuário"
|
||||
}
|
||||
},
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Tamanho total",
|
||||
"creating": "Criando...",
|
||||
"create": "Criar Compartilhamento"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Configurar proteção por senha e opções de segurança para este compartilhamento",
|
||||
"info": {
|
||||
"title": "Como funciona:",
|
||||
"withoutPassword": "Qualquer pessoa com o link pode acessar este compartilhamento sem senha.",
|
||||
"withPassword": "Os usuários precisarão digitar a senha para acessar este compartilhamento."
|
||||
},
|
||||
"existingPasswordMessage": "Este compartilhamento já tem uma senha. Se você quiser atualizá-la, digite a nova senha no campo abaixo e salve.",
|
||||
"passwordProtection": "Proteção por Senha",
|
||||
"newPassword": "Nova Senha",
|
||||
"error": {
|
||||
"updateFailed": "Falha ao atualizar configurações de segurança"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Requisitos da senha:",
|
||||
"minLength": "Pelo menos 2 caracteres"
|
||||
},
|
||||
"success": {
|
||||
"passwordUpdated": "Senha atualizada com sucesso",
|
||||
"passwordRemoved": "Proteção por senha removida com sucesso",
|
||||
"passwordSet": "Proteção por senha habilitada com sucesso"
|
||||
},
|
||||
"password": "Senha",
|
||||
"validation": {
|
||||
"passwordRequired": "Senha é obrigatória",
|
||||
"passwordTooShort": "A senha deve ter pelo menos 2 caracteres"
|
||||
},
|
||||
"currentStatus": "Status Atual",
|
||||
"passwordPlaceholder": "Digite uma senha segura",
|
||||
"title": "Configurações de Segurança do Compartilhamento"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Nunca Expira",
|
||||
"success": {
|
||||
"expirationUpdated": "Data de expiração atualizada com sucesso",
|
||||
"expirationRemoved": "Expiração removida com sucesso - o compartilhamento agora é permanente",
|
||||
"expirationSet": "Data de expiração definida com sucesso"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Você pode alterar ou remover a data de expiração a qualquer momento",
|
||||
"willBeInaccessible": "O compartilhamento ficará inacessível após esta data",
|
||||
"noExpiration": "Este compartilhamento nunca expirará e permanecerá acessível indefinidamente.",
|
||||
"title": "Sobre expiração:"
|
||||
},
|
||||
"enableExpiration": "Habilitar Expiração",
|
||||
"title": "Configurações de Expiração do Compartilhamento",
|
||||
"subtitle": "Configurar quando este compartilhamento expirará",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "A data de expiração deve estar no futuro",
|
||||
"dateRequired": "Selecione uma data de expiração"
|
||||
},
|
||||
"currentStatus": "Status Atual",
|
||||
"error": {
|
||||
"updateFailed": "Falha ao atualizar configurações de expiração"
|
||||
},
|
||||
"expires": "Expira:",
|
||||
"expirationDate": "Data de Expiração"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Да",
|
||||
"no": "Нет",
|
||||
"dashboard": "Панель управления",
|
||||
"back": "Назад"
|
||||
"back": "Назад",
|
||||
"updating": "Обновление...",
|
||||
"saving": "Сохранение...",
|
||||
"update": "Обновить"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Создать общий доступ",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Последние загрузки",
|
||||
"viewAll": "Показать все",
|
||||
"uploadFile": "Загрузить файл",
|
||||
"noFiles": "Файлы еще не загружены"
|
||||
"noFiles": "Файлы еще не загружены",
|
||||
"upload": "Загрузить"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Последние общие доступы",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Не удалось удалить получателя",
|
||||
"sendingNotifications": "Отправка уведомлений...",
|
||||
"notifySuccess": "Получатели успешно уведомлены",
|
||||
"notifyError": "Не удалось уведомить получателей"
|
||||
"notifyError": "Не удалось уведомить получателей",
|
||||
"bulkNotifySuccess": "Уведомления отправлены {count} получателям",
|
||||
"selectAll": "Выбрать все",
|
||||
"singleNotifySuccess": "Уведомление отправлено {email}",
|
||||
"removeSingle": "Удалить этого получателя",
|
||||
"selectRecipient": "Выбрать {email}",
|
||||
"bulkRemoveSuccess": "{count} получателей успешно удалены",
|
||||
"notifySingle": "Уведомить этого получателя",
|
||||
"notifySelected": "Уведомить выбранных",
|
||||
"invalidEmail": "Пожалуйста, введите действительный адрес электронной почты",
|
||||
"noRecipientsDescription": "Добавьте получателей для обмена этим контентом по электронной почте",
|
||||
"singleNotifyError": "Не удалось уведомить получателя",
|
||||
"bulkRemoveError": "Не удалось удалить выбранных получателей",
|
||||
"modalDescription": "Добавляйте и управляйте получателями для этого общего доступа. Вы можете уведомлять всех или конкретных получателей при настроенном SMTP.",
|
||||
"duplicateEmail": "Этот получатель уже добавлен",
|
||||
"removeSelected": "Удалить выбранных",
|
||||
"selectedCount": "{count} выбрано",
|
||||
"addRecipient": "Добавить получателя",
|
||||
"bulkNotifyError": "Не удалось уведомить выбранных получателей"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -312,7 +334,7 @@
|
||||
"firstName": "Имя",
|
||||
"lastName": "Фамилия",
|
||||
"username": "Имя пользователя",
|
||||
"email": "Email",
|
||||
"email": "Эл. почта",
|
||||
"password": "Пароль"
|
||||
},
|
||||
"buttons": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "Получатели",
|
||||
"notAvailable": "Н/Д",
|
||||
"invalidDate": "Неверная дата",
|
||||
"loadError": "Ошибка загрузки деталей общего доступа"
|
||||
"loadError": "Ошибка загрузки деталей общего доступа",
|
||||
"editSecurity": "Изменить безопасность",
|
||||
"editExpiration": "Изменить срок действия"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Общий доступ успешно удален",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Ошибка при уведомлении получателей",
|
||||
"bulkDeleteError": "Не удалось удалить общие папки",
|
||||
"bulkDeleteLoading": "Удаление {count, plural, =1 {1 общей папки} other {# общих папок}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 общая папка успешно удалена} other {# общих папок успешно удалены}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 общая папка успешно удалена} other {# общих папок успешно удалены}}",
|
||||
"securityUpdateError": "Не удалось обновить настройки безопасности",
|
||||
"expirationUpdateError": "Не удалось обновить настройки истечения",
|
||||
"securityUpdateSuccess": "Настройки безопасности успешно обновлены",
|
||||
"expirationUpdateSuccess": "Настройки истечения успешно обновлены"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Общий размер",
|
||||
"creating": "Создание...",
|
||||
"create": "Создать Общий Доступ"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Настройте защиту паролем и параметры безопасности для этого общего доступа",
|
||||
"info": {
|
||||
"title": "Как это работает:",
|
||||
"withoutPassword": "Любой, у кого есть ссылка, может получить доступ к этому общему доступу без пароля.",
|
||||
"withPassword": "Пользователям нужно будет ввести пароль для доступа к этому общему доступу."
|
||||
},
|
||||
"existingPasswordMessage": "У этого общего доступа уже есть пароль. Если вы хотите его обновить, введите новый пароль в поле ниже и сохраните.",
|
||||
"passwordProtection": "Защита паролем",
|
||||
"error": {
|
||||
"updateFailed": "Не удалось обновить настройки безопасности"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Требования к паролю:",
|
||||
"minLength": "Минимум 2 символа"
|
||||
},
|
||||
"newPassword": "Новый пароль",
|
||||
"success": {
|
||||
"passwordUpdated": "Пароль успешно обновлен",
|
||||
"passwordRemoved": "Защита паролем успешно удалена",
|
||||
"passwordSet": "Защита паролем успешно включена"
|
||||
},
|
||||
"password": "Пароль",
|
||||
"validation": {
|
||||
"passwordRequired": "Требуется пароль",
|
||||
"passwordTooShort": "Пароль должен содержать не менее 2 символов"
|
||||
},
|
||||
"currentStatus": "Текущий статус",
|
||||
"passwordPlaceholder": "Введите надежный пароль",
|
||||
"title": "Настройки безопасности общего доступа"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Никогда не истекает",
|
||||
"success": {
|
||||
"expirationUpdated": "Дата истечения успешно обновлена",
|
||||
"expirationRemoved": "Истечение успешно удалено - общий доступ теперь постоянный",
|
||||
"expirationSet": "Дата истечения успешно установлена"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Вы можете изменить или удалить дату истечения в любое время",
|
||||
"willBeInaccessible": "Общий доступ станет недоступным после этой даты",
|
||||
"noExpiration": "Этот общий доступ никогда не истечет и останется доступным бессрочно.",
|
||||
"title": "Об истечении:"
|
||||
},
|
||||
"enableExpiration": "Включить срок действия",
|
||||
"title": "Настройки истечения общего доступа",
|
||||
"subtitle": "Настройте, когда истечет этот общий доступ",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "Дата истечения должна быть в будущем",
|
||||
"dateRequired": "Пожалуйста, выберите дату истечения"
|
||||
},
|
||||
"currentStatus": "Текущий статус",
|
||||
"error": {
|
||||
"updateFailed": "Не удалось обновить настройки истечения"
|
||||
},
|
||||
"expires": "Истекает:",
|
||||
"expirationDate": "Дата истечения"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "Evet",
|
||||
"no": "Hayır",
|
||||
"dashboard": "Kontrol Paneli",
|
||||
"back": "Geri"
|
||||
"back": "Geri",
|
||||
"updating": "Güncelleniyor...",
|
||||
"saving": "Kaydediliyor...",
|
||||
"update": "Güncelle"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "Paylaşım Oluştur",
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "Son Yüklemeler",
|
||||
"viewAll": "Tümünü Görüntüle",
|
||||
"uploadFile": "Dosya Yükle",
|
||||
"noFiles": "Henüz dosya yüklenmedi"
|
||||
"noFiles": "Henüz dosya yüklenmedi",
|
||||
"upload": "Yükle"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "Son Paylaşımlar",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "Alıcı kaldırılamadı",
|
||||
"sendingNotifications": "Bildirimler gönderiliyor...",
|
||||
"notifySuccess": "Alıcılar başarıyla bildirildi",
|
||||
"notifyError": "Alıcılara bildirim gönderilemedi"
|
||||
"notifyError": "Alıcılara bildirim gönderilemedi",
|
||||
"bulkNotifySuccess": "Bildirimleri {count} alıcıya gönderildi",
|
||||
"selectAll": "Tümünü seç",
|
||||
"singleNotifySuccess": "Bildirim {email} adresine gönderildi",
|
||||
"removeSingle": "Bu alıcıyı kaldır",
|
||||
"selectRecipient": "{email} seç",
|
||||
"bulkRemoveSuccess": "{count} alıcı başarıyla kaldırıldı",
|
||||
"notifySingle": "Bu alıcıyı bilgilendir",
|
||||
"notifySelected": "Seçilenleri Bilgilendir",
|
||||
"invalidEmail": "Lütfen geçerli bir e-posta adresi girin",
|
||||
"noRecipientsDescription": "Bu içeriği e-posta ile paylaşmak için alıcılar ekleyin",
|
||||
"singleNotifyError": "Alıcıya bildirim gönderme başarısız",
|
||||
"bulkRemoveError": "Seçilen alıcıları kaldırma başarısız",
|
||||
"modalDescription": "Bu paylaşım için alıcıları ekleyin ve yönetin. SMTP yapılandırıldığında tüm veya belirli alıcılara bildirim gönderebilirsiniz.",
|
||||
"duplicateEmail": "Bu alıcı zaten eklenmiş",
|
||||
"removeSelected": "Seçilenleri Kaldır",
|
||||
"selectedCount": "{count} seçildi",
|
||||
"addRecipient": "Alıcı Ekle",
|
||||
"bulkNotifyError": "Seçilen alıcılara bildirim gönderme başarısız"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -536,7 +558,9 @@
|
||||
"recipients": "Alıcılar",
|
||||
"notAvailable": "M/D",
|
||||
"invalidDate": "Geçersiz tarih",
|
||||
"loadError": "Paylaşım detaylarını yükleme başarısız"
|
||||
"loadError": "Paylaşım detaylarını yükleme başarısız",
|
||||
"editSecurity": "Güvenlik Düzenle",
|
||||
"editExpiration": "Son Kullanma Düzenle"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "Paylaşım başarıyla silindi",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "Alıcılara bildirim gönderilemedi",
|
||||
"bulkDeleteError": "Paylaşımları silme başarısız",
|
||||
"bulkDeleteLoading": "{count, plural, =1 {1 paylaşım} other {# paylaşım}} siliniyor...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 paylaşım başarıyla silindi} other {# paylaşım başarıyla silindi}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1 paylaşım başarıyla silindi} other {# paylaşım başarıyla silindi}}",
|
||||
"securityUpdateError": "Güvenlik ayarlarını güncelleme başarısız",
|
||||
"expirationUpdateError": "Son kullanma ayarlarını güncelleme başarısız",
|
||||
"securityUpdateSuccess": "Güvenlik ayarları başarıyla güncellendi",
|
||||
"expirationUpdateSuccess": "Son kullanma ayarları başarıyla güncellendi"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "Toplam boyut",
|
||||
"creating": "Oluşturuluyor...",
|
||||
"create": "Paylaşım Oluştur"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "Bu paylaşım için şifre koruması ve güvenlik seçeneklerini yapılandırın",
|
||||
"info": {
|
||||
"title": "Nasıl çalışır:",
|
||||
"withoutPassword": "Bağlantıya sahip herkes bu paylaşıma şifre olmadan erişebilir.",
|
||||
"withPassword": "Kullanıcıların bu paylaşıma erişmek için şifre girmeleri gerekecek."
|
||||
},
|
||||
"existingPasswordMessage": "Bu paylaşımın zaten bir şifresi var. Güncellemek istiyorsanız, aşağıdaki alana yeni şifreyi girin ve kaydedin.",
|
||||
"passwordProtection": "Şifre Koruması",
|
||||
"error": {
|
||||
"updateFailed": "Güvenlik ayarlarını güncelleme başarısız"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "Şifre gereksinimleri:",
|
||||
"minLength": "En az 2 karakter"
|
||||
},
|
||||
"newPassword": "Yeni Şifre",
|
||||
"success": {
|
||||
"passwordUpdated": "Şifre başarıyla güncellendi",
|
||||
"passwordRemoved": "Şifre koruması başarıyla kaldırıldı",
|
||||
"passwordSet": "Şifre koruması başarıyla etkinleştirildi"
|
||||
},
|
||||
"password": "Şifre",
|
||||
"validation": {
|
||||
"passwordRequired": "Şifre gereklidir",
|
||||
"passwordTooShort": "Şifre en az 2 karakter olmalıdır"
|
||||
},
|
||||
"currentStatus": "Mevcut Durum",
|
||||
"passwordPlaceholder": "Güvenli bir şifre girin",
|
||||
"title": "Paylaşım Güvenlik Ayarları"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "Asla Sona Ermez",
|
||||
"success": {
|
||||
"expirationUpdated": "Son kullanma tarihi başarıyla güncellendi",
|
||||
"expirationRemoved": "Son kullanma başarıyla kaldırıldı - paylaşım artık kalıcı",
|
||||
"expirationSet": "Son kullanma tarihi başarıyla ayarlandı"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "Son kullanma tarihini istediğiniz zaman değiştirebilir veya kaldırabilirsiniz",
|
||||
"willBeInaccessible": "Paylaşım bu tarihten sonra erişilemez hale gelecek",
|
||||
"noExpiration": "Bu paylaşım asla sona ermeyecek ve süresiz olarak erişilebilir kalacak.",
|
||||
"title": "Son kullanma hakkında:"
|
||||
},
|
||||
"enableExpiration": "Son Kullanmayı Etkinleştir",
|
||||
"title": "Paylaşım Son Kullanma Ayarları",
|
||||
"subtitle": "Bu paylaşımın ne zaman sona ereceğini yapılandırın",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "Son kullanma tarihi gelecekte olmalıdır",
|
||||
"dateRequired": "Lütfen bir son kullanma tarihi seçin"
|
||||
},
|
||||
"currentStatus": "Mevcut Durum",
|
||||
"error": {
|
||||
"updateFailed": "Son kullanma ayarlarını güncelleme başarısız"
|
||||
},
|
||||
"expires": "Sona erer:",
|
||||
"expirationDate": "Son Kullanma Tarihi"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"dashboard": "仪表板",
|
||||
"back": "返回"
|
||||
"back": "返回",
|
||||
"updating": "更新中...",
|
||||
"saving": "保存中...",
|
||||
"update": "更新"
|
||||
},
|
||||
"createShare": {
|
||||
"title": "创建分享",
|
||||
@@ -55,7 +58,7 @@
|
||||
"descriptionLabel": "描述",
|
||||
"descriptionPlaceholder": "请输入文件描述",
|
||||
"deleteFile": "删除文件",
|
||||
"deleteConfirmation": "您确定要删除“{fileName}”吗?",
|
||||
"deleteConfirmation": "您确定要删除{fileName}吗?",
|
||||
"deleteWarning": "此操作不可撤销。"
|
||||
},
|
||||
"fileManager": {
|
||||
@@ -275,7 +278,8 @@
|
||||
"title": "最近上传",
|
||||
"viewAll": "查看全部",
|
||||
"uploadFile": "上传文件",
|
||||
"noFiles": "尚未上传文件"
|
||||
"noFiles": "尚未上传文件",
|
||||
"upload": "上传"
|
||||
},
|
||||
"recentShares": {
|
||||
"title": "最近共享",
|
||||
@@ -296,7 +300,25 @@
|
||||
"removeError": "删除收件人失败",
|
||||
"sendingNotifications": "正在发送通知...",
|
||||
"notifySuccess": "收件人通知成功",
|
||||
"notifyError": "通知收件人失败"
|
||||
"notifyError": "通知收件人失败",
|
||||
"bulkNotifySuccess": "已向{count}位收件人发送通知",
|
||||
"selectAll": "全选",
|
||||
"singleNotifySuccess": "已向{email}发送通知",
|
||||
"removeSingle": "移除此收件人",
|
||||
"selectRecipient": "选择{email}",
|
||||
"bulkRemoveSuccess": "成功移除{count}位收件人",
|
||||
"notifySingle": "通知此收件人",
|
||||
"notifySelected": "通知选中项",
|
||||
"invalidEmail": "请输入有效的电子邮件地址",
|
||||
"noRecipientsDescription": "添加收件人以通过电子邮件分享此内容",
|
||||
"singleNotifyError": "通知收件人失败",
|
||||
"bulkRemoveError": "移除选中的收件人失败",
|
||||
"modalDescription": "为此分享添加和管理收件人。当配置了SMTP时,您可以通知所有或特定收件人。",
|
||||
"duplicateEmail": "此收件人已经添加过了",
|
||||
"removeSelected": "移除选中项",
|
||||
"selectedCount": "已选择{count}个",
|
||||
"addRecipient": "添加收件人",
|
||||
"bulkNotifyError": "通知选中收件人失败"
|
||||
},
|
||||
"register": {
|
||||
"validation": {
|
||||
@@ -528,15 +550,17 @@
|
||||
"notAvailable": "不适用",
|
||||
"invalidDate": "无效的日期",
|
||||
"loadError": "加载共享详情失败",
|
||||
"editLink": "Editar Link",
|
||||
"openLink": "Abrir em nova guia",
|
||||
"shareLink": "Link de Compartilhamento",
|
||||
"noDescription": "Nenhuma descrição fornecida",
|
||||
"copyLink": "Copiar link",
|
||||
"generateLink": "Gerar Link",
|
||||
"noLink": "Nenhum link gerado ainda",
|
||||
"editLink": "编辑链接",
|
||||
"openLink": "在新标签页中打开",
|
||||
"shareLink": "分享链接",
|
||||
"noDescription": "未提供描述",
|
||||
"copyLink": "复制链接",
|
||||
"generateLink": "生成链接",
|
||||
"noLink": "尚未生成链接",
|
||||
"description": "描述",
|
||||
"linkCopied": "Link copiado para a área de transferência"
|
||||
"linkCopied": "链接已复制到剪贴板",
|
||||
"editSecurity": "编辑安全",
|
||||
"editExpiration": "编辑过期"
|
||||
},
|
||||
"shareManager": {
|
||||
"deleteSuccess": "共享删除成功",
|
||||
@@ -554,7 +578,11 @@
|
||||
"notifyError": "通知收件人失败",
|
||||
"bulkDeleteError": "删除共享失败",
|
||||
"bulkDeleteLoading": "正在删除{count, plural, =1 {1个共享} other {#个共享}}...",
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1个共享删除成功} other {#个共享删除成功}}"
|
||||
"bulkDeleteSuccess": "{count, plural, =1 {1个共享删除成功} other {#个共享删除成功}}",
|
||||
"securityUpdateError": "更新安全设置失败",
|
||||
"expirationUpdateError": "更新过期设置失败",
|
||||
"securityUpdateSuccess": "安全设置更新成功",
|
||||
"expirationUpdateSuccess": "过期设置更新成功"
|
||||
},
|
||||
"shares": {
|
||||
"errors": {
|
||||
@@ -790,5 +818,63 @@
|
||||
"totalSize": "总大小",
|
||||
"creating": "创建中...",
|
||||
"create": "创建分享"
|
||||
},
|
||||
"shareSecurity": {
|
||||
"subtitle": "为此分享配置密码保护和安全选项",
|
||||
"info": {
|
||||
"title": "工作原理:",
|
||||
"withoutPassword": "任何拥有链接的人都可以在没有密码的情况下访问此分享。",
|
||||
"withPassword": "用户需要输入密码才能访问此分享。"
|
||||
},
|
||||
"existingPasswordMessage": "此分享已有密码。如果您想更新它,请在下面的字段中输入新密码并保存。",
|
||||
"passwordProtection": "密码保护",
|
||||
"error": {
|
||||
"updateFailed": "更新安全设置失败"
|
||||
},
|
||||
"passwordRequirements": {
|
||||
"title": "密码要求:",
|
||||
"minLength": "至少2个字符"
|
||||
},
|
||||
"newPassword": "新密码",
|
||||
"success": {
|
||||
"passwordUpdated": "密码更新成功",
|
||||
"passwordRemoved": "密码保护移除成功",
|
||||
"passwordSet": "密码保护启用成功"
|
||||
},
|
||||
"password": "密码",
|
||||
"validation": {
|
||||
"passwordRequired": "密码是必需的",
|
||||
"passwordTooShort": "密码必须至少包含2个字符"
|
||||
},
|
||||
"currentStatus": "当前状态",
|
||||
"passwordPlaceholder": "输入安全密码",
|
||||
"title": "分享安全设置"
|
||||
},
|
||||
"shareExpiration": {
|
||||
"neverExpires": "永不过期",
|
||||
"success": {
|
||||
"expirationUpdated": "过期日期更新成功",
|
||||
"expirationRemoved": "过期已移除成功 - 分享现在是永久的",
|
||||
"expirationSet": "过期日期设置成功"
|
||||
},
|
||||
"info": {
|
||||
"canBeChanged": "您可以随时更改或移除过期日期",
|
||||
"willBeInaccessible": "分享在此日期后将无法访问",
|
||||
"noExpiration": "此分享永不过期,将始终可访问。",
|
||||
"title": "关于过期:"
|
||||
},
|
||||
"enableExpiration": "启用过期",
|
||||
"title": "分享过期设置",
|
||||
"subtitle": "配置此分享何时过期",
|
||||
"validation": {
|
||||
"dateMustBeFuture": "过期日期必须在将来",
|
||||
"dateRequired": "请选择过期日期"
|
||||
},
|
||||
"currentStatus": "当前状态",
|
||||
"error": {
|
||||
"updateFailed": "更新过期设置失败"
|
||||
},
|
||||
"expires": "过期:",
|
||||
"expirationDate": "过期日期"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,11 @@ import { DeleteConfirmationModal } from "@/components/modals/delete-confirmation
|
||||
import { GenerateShareLinkModal } from "@/components/modals/generate-share-link-modal";
|
||||
import { ShareActionsModals } from "@/components/modals/share-actions-modals";
|
||||
import { ShareDetailsModal } from "@/components/modals/share-details-modal";
|
||||
import { ShareExpirationModal } from "@/components/modals/share-expiration-modal";
|
||||
import { ShareFileModal } from "@/components/modals/share-file-modal";
|
||||
import { ShareMultipleFilesModal } from "@/components/modals/share-multiple-files-modal";
|
||||
import { ShareSecurityModal } from "@/components/modals/share-security-modal";
|
||||
import { UploadFileModal } from "@/components/modals/upload-file-modal";
|
||||
import { SharesModalsProps } from "../types";
|
||||
|
||||
export function SharesModals({
|
||||
@@ -63,6 +68,8 @@ export function SharesModals({
|
||||
onClose={onCloseViewDetails}
|
||||
onUpdateName={shareManager.handleUpdateName}
|
||||
onUpdateDescription={shareManager.handleUpdateDescription}
|
||||
onUpdateSecurity={shareManager.handleUpdateSecurity}
|
||||
onUpdateExpiration={shareManager.handleUpdateExpiration}
|
||||
onGenerateLink={shareManager.handleGenerateLink}
|
||||
onManageFiles={shareManager.setShareToManageFiles}
|
||||
refreshTrigger={shareDetailsRefresh}
|
||||
@@ -76,6 +83,36 @@ export function SharesModals({
|
||||
onGenerate={shareManager.handleGenerateLink}
|
||||
onSuccess={handleShareSuccess}
|
||||
/>
|
||||
|
||||
<ShareSecurityModal
|
||||
shareId={shareManager.shareToManageSecurity?.id || null}
|
||||
share={shareManager.shareToManageSecurity || null}
|
||||
onClose={() => shareManager.setShareToManageSecurity(null)}
|
||||
onSuccess={handleShareSuccess}
|
||||
/>
|
||||
|
||||
<ShareExpirationModal
|
||||
shareId={shareManager.shareToManageExpiration?.id || null}
|
||||
share={shareManager.shareToManageExpiration || null}
|
||||
onClose={() => shareManager.setShareToManageExpiration(null)}
|
||||
onSuccess={handleShareSuccess}
|
||||
/>
|
||||
|
||||
<ShareMultipleFilesModal
|
||||
files={fileManager.filesToShare}
|
||||
isOpen={!!fileManager.filesToShare}
|
||||
onClose={() => fileManager.setFilesToShare(null)}
|
||||
onSuccess={() => {
|
||||
fileManager.handleShareBulkSuccess();
|
||||
onSuccess();
|
||||
}}
|
||||
/>
|
||||
|
||||
<UploadFileModal
|
||||
isOpen={!!shareManager.shareToEdit}
|
||||
onClose={() => shareManager.setShareToEdit(null)}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ export function SharesTableContainer({ shares, onCopyLink, onCreateShare, shareM
|
||||
onEdit={shareManager.setShareToEdit}
|
||||
onUpdateName={shareManager.handleUpdateName}
|
||||
onUpdateDescription={shareManager.handleUpdateDescription}
|
||||
onUpdateSecurity={shareManager.setShareToManageSecurity}
|
||||
onUpdateExpiration={shareManager.setShareToManageExpiration}
|
||||
onGenerateLink={shareManager.setShareToGenerateLink}
|
||||
onManageFiles={shareManager.setShareToManageFiles}
|
||||
onManageRecipients={shareManager.setShareToManageRecipients}
|
||||
|
||||
@@ -20,7 +20,8 @@ export function RecentFiles({ files, fileManager, onOpenUploadModal }: RecentFil
|
||||
<IconCloudUpload className="text-xl text-gray-500" />
|
||||
{t("recentFiles.title")}
|
||||
</CardTitle>
|
||||
{files.length >= 5 ? (
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
className="font-semibold text-sm cursor-pointer"
|
||||
variant="outline"
|
||||
@@ -30,7 +31,7 @@ export function RecentFiles({ files, fileManager, onOpenUploadModal }: RecentFil
|
||||
<IconFolderOpen className="h-4 w-4" />
|
||||
{t("recentFiles.viewAll")}
|
||||
</Button>
|
||||
) : (
|
||||
|
||||
<Button
|
||||
className="font-semibold text-sm cursor-pointer"
|
||||
variant="outline"
|
||||
@@ -40,7 +41,7 @@ export function RecentFiles({ files, fileManager, onOpenUploadModal }: RecentFil
|
||||
<IconCloudUpload className="h-4 w-4" />
|
||||
{t("recentFiles.upload")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
@@ -21,7 +21,8 @@ export function RecentShares({ shares, shareManager, onOpenCreateModal, onCopyLi
|
||||
<IconShare className="text-xl text-gray-500" />
|
||||
{t("recentShares.title")}
|
||||
</h2>
|
||||
{shares.length >= 5 ? (
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
className="font-semibold text-sm cursor-pointer"
|
||||
variant="outline"
|
||||
@@ -31,7 +32,7 @@ export function RecentShares({ shares, shareManager, onOpenCreateModal, onCopyLi
|
||||
<IconShare className="h-4 w-4" />
|
||||
{t("recentShares.viewAll")}
|
||||
</Button>
|
||||
) : shares.length === 0 ? null : (
|
||||
|
||||
<Button
|
||||
className="font-semibold text-sm cursor-pointer"
|
||||
variant="outline"
|
||||
@@ -41,7 +42,7 @@ export function RecentShares({ shares, shareManager, onOpenCreateModal, onCopyLi
|
||||
<IconPlus className="h-4 w-4" />
|
||||
{t("recentShares.createShare")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{shares.length > 0 ? (
|
||||
@@ -53,6 +54,8 @@ export function RecentShares({ shares, shareManager, onOpenCreateModal, onCopyLi
|
||||
onEdit={shareManager.setShareToEdit}
|
||||
onUpdateName={shareManager.handleUpdateName}
|
||||
onUpdateDescription={shareManager.handleUpdateDescription}
|
||||
onUpdateSecurity={shareManager.setShareToManageSecurity}
|
||||
onUpdateExpiration={shareManager.setShareToManageExpiration}
|
||||
onGenerateLink={shareManager.setShareToGenerateLink}
|
||||
onManageFiles={shareManager.setShareToManageFiles}
|
||||
onManageRecipients={shareManager.setShareToManageRecipients}
|
||||
|
||||
@@ -9,8 +9,10 @@ import { FilePreviewModal } from "@/components/modals/file-preview-modal";
|
||||
import { GenerateShareLinkModal } from "@/components/modals/generate-share-link-modal";
|
||||
import { ShareActionsModals } from "@/components/modals/share-actions-modals";
|
||||
import { ShareDetailsModal } from "@/components/modals/share-details-modal";
|
||||
import { ShareExpirationModal } from "@/components/modals/share-expiration-modal";
|
||||
import { ShareFileModal } from "@/components/modals/share-file-modal";
|
||||
import { ShareMultipleFilesModal } from "@/components/modals/share-multiple-files-modal";
|
||||
import { ShareSecurityModal } from "@/components/modals/share-security-modal";
|
||||
import { UploadFileModal } from "@/components/modals/upload-file-modal";
|
||||
import { DashboardModalsProps } from "../types";
|
||||
|
||||
@@ -113,12 +115,28 @@ export function DashboardModals({ modals, fileManager, shareManager, onSuccess }
|
||||
onClose={() => shareManager.setShareToViewDetails(null)}
|
||||
onUpdateName={shareManager.handleUpdateName}
|
||||
onUpdateDescription={shareManager.handleUpdateDescription}
|
||||
onUpdateSecurity={shareManager.handleUpdateSecurity}
|
||||
onUpdateExpiration={shareManager.handleUpdateExpiration}
|
||||
onGenerateLink={shareManager.handleGenerateLink}
|
||||
onManageFiles={shareManager.setShareToManageFiles}
|
||||
refreshTrigger={shareDetailsRefresh}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
|
||||
<ShareSecurityModal
|
||||
shareId={shareManager.shareToManageSecurity?.id || null}
|
||||
share={shareManager.shareToManageSecurity || null}
|
||||
onClose={() => shareManager.setShareToManageSecurity(null)}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
|
||||
<ShareExpirationModal
|
||||
shareId={shareManager.shareToManageExpiration?.id || null}
|
||||
share={shareManager.shareToManageExpiration || null}
|
||||
onClose={() => shareManager.setShareToManageExpiration(null)}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
|
||||
<GenerateShareLinkModal
|
||||
share={shareManager.shareToGenerateLink || null}
|
||||
shareId={shareManager.shareToGenerateLink?.id || null}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { IconBell, IconMail, IconPlus, IconTrash } from "@tabler/icons-react";
|
||||
import { IconBell, IconCheck, IconMail, IconPlus, IconTrash, IconUsers, IconX } from "@tabler/icons-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useShareContext } from "@/contexts/share-context";
|
||||
import { addRecipients, notifyRecipients, removeRecipients } from "@/http/endpoints";
|
||||
|
||||
@@ -29,39 +31,95 @@ export function RecipientSelector({ shareId, selectedRecipients, shareAlias, onS
|
||||
const { smtpEnabled } = useShareContext();
|
||||
const [recipients, setRecipients] = useState<string[]>(selectedRecipients?.map((recipient) => recipient.email) || []);
|
||||
const [newRecipient, setNewRecipient] = useState("");
|
||||
const [selectedForAction, setSelectedForAction] = useState<Set<string>>(new Set());
|
||||
const [isAddingRecipient, setIsAddingRecipient] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setRecipients(selectedRecipients?.map((recipient) => recipient.email) || []);
|
||||
setSelectedForAction(new Set());
|
||||
}, [selectedRecipients]);
|
||||
|
||||
const handleAddRecipient = () => {
|
||||
if (newRecipient && !recipients.includes(newRecipient)) {
|
||||
addRecipients(shareId, { emails: [newRecipient] })
|
||||
.then(() => {
|
||||
setRecipients([...recipients, newRecipient]);
|
||||
setNewRecipient("");
|
||||
toast.success(t("recipientSelector.addSuccess"));
|
||||
onSuccess();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(t("recipientSelector.addError"));
|
||||
});
|
||||
const isValidEmail = (email: string) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
|
||||
const handleAddRecipient = async () => {
|
||||
if (!newRecipient.trim()) return;
|
||||
|
||||
if (!isValidEmail(newRecipient)) {
|
||||
toast.error(t("recipientSelector.invalidEmail"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipients.includes(newRecipient)) {
|
||||
toast.error(t("recipientSelector.duplicateEmail"));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsAddingRecipient(true);
|
||||
try {
|
||||
await addRecipients(shareId, { emails: [newRecipient] });
|
||||
setRecipients([...recipients, newRecipient]);
|
||||
setNewRecipient("");
|
||||
toast.success(t("recipientSelector.addSuccess"));
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
toast.error(t("recipientSelector.addError"));
|
||||
} finally {
|
||||
setIsAddingRecipient(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveRecipient = (email: string) => {
|
||||
removeRecipients(shareId, { emails: [email] })
|
||||
.then(() => {
|
||||
setRecipients(recipients.filter((r) => r !== email));
|
||||
toast.success(t("recipientSelector.removeSuccess"));
|
||||
onSuccess();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(t("recipientSelector.removeError"));
|
||||
const handleRemoveRecipient = async (email: string) => {
|
||||
try {
|
||||
await removeRecipients(shareId, { emails: [email] });
|
||||
setRecipients(recipients.filter((r) => r !== email));
|
||||
setSelectedForAction((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(email);
|
||||
return newSet;
|
||||
});
|
||||
toast.success(t("recipientSelector.removeSuccess"));
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
toast.error(t("recipientSelector.removeError"));
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotifyRecipients = async () => {
|
||||
const handleRemoveSelected = async () => {
|
||||
const emailsToRemove = Array.from(selectedForAction);
|
||||
try {
|
||||
await removeRecipients(shareId, { emails: emailsToRemove });
|
||||
setRecipients(recipients.filter((r) => !selectedForAction.has(r)));
|
||||
setSelectedForAction(new Set());
|
||||
toast.success(t("recipientSelector.bulkRemoveSuccess", { count: emailsToRemove.length }));
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
toast.error(t("recipientSelector.bulkRemoveError"));
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotifySelected = async () => {
|
||||
if (!shareAlias) return;
|
||||
|
||||
const emailsToNotify = Array.from(selectedForAction);
|
||||
const link = `${window.location.origin}/s/${shareAlias}`;
|
||||
const loadingToast = toast.loading(t("recipientSelector.sendingNotifications"));
|
||||
|
||||
try {
|
||||
await notifyRecipients(shareId, { shareLink: link });
|
||||
toast.dismiss(loadingToast);
|
||||
toast.success(t("recipientSelector.bulkNotifySuccess", { count: emailsToNotify.length }));
|
||||
setSelectedForAction(new Set());
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.dismiss(loadingToast);
|
||||
toast.error(t("recipientSelector.bulkNotifyError"));
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotifyAll = async () => {
|
||||
if (!shareAlias) return;
|
||||
|
||||
const link = `${window.location.origin}/s/${shareAlias}`;
|
||||
@@ -78,61 +136,200 @@ export function RecipientSelector({ shareId, selectedRecipients, shareAlias, onS
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
if (checked) {
|
||||
setSelectedForAction(new Set(recipients));
|
||||
} else {
|
||||
setSelectedForAction(new Set());
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectRecipient = (email: string, checked: boolean) => {
|
||||
const newSelected = new Set(selectedForAction);
|
||||
if (checked) {
|
||||
newSelected.add(email);
|
||||
} else {
|
||||
newSelected.delete(email);
|
||||
}
|
||||
setSelectedForAction(newSelected);
|
||||
};
|
||||
|
||||
const isAllSelected = recipients.length > 0 && selectedForAction.size === recipients.length;
|
||||
const hasSelection = selectedForAction.size > 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 mb-4">
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<IconMail className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||
<Input
|
||||
className="pl-9"
|
||||
placeholder={t("recipientSelector.emailPlaceholder")}
|
||||
value={newRecipient}
|
||||
onChange={(e) => setNewRecipient(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && handleAddRecipient()}
|
||||
/>
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconPlus className="h-5 w-5 text-primary" />
|
||||
<h3 className="text-lg font-medium">{t("recipientSelector.addRecipient")}</h3>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
<div className="relative flex-1">
|
||||
<IconMail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground h-4 w-4" />
|
||||
<Input
|
||||
className="pl-9 h-10"
|
||||
placeholder={t("recipientSelector.emailPlaceholder")}
|
||||
value={newRecipient}
|
||||
onChange={(e) => setNewRecipient(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && !isAddingRecipient && handleAddRecipient()}
|
||||
disabled={isAddingRecipient}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleAddRecipient}
|
||||
disabled={!newRecipient.trim() || isAddingRecipient}
|
||||
className="h-10 px-6 sm:w-auto w-full"
|
||||
>
|
||||
{isAddingRecipient ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-background border-t-transparent" />
|
||||
{t("common.loading")}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<IconPlus className="h-4 w-4" />
|
||||
{t("recipientSelector.add")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<Button onClick={handleAddRecipient}>
|
||||
<IconPlus className="h-4 w-4" />
|
||||
{t("recipientSelector.add")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-medium">{t("recipientSelector.recipients", { count: recipients.length })}</h3>
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconUsers className="h-5 w-5 text-primary" />
|
||||
<h3 className="text-lg font-medium">{t("recipientSelector.recipients", { count: recipients.length })}</h3>
|
||||
</div>
|
||||
|
||||
{recipients.length > 0 && shareAlias && smtpEnabled === "true" && (
|
||||
<Button variant="outline" size="sm" onClick={handleNotifyRecipients}>
|
||||
<Button variant="outline" size="sm" onClick={handleNotifyAll} className="sm:w-auto w-full">
|
||||
<IconBell className="h-4 w-4" />
|
||||
{t("recipientSelector.notifyAll")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="max-h-[400px] overflow-y-auto">
|
||||
<div className="flex flex-col gap-2">
|
||||
{recipients.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-500">{t("recipientSelector.noRecipients")}</div>
|
||||
) : (
|
||||
recipients.map((email, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-3 bg-secondary rounded-lg hover:bg-secondary/80"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconMail className="text-gray-500 h-4 w-4" />
|
||||
<span>{email}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
onClick={() => handleRemoveRecipient(email)}
|
||||
>
|
||||
<IconTrash className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
||||
{hasSelection && (
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 p-3 bg-blue-50 dark:bg-blue-950/30 border border-blue-200 dark:border-blue-800 rounded-lg">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconCheck className="h-4 w-4 text-blue-600" />
|
||||
<span className="text-sm font-medium text-blue-900 dark:text-blue-100">
|
||||
{t("recipientSelector.selectedCount", { count: selectedForAction.size })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2">
|
||||
{smtpEnabled === "true" && shareAlias && (
|
||||
<Button variant="outline" size="sm" onClick={handleNotifySelected} className="sm:w-auto w-full">
|
||||
<IconBell className="h-4 w-4" />
|
||||
{t("recipientSelector.notifySelected")}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="destructive" size="sm" onClick={handleRemoveSelected} className="sm:w-auto w-full">
|
||||
<IconTrash className="h-4 w-4" />
|
||||
{t("recipientSelector.removeSelected")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSelectedForAction(new Set())}
|
||||
className="h-8 w-8 p-0 self-center"
|
||||
title={t("common.cancel")}
|
||||
>
|
||||
<IconX className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
{recipients.length === 0 ? (
|
||||
<div className="text-center py-12 px-6">
|
||||
<div className="mx-auto w-16 h-16 bg-muted rounded-full flex items-center justify-center mb-4">
|
||||
<IconUsers className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
<h4 className="text-lg font-medium mb-2">{t("recipientSelector.noRecipients")}</h4>
|
||||
<p className="text-sm text-muted-foreground mb-4">{t("recipientSelector.noRecipientsDescription")}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 p-4 border-b bg-muted/30">
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onCheckedChange={handleSelectAll}
|
||||
aria-label={t("recipientSelector.selectAll")}
|
||||
/>
|
||||
<span className="text-sm font-medium text-muted-foreground">{t("recipientSelector.selectAll")}</span>
|
||||
</div>
|
||||
|
||||
<div className="divide-y max-h-80 overflow-y-auto">
|
||||
{recipients.map((email, index) => {
|
||||
const isSelected = selectedForAction.has(email);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex items-center gap-3 p-4 hover:bg-muted/50 transition-colors ${
|
||||
isSelected ? "bg-blue-50 dark:bg-blue-950/30" : ""
|
||||
}`}
|
||||
>
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={(checked) => handleSelectRecipient(email, checked as boolean)}
|
||||
aria-label={t("recipientSelector.selectRecipient", { email })}
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<div className="w-8 h-8 bg-primary/10 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<IconMail className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<span className="truncate font-medium">{email}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{smtpEnabled === "true" && shareAlias && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-blue-600 hover:text-blue-700 hover:bg-blue-100 dark:hover:bg-blue-900/30"
|
||||
onClick={async () => {
|
||||
const link = `${window.location.origin}/s/${shareAlias}`;
|
||||
const loadingToast = toast.loading(t("recipientSelector.sendingNotifications"));
|
||||
|
||||
try {
|
||||
await notifyRecipients(shareId, { shareLink: link });
|
||||
toast.dismiss(loadingToast);
|
||||
toast.success(t("recipientSelector.singleNotifySuccess", { email }));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.dismiss(loadingToast);
|
||||
toast.error(t("recipientSelector.singleNotifyError"));
|
||||
}
|
||||
}}
|
||||
title={t("recipientSelector.notifySingle")}
|
||||
>
|
||||
<IconBell className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
onClick={() => handleRemoveRecipient(email)}
|
||||
title={t("recipientSelector.removeSingle")}
|
||||
>
|
||||
<IconTrash className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -234,16 +234,21 @@ export function ShareActionsModals({
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={!!shareToManageRecipients} onOpenChange={() => onCloseManageRecipients()}>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[700px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("shareActions.manageRecipientsTitle")}</DialogTitle>
|
||||
<DialogContent className="sm:max-w-[500px] md:max-w-[650px] max-h-[85vh] overflow-hidden">
|
||||
<DialogHeader className="space-y-3">
|
||||
<DialogTitle className="text-xl font-semibold">{t("shareActions.manageRecipientsTitle")}</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
{t("recipientSelector.modalDescription")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<RecipientSelector
|
||||
selectedRecipients={shareToManageRecipients?.recipients || []}
|
||||
shareAlias={shareToManageRecipients?.alias?.alias}
|
||||
shareId={shareToManageRecipients?.id}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
<div className="overflow-y-auto max-h-[calc(85vh-140px)] py-2">
|
||||
<RecipientSelector
|
||||
selectedRecipients={shareToManageRecipients?.recipients || []}
|
||||
shareAlias={shareToManageRecipients?.alias?.alias}
|
||||
shareId={shareToManageRecipients?.id}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
|
||||
@@ -30,6 +30,8 @@ import { Loader } from "@/components/ui/loader";
|
||||
import { getShare } from "@/http/endpoints";
|
||||
import { getFileIcon } from "@/utils/file-icons";
|
||||
import { GenerateShareLinkModal } from "./generate-share-link-modal";
|
||||
import { ShareExpirationModal } from "./share-expiration-modal";
|
||||
import { ShareSecurityModal } from "./share-security-modal";
|
||||
|
||||
interface ShareDetailsModalProps {
|
||||
shareId: string | null;
|
||||
@@ -38,6 +40,8 @@ interface ShareDetailsModalProps {
|
||||
onUpdateDescription?: (shareId: string, newDescription: string) => Promise<void>;
|
||||
onGenerateLink?: (shareId: string, alias: string) => Promise<void>;
|
||||
onManageFiles?: (share: any) => void;
|
||||
onUpdateSecurity?: (shareId: string) => Promise<void>;
|
||||
onUpdateExpiration?: (shareId: string) => Promise<void>;
|
||||
refreshTrigger?: number;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
@@ -68,6 +72,8 @@ export function ShareDetailsModal({
|
||||
onUpdateDescription,
|
||||
onGenerateLink,
|
||||
onManageFiles,
|
||||
onUpdateSecurity,
|
||||
onUpdateExpiration,
|
||||
refreshTrigger,
|
||||
onSuccess,
|
||||
}: ShareDetailsModalProps) {
|
||||
@@ -78,6 +84,8 @@ export function ShareDetailsModal({
|
||||
const [editValue, setEditValue] = useState("");
|
||||
const [pendingChanges, setPendingChanges] = useState<{ name?: string; description?: string }>({});
|
||||
const [showLinkModal, setShowLinkModal] = useState(false);
|
||||
const [showSecurityModal, setShowSecurityModal] = useState(false);
|
||||
const [showExpirationModal, setShowExpirationModal] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -93,12 +101,10 @@ export function ShareDetailsModal({
|
||||
}
|
||||
}, [editingField]);
|
||||
|
||||
// Clear pending changes when share is updated
|
||||
useEffect(() => {
|
||||
setPendingChanges({});
|
||||
}, [share]);
|
||||
|
||||
// Refresh data when external update happens
|
||||
useEffect(() => {
|
||||
if (refreshTrigger) {
|
||||
loadShareDetails();
|
||||
@@ -140,7 +146,6 @@ export function ShareDetailsModal({
|
||||
|
||||
const { field } = editingField;
|
||||
|
||||
// Update local state optimistically
|
||||
setPendingChanges((prev) => ({
|
||||
...prev,
|
||||
[field]: editValue,
|
||||
@@ -153,14 +158,13 @@ export function ShareDetailsModal({
|
||||
await onUpdateDescription(shareId, editValue);
|
||||
}
|
||||
|
||||
// Reload share details to get updated data
|
||||
await loadShareDetails();
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to update:", error);
|
||||
// Revert optimistic update on error
|
||||
|
||||
setPendingChanges((prev) => {
|
||||
const newState = { ...prev };
|
||||
delete newState[field];
|
||||
@@ -216,6 +220,22 @@ export function ShareDetailsModal({
|
||||
}
|
||||
};
|
||||
|
||||
const handleSecurityUpdated = async () => {
|
||||
setShowSecurityModal(false);
|
||||
await loadShareDetails();
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
const handleExpirationUpdated = async () => {
|
||||
setShowExpirationModal(false);
|
||||
await loadShareDetails();
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
if (!share) return null;
|
||||
|
||||
const shareLink = share?.alias?.alias ? `${window.location.origin}/s/${share.alias.alias}` : null;
|
||||
@@ -239,7 +259,6 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{/* Key metrics */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="text-center p-2 bg-muted/30 rounded-lg">
|
||||
<p className="text-lg font-semibold text-green-600">{share.viewCount || 0}</p>
|
||||
@@ -255,13 +274,11 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Basic Information */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 border-b pb-2">
|
||||
<h3 className="text-base font-medium text-foreground">{t("shareDetails.basicInfo")}</h3>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<label className="text-sm font-medium text-muted-foreground">{t("shareDetails.name")}</label>
|
||||
@@ -308,7 +325,6 @@ export function ShareDetailsModal({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<label className="text-sm font-medium text-muted-foreground">
|
||||
@@ -359,10 +375,20 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Share Link Section */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 border-b pb-2">
|
||||
<h3 className="text-base font-medium text-foreground">{t("shareDetails.shareLink")}</h3>
|
||||
{onGenerateLink && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-5 w-5 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setShowLinkModal(true)}
|
||||
title={shareLink ? t("shareDetails.editLink") : t("shareDetails.generateLink")}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{shareLink ? (
|
||||
<div className="flex gap-2">
|
||||
@@ -385,37 +411,30 @@ export function ShareDetailsModal({
|
||||
>
|
||||
<IconExternalLink className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => setShowLinkModal(true)}
|
||||
title={t("shareDetails.editLink")}
|
||||
>
|
||||
<IconEdit className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-between p-2 bg-muted/20 rounded-lg">
|
||||
<p className="text-sm text-muted-foreground">{t("shareDetails.noLink")}</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowLinkModal(true)}
|
||||
className="gap-1 h-7 text-xs"
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
{t("shareDetails.generateLink")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Dates and Security in Grid */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Dates */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-base font-medium text-foreground border-b pb-2">{t("shareDetails.dates")}</h3>
|
||||
<div className="flex items-center gap-2 border-b pb-2">
|
||||
<h3 className="text-base font-medium text-foreground">{t("shareDetails.dates")}</h3>
|
||||
{onUpdateExpiration && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-5 w-5 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setShowExpirationModal(true)}
|
||||
title={t("shareDetails.editExpiration")}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<div className="text-xs font-medium text-muted-foreground">{t("shareDetails.created")}</div>
|
||||
@@ -430,11 +449,21 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Security */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-base font-medium text-foreground border-b pb-2">
|
||||
{t("shareDetails.security")}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 border-b pb-2">
|
||||
<h3 className="text-base font-medium text-foreground">{t("shareDetails.security")}</h3>
|
||||
{onUpdateSecurity && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-5 w-5 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setShowSecurityModal(true)}
|
||||
title={t("shareDetails.editSecurity")}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{share.security?.hasPassword ? (
|
||||
<Badge variant="secondary" className="bg-yellow-500/20 text-yellow-700 border-yellow-200 w-fit">
|
||||
@@ -456,7 +485,6 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Files */}
|
||||
{share.files && share.files.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 border-b pb-2">
|
||||
@@ -504,7 +532,6 @@ export function ShareDetailsModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recipients */}
|
||||
{share.recipients && share.recipients.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-base font-medium text-foreground border-b pb-2">
|
||||
@@ -541,6 +568,22 @@ export function ShareDetailsModal({
|
||||
onSuccess={handleLinkGenerated}
|
||||
/>
|
||||
)}
|
||||
{showSecurityModal && (
|
||||
<ShareSecurityModal
|
||||
shareId={shareId}
|
||||
share={share}
|
||||
onClose={() => setShowSecurityModal(false)}
|
||||
onSuccess={handleSecurityUpdated}
|
||||
/>
|
||||
)}
|
||||
{showExpirationModal && (
|
||||
<ShareExpirationModal
|
||||
shareId={shareId}
|
||||
share={share}
|
||||
onClose={() => setShowExpirationModal(false)}
|
||||
onSuccess={handleExpirationUpdated}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
203
apps/web/src/components/modals/share-expiration-modal.tsx
Normal file
203
apps/web/src/components/modals/share-expiration-modal.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { IconCalendar, IconClock, IconClockOff } from "@tabler/icons-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Loader } from "@/components/ui/loader";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { updateShare } from "@/http/endpoints";
|
||||
|
||||
interface ShareExpirationModalProps {
|
||||
shareId: string | null;
|
||||
share: any;
|
||||
onClose: () => void;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
export function ShareExpirationModal({ shareId, share, onClose, onSuccess }: ShareExpirationModalProps) {
|
||||
const t = useTranslations();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [hasExpiration, setHasExpiration] = useState(false);
|
||||
const [expirationDate, setExpirationDate] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (share) {
|
||||
const hasCurrentExpiration = !!share.expiration;
|
||||
setHasExpiration(hasCurrentExpiration);
|
||||
|
||||
if (hasCurrentExpiration) {
|
||||
// Converter para formato datetime-local
|
||||
const date = new Date(share.expiration);
|
||||
setExpirationDate(date.toISOString().slice(0, 16));
|
||||
} else {
|
||||
setExpirationDate("");
|
||||
}
|
||||
}
|
||||
}, [share]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!shareId) return;
|
||||
|
||||
// Validação
|
||||
if (hasExpiration) {
|
||||
if (!expirationDate.trim()) {
|
||||
toast.error(t("shareExpiration.validation.dateRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedDate = new Date(expirationDate);
|
||||
const now = new Date();
|
||||
|
||||
if (selectedDate <= now) {
|
||||
toast.error(t("shareExpiration.validation.dateMustBeFuture"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await updateShare({
|
||||
id: shareId,
|
||||
expiration: hasExpiration ? new Date(expirationDate).toISOString() : undefined,
|
||||
});
|
||||
|
||||
const successMessage = hasExpiration
|
||||
? share?.expiration
|
||||
? t("shareExpiration.success.expirationUpdated")
|
||||
: t("shareExpiration.success.expirationSet")
|
||||
: t("shareExpiration.success.expirationRemoved");
|
||||
|
||||
toast.success(successMessage);
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Failed to update share expiration:", error);
|
||||
toast.error(t("shareExpiration.error.updateFailed"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleExpirationToggle = (checked: boolean) => {
|
||||
setHasExpiration(checked);
|
||||
if (!checked) {
|
||||
setExpirationDate("");
|
||||
} else if (!expirationDate) {
|
||||
// Definir data padrão para 7 dias no futuro
|
||||
const defaultDate = new Date();
|
||||
defaultDate.setDate(defaultDate.getDate() + 7);
|
||||
setExpirationDate(defaultDate.toISOString().slice(0, 16));
|
||||
}
|
||||
};
|
||||
|
||||
// Determina o texto do botão
|
||||
const getButtonText = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader size="sm" />
|
||||
{share?.expiration ? t("common.updating") : t("common.saving")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return share?.expiration ? t("common.update") : t("common.save");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={!!shareId} onOpenChange={() => onClose()}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("shareExpiration.title")}</DialogTitle>
|
||||
<DialogDescription>{t("shareExpiration.subtitle")}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4 space-y-6">
|
||||
{/* Status Atual */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-medium text-foreground">{t("shareExpiration.currentStatus")}</h3>
|
||||
<div className="flex gap-2">
|
||||
{share?.expiration ? (
|
||||
<div className="bg-yellow-500/20 text-yellow-800 border border-yellow-300 dark:bg-yellow-500/10 dark:text-yellow-400 dark:border-yellow-500/20 rounded-md px-2 py-1 text-xs font-medium flex items-center gap-1">
|
||||
<IconClock className="h-3 w-3" />
|
||||
{t("shareExpiration.expires")} {new Date(share.expiration).toLocaleString()}
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-green-500/20 text-green-800 border border-green-300 dark:bg-green-500/10 dark:text-green-400 dark:border-green-500/20 rounded-md px-2 py-1 text-xs font-medium flex items-center gap-1">
|
||||
<IconClockOff className="h-3 w-3" />
|
||||
{t("shareExpiration.neverExpires")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Configuração de Expiração */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="expiration-enabled" checked={hasExpiration} onCheckedChange={handleExpirationToggle} />
|
||||
<Label htmlFor="expiration-enabled" className="flex items-center gap-2">
|
||||
<IconCalendar size={16} />
|
||||
{t("shareExpiration.enableExpiration")}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{hasExpiration && (
|
||||
<div className="space-y-4 pl-6 border-l-2 border-muted">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="expiration-date">{t("shareExpiration.expirationDate")}</Label>
|
||||
<Input
|
||||
id="expiration-date"
|
||||
type="datetime-local"
|
||||
value={expirationDate}
|
||||
onChange={(e) => setExpirationDate(e.target.value)}
|
||||
min={new Date().toISOString().slice(0, 16)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-muted-foreground space-y-1">
|
||||
<p>{t("shareExpiration.info.title")}</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-2">
|
||||
<li>{t("shareExpiration.info.willBeInaccessible")}</li>
|
||||
<li>{t("shareExpiration.info.canBeChanged")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!hasExpiration && (
|
||||
<div className="pl-6 border-l-2 border-muted">
|
||||
<div className="bg-muted/50 border border-border rounded-lg p-3">
|
||||
<p className="text-sm text-muted-foreground">{t("shareExpiration.info.noExpiration")}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={onClose} disabled={isLoading}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={isLoading}>
|
||||
{getButtonText()}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
226
apps/web/src/components/modals/share-security-modal.tsx
Normal file
226
apps/web/src/components/modals/share-security-modal.tsx
Normal file
@@ -0,0 +1,226 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { IconCheck, IconEye, IconEyeOff, IconLock, IconLockOpen, IconX } from "@tabler/icons-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Loader } from "@/components/ui/loader";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { updateSharePassword } from "@/http/endpoints";
|
||||
|
||||
interface ShareSecurityModalProps {
|
||||
shareId: string | null;
|
||||
share: any;
|
||||
onClose: () => void;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
export function ShareSecurityModal({ shareId, share, onClose, onSuccess }: ShareSecurityModalProps) {
|
||||
const t = useTranslations();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [hasPassword, setHasPassword] = useState(false);
|
||||
const [password, setPassword] = useState("");
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (share?.security) {
|
||||
const hasCurrentPassword = share.security.hasPassword || false;
|
||||
setHasPassword(hasCurrentPassword);
|
||||
// Campo sempre começa vazio
|
||||
setPassword("");
|
||||
}
|
||||
}, [share]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!shareId) return;
|
||||
|
||||
// Validação de senha
|
||||
if (hasPassword) {
|
||||
if (!password.trim()) {
|
||||
toast.error(t("shareSecurity.validation.passwordRequired"));
|
||||
return;
|
||||
}
|
||||
if (password.length < 2) {
|
||||
toast.error(t("shareSecurity.validation.passwordTooShort"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await updateSharePassword(shareId, {
|
||||
password: hasPassword ? password : null,
|
||||
});
|
||||
|
||||
const successMessage = hasPassword
|
||||
? share?.security?.hasPassword
|
||||
? t("shareSecurity.success.passwordUpdated")
|
||||
: t("shareSecurity.success.passwordSet")
|
||||
: t("shareSecurity.success.passwordRemoved");
|
||||
|
||||
toast.success(successMessage);
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Failed to update share security:", error);
|
||||
toast.error(t("shareSecurity.error.updateFailed"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordToggle = (checked: boolean) => {
|
||||
setHasPassword(checked);
|
||||
if (!checked) {
|
||||
setPassword("");
|
||||
setShowPassword(false);
|
||||
}
|
||||
};
|
||||
|
||||
const togglePasswordVisibility = () => {
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
// Determina o texto do botão
|
||||
const getButtonText = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader size="sm" />
|
||||
{share?.security?.hasPassword ? t("common.updating") : t("common.saving")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return share?.security?.hasPassword ? t("common.update") : t("common.save");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={!!shareId} onOpenChange={() => onClose()}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("shareSecurity.title")}</DialogTitle>
|
||||
<DialogDescription>{t("shareSecurity.subtitle")}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4 space-y-6">
|
||||
{/* Status Atual */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-medium text-foreground">{t("shareSecurity.currentStatus")}</h3>
|
||||
<div className="flex gap-2">
|
||||
{share?.security?.hasPassword ? (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-yellow-500/20 text-yellow-800 border-yellow-300 dark:bg-yellow-500/10 dark:text-yellow-400 dark:border-yellow-500/20"
|
||||
>
|
||||
<IconLock className="h-3 w-3 mr-1" />
|
||||
{t("shareDetails.passwordProtected")}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-green-500/20 text-green-800 border-green-300 dark:bg-green-500/10 dark:text-green-400 dark:border-green-500/20"
|
||||
>
|
||||
<IconLockOpen className="h-3 w-3 mr-1" />
|
||||
{t("shareDetails.publicAccess")}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Configuração de Senha */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="password-protection" checked={hasPassword} onCheckedChange={handlePasswordToggle} />
|
||||
<Label htmlFor="password-protection" className="flex items-center gap-2">
|
||||
<IconLock size={16} />
|
||||
{t("shareSecurity.passwordProtection")}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{hasPassword && (
|
||||
<div className="space-y-4 pl-6 border-l-2 border-muted">
|
||||
{/* Mensagem explicativa quando já tem senha */}
|
||||
{share?.security?.hasPassword && (
|
||||
<div className="bg-muted/50 border border-border rounded-lg p-3">
|
||||
<p className="text-sm text-muted-foreground">{t("shareSecurity.existingPasswordMessage")}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">
|
||||
{share?.security?.hasPassword ? t("shareSecurity.newPassword") : t("shareSecurity.password")}
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder={t("shareSecurity.passwordPlaceholder")}
|
||||
className="pr-10"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-0 top-0 h-full w-10 hover:bg-transparent"
|
||||
onClick={togglePasswordVisibility}
|
||||
>
|
||||
{showPassword ? (
|
||||
<IconEyeOff className="h-4 w-4 text-muted-foreground hover:text-foreground" />
|
||||
) : (
|
||||
<IconEye className="h-4 w-4 text-muted-foreground hover:text-foreground" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-muted-foreground space-y-1">
|
||||
<p>{t("shareSecurity.passwordRequirements.title")}</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-2">
|
||||
<li>{t("shareSecurity.passwordRequirements.minLength")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Informações Adicionais */}
|
||||
<div className="bg-muted/30 p-3 rounded-lg">
|
||||
<div className="text-sm space-y-1">
|
||||
<p className="font-medium text-muted-foreground">{t("shareSecurity.info.title")}</p>
|
||||
<p className="text-muted-foreground">
|
||||
{hasPassword ? t("shareSecurity.info.withPassword") : t("shareSecurity.info.withoutPassword")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={onClose} disabled={isLoading}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={isLoading}>
|
||||
{getButtonText()}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -37,6 +37,8 @@ export interface SharesTableProps {
|
||||
onEdit: (share: any) => void;
|
||||
onUpdateName: (shareId: string, newName: string) => void;
|
||||
onUpdateDescription: (shareId: string, newDescription: string) => void;
|
||||
onUpdateSecurity?: (share: any) => void;
|
||||
onUpdateExpiration?: (share: any) => void;
|
||||
onManageFiles: (share: any) => void;
|
||||
onManageRecipients: (share: any) => void;
|
||||
onViewDetails: (share: any) => void;
|
||||
@@ -53,6 +55,8 @@ export function SharesTable({
|
||||
onEdit,
|
||||
onUpdateName,
|
||||
onUpdateDescription,
|
||||
onUpdateSecurity,
|
||||
onUpdateExpiration,
|
||||
onManageFiles,
|
||||
onManageRecipients,
|
||||
onViewDetails,
|
||||
@@ -66,7 +70,10 @@ export function SharesTable({
|
||||
const { smtpEnabled } = useShareContext();
|
||||
const [editingField, setEditingField] = useState<{ shareId: string; field: "name" | "description" } | null>(null);
|
||||
const [editValue, setEditValue] = useState("");
|
||||
const [hoveredField, setHoveredField] = useState<{ shareId: string; field: "name" | "description" } | null>(null);
|
||||
const [hoveredField, setHoveredField] = useState<{
|
||||
shareId: string;
|
||||
field: "name" | "description" | "security" | "expiration" | "files" | "recipients";
|
||||
} | null>(null);
|
||||
const [pendingChanges, setPendingChanges] = useState<{ [shareId: string]: { name?: string; description?: string } }>(
|
||||
{}
|
||||
);
|
||||
@@ -246,6 +253,10 @@ export function SharesTable({
|
||||
const isEditingDescription = editingField?.shareId === share.id && editingField?.field === "description";
|
||||
const isHoveringName = hoveredField?.shareId === share.id && hoveredField?.field === "name";
|
||||
const isHoveringDescription = hoveredField?.shareId === share.id && hoveredField?.field === "description";
|
||||
const isHoveringSecurity = hoveredField?.shareId === share.id && hoveredField?.field === "security";
|
||||
const isHoveringExpiration = hoveredField?.shareId === share.id && hoveredField?.field === "expiration";
|
||||
const isHoveringFiles = hoveredField?.shareId === share.id && hoveredField?.field === "files";
|
||||
const isHoveringRecipients = hoveredField?.shareId === share.id && hoveredField?.field === "recipients";
|
||||
const isSelected = selectedShares.has(share.id);
|
||||
const displayName = getDisplayValue(share, "name");
|
||||
const displayDescription = getDisplayValue(share, "description");
|
||||
@@ -391,7 +402,32 @@ export function SharesTable({
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4">{format(new Date(share.createdAt), "MM/dd/yyyy HH:mm")}</TableCell>
|
||||
<TableCell className="h-12 px-4">
|
||||
{share.expiration ? format(new Date(share.expiration), "MM/dd/yyyy HH:mm") : t("sharesTable.never")}
|
||||
<div
|
||||
className="flex items-center gap-1 min-w-0"
|
||||
onMouseEnter={() => setHoveredField({ shareId: share.id, field: "expiration" })}
|
||||
onMouseLeave={() => setHoveredField(null)}
|
||||
>
|
||||
<span className="text-sm">
|
||||
{share.expiration
|
||||
? format(new Date(share.expiration), "MM/dd/yyyy HH:mm")
|
||||
: t("sharesTable.never")}
|
||||
</span>
|
||||
<div className="w-6 flex justify-center flex-shrink-0">
|
||||
{isHoveringExpiration && onUpdateExpiration && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-6 w-6 text-muted-foreground hover:text-foreground hidden sm:block"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onUpdateExpiration(share);
|
||||
}}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4">
|
||||
<Badge
|
||||
@@ -410,29 +446,96 @@ export function SharesTable({
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={`flex items-center gap-1 ${
|
||||
share.security.hasPassword
|
||||
? "bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-500"
|
||||
: "bg-green-500/20 hover:bg-green-500/30 text-green-500"
|
||||
}`}
|
||||
<div
|
||||
className="flex items-center gap-1 min-w-0"
|
||||
onMouseEnter={() => setHoveredField({ shareId: share.id, field: "security" })}
|
||||
onMouseLeave={() => setHoveredField(null)}
|
||||
>
|
||||
{share.security.hasPassword ? (
|
||||
<IconLock className="h-4 w-4" />
|
||||
) : (
|
||||
<IconLockOpen className="h-4 w-4" />
|
||||
)}
|
||||
{share.security.hasPassword
|
||||
? t("sharesTable.security.protected")
|
||||
: t("sharesTable.security.public")}
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={`flex items-center gap-1 ${
|
||||
share.security.hasPassword
|
||||
? "bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-500"
|
||||
: "bg-green-500/20 hover:bg-green-500/30 text-green-500"
|
||||
}`}
|
||||
>
|
||||
{share.security.hasPassword ? (
|
||||
<IconLock className="h-4 w-4" />
|
||||
) : (
|
||||
<IconLockOpen className="h-4 w-4" />
|
||||
)}
|
||||
{share.security.hasPassword
|
||||
? t("sharesTable.security.protected")
|
||||
: t("sharesTable.security.public")}
|
||||
</Badge>
|
||||
<div className="w-6 flex justify-center flex-shrink-0">
|
||||
{isHoveringSecurity && onUpdateSecurity && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-6 w-6 text-muted-foreground hover:text-foreground hidden sm:block"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onUpdateSecurity(share);
|
||||
}}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4">
|
||||
{share.files?.length || 0} {t("sharesTable.filesCount")}
|
||||
<div
|
||||
className="flex items-center gap-1 min-w-0"
|
||||
onMouseEnter={() => setHoveredField({ shareId: share.id, field: "files" })}
|
||||
onMouseLeave={() => setHoveredField(null)}
|
||||
>
|
||||
<span className="text-sm">
|
||||
{share.files?.length || 0} {t("sharesTable.filesCount")}
|
||||
</span>
|
||||
<div className="w-6 flex justify-center flex-shrink-0">
|
||||
{isHoveringFiles && onManageFiles && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-6 w-6 text-muted-foreground hover:text-foreground hidden sm:block"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onManageFiles(share);
|
||||
}}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4">
|
||||
{share.recipients?.length || 0} {t("sharesTable.recipientsCount")}
|
||||
<div
|
||||
className="flex items-center gap-1 min-w-0"
|
||||
onMouseEnter={() => setHoveredField({ shareId: share.id, field: "recipients" })}
|
||||
onMouseLeave={() => setHoveredField(null)}
|
||||
>
|
||||
<span className="text-sm">
|
||||
{share.recipients?.length || 0} {t("sharesTable.recipientsCount")}
|
||||
</span>
|
||||
<div className="w-6 flex justify-center flex-shrink-0">
|
||||
{isHoveringRecipients && onManageRecipients && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-6 w-6 text-muted-foreground hover:text-foreground hidden sm:block"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onManageRecipients(share);
|
||||
}}
|
||||
>
|
||||
<IconEdit className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="h-12 px-4 text-right">
|
||||
<DropdownMenu>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
deleteShare,
|
||||
notifyRecipients,
|
||||
updateShare,
|
||||
updateSharePassword,
|
||||
} from "@/http/endpoints";
|
||||
import { ListUserShares200SharesItem } from "@/http/models/listUserShares200SharesItem";
|
||||
|
||||
@@ -19,6 +20,8 @@ export interface ShareManagerHook {
|
||||
shareToEdit: ListUserShares200SharesItem | null;
|
||||
shareToManageFiles: ListUserShares200SharesItem | null;
|
||||
shareToManageRecipients: ListUserShares200SharesItem | null;
|
||||
shareToManageSecurity: ListUserShares200SharesItem | null;
|
||||
shareToManageExpiration: ListUserShares200SharesItem | null;
|
||||
shareToViewDetails: ListUserShares200SharesItem | null;
|
||||
shareToGenerateLink: ListUserShares200SharesItem | null;
|
||||
sharesToDelete: ListUserShares200SharesItem[] | null;
|
||||
@@ -26,6 +29,8 @@ export interface ShareManagerHook {
|
||||
setShareToEdit: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToManageFiles: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToManageRecipients: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToManageSecurity: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToManageExpiration: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToViewDetails: (share: ListUserShares200SharesItem | null) => void;
|
||||
setShareToGenerateLink: (share: ListUserShares200SharesItem | null) => void;
|
||||
setSharesToDelete: (shares: ListUserShares200SharesItem[] | null) => void;
|
||||
@@ -35,6 +40,8 @@ export interface ShareManagerHook {
|
||||
handleEdit: (shareId: string, data: any) => Promise<void>;
|
||||
handleUpdateName: (shareId: string, newName: string) => Promise<void>;
|
||||
handleUpdateDescription: (shareId: string, newDescription: string) => Promise<void>;
|
||||
handleUpdateSecurity: (shareId: string) => Promise<void>;
|
||||
handleUpdateExpiration: (shareId: string) => Promise<void>;
|
||||
handleManageFiles: (shareId: string, files: any[]) => Promise<void>;
|
||||
handleManageRecipients: (shareId: string, recipients: any[]) => Promise<void>;
|
||||
handleGenerateLink: (shareId: string, alias: string) => Promise<void>;
|
||||
@@ -48,6 +55,8 @@ export function useShareManager(onSuccess: () => void) {
|
||||
const [shareToEdit, setShareToEdit] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToManageFiles, setShareToManageFiles] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToManageRecipients, setShareToManageRecipients] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToManageSecurity, setShareToManageSecurity] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToManageExpiration, setShareToManageExpiration] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToViewDetails, setShareToViewDetails] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [shareToGenerateLink, setShareToGenerateLink] = useState<ListUserShares200SharesItem | null>(null);
|
||||
const [sharesToDelete, setSharesToDelete] = useState<ListUserShares200SharesItem[] | null>(null);
|
||||
@@ -130,6 +139,26 @@ export function useShareManager(onSuccess: () => void) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateSecurity = async (shareId: string) => {
|
||||
try {
|
||||
await onSuccess();
|
||||
toast.success(t("shareManager.securityUpdateSuccess"));
|
||||
} catch (error) {
|
||||
toast.error(t("shareManager.securityUpdateError"));
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateExpiration = async (shareId: string) => {
|
||||
try {
|
||||
await onSuccess();
|
||||
toast.success(t("shareManager.expirationUpdateSuccess"));
|
||||
} catch (error) {
|
||||
toast.error(t("shareManager.expirationUpdateError"));
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleManageFiles = async (shareId: string, files: string[]) => {
|
||||
try {
|
||||
await addFiles(shareId, { files });
|
||||
@@ -186,6 +215,8 @@ export function useShareManager(onSuccess: () => void) {
|
||||
shareToEdit,
|
||||
shareToManageFiles,
|
||||
shareToManageRecipients,
|
||||
shareToManageSecurity,
|
||||
shareToManageExpiration,
|
||||
shareToViewDetails,
|
||||
shareToGenerateLink,
|
||||
sharesToDelete,
|
||||
@@ -193,6 +224,8 @@ export function useShareManager(onSuccess: () => void) {
|
||||
setShareToEdit,
|
||||
setShareToManageFiles,
|
||||
setShareToManageRecipients,
|
||||
setShareToManageSecurity,
|
||||
setShareToManageExpiration,
|
||||
setShareToViewDetails,
|
||||
setShareToGenerateLink,
|
||||
setSharesToDelete,
|
||||
@@ -202,6 +235,8 @@ export function useShareManager(onSuccess: () => void) {
|
||||
handleEdit,
|
||||
handleUpdateName,
|
||||
handleUpdateDescription,
|
||||
handleUpdateSecurity,
|
||||
handleUpdateExpiration,
|
||||
handleManageFiles,
|
||||
handleManageRecipients,
|
||||
handleGenerateLink,
|
||||
|
||||
Reference in New Issue
Block a user