Merge branch 'develop' into feature/update-react-router-dom
This commit is contained in:
commit
e04a925303
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "إعدادات اللغة والمنطقة الزمنية هي طريقة لتغير لغة البوابة بأكملها لجميع مستخدمين البوابة ولتهيئة المنطقة الزمنية حيث يمكنهم متابعة أحداث البوابة المعروضة بالوقت والتاريخ الصحيح.",
|
||||
"LastUpdate": "اخر تحديث: {{date}}",
|
||||
"LicenseLimitCounter": "حد الترخيص للمدراء/المستخدمين ذوي الصلاحيات:",
|
||||
"LicenseLimitDescription": "يتكون عداد حد الترخيص من: الحسابات الموجودة بالفعل في {{productName}} والمستخدمين الجدد الذين تريد استيرادهم. إذا قمت باستيراد مستخدمين لديهم حساب {{productName}} بالفعل، فلن يتم احتسابهم مرة أخرى في العداد. يسمح لك ترخيص {{productName}} الخاص بك بالحصول على 100 مستخدم كحد أقصى.",
|
||||
"LicenseLimitDescription": "يتكون عداد حد الترخيص من: الحسابات الموجودة بالفعل في {{productName}} والمستخدمين الجدد الذين تريد استيرادهم. إذا قمت باستيراد مستخدمين لديهم حساب {{productName}} بالفعل، فلن يتم احتسابهم مرة أخرى في العداد. يسمح لك ترخيص {{productName}} الخاص بك بالحصول على {{maxLimit}} مستخدم كحد أقصى.",
|
||||
"Lifetime": "الوقت المتبقي (دقيقة)",
|
||||
"LimitThemesTooltip": "يمكنك فقط إنشاء 3 قوالب مخصصة. لإنشاء واحدة جديدة ، يجب عليك حذف أحد القوالب السابقة.",
|
||||
"LocalFile": "ملف محلي",
|
||||
|
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Dil və Saat qurşağı Parametrləri bütün portal istifadəçiləri üçün bütün portalın dilini dəyişmək və portaldakı bütün hadisələrin düzgün tarix və vaxtla göstərilməsi üçün saat qurşağını konfiqurasiya etmək üsuludur.",
|
||||
"LastUpdate": "Son yeniləmə: {{date}}",
|
||||
"LicenseLimitCounter": "Lisenziya limiti Administratorlar/Güc:",
|
||||
"LicenseLimitDescription": "Lisenziya limiti sayğacı aşağıdakılardan ibarətdir: {{productName}}-də mövcud hesablar və idxal etmək istədiyiniz yeni istifadəçilər. Artıq {{productName}} hesabı olan istifadəçiləri idxal etsəniz, onlar bir daha sayğacda sayılmayacaqlar. {{productName}} lisenziyanız maksimum 100 istifadəçinin mövcud olmasına imkan verir.",
|
||||
"LicenseLimitDescription": "Lisenziya limiti sayğacı aşağıdakılardan ibarətdir: {{productName}}-də mövcud hesablar və idxal etmək istədiyiniz yeni istifadəçilər. Artıq {{productName}} hesabı olan istifadəçiləri idxal etsəniz, onlar bir daha sayğacda sayılmayacaqlar. {{productName}} lisenziyanız maksimum {{maxLimit}} istifadəçinin mövcud olmasına imkan verir.",
|
||||
"Lifetime": "Ömürlük (dəq)",
|
||||
"LimitThemesTooltip": "Siz yalnız 3 fərdi mövzu yarada bilərsiniz. Yenisini yaratmaq üçün əvvəlki mövzulardan birini silməlisiniz.",
|
||||
"LocalFile": "Yerli fayl",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Настройките за Език и Часова зона са начин за промяна на езика на целия портал за всички потребители на портала и за конфигуриране на часовата зона, така че всички събития на портала да се показват с правилните дата и час.",
|
||||
"LastUpdate": "Последна актуализация: {{date}}",
|
||||
"LicenseLimitCounter": "Ограничение на лиценза Администратори/Мощност:",
|
||||
"LicenseLimitDescription": "Броячът за ограничение на лиценза се състои от: вече съществуващи профили в {{productName}} и нови потребители, които искате да входирате. Ако входирате потребители, които вече имат профил в {{productName}}, те няма да бъдат преброени отново в брояча. Вашият лиценз за {{productName}} ви позволява да имате максимум 100 потребителя.",
|
||||
"LicenseLimitDescription": "Броячът за ограничение на лиценза се състои от: вече съществуващи профили в {{productName}} и нови потребители, които искате да входирате. Ако входирате потребители, които вече имат профил в {{productName}}, те няма да бъдат преброени отново в брояча. Вашият лиценз за {{productName}} ви позволява да имате максимум {{maxLimit}} потребителя.",
|
||||
"Lifetime": "Живот (мин.)",
|
||||
"LimitThemesTooltip": "Можете да създадете само 3 персонализирани теми. За да създадете нова, трябва да изтриете една от предишните теми.",
|
||||
"LocalFile": "Локален файл",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Nastavení jazyka a časového pásma umožňuje změnit jazyk celého portálu pro všechny uživatele portálu a nakonfigurovat časové pásmo tak, aby se všechny události portálu zobrazovaly se správným datem a časem.",
|
||||
"LastUpdate": "Poslední aktualizace: {{date}}",
|
||||
"LicenseLimitCounter": "Omezení licence Administrátoři/možnosti:",
|
||||
"LicenseLimitDescription": "Počítadlo licenčních limitů se skládá z: již existujících účtů v {{productName}} a nových uživatelů, které chcete importovat. Pokud importujete uživatele, kteří již účet v {{productName}} mají, nebudou do počítadla znovu započítáni. Vaše licence {{productName}} vám umožňuje mít maximálně 100 uživatelů.",
|
||||
"LicenseLimitDescription": "Počítadlo licenčních limitů se skládá z: již existujících účtů v {{productName}} a nových uživatelů, které chcete importovat. Pokud importujete uživatele, kteří již účet v {{productName}} mají, nebudou do počítadla znovu započítáni. Vaše licence {{productName}} vám umožňuje mít maximálně {{maxLimit}} uživatelů.",
|
||||
"Lifetime": "Životnost (min)",
|
||||
"LimitThemesTooltip": "Můžete vytvořit pouze 3 vlastní motivy. Chcete-li vytvořit nové, musíte odstranit jedno z předchozích témat.",
|
||||
"LocalFile": "Místní soubor",
|
||||
|
@ -145,7 +145,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Sprache- und Zeitzoneneinstellungen ist eine Möglichkeit, die Sprache des gesamten Portals für alle Portalbenutzer zu ändern und die Zeitzone so zu konfigurieren, dass alle Ereignisse des Portals mit dem richtigen Datum und der richtigen Zeit angezeigt werden.",
|
||||
"LastUpdate": "Letzte Aktualisierung: {{date}}",
|
||||
"LicenseLimitCounter": "Lizenzbeschränkung Administratoren/Powerusers:",
|
||||
"LicenseLimitDescription": "Der Lizenzlimitzähler besteht aus: bereits vorhandenen Konten in {{productName}} und neuen Benutzern, die Sie importieren möchten. Wenn Sie Benutzer importieren, die bereits über ein {{productName}}-Konto verfügen, werden diese im Zähler nicht erneut gezählt. Mit Ihrer {{productName}}-Lizenz können Sie maximal 100 Benutzer haben.",
|
||||
"LicenseLimitDescription": "Der Lizenzlimitzähler besteht aus: bereits vorhandenen Konten in {{productName}} und neuen Benutzern, die Sie importieren möchten. Wenn Sie Benutzer importieren, die bereits über ein {{productName}}-Konto verfügen, werden diese im Zähler nicht erneut gezählt. Mit Ihrer {{productName}}-Lizenz können Sie maximal {{maxLimit}} Benutzer haben.",
|
||||
"Lifetime": "Lebenszeit (min)",
|
||||
"LimitThemesTooltip": "Sie können nur 3 benutzerdefinierte Themen erstellen. Um ein neues Thema zu erstellen, müssen Sie eines der vorherigen Themen löschen.",
|
||||
"LocalFile": "Lokale Datei",
|
||||
|
@ -145,7 +145,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Οι Ρυθμίσεις γλώσσας και ζώνης ώρας είναι ένας τρόπος για να αλλάξετε τη γλώσσα ολόκληρης της πύλης για όλους τους χρήστες της πύλης και να ρυθμίσετε τη ζώνη ώρας, ώστε όλα τα συμβάντα της πύλης να εμφανίζονται με τη σωστή ημερομηνία και ώρα.",
|
||||
"LastUpdate": "Τελευταία ενημέρωση: {{date}}",
|
||||
"LicenseLimitCounter": "Όριο άδειας χρήσης Διαχειριστές/Ισχύς:",
|
||||
"LicenseLimitDescription": "Ο μετρητής του ορίου αδειών χρήσης αποτελείται από: τους ήδη υπάρχοντες λογαριασμούς στο {{productName}} και τους νέους χρήστες που θέλετε να εισαγάγετε. Εάν εισαγάγετε χρήστες που έχουν ήδη λογαριασμό στο {{productName}}, δεν θα υπολογίζονται ξανά στον μετρητή. Η άδεια χρήσης του {{productName}} σάς επιτρέπει να έχετε το πολύ 100 χρήστες.",
|
||||
"LicenseLimitDescription": "Ο μετρητής του ορίου αδειών χρήσης αποτελείται από: τους ήδη υπάρχοντες λογαριασμούς στο {{productName}} και τους νέους χρήστες που θέλετε να εισαγάγετε. Εάν εισαγάγετε χρήστες που έχουν ήδη λογαριασμό στο {{productName}}, δεν θα υπολογίζονται ξανά στον μετρητή. Η άδεια χρήσης του {{productName}} σάς επιτρέπει να έχετε το πολύ {{maxLimit}} χρήστες.",
|
||||
"Lifetime": "Διάρκεια ζωής (λεπτά)",
|
||||
"LimitThemesTooltip": "Μπορείτε να δημιουργήσετε μόνο 3 προσαρμοσμένα θέματα. Για να δημιουργήσετε ένα νέο, πρέπει να διαγράψετε ένα από τα προηγούμενα θέματα.",
|
||||
"LocalFile": "Τοπικό αρχείο",
|
||||
|
@ -14,6 +14,7 @@
|
||||
"HotkeysCreateSpreadsheet": "Create spreadsheet",
|
||||
"HotkeysCreatingObjects": "Creating items",
|
||||
"HotkeysCutSelected": "Cut selected items to the clipboard",
|
||||
"HotkeysRenameSelected": "Rename selected item",
|
||||
"HotkeysExtendSelectionDown": "Extend selection down",
|
||||
"HotkeysExtendSelectionLeft": "Extend selection left",
|
||||
"HotkeysExtendSelectionRight": "Extend selection right",
|
||||
|
@ -10,6 +10,7 @@
|
||||
"AccountsWithoutEmails": "We found <1>{{users}} users</1> without emails. You can fill their emails or continue without this action.",
|
||||
"AccountsWithoutEmailsNextStep": "We found <1>{{users}} users</1> without emails. You can add necessary data to their accounts on the next step.",
|
||||
"AddAllowedIP": "Add allowed IP address",
|
||||
"AddEmail": "Add e-mail",
|
||||
"AddEmails": "Add emails to incomplete accounts",
|
||||
"AddEmailsDescription": "Check list of unimported users to import into {{organizationName}} {{productName}}.",
|
||||
"AddEmailsWarning": "You don't have users with emails. Please proceed to the next step to add them.",
|
||||
@ -65,6 +66,7 @@
|
||||
"ButtonsColor": "Buttons",
|
||||
"ByApp": "By authenticator app",
|
||||
"BySms": "By sms",
|
||||
"CancelImport": "Cancel import",
|
||||
"ChangeLogoButton": "Change Logo",
|
||||
"Characters": "{{length}} characters",
|
||||
"CheckPeriod": "Check period (sec)",
|
||||
@ -129,6 +131,7 @@
|
||||
"ErrorsWereFound": "{{errors}} errors were found",
|
||||
"ForcePathStyle": "Force Path Style",
|
||||
"GroupsDescription": "Users which you selected in the previous step will be imported into groups created in {{serviceName}}. Groups will appear in the Accounts section.",
|
||||
"GoogleDriveFiles": "Google Drive's Files",
|
||||
"HexCode": "Hex code",
|
||||
"Import": "Import",
|
||||
"ImportCompleteDescriptionGoogle": "Data import from Google Workspace to {{organizationName}} {{productName}} is complete!",
|
||||
@ -151,7 +154,7 @@
|
||||
"LastUpdate": "Last update: {{date}}",
|
||||
"LDAP": "LDAP Settings",
|
||||
"LicenseLimitCounter": "License limit Admins/Power users:",
|
||||
"LicenseLimitDescription": "The license limit counter consists of: already existing accounts in {{productName}} and new users that you want to import. If you import users who already have a {{productName}} account, they will not be counted again in the counter. Your {{productName}} license allows you to have a maximum of 100 users.",
|
||||
"LicenseLimitDescription": "The license limit counter consists of: already existing accounts in {{productName}} and new users that you want to import. If you import users who already have a {{productName}} account, they will not be counted again in the counter. Your {{productName}} license allows you to have a maximum of {{maxLimit}} users.",
|
||||
"Lifetime": "Lifetime (min)",
|
||||
"LimitThemesTooltip": "You can only create 3 custom themes. To create a new one, you must delete one of the previous themes.",
|
||||
"LocalFile": "Local file",
|
||||
@ -299,6 +302,7 @@
|
||||
"UserAgreement": "I confirm and want to proceed",
|
||||
"UserLimitExceeded": "User limit exceeded. To proceed to the next step, please adjust the number of users or increase the {{productName}} user limit.",
|
||||
"UsersAreRegistered": "You selected users registered in your {{productName}}, with the roles already set. Please proceed to the next step or go back to select more users.",
|
||||
"UsersFiles": "User’s Files",
|
||||
"UsersSectionDescription": "Section \"Users\" includes the users you selected in the previous step. By default, it is always enabled and can't be unselected.",
|
||||
"UseSpecialChar": "Use special characters",
|
||||
"UseUpperCase": "Use capital letters",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "La configuración de idioma y zona horaria permite cambiar el idioma de todo el portal para todos los usuarios del mismo y configurar la zona horaria para que todos los eventos del portal se muestren con la fecha y hora correctas.",
|
||||
"LastUpdate": "Última actualización: {{date}}",
|
||||
"LicenseLimitCounter": "Límite de licencia para Administradores/Usuarios avanzados:",
|
||||
"LicenseLimitDescription": "El contador de límite de licencias consta de: cuentas ya existentes en {{productName}} y nuevos usuarios que desea importar. Si importa usuarios que ya tienen una cuenta de {{productName}}, no volverán a contarse en el contador. Su licencia de {{productName}} le permite tener un máximo de 100 usuarios.",
|
||||
"LicenseLimitDescription": "El contador de límite de licencias consta de: cuentas ya existentes en {{productName}} y nuevos usuarios que desea importar. Si importa usuarios que ya tienen una cuenta de {{productName}}, no volverán a contarse en el contador. Su licencia de {{productName}} le permite tener un máximo de {{maxLimit}} usuarios.",
|
||||
"Lifetime": "Duración (min)",
|
||||
"LimitThemesTooltip": "Solo puede crear 3 temas personalizados. Para crear uno nuevo, debe eliminar uno de los temas anteriores.",
|
||||
"LocalFile": "Archivo local",
|
||||
|
@ -143,7 +143,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Kieli- ja aikavyöhyke-asetukset on tapa muuttaa koko portaalin kieli kaikille käyttäjille ja määrittää aikavyöhyke, jotta kaikki portaalin tapahtumat näytetään oikeana päivänä ja aikana.",
|
||||
"LastUpdate": "Viimeinen päivitys: {{date}}",
|
||||
"LicenseLimitCounter": "Lisenssiraja Järjestelmänvalvojat/Virta:",
|
||||
"LicenseLimitDescription": "Lisenssirajalaskuri koostuu jo olemassa olevista {{productName}}-n tileistä ja uusistä käyttäjistä, jotka haluat tuoda. Jos tuot käyttäjiä, joilla on jo {{productName}}-tili, heitä ei lasketa uudelleen laskuriin. {{productName}}-lisenssilläsi voi olla enintaan 100 käyttäjää.",
|
||||
"LicenseLimitDescription": "Lisenssirajalaskuri koostuu jo olemassa olevista {{productName}}-n tileistä ja uusistä käyttäjistä, jotka haluat tuoda. Jos tuot käyttäjiä, joilla on jo {{productName}}-tili, heitä ei lasketa uudelleen laskuriin. {{productName}}-lisenssilläsi voi olla enintaan {{maxLimit}} käyttäjää.",
|
||||
"Lifetime": "Elinikä (min)",
|
||||
"LimitThemesTooltip": "Voit luoda vain 3 räätälöityä teemaa. Luodaksesi uuden, sinun täytyy poistaa yksi edellisistä teemoista.",
|
||||
"LocalFile": "Paikallinen tiedosto",
|
||||
|
@ -145,7 +145,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Les paramètres de langue et de fuseau horaire permettent de modifier la langue de l'ensemble du portail pour tous les utilisateurs du portail et de configurer le fuseau horaire de manière à ce que tous les événements du portail soient affichés avec la date et l'heure correctes.",
|
||||
"LastUpdate": "Dernière mise à jour: {{date}}",
|
||||
"LicenseLimitCounter": "Limite autorisée des Administrateurs/Utilisateurs avancés",
|
||||
"LicenseLimitDescription": "Le compteur de la limite autorisée inclut: les comptes existants dans {{productName}} et de nouveaux utilisateurs que vous souhaitez importer. Lors de l'importation, le compteur ne prend pas en compte les utilisateurs qui ont déjà un compte {{productName}}. Votre licence {{productName}} vous permet d'avoir 100 utilisateurs au maximum.",
|
||||
"LicenseLimitDescription": "Le compteur de la limite autorisée inclut: les comptes existants dans {{productName}} et de nouveaux utilisateurs que vous souhaitez importer. Lors de l'importation, le compteur ne prend pas en compte les utilisateurs qui ont déjà un compte {{productName}}. Votre licence {{productName}} vous permet d'avoir {{maxLimit}} utilisateurs au maximum.",
|
||||
"Lifetime": "Durée de vie (min)",
|
||||
"LimitThemesTooltip": "Vous ne pouvez créer que 3 thèmes personnalisés. Pour en créer un nouveau, vous devez supprimer l’un des thèmes précédents.",
|
||||
"LocalFile": "Fichier local",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Լեզվի և ժամային գոտու կարգավորումները պորտալի բոլոր օգտագործողների համար ամբողջ պորտալի լեզուն փոխելու և ժամային գոտին այնպես կարգավորելու միջոց է, որպեսզի պորտալի բոլոր իրադարձությունները ցուցադրվեն ճիշտ ամսաթվով և ժամով:",
|
||||
"LastUpdate": "Վերջին թարմացում: {{date}}",
|
||||
"LicenseLimitCounter": "Լիցենզիայի սահմանաչափ ադմիններ/հզորություն.",
|
||||
"LicenseLimitDescription": "Լիցենզիայի սահմանաչափը բաղկացած է {{productName}}-ում արդեն գոյություն ունեցող հաշիվներից և նոր օգտվողներից, որոնք ցանկանում եք ներմուծել: Եթե ներմուծում եք օգտատերեր, ովքեր արդեն ունեն {{productName}} հաշիվ, նրանք կրկին չեն հաշվվելու հաշվիչում: Ձեր {{productName}} լիցենզիան թույլ է տալիս ունենալ առավելագույնը 100 օգտվող:",
|
||||
"LicenseLimitDescription": "Լիցենզիայի սահմանաչափը բաղկացած է {{productName}}-ում արդեն գոյություն ունեցող հաշիվներից և նոր օգտվողներից, որոնք ցանկանում եք ներմուծել: Եթե ներմուծում եք օգտատերեր, ովքեր արդեն ունեն {{productName}} հաշիվ, նրանք կրկին չեն հաշվվելու հաշվիչում: Ձեր {{productName}} լիցենզիան թույլ է տալիս ունենալ առավելագույնը {{maxLimit}} օգտվող:",
|
||||
"Lifetime": "Աշխատաժամ (րոպե)",
|
||||
"LimitThemesTooltip": "Դուք կարող եք ստեղծել միայն 3 հատուկ թեմա: Նորը ստեղծելու համար դուք պետք է ջնջեք նախորդ թեմաներից մեկը:",
|
||||
"LocalFile": "Տեղական ֆայլ",
|
||||
|
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Le impostazioni di lingua e fuso orario rappresentano un modo per cambiare la lingua dell'intero portale per tutti gli utenti del portale e per configurare il fuso orario in modo che tutti gli eventi del portale vengano visualizzati con la data e l'ora corrette.",
|
||||
"LastUpdate": "Ultimo aggiornamento: {{date}}",
|
||||
"LicenseLimitCounter": "Limite di licenza Amministratori/Utenti esperti:",
|
||||
"LicenseLimitDescription": "Il contatore del limite di licenza è composto da: account già esistenti in {{productName}} e nuovi utenti che si desidera importare. Se importi utenti che già dispongono di un account {{productName}}, questi non verranno contati nuovamente nel contatore. La tua licenza {{productName}} ti consente di avere un massimo di 100 utenti.",
|
||||
"LicenseLimitDescription": "Il contatore del limite di licenza è composto da: account già esistenti in {{productName}} e nuovi utenti che si desidera importare. Se importi utenti che già dispongono di un account {{productName}}, questi non verranno contati nuovamente nel contatore. La tua licenza {{productName}} ti consente di avere un massimo di {{maxLimit}} utenti.",
|
||||
"Lifetime": "Durata (min)",
|
||||
"LimitThemesTooltip": "Puoi creare solo 3 temi personalizzati. Per crearne uno nuovo, devi eliminare uno dei temi precedenti.",
|
||||
"LocalFile": "File locale",
|
||||
|
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "言語とタイムゾーンの設定は、すべてのポータルユーザーのためにポータル全体の言語を変更し、ポータルのすべてのイベントが正しい日付と時刻で表示されるようにタイムゾーンを設定する方法です。",
|
||||
"LastUpdate": "最終更新日:{{date}} ",
|
||||
"LicenseLimitCounter": "ライセンス制限 管理者/パワー:",
|
||||
"LicenseLimitDescription": "ライセンス制限カウンターは、{{productName}}に既に存在するアカウントと、インポートする新規ユーザーで構成されます。既に{{productName}}アカウントを持っているユーザーをインポートした場合、そのユーザーはカウントされません。現在の{{productName}}ライセンスでは、最大100人のユーザーを持つことができます。",
|
||||
"LicenseLimitDescription": "ライセンス制限カウンターは、{{productName}}に既に存在するアカウントと、インポートする新規ユーザーで構成されます。既に{{productName}}アカウントを持っているユーザーをインポートした場合、そのユーザーはカウントされません。現在の{{productName}}ライセンスでは、最大{{maxLimit}}人のユーザーを持つことができます。",
|
||||
"Lifetime": "有効期間(分)",
|
||||
"LimitThemesTooltip": "カスタムテーマは3つまでしか作成できません。新しく作成する場合は、以前のテーマを1つ削除する必要があります。",
|
||||
"LocalFile": "ローカルファイル",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "언어 및 시간대 설정을 통해 모든 포털 사용자에 대해 전체 포털의 언어를 변경하고 포털의 모든 이벤트가 올바른 날짜와 시간으로 표시되도록 시간대를 구성할 수 있습니다.",
|
||||
"LastUpdate": "마지막 업데이트: {{date}}",
|
||||
"LicenseLimitCounter": "라이센스 제한 관리자/권한:",
|
||||
"LicenseLimitDescription": "라이센스 제한 카운터는 {{productName}}의 기존 계정과 가져오기하려는 새 사용자로 구성됩니다. 이미 {{productName}} 계정이 있는 사용자를 가져오기하면 해당 사용자는 카운터에 다시 계산되지 않습니다.{{productName}} 라이센스를 사용하면 최대 100명의 사용자를 보유할 수 있습니다.",
|
||||
"LicenseLimitDescription": "라이센스 제한 카운터는 {{productName}}의 기존 계정과 가져오기하려는 새 사용자로 구성됩니다. 이미 {{productName}} 계정이 있는 사용자를 가져오기하면 해당 사용자는 카운터에 다시 계산되지 않습니다.{{productName}} 라이센스를 사용하면 최대 {{maxLimit}}명의 사용자를 보유할 수 있습니다.",
|
||||
"Lifetime": "수명(분)",
|
||||
"LimitThemesTooltip": "3개의 사용자 지정 테마만 생성할 수 있습니다. 새 테마를 생성하려면 이미 생성한 테마 중 하나를 삭제해야 합니다.",
|
||||
"LocalFile": "로컬 파일",
|
||||
|
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Valodas un Laika joslas iestatījumi ir veids, kā mainīt visa portāla valodu visiem portāla lietotājiem un konfigurēt laika joslu tā, lai visi portāla notikumi tiktu rādīti ar pareizo datumu un laiku.",
|
||||
"LastUpdate": "Pēdējā atjaunināšana: {{date}}",
|
||||
"LicenseLimitCounter": "Licences ierobežojums Administratori/jauda:",
|
||||
"LicenseLimitDescription": "Licenču ierobežojumu skaitītājs sastāv no: jau esošiem kontiem {{productName}} un jauniem lietotājiem, kurus vēlaties importēt. Ja importējat lietotājus, kuriem jau ir {{productName}} konts, tie vairs netiks skaitīti skaitītājā. Jūsu {{productName}} licence ļauj jums uzņemt ne vairāk kā 100 lietotājus.",
|
||||
"LicenseLimitDescription": "Licenču ierobežojumu skaitītājs sastāv no: jau esošiem kontiem {{productName}} un jauniem lietotājiem, kurus vēlaties importēt. Ja importējat lietotājus, kuriem jau ir {{productName}} konts, tie vairs netiks skaitīti skaitītājā. Jūsu {{productName}} licence ļauj jums uzņemt ne vairāk kā {{maxLimit}} lietotājus.",
|
||||
"Lifetime": "Kalpošanas laiks (min.)",
|
||||
"LimitThemesTooltip": "Varat izveidot tikai trīs pielāgotus dizainus. Lai izveidotu jaunu dizainu, ir jāizdzēš viens no iepriekšējiem dizainiem.",
|
||||
"LocalFile": "Vietējais fails",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Taal- en tijdzone-instellingen is een manier om de taal van de hele portaal te wijzigen voor alle portaalgebruikers en om de tijdzone te configureren, zodat alle gebeurtenissen van de portal met de juiste datum en tijd worden weergegeven.",
|
||||
"LastUpdate": "Laatst bijgewerkt: {{date}}",
|
||||
"LicenseLimitCounter": "Licentielimiet Beheerders/Power:",
|
||||
"LicenseLimitDescription": "De licentielimiet teller bestaat uit: reeds bestaande accounts in {{productName}} en nieuwe gebruikers die u wilt importeren. Als u gebruikers importeert die al een {{productName}}-account hebben, zullen zij niet meer worden meegeteld in de teller. Met uw {{productName}}-licentie kunt u maximaal 100 gebruikers hebben.",
|
||||
"LicenseLimitDescription": "De licentielimiet teller bestaat uit: reeds bestaande accounts in {{productName}} en nieuwe gebruikers die u wilt importeren. Als u gebruikers importeert die al een {{productName}}-account hebben, zullen zij niet meer worden meegeteld in de teller. Met uw {{productName}}-licentie kunt u maximaal {{maxLimit}} gebruikers hebben.",
|
||||
"Lifetime": "Levensduur (min)",
|
||||
"LimitThemesTooltip": "U kunt slechts 3 aangepaste thema's maken. Om een nieuwe aan te maken, moet u een van de vorige thema's verwijderen.",
|
||||
"LocalFile": "Lokaal bestand",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Ustawienia języka i strefy czasowej to sposób na zmianę języka całego portalu dla wszystkich jego użytkowników oraz konfigurację strefy czasowej w taki sposób, aby wszystkie zdarzenia w portalu były wyświetlane z prawidłową datą i godziną.",
|
||||
"LastUpdate": "Ostatnia aktualizacja: {{date}}",
|
||||
"LicenseLimitCounter": "Limit licencji Administratorzy/Użytkownicy z większą liczbą uprawnień:",
|
||||
"LicenseLimitDescription": "Licznik limitu licencji składa się z: kont istniejących już w {{productName}} oraz nowych użytkowników, których chcesz zaimportować. Jeśli zaimportujesz użytkowników, którzy mają już konto {{productName}}, nie zostaną oni ponownie uwzględnieni na liczniku. Twoja licencja {{productName}} pozwala Ci mieć maksymalnie 100 użytkowników.",
|
||||
"LicenseLimitDescription": "Licznik limitu licencji składa się z: kont istniejących już w {{productName}} oraz nowych użytkowników, których chcesz zaimportować. Jeśli zaimportujesz użytkowników, którzy mają już konto {{productName}}, nie zostaną oni ponownie uwzględnieni na liczniku. Twoja licencja {{productName}} pozwala Ci mieć maksymalnie {{maxLimit}} użytkowników.",
|
||||
"Lifetime": "Długość (min)",
|
||||
"LimitThemesTooltip": "Możesz utworzyć tylko 3 niestandardowe motywy. Aby utworzyć nowy, musisz usunąć jeden z poprzednich motywów.",
|
||||
"LocalFile": "Plik lokalny",
|
||||
|
@ -149,7 +149,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Configurações de Idioma e Fuso Horário é uma forma de alterar o idioma de todo o portal para todos os usuários do portal e de configurar o fuso horário para que todos os eventos do portal sejam mostrados com a data e hora corretas.",
|
||||
"LastUpdate": "Última atualização: {{date}}",
|
||||
"LicenseLimitCounter": "Limite de licença Administradores/Usuários avançados:",
|
||||
"LicenseLimitDescription": "O contador de limite de licenças é composto por: contas já existentes no {{productName}} e novos usuários que você deseja importar. Se você importar usuários que já possuem conta no {{productName}}, eles não serão contabilizados novamente no contador. Sua licença {{productName}} permite que você tenha no máximo 100 usuários.",
|
||||
"LicenseLimitDescription": "O contador de limite de licenças é composto por: contas já existentes no {{productName}} e novos usuários que você deseja importar. Se você importar usuários que já possuem conta no {{productName}}, eles não serão contabilizados novamente no contador. Sua licença {{productName}} permite que você tenha no máximo {{maxLimit}} usuários.",
|
||||
"Lifetime": "Vida útil (min)",
|
||||
"LimitThemesTooltip": "Você só pode criar 3 temas personalizados. Para criar um novo, você deve excluir um dos temas anteriores.",
|
||||
"LocalFile": "Arquivo local",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Configurações de Idioma e Fuso Horário é uma forma de alterar o idioma de todo o portal para todos os usuários do portal e de configurar o fuso horário para que todos os eventos do portal sejam mostrados com a data e hora corretas.",
|
||||
"LastUpdate": "Última atualização: {{date}}",
|
||||
"LicenseLimitCounter": "Limite de licença Administradores/Utilizadores com poder:",
|
||||
"LicenseLimitDescription": "O contador de limite de licenças é composto por: contas já existentes no {{productName}} e novos usuários que você deseja importar. Caso você importe usuários que já possuem conta no {{productName}}, eles não serão contabilizados novamente no contador. Sua licença {{productName}} permite que você tenha no máximo 100 usuários.",
|
||||
"LicenseLimitDescription": "O contador de limite de licenças é composto por: contas já existentes no {{productName}} e novos usuários que você deseja importar. Caso você importe usuários que já possuem conta no {{productName}}, eles não serão contabilizados novamente no contador. Sua licença {{productName}} permite que você tenha no máximo {{maxLimit}} usuários.",
|
||||
"Lifetime": "Duração (min)",
|
||||
"LimitThemesTooltip": "Só se podem criar 3 temas personalizados. Para criar um novo, é necessário eliminar um dos temas anteriores.",
|
||||
"LocalFile": "Ficheiro local",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Setări limbă și fus orar este modul de a schimba limba portalului pentru toți utilizatorii portalului și de a configura fusul orar pentru că ora și data evenimentelor de pe portal este afișată corect.",
|
||||
"LastUpdate": "Ultima actualizare: {{date}}",
|
||||
"LicenseLimitCounter": "Limita impusă de licență administratori/utilizatori avansați:",
|
||||
"LicenseLimitDescription": "Contorul limitei de licență este format din: conturile deja existente în spațiu {{productName}} și utilizatorii noi pe care doriți să le importați. Dacă importați utilizatorii care au deja un cont {{productName}}, aceștia nu vor fi înregistrate de contorul din nou. Numărul maxim de 100 utilizatori este permis conform licenței dvs {{productName}}.",
|
||||
"LicenseLimitDescription": "Contorul limitei de licență este format din: conturile deja existente în spațiu {{productName}} și utilizatorii noi pe care doriți să le importați. Dacă importați utilizatorii care au deja un cont {{productName}}, aceștia nu vor fi înregistrate de contorul din nou. Numărul maxim de {{maxLimit}} utilizatori este permis conform licenței dvs {{productName}}.",
|
||||
"Lifetime": "Durata (min)",
|
||||
"LimitThemesTooltip": "Puteţi crea doar 3 teme particularizate. Pentru a crea o temă nouă, trebuie să ştergeţi una dintre temele anterioare.",
|
||||
"LocalFile": "Fișierul local",
|
||||
|
@ -149,7 +149,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Настройки языка и часового пояса позволяют изменить язык всего портала для всех пользователей и настроить часовой пояс, чтобы все события на портале отображались с корректной датой и временем.",
|
||||
"LastUpdate": "Последнее обновление: {{date}}",
|
||||
"LicenseLimitCounter": "Ограничение лицензии Администраторы/Опытные пользователи:",
|
||||
"LicenseLimitDescription": "Счетчик лимита лицензии состоит из: уже существующих учетных записей в {{productName}} и новых пользователей, которых вы хотите импортировать. Если вы импортируете пользователей, у которых уже есть учетная запись {{productName}}, они больше не будут учитываться в счетчике. Ваша лицензия {{productName}} позволяет вам иметь максимум 100 пользователей.",
|
||||
"LicenseLimitDescription": "Счетчик лимита лицензии состоит из: уже существующих учетных записей в {{productName}} и новых пользователей, которых вы хотите импортировать. Если вы импортируете пользователей, у которых уже есть учетная запись {{productName}}, они больше не будут учитываться в счетчике. Ваша лицензия {{productName}} позволяет вам иметь максимум {{maxLimit}} пользователей.",
|
||||
"Lifetime": "Срок службы (мин)",
|
||||
"LimitThemesTooltip": "Вы можете создать только 3 пользовательские темы. Чтобы создать новую, необходимо удалить одну из предыдущих тем.",
|
||||
"LocalFile": "Локальный файл",
|
||||
|
@ -136,7 +136,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "භාෂාව සහ වේලා කලාපයේ සැකසුම් මගින් අවකාශයේ භාෂාව සහ වේලා කලාපය සැකසීමට හැකිය. එමඟින්, සියළුම ක්රියාකාරකම්වල දිනය සහ වේලාව නිවැරදිව පෙන්වනු ඇත. පරිශ්රීලකයින්ට තමන්ගේ පැතිකඩ හරහා ද රිසි පරිදි මෙම සැකසුම් වෙනස් කිරීමට හැකිය.",
|
||||
"LastUpdate": "අවසාන යාවත්කාලය: {{date}}",
|
||||
"LicenseLimitCounter": "බලපත්රයේ සීමාව පරිපාලකයින්/බලවත්:",
|
||||
"LicenseLimitDescription": "බලපත්රයේ සීමාව ගණනය වන්නේ {{productName}} හි දැනටමත් පවතින ගිණුම් සහ ඔබට ආයාත කිරීමට වුවමනා නව පරිශ්රීලකයින් අනුව වේ. ඔබ දැනටමත් {{productName}} ගිණුම් තිබෙන පරිශ්රීලකයින් ආයාත කළහොත් ඔවුන් නැවත ගණනය නොවේ. ඔබගේ {{productName}} බලපත්රය සඳහා උපරිම පරිශ්රීලකයින් 100 ක් ලද හැකිය.",
|
||||
"LicenseLimitDescription": "බලපත්රයේ සීමාව ගණනය වන්නේ {{productName}} හි දැනටමත් පවතින ගිණුම් සහ ඔබට ආයාත කිරීමට වුවමනා නව පරිශ්රීලකයින් අනුව වේ. ඔබ දැනටමත් {{productName}} ගිණුම් තිබෙන පරිශ්රීලකයින් ආයාත කළහොත් ඔවුන් නැවත ගණනය නොවේ. ඔබගේ {{productName}} බලපත්රය සඳහා උපරිම පරිශ්රීලකයින් {{maxLimit}} ක් ලද හැකිය.",
|
||||
"Lifetime": "ආයුකාලය (විනාඩි)",
|
||||
"LimitThemesTooltip": "ඔබට අභිරුචි තේමා 𑇣 (3) ක් සෑදීමට හැකිය. නව එකක් සෑදීමට, කලින් සෑදූ තේමාවක් මකා දැමිය යුතුය.",
|
||||
"LocalFile": "ස්ථානීය ගොනුව",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Nastavenie jazyka a časového pásma slúži na zmenu jazyka celého portálu pre všetkých používateľov portálu a na konfiguráciu časového pásma, aby sa všetky udalosti portálu zobrazovali so správnym dátumom a časom.",
|
||||
"LastUpdate": "Posledná aktualizácia: {{date}}",
|
||||
"LicenseLimitCounter": "Obmedzenie licencie Administrátori/Pokročilí používatelia",
|
||||
"LicenseLimitDescription": "Obmedzenie licencie sa počíta takto: už existujúce účty v {{productName}} a nové používatelia, ktorých chcete importovať. Ak importujete používateľov, ktorí už majú účet v {{productName}}, nebudú sa započítavať. Vaša licencia {{productName}} vám umožňuje mať maximálne 100 používateľov.",
|
||||
"LicenseLimitDescription": "Obmedzenie licencie sa počíta takto: už existujúce účty v {{productName}} a nové používatelia, ktorých chcete importovať. Ak importujete používateľov, ktorí už majú účet v {{productName}}, nebudú sa započítavať. Vaša licencia {{productName}} vám umožňuje mať maximálne {{maxLimit}} používateľov.",
|
||||
"Lifetime": "Životnosť (min)",
|
||||
"LimitThemesTooltip": "Môžete vytvoriť iba 3 vlastné témy. Ak chcete vytvoriť novú, musíte vymazať jednu z predchádzajúcich tém.",
|
||||
"LocalFile": "Miestny súbor",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Nastavitve jezika in časovnega pasu so način, kako spremeniti jezik celotnega portala za vse uporabnike portala in konfigurirati časovni pas, tako da bodo vsi dogodki na portalu prikazani s pravilnim datumom in uro.",
|
||||
"LastUpdate": "Zadnja posodobitev: {{date}}",
|
||||
"LicenseLimitCounter": "Omejitev licence Skrbniki/Napredni uporabniki:",
|
||||
"LicenseLimitDescription": "Števec omejitev licenc sestavljajo: že obstoječi računi v {{productName}} in novi uporabniki, ki jih želite uvoziti. Če uvozite uporabnike, ki že imajo račun {{productName}}, ne bodo ponovno šteti v števcu. Vaša licenca {{productName}} vam omogoča, da imate največ 100 uporabnikov.",
|
||||
"LicenseLimitDescription": "Števec omejitev licenc sestavljajo: že obstoječi računi v {{productName}} in novi uporabniki, ki jih želite uvoziti. Če uvozite uporabnike, ki že imajo račun {{productName}}, ne bodo ponovno šteti v števcu. Vaša licenca {{productName}} vam omogoča, da imate največ {{maxLimit}} uporabnikov.",
|
||||
"Lifetime": "Življenjska doba (min.)",
|
||||
"LimitThemesTooltip": "Ustvarite lahko samo 3 teme po meri. Če želite ustvariti novo, morate izbrisati eno od prejšnjih tem.",
|
||||
"LocalFile": "Lokalna datoteka",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Поставке jезика и временске зоне омогућавају промену језика целог портала за све кориснике портала и конфигурацију временске зоне тако да ће сви догађаји на порталу бити приказани са тачним датумом и временом.",
|
||||
"LastUpdate": "Последње ажурирање: {{date}}",
|
||||
"LicenseLimitCounter": "Ограничење лиценце за Админе/Напредни корисници",
|
||||
"LicenseLimitDescription": "Бројач ограничења лиценце састоји се од: већ постојећих налога у {{productName}}-у и нових корисника које желите да увезете. Ако увезете кориснике који већ имају налог у {{productName}}-у, неће бити поново бројани у бројачу. Ваша {{productName}} лиценца вам дозвољава да имате максимално 100 корисника.",
|
||||
"LicenseLimitDescription": "Бројач ограничења лиценце састоји се од: већ постојећих налога у {{productName}}-у и нових корисника које желите да увезете. Ако увезете кориснике који већ имају налог у {{productName}}-у, неће бити поново бројани у бројачу. Ваша {{productName}} лиценца вам дозвољава да имате максимално {{maxLimit}} корисника.",
|
||||
"Lifetime": "Време трајања (мин)",
|
||||
"LimitThemesTooltip": "Можете само креирати 3 прилагођене теме. Да бисте креитрали нову, морате обрисати једну од претходних.",
|
||||
"LocalFile": "Локална датотека",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Postavke jezika i vremenske zone omogućavaju promenu jezika celog portala za sve korisnike portala i konfiguraciju vremenske zone tako da će svi događaji na portalu biti prikazani sa tačnim datumom i vremenom.",
|
||||
"LastUpdate": "Poslednje ažuriranje: {{date}}",
|
||||
"LicenseLimitCounter": "Ograničenje licence za Admine/Napredni korisnici",
|
||||
"LicenseLimitDescription": "Brojač ograničenja licence sastoji se od: već postojećih naloga u {{productName}}-u i novih korisnika koje želite da uvezete. Ako uvezete korisnike koji već imaju nalog u {{productName}}-u, neće biti ponovo brojani u brojaču. Vaša {{productName}} licenca vam dozvoljava da imate maksimalno 100 korisnika.",
|
||||
"LicenseLimitDescription": "Brojač ograničenja licence sastoji se od: već postojećih naloga u {{productName}}-u i novih korisnika koje želite da uvezete. Ako uvezete korisnike koji već imaju nalog u {{productName}}-u, neće biti ponovo brojani u brojaču. Vaša {{productName}} licenca vam dozvoljava da imate maksimalno {{maxLimit}} korisnika.",
|
||||
"Lifetime": "Vreme trajanja (min)",
|
||||
"LimitThemesTooltip": "Možete samo kreirati 3 prilagođene teme. Da biste kreitrali novu, morate obrisati jednu od prethodnih.",
|
||||
"LocalFile": "Lokalna datoteka",
|
||||
|
@ -147,7 +147,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Dil ve Saat Dilimi Ayarları, tüm portal kullanıcıları için tüm portalın dilini değiştirmenin ve portaldaki tüm olayların doğru tarih ve saatle gösterilmesini sağlayacak şekilde saat dilimini yapılandırmanın bir yoludur.",
|
||||
"LastUpdate": "Son güncelleme: {{date}}",
|
||||
"LicenseLimitCounter": "Lisans sınırı Yöneticiler/Uzman:",
|
||||
"LicenseLimitDescription": "Lisans limiti sayacı, {{productName}}’de zaten var olan hesaplar ve içe aktarmak istediğiniz yeni kullanıcılardan oluşur. Zaten bir {{productName}} hesabı olan kullanıcıları içe aktarırsanız, bunlar sayaçta tekrar sayılmayacaktır. {{productName}} lisansınız en fazla 100 kullanıcıya sahip olmanıza izin verir.",
|
||||
"LicenseLimitDescription": "Lisans limiti sayacı, {{productName}}’de zaten var olan hesaplar ve içe aktarmak istediğiniz yeni kullanıcılardan oluşur. Zaten bir {{productName}} hesabı olan kullanıcıları içe aktarırsanız, bunlar sayaçta tekrar sayılmayacaktır. {{productName}} lisansınız en fazla {{maxLimit}} kullanıcıya sahip olmanıza izin verir.",
|
||||
"Lifetime": "Kullanım ömrü (dk)",
|
||||
"LimitThemesTooltip": "Yalnızca 3 özel tema oluşturabilirsiniz. Yeni bir tane oluşturmak için önceki temalardan birini silmeniz gerekir.",
|
||||
"LocalFile": "Yerel dosya",
|
||||
|
@ -148,7 +148,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Параметри мови та часового поясу — це спосіб змінити мову усього порталу для всіх користувачів порталу, а також налаштувати часові пояси, щоб усі події порталу показувалися із правильною датою та часом.",
|
||||
"LastUpdate": "Останнє оновлення: {{date}}",
|
||||
"LicenseLimitCounter": "Ліцензійне обмеження адміністраторів/досвідчених користувачів:",
|
||||
"LicenseLimitDescription": "Лічильник ліцензійних обмежень враховує облікові записи, що вже існують у просторі {{productName}}, та нових користувачів, яких ви хочете імпортувати. Якщо ви імпортуєте користувачів, які вже мають обліковий запис {{productName}}, лічильник не буде враховувати їх повторно. Ваша ліцензія {{productName}} дозволяє мати максимум 100 користувачів.",
|
||||
"LicenseLimitDescription": "Лічильник ліцензійних обмежень враховує облікові записи, що вже існують у просторі {{productName}}, та нових користувачів, яких ви хочете імпортувати. Якщо ви імпортуєте користувачів, які вже мають обліковий запис {{productName}}, лічильник не буде враховувати їх повторно. Ваша ліцензія {{productName}} дозволяє мати максимум {{maxLimit}} користувачів.",
|
||||
"Lifetime": "Час життя (хв.)",
|
||||
"LimitThemesTooltip": "Ви можете створити лише 3 власні теми. Щоб створити нову тему, необхідно видалити одну з попередніх тем.",
|
||||
"LocalFile": "Локальний файл",
|
||||
|
@ -146,7 +146,7 @@
|
||||
"LanguageAndTimeZoneSettingsNavDescription": "Cài đặt ngôn ngữ và múi giờ là một cách để thay đổi ngôn ngữ của toàn bộ cổng thông tin cho tất cả người dùng cổng thông tin và định cấu hình múi giờ để tất cả các sự kiện của cổng thông tin sẽ được hiển thị với ngày và giờ chính xác.",
|
||||
"LastUpdate": "Ngày cập nhật cuối cùng: {{date}}",
|
||||
"LicenseLimitCounter": "Giới hạn bản quyền Quản trị viên/Người dùng cấp cao:",
|
||||
"LicenseLimitDescription": "Số lượng giới hạn bản quyền bao gồm: tài khoản đã tồn tại trong {{productName}} và người dùng mới mà bạn muốn nhập. Nếu bạn nhập người dùng đã có tài khoản {{productName}}, họ sẽ không được tính lại trong số lượng. Bản quyền {{productName}} cho phép bạn có tối đa 100 người dùng.",
|
||||
"LicenseLimitDescription": "Số lượng giới hạn bản quyền bao gồm: tài khoản đã tồn tại trong {{productName}} và người dùng mới mà bạn muốn nhập. Nếu bạn nhập người dùng đã có tài khoản {{productName}}, họ sẽ không được tính lại trong số lượng. Bản quyền {{productName}} cho phép bạn có tối đa {{maxLimit}} người dùng.",
|
||||
"Lifetime": "Thời gian tồn tại (phút)",
|
||||
"LimitThemesTooltip": "Bạn chỉ có thể tạo 3 theme tùy chỉnh. Để tạo một theme mới, bạn phải xóa một trong các theme trước đó.",
|
||||
"LocalFile": "Tập tin cục bộ",
|
||||
|
@ -144,6 +144,18 @@ const withHotkeys = (Component) => {
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const onRename = () => {
|
||||
if (selection.length === 1) {
|
||||
const item = selection[0];
|
||||
|
||||
if (!item.contextOptions.includes("rename")) return;
|
||||
|
||||
const event = new Event(Events.RENAME);
|
||||
event.item = item;
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
const onCreateRoom = () => {
|
||||
if (!isVisitor && isRoomsFolder && security?.Create) {
|
||||
if (isGracePeriod) {
|
||||
@ -385,6 +397,8 @@ const withHotkeys = (Component) => {
|
||||
hotkeysFilter,
|
||||
);
|
||||
|
||||
useHotkeys("f2", onRename, hotkeysFilter);
|
||||
|
||||
//Upload file
|
||||
useHotkeys(
|
||||
"Shift+u",
|
||||
|
@ -138,14 +138,13 @@ export default inject(
|
||||
usersStore;
|
||||
const { setCustomRoomQuota, needResetFilesSelection } = filesStore;
|
||||
|
||||
const { defaultUsersQuota, defaultRoomsQuota } = currentQuotaStore;
|
||||
const {
|
||||
selection: infoPanelSelection,
|
||||
infoPanelSelection,
|
||||
setNewInfoPanelSelection,
|
||||
setInfoPanelSelection,
|
||||
} = infoPanelStore;
|
||||
|
||||
const inRoom = infoPanelSelection?.inRoom;
|
||||
const inRoom = !!infoPanelSelection?.navigationPath;
|
||||
const needResetSelection =
|
||||
type === "user" ? needResetUserSelection : needResetFilesSelection;
|
||||
|
||||
|
@ -108,9 +108,6 @@ const RenameEvent = ({
|
||||
: renameFolder(item.id, value)
|
||||
.then(() => completeAction(item, type))
|
||||
.then(() => {
|
||||
if (selectedFolderId === item.id) {
|
||||
setSelectedFolder({ title: value });
|
||||
}
|
||||
toastr.success(
|
||||
t("FolderRenamed", {
|
||||
folderTitle: item.title,
|
||||
|
@ -59,7 +59,7 @@ const Main = (props) => {
|
||||
window.addEventListener("resize", onResize);
|
||||
|
||||
return () => {
|
||||
window.addEventListener("resize", onResize);
|
||||
window.removeEventListener("resize", onResize);
|
||||
|
||||
clearTimeout(updateSizeRef.current);
|
||||
};
|
||||
|
@ -63,6 +63,7 @@ const StyledNav = styled.nav`
|
||||
.icon-profile-menu {
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
z-index: 300;
|
||||
}
|
||||
`;
|
||||
const HeaderNav = ({
|
||||
|
@ -87,6 +87,7 @@ const SpaceQuota = (props) => {
|
||||
|
||||
needResetSelection,
|
||||
setSelected,
|
||||
inRoom,
|
||||
} = props;
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -123,7 +124,7 @@ const SpaceQuota = (props) => {
|
||||
|
||||
if (action === "no-quota") {
|
||||
try {
|
||||
const items = await updateQuota(-1, [item.id]);
|
||||
const items = await updateQuota(-1, [item.id], inRoom);
|
||||
|
||||
options.map((item) => {
|
||||
if (item.key === "no-quota") item.label = t("Common:Unlimited");
|
||||
@ -140,7 +141,7 @@ const SpaceQuota = (props) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await resetQuota([item.id]);
|
||||
const items = await resetQuota([item.id], inRoom);
|
||||
|
||||
options.map((item) => {
|
||||
if (item.key === "default-quota") item.label = defaultQuotaSize;
|
||||
@ -204,7 +205,13 @@ const SpaceQuota = (props) => {
|
||||
|
||||
export default inject(
|
||||
(
|
||||
{ peopleStore, filesActionsStore, filesStore, currentQuotaStore },
|
||||
{
|
||||
peopleStore,
|
||||
filesActionsStore,
|
||||
filesStore,
|
||||
currentQuotaStore,
|
||||
infoPanelStore,
|
||||
},
|
||||
{ type },
|
||||
) => {
|
||||
const { changeUserQuota, usersStore, selectionStore } = peopleStore;
|
||||
@ -225,6 +232,9 @@ export default inject(
|
||||
defaultRoomsQuota,
|
||||
} = currentQuotaStore;
|
||||
|
||||
const { infoPanelSelection } = infoPanelStore;
|
||||
const inRoom = !!infoPanelSelection?.navigationPath;
|
||||
|
||||
const { setSelected: setUsersSelected } = selectionStore;
|
||||
|
||||
const changeQuota = type === "user" ? changeUserQuota : changeRoomQuota;
|
||||
@ -251,6 +261,7 @@ export default inject(
|
||||
resetQuota,
|
||||
defaultSize,
|
||||
needResetSelection,
|
||||
inRoom,
|
||||
};
|
||||
},
|
||||
)(observer(SpaceQuota));
|
||||
|
@ -47,7 +47,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -52,6 +52,7 @@ import {
|
||||
StyledSelectedOwner,
|
||||
} from "./StyledDialog";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
import { EmployeeActivationStatus } from "@docspace/shared/enums";
|
||||
|
||||
const ChangePortalOwnerDialog = ({
|
||||
t,
|
||||
@ -91,7 +92,7 @@ const ChangePortalOwnerDialog = ({
|
||||
onClose && onClose();
|
||||
toastr.success(
|
||||
t("Settings:ConfirmEmailSended", {
|
||||
ownerName: selectedUser.label,
|
||||
ownerName: displayName,
|
||||
}),
|
||||
);
|
||||
})
|
||||
@ -122,6 +123,14 @@ const ChangePortalOwnerDialog = ({
|
||||
t("DeactivateOrDeletePortal", { productName: PRODUCT_NAME }),
|
||||
];
|
||||
|
||||
const filter = React.useMemo(() => {
|
||||
const newFilter = new Filter();
|
||||
|
||||
newFilter.employeeStatus = EmployeeActivationStatus.Activated;
|
||||
|
||||
return newFilter;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
displayType={"aside"}
|
||||
@ -149,6 +158,7 @@ const ChangePortalOwnerDialog = ({
|
||||
}}
|
||||
currentUserId={id}
|
||||
disableDisabledUsers
|
||||
filter={filter}
|
||||
/>
|
||||
</ModalDialog.Container>
|
||||
)}
|
||||
|
@ -0,0 +1,119 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ModalDialog,
|
||||
ModalDialogType,
|
||||
} from "@docspace/shared/components/modal-dialog";
|
||||
import { Button, ButtonSize } from "@docspace/shared/components/button";
|
||||
import { EmailInput } from "@docspace/shared/components/email-input";
|
||||
import { InputType } from "@docspace/shared/components/text-input";
|
||||
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import ModalDialogContainer from "../ModalDialogContainer";
|
||||
|
||||
const DialogBodyWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
`;
|
||||
|
||||
interface EmailChangeDialogProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
tempEmail: string;
|
||||
handleEmailChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onValidateEmail: (res: TValidate) => {
|
||||
isValid: boolean;
|
||||
errors: string[];
|
||||
};
|
||||
hasError: boolean;
|
||||
checkEmailValidity: () => void;
|
||||
handleSave: () => void;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
const EmailChangeDialog = ({
|
||||
visible,
|
||||
onClose,
|
||||
tempEmail,
|
||||
handleEmailChange,
|
||||
onValidateEmail,
|
||||
hasError,
|
||||
checkEmailValidity,
|
||||
handleSave,
|
||||
displayName,
|
||||
}: EmailChangeDialogProps) => {
|
||||
const { t } = useTranslation(["Settings", "SMTPSettings", "Common"]);
|
||||
|
||||
return (
|
||||
<ModalDialogContainer
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
displayType={ModalDialogType.modal}
|
||||
>
|
||||
<ModalDialog.Header>{t("AddEmail")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<DialogBodyWrapper>
|
||||
<Text>{displayName}</Text>
|
||||
<EmailInput
|
||||
placeholder={t("SMTPSettings:EnterEmail")}
|
||||
className="import-email-input"
|
||||
value={tempEmail}
|
||||
onChange={handleEmailChange}
|
||||
type={InputType.email}
|
||||
onValidateInput={onValidateEmail}
|
||||
hasError={hasError}
|
||||
onBlur={checkEmailValidity}
|
||||
isAutoFocussed
|
||||
scale
|
||||
/>
|
||||
</DialogBodyWrapper>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
label={t("Common:SaveButton")}
|
||||
size={ButtonSize.normal}
|
||||
scale
|
||||
primary
|
||||
onClick={handleSave}
|
||||
isDisabled={hasError}
|
||||
/>
|
||||
<Button
|
||||
label={t("Common:CancelButton")}
|
||||
size={ButtonSize.normal}
|
||||
scale
|
||||
onClick={onClose}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailChangeDialog;
|
@ -523,6 +523,7 @@ const AddUsersPanel = ({
|
||||
// Todo: Update groups empty screen texts when they are ready
|
||||
headerLabel: t("Common:ListAccounts"),
|
||||
withoutBackButton: false,
|
||||
withoutBorder: true,
|
||||
onBackClick,
|
||||
}}
|
||||
onSelect={onSelect}
|
||||
|
@ -31,9 +31,14 @@ const StyledModalDialog = styled(ModalDialog)`
|
||||
.modal-header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledBody = styled.div`
|
||||
padding: 0 16px;
|
||||
.embedding-panel_header-link {
|
||||
margin: 10px 0 2px;
|
||||
}
|
||||
@ -132,10 +137,10 @@ const StyledBody = styled.div`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
left: 16px;
|
||||
left: 32px;
|
||||
`
|
||||
: css`
|
||||
right: 16px;
|
||||
right: 32px;
|
||||
`}
|
||||
}
|
||||
|
||||
|
@ -462,6 +462,7 @@ const EmbeddingPanelComponent = (props: EmbeddingPanelProps) => {
|
||||
selectedOption={selectedLink as TOption}
|
||||
displaySelectedOption
|
||||
directionY="bottom"
|
||||
withLabel={false}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -73,6 +73,12 @@ const ActionsBlock = ({ t, textStyles, keyTextStyles, CtrlKey }) => {
|
||||
<Text {...keyTextStyles}> {CtrlKey} + x</Text>
|
||||
</>
|
||||
</Row>
|
||||
<Row className="hotkeys_row">
|
||||
<>
|
||||
<Text {...textStyles}>{t("HotkeysRenameSelected")}</Text>
|
||||
<Text {...keyTextStyles}> F2</Text>
|
||||
</>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -325,6 +325,28 @@ const StyledInviteInputContainer = styled.div`
|
||||
|
||||
.header_aside-panel {
|
||||
max-width: 100% !important;
|
||||
|
||||
.selector_body_tabs {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
margin-bottom: 16px;
|
||||
padding: 0;
|
||||
width: -webkit-fill-available;
|
||||
|
||||
.sticky-indent {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
.scroll-body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.scroll-body > div {
|
||||
justify-content: left;
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -109,7 +109,6 @@ const ConfirmRoute = ({
|
||||
|
||||
switch (validationResult) {
|
||||
case ValidationResult.Ok:
|
||||
case ValidationResult.UserExisted:
|
||||
const confirmHeader = search.slice(1);
|
||||
const linkData = {
|
||||
...confirmLinkData,
|
||||
@ -129,6 +128,19 @@ const ConfirmRoute = ({
|
||||
|
||||
setState((val) => ({ ...val, isLoaded: true, linkData, roomData }));
|
||||
break;
|
||||
case ValidationResult.UserExisted:
|
||||
const finalUrl = res?.roomId
|
||||
? `/rooms/shared/${res?.roomId}/filter?folder=${res?.roomId}`
|
||||
: defaultPage;
|
||||
|
||||
console.log("user already exists", {
|
||||
confirmLinkData,
|
||||
validationResult,
|
||||
finalUrl,
|
||||
});
|
||||
|
||||
window.location.replace(finalUrl);
|
||||
break;
|
||||
case ValidationResult.Invalid:
|
||||
console.error("invalid link", {
|
||||
confirmLinkData,
|
||||
|
@ -134,10 +134,11 @@ export const getCategoryType = (location) => {
|
||||
if (pathname.indexOf("personal") > -1) {
|
||||
categoryType = CategoryType.Personal;
|
||||
} else if (pathname.indexOf("shared") > -1) {
|
||||
categoryType =
|
||||
pathname.indexOf("shared/filter") > -1
|
||||
? CategoryType.Shared
|
||||
: CategoryType.SharedRoom;
|
||||
const regexp = /(rooms)\/([\d])\/(shared)/;
|
||||
|
||||
categoryType = !regexp.test(location)
|
||||
? CategoryType.Shared
|
||||
: CategoryType.SharedRoom;
|
||||
} else if (pathname.indexOf("share") > -1) {
|
||||
categoryType = CategoryType.PublicRoom;
|
||||
} else if (pathname.indexOf("archive") > -1) {
|
||||
|
@ -46,6 +46,7 @@ import { FormWrapper } from "@docspace/shared/components/form-wrapper";
|
||||
import PortalLogo from "@docspace/shared/components/portal-logo/PortalLogo";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
import ConfirmRoute from "SRC_DIR/helpers/confirmRoute";
|
||||
import { AuthenticatedAction } from "SRC_DIR/helpers/enums";
|
||||
|
||||
const ContinuePortal = (props) => {
|
||||
const { t, greetingTitle, linkData } = props;
|
||||
@ -127,7 +128,7 @@ const ComponentWrapper = inject(({ settingsStore }) => ({
|
||||
|
||||
export const Component = () => {
|
||||
return (
|
||||
<ConfirmRoute>
|
||||
<ConfirmRoute doAuthenticated={AuthenticatedAction.Logout}>
|
||||
<ComponentWrapper />
|
||||
</ConfirmRoute>
|
||||
);
|
||||
|
@ -214,7 +214,7 @@ const CreateUserForm = (props) => {
|
||||
});
|
||||
|
||||
const finalUrl = roomId
|
||||
? `/rooms/shared/filter?folder=${roomId}`
|
||||
? `/rooms/shared/${roomId}/filter?folder=${roomId}`
|
||||
: defaultPage;
|
||||
|
||||
if (roomId) {
|
||||
@ -326,7 +326,7 @@ const CreateUserForm = (props) => {
|
||||
signupOAuth(signupAccount)
|
||||
.then(() => {
|
||||
const url = roomData.roomId
|
||||
? `/rooms/shared/filter?folder=${roomData.roomId}/`
|
||||
? `/rooms/shared/${roomData.roomId}/filter?folder=${roomData.roomId}/`
|
||||
: defaultPage;
|
||||
window.location.replace(url);
|
||||
})
|
||||
@ -354,7 +354,7 @@ const CreateUserForm = (props) => {
|
||||
//console.log({ res });
|
||||
|
||||
const finalUrl = roomData.roomId
|
||||
? `/rooms/shared/filter?folder=${roomData.roomId}`
|
||||
? `/rooms/shared/${roomData.roomId}/filter?folder=${roomData.roomId}`
|
||||
: defaultPage;
|
||||
|
||||
const isConfirm = typeof res === "string" && res.includes("confirm");
|
||||
|
@ -46,6 +46,7 @@ import { FormWrapper } from "@docspace/shared/components/form-wrapper";
|
||||
import PortalLogo from "@docspace/shared/components/portal-logo/PortalLogo";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
import ConfirmRoute from "SRC_DIR/helpers/confirmRoute";
|
||||
import { AuthenticatedAction } from "SRC_DIR/helpers/enums";
|
||||
|
||||
const DeactivatePortal = (props) => {
|
||||
const { t, greetingTitle, linkData, companyInfoSettingsData } = props;
|
||||
@ -134,7 +135,7 @@ const ComponentWrapper = inject(({ settingsStore }) => ({
|
||||
|
||||
export const Component = () => {
|
||||
return (
|
||||
<ConfirmRoute>
|
||||
<ConfirmRoute doAuthenticated={AuthenticatedAction.Logout}>
|
||||
<ComponentWrapper />
|
||||
</ConfirmRoute>
|
||||
);
|
||||
|
@ -51,17 +51,18 @@ import ConfirmRoute from "SRC_DIR/helpers/confirmRoute";
|
||||
const RemovePortal = (props) => {
|
||||
const { t, greetingTitle, linkData, companyInfoSettingsData } = props;
|
||||
const [isRemoved, setIsRemoved] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const url = companyInfoSettingsData?.site
|
||||
? companyInfoSettingsData.site
|
||||
: "https://onlyoffice.com";
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onDeleteClick = async () => {
|
||||
try {
|
||||
await deletePortal(linkData.confirmHeader);
|
||||
const res = await deletePortal(linkData.confirmHeader);
|
||||
setIsRemoved(true);
|
||||
setTimeout(() => (location.href = url), 10000);
|
||||
setTimeout(() => (location.href = res ? res : url), 10000);
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ export const HistoryItemList = ({
|
||||
iconName={FolderLocationReactSvgUrl}
|
||||
size="16"
|
||||
isFill
|
||||
onClick={() => checkAndOpenLocationAction!(item)}
|
||||
onClick={() => checkAndOpenLocationAction!(item, actionType)}
|
||||
title={t("Files:OpenLocation")}
|
||||
/>
|
||||
</StyledHistoryBlockFile>
|
||||
|
@ -59,8 +59,8 @@ const GroupsRow = ({
|
||||
changeGroupContextSelection(item, !rightMouseButtonClick);
|
||||
};
|
||||
|
||||
const onOpenGroup = () => {
|
||||
openGroupAction(item.id, true, item.name);
|
||||
const onOpenGroup = (e) => {
|
||||
openGroupAction(item.id, true, item.name, e);
|
||||
};
|
||||
|
||||
const nameColor =
|
||||
|
@ -62,8 +62,8 @@ const GroupsTableItem = ({
|
||||
changeGroupContextSelection(item, !rightMouseButtonClick);
|
||||
};
|
||||
|
||||
const onOpenGroup = () => {
|
||||
openGroupAction(item.id, true, item.name);
|
||||
const onOpenGroup = (e) => {
|
||||
openGroupAction(item.id, true, item.name, e);
|
||||
};
|
||||
|
||||
const onRowClick = (e) => {
|
||||
|
@ -209,6 +209,7 @@ const StyledTileContainer = styled.div`
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
margin-top: 16px;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
|
@ -646,7 +646,6 @@ export default inject(
|
||||
pathParts,
|
||||
navigationPath,
|
||||
security,
|
||||
canCopyPublicLink,
|
||||
rootFolderType,
|
||||
shared,
|
||||
} = selectedFolderStore;
|
||||
@ -663,8 +662,6 @@ export default inject(
|
||||
|
||||
const isRoom = !!roomType;
|
||||
const isPublicRoomType = roomType === RoomsType.PublicRoom;
|
||||
const isCustomRoomType = roomType === RoomsType.CustomRoom;
|
||||
const isFormRoomType = roomType === RoomsType.FormRoom;
|
||||
|
||||
const {
|
||||
onCreateAndCopySharedLink,
|
||||
@ -717,14 +714,9 @@ export default inject(
|
||||
const sharedItem = navigationPath.find((r) => r.shared);
|
||||
|
||||
const showNavigationButton =
|
||||
isLoading || !security?.CopyLink
|
||||
isLoading || !security?.CopyLink || isPublicRoom || isArchive
|
||||
? false
|
||||
: (!isPublicRoom &&
|
||||
!isArchive &&
|
||||
canCopyPublicLink &&
|
||||
(isPublicRoomType || isCustomRoomType || isFormRoomType) &&
|
||||
shared) ||
|
||||
(sharedItem && sharedItem.canCopyPublicLink);
|
||||
: security?.Read && (shared || sharedItem);
|
||||
|
||||
return {
|
||||
showText: settingsStore.showText,
|
||||
@ -748,7 +740,6 @@ export default inject(
|
||||
|
||||
setSelected,
|
||||
security,
|
||||
canCopyPublicLink,
|
||||
|
||||
getHeaderMenu,
|
||||
getCheckboxItemLabel,
|
||||
|
@ -243,12 +243,6 @@ const ArticleBodyContent = (props) => {
|
||||
return "Common:FreeAccessToLicensedVersion";
|
||||
case "DataImport":
|
||||
return t("DataImport");
|
||||
case "ImportFromGoogle":
|
||||
return t("ImportFromGoogle");
|
||||
case "ImportFromNextcloud":
|
||||
return t("ImportFromNextcloud");
|
||||
case "ImportFromPortal":
|
||||
return t("ImportFromPortal");
|
||||
case "StorageManagement":
|
||||
return t("StorageManagement");
|
||||
default:
|
||||
|
@ -185,6 +185,7 @@ const SectionHeaderContent = (props) => {
|
||||
setIsLoadedSectionHeader,
|
||||
isSSOAvailable,
|
||||
organizationName,
|
||||
workspace,
|
||||
} = props;
|
||||
|
||||
const navigate = useNavigate();
|
||||
@ -341,12 +342,16 @@ const SectionHeaderContent = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const pathname = location.pathname;
|
||||
|
||||
const isServicePage =
|
||||
pathname.includes("google") ||
|
||||
pathname.includes("nextcloud") ||
|
||||
pathname.includes("onlyoffice");
|
||||
const translatedHeader =
|
||||
header === "ImportHeader"
|
||||
? workspace === "GoogleWorkspace"
|
||||
? t("ImportFromGoogle")
|
||||
: workspace === "Nextcloud"
|
||||
? t("ImportFromNextcloud")
|
||||
: workspace === "Workspace"
|
||||
? t("ImportFromPortal", { organizationName })
|
||||
: t("DataImport")
|
||||
: t(header, { organizationName });
|
||||
|
||||
return (
|
||||
<StyledContainer isHeaderVisible={isHeaderVisible}>
|
||||
@ -376,18 +381,7 @@ const SectionHeaderContent = (props) => {
|
||||
)}
|
||||
<Headline type="content" truncate={true}>
|
||||
<div className="settings-section_header">
|
||||
<div className="header">
|
||||
{isMobile() && isServicePage && (
|
||||
<IconButton
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
size="17"
|
||||
isFill={true}
|
||||
onClick={onBackToParent}
|
||||
className="arrow-button"
|
||||
/>
|
||||
)}
|
||||
{t(header, { organizationName })}
|
||||
</div>
|
||||
<div className="header">{translatedHeader}</div>
|
||||
{isNeedPaidIcon ? (
|
||||
<Badge
|
||||
backgroundColor="#EDC409"
|
||||
@ -422,51 +416,62 @@ const SectionHeaderContent = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ settingsStore, currentQuotaStore, setup, common }) => {
|
||||
const {
|
||||
isBrandingAndCustomizationAvailable,
|
||||
isRestoreAndAutoBackupAvailable,
|
||||
isSSOAvailable,
|
||||
} = currentQuotaStore;
|
||||
const { addUsers, removeAdmins } = setup.headerAction;
|
||||
const { toggleSelector } = setup;
|
||||
const {
|
||||
selected,
|
||||
setSelected,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
isHeaderVisible,
|
||||
deselectUser,
|
||||
selectAll,
|
||||
selection,
|
||||
} = setup.selectionStore;
|
||||
const { admins, selectorIsOpen } = setup.security.accessRight;
|
||||
const { isLoadedSectionHeader, setIsLoadedSectionHeader } = common;
|
||||
export default inject(
|
||||
({
|
||||
settingsStore,
|
||||
currentQuotaStore,
|
||||
setup,
|
||||
common,
|
||||
importAccountsStore,
|
||||
}) => {
|
||||
const {
|
||||
isBrandingAndCustomizationAvailable,
|
||||
isRestoreAndAutoBackupAvailable,
|
||||
isSSOAvailable,
|
||||
} = currentQuotaStore;
|
||||
const { addUsers, removeAdmins } = setup.headerAction;
|
||||
const { toggleSelector } = setup;
|
||||
const {
|
||||
selected,
|
||||
setSelected,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
isHeaderVisible,
|
||||
deselectUser,
|
||||
selectAll,
|
||||
selection,
|
||||
} = setup.selectionStore;
|
||||
const { admins, selectorIsOpen } = setup.security.accessRight;
|
||||
const { isLoadedSectionHeader, setIsLoadedSectionHeader } = common;
|
||||
|
||||
const { organizationName } = settingsStore;
|
||||
const { organizationName } = settingsStore;
|
||||
|
||||
return {
|
||||
addUsers,
|
||||
removeAdmins,
|
||||
selected,
|
||||
setSelected,
|
||||
admins,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
isHeaderVisible,
|
||||
deselectUser,
|
||||
selectAll,
|
||||
toggleSelector,
|
||||
selectorIsOpen,
|
||||
selection,
|
||||
isLoadedSectionHeader,
|
||||
setIsLoadedSectionHeader,
|
||||
isBrandingAndCustomizationAvailable,
|
||||
isRestoreAndAutoBackupAvailable,
|
||||
isSSOAvailable,
|
||||
organizationName,
|
||||
};
|
||||
})(
|
||||
const { workspace } = importAccountsStore;
|
||||
|
||||
return {
|
||||
addUsers,
|
||||
removeAdmins,
|
||||
selected,
|
||||
setSelected,
|
||||
admins,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
isHeaderVisible,
|
||||
deselectUser,
|
||||
selectAll,
|
||||
toggleSelector,
|
||||
selectorIsOpen,
|
||||
selection,
|
||||
isLoadedSectionHeader,
|
||||
setIsLoadedSectionHeader,
|
||||
isBrandingAndCustomizationAvailable,
|
||||
isRestoreAndAutoBackupAvailable,
|
||||
isSSOAvailable,
|
||||
organizationName,
|
||||
workspace,
|
||||
};
|
||||
},
|
||||
)(
|
||||
withLoading(
|
||||
withTranslation(["Settings", "SingleSignOn", "Common", "JavascriptSdk"])(
|
||||
observer(SectionHeaderContent),
|
||||
|
@ -1,150 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isTablet } from "@docspace/shared/utils/device";
|
||||
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { ProgressBar } from "@docspace/shared/components/progress-bar";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.data-import-progress-bar {
|
||||
margin-top: -8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ImportProcessingStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
isFifthStep,
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
}) => {
|
||||
const [percent, setPercent] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const uploadInterval = useRef(null);
|
||||
|
||||
const handleFileMigration = async () => {
|
||||
setIsLoading(true);
|
||||
setPercent(0);
|
||||
setIsVisible(true);
|
||||
try {
|
||||
await proceedFileMigration("GoogleWorkspace");
|
||||
|
||||
uploadInterval.current = setInterval(async () => {
|
||||
const res = await getMigrationStatus();
|
||||
setPercent(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsLoading(false);
|
||||
setIsVisible(false);
|
||||
setPercent(100);
|
||||
setTimeout(() => {
|
||||
onNextStep();
|
||||
}, 1000);
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// const hideCancelDialog = () => setIsVisible(false);
|
||||
|
||||
// const onCancel = () => {
|
||||
// setIsVisible(true);
|
||||
// };
|
||||
|
||||
// const handleCancelMigration = () => {
|
||||
// setIsLoading(false);
|
||||
// cancelMigration();
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
handleFileMigration();
|
||||
|
||||
return () => clearInterval(uploadInterval.current);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={percent}
|
||||
isInfiniteProgress={isVisible}
|
||||
className="data-import-progress-bar"
|
||||
/>
|
||||
{/* <Button
|
||||
size={isTablet() ? "medium" : "small"}
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
|
||||
{isVisible && (
|
||||
<CancelUploadDialog
|
||||
visible={isVisible}
|
||||
loading={false}
|
||||
isFifthStep={isFifthStep}
|
||||
cancelMigration={handleCancelMigration}
|
||||
onClose={hideCancelDialog}
|
||||
/>
|
||||
)} */}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
};
|
||||
})(observer(ImportProcessingStep));
|
@ -1,160 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import ImportSection from "../../../sub-components/ImportSection";
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import PeopleIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg";
|
||||
import AccountsIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg";
|
||||
import DocumentsIcon from "PUBLIC_DIR/images/catalog.documents.react.svg";
|
||||
import RoomsIcon from "PUBLIC_DIR/images/catalog.rooms.react.svg";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.save-cancel-buttons {
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ImportStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
showReminder,
|
||||
importOptions,
|
||||
setImportOptions,
|
||||
}) => {
|
||||
const onChange = (e, name) => {
|
||||
const checked = e.target.checked;
|
||||
setImportOptions({ [name]: checked });
|
||||
};
|
||||
|
||||
const serviceName = "Google Workspace";
|
||||
const users =
|
||||
t("Settings:Employees")[0].toUpperCase() + t("Settings:Employees").slice(1);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<ImportSection
|
||||
isChecked
|
||||
sectionName={users}
|
||||
description={t("Settings:UsersSectionDescription")}
|
||||
exportSection={{ sectionName: users, workspace: serviceName }}
|
||||
importSection={{
|
||||
sectionName: t("Common:Accounts"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: PeopleIcon,
|
||||
}}
|
||||
isDisabled
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importGroups}
|
||||
onChange={(e) => onChange(e, "importGroups")}
|
||||
sectionName={t("Common:Groups")}
|
||||
description={t("Settings:GroupsDescription", { serviceName })}
|
||||
exportSection={{
|
||||
sectionName: t("Common:Groups"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Accounts"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: AccountsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importPersonalFiles}
|
||||
onChange={(e) => onChange(e, "importPersonalFiles")}
|
||||
sectionName={t("Settings:PersonalFiles")}
|
||||
description={t("Settings:PersonalFilesDescription")}
|
||||
exportSection={{
|
||||
sectionName: "Google Drive's Files",
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Documents"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: DocumentsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importSharedFiles}
|
||||
onChange={(e) => onChange(e, "importSharedFiles")}
|
||||
sectionName={t("Settings:SharedFiles")}
|
||||
description={t("Settings:SharedFilesDescription")}
|
||||
exportSection={{
|
||||
sectionName: t("Settings:SharedFiles"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Documents"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: DocumentsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importSharedFolders}
|
||||
onChange={(e) => onChange(e, "importSharedFolders")}
|
||||
sectionName={t("Settings:SharedFolders")}
|
||||
description={t("Settings:SharedFoldersDescription")}
|
||||
exportSection={{
|
||||
sectionName: t("Settings:SharedFolders"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Rooms"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: RoomsIcon,
|
||||
}}
|
||||
/>
|
||||
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onNextStep}
|
||||
onCancelClick={onPrevStep}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
showReminder
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const { importOptions, setImportOptions } = importAccountsStore;
|
||||
|
||||
return {
|
||||
importOptions,
|
||||
setImportOptions,
|
||||
};
|
||||
})(observer(ImportStep));
|
@ -1,450 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
|
||||
import { isTablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { FileInput } from "@docspace/shared/components/file-input";
|
||||
import { ProgressBar } from "@docspace/shared/components/progress-bar";
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.select-file-title {
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-bottom: 4px;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.select-file-input {
|
||||
height: 32px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.icon-button_svg {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.client.settings.migration.fileInputIconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-file-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
width: 350px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ErrorBlock = styled.div`
|
||||
max-width: 700px;
|
||||
|
||||
.complete-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
|
||||
}
|
||||
|
||||
.save-cancel-buttons {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FAILS_TRIES = 1;
|
||||
|
||||
const SelectFileStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
showReminder,
|
||||
setShowReminder,
|
||||
cancelDialogVisible,
|
||||
setCancelDialogVisible,
|
||||
initMigrationName,
|
||||
multipleFileUploading,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
}) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [showErrorText, setShowErrorText] = useState(false);
|
||||
const [isFileError, setIsFileError] = useState(false);
|
||||
const [fileName, setFileName] = useState(null);
|
||||
const [isBackupEmpty, setIsBackupEmpty] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const isAbort = useRef(false);
|
||||
const uploadInterval = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [failTries, setFailsTries] = useState(FAILS_TRIES);
|
||||
|
||||
const goBack = () => {
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
};
|
||||
|
||||
const checkMigrationStatusAndUpdate = async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res || res.parseResult.migratorName !== "GoogleWorkspace") {
|
||||
clearInterval(uploadInterval.current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "parse" && !res.isCompleted) {
|
||||
setProgress(res.progress);
|
||||
setIsFileLoading(true);
|
||||
} else {
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
|
||||
setIsFileError(false);
|
||||
setShowReminder(true);
|
||||
|
||||
if (res.parseResult.files?.length > 0) {
|
||||
setFileName(res.parseResult.files.join(", "));
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
setIsFileError(false);
|
||||
setShowReminder(false);
|
||||
setFileName(null);
|
||||
clearInterval(uploadInterval.current);
|
||||
} else if (res.isCompleted || res.progress === 100) {
|
||||
if (
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length >
|
||||
0
|
||||
) {
|
||||
setUsers(res.parseResult);
|
||||
setShowReminder(true);
|
||||
onNextStep && onNextStep();
|
||||
}
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(error.message || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setShowReminder(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
|
||||
uploadInterval.current = setInterval(() => {
|
||||
checkMigrationStatusAndUpdate();
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(uploadInterval.current);
|
||||
}, []);
|
||||
|
||||
const onUploadToServer = () => {
|
||||
setShowReminder(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
};
|
||||
|
||||
const onUploadFile = async (file) => {
|
||||
setIsVisible(true);
|
||||
try {
|
||||
if (file.length) {
|
||||
await multipleFileUploading(file, setProgress, isAbort);
|
||||
} else {
|
||||
await singleFileUploading(file, setProgress, isAbort);
|
||||
}
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
await initMigrationName(searchParams.get("service"));
|
||||
|
||||
uploadInterval.current = setInterval(async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res && failTries) {
|
||||
setFailsTries((tries) => tries - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
toastr.error(res.error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
setShowErrorText(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.parseResult.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsFileLoading(false);
|
||||
setIsVisible(false);
|
||||
if (
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length >
|
||||
0
|
||||
) {
|
||||
setUsers(res.parseResult);
|
||||
setShowReminder(true);
|
||||
setIsBackupEmpty(false);
|
||||
} else {
|
||||
setIsBackupEmpty(true);
|
||||
cancelMigration();
|
||||
}
|
||||
}
|
||||
|
||||
setProgress(res?.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
setShowErrorText(false);
|
||||
} catch (error) {
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
setIsError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
} finally {
|
||||
isAbort.current = false;
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectFile = (file) => {
|
||||
setProgress(0);
|
||||
setIsFileError(false);
|
||||
setShowReminder(false);
|
||||
setIsFileLoading(true);
|
||||
setFailsTries(FAILS_TRIES);
|
||||
try {
|
||||
onUploadFile(file);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDownloadArchives = async () => {
|
||||
try {
|
||||
await getMigrationStatus()
|
||||
.then(
|
||||
(res) =>
|
||||
new Blob([res.parseResult.failedArchives], {
|
||||
type: "text/csv;charset=utf-8",
|
||||
}),
|
||||
)
|
||||
.then((blob) => {
|
||||
let a = document.createElement("a");
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = "unsupported_files";
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setCancelDialogVisible(true);
|
||||
};
|
||||
|
||||
const handleCancelMigration = () => {
|
||||
isAbort.current = true;
|
||||
setProgress(0);
|
||||
setIsFileLoading(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
cancelMigration();
|
||||
};
|
||||
|
||||
const hideCancelDialog = () => setCancelDialogVisible(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper>
|
||||
<Text className="select-file-title">
|
||||
{t("Settings:ChooseBackupFiles")}
|
||||
</Text>
|
||||
<FileInput
|
||||
scale
|
||||
onInput={onSelectFile}
|
||||
className="select-file-input"
|
||||
placeholder={fileName || t("Settings:BackupFiles")}
|
||||
isDisabled={isFileLoading}
|
||||
accept={[".zip"]}
|
||||
/>
|
||||
</Wrapper>
|
||||
|
||||
{isFileLoading ? (
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={progress}
|
||||
isInfiniteProgress={isVisible}
|
||||
className="select-file-progress-bar"
|
||||
label={t("Settings:BackupFilesUploading")}
|
||||
/>
|
||||
<Button
|
||||
size={isTablet() ? "medium" : "small"}
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</Wrapper>
|
||||
) : (
|
||||
<ErrorBlock>
|
||||
{isFileError && (
|
||||
<Box>
|
||||
<ProgressBar
|
||||
percent={100}
|
||||
className="complete-progress-bar"
|
||||
label={t("Common:LoadingIsComplete")}
|
||||
/>
|
||||
<Text className="error-text">
|
||||
{showErrorText
|
||||
? t("Settings:UnsupportedFilesDescription")
|
||||
: t("Settings:UnsupportedFilesWithUploadDesc")}
|
||||
</Text>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered
|
||||
fontWeight={600}
|
||||
onClick={onDownloadArchives}
|
||||
>
|
||||
{t("Settings:CheckUnsupportedFiles")}
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isBackupEmpty && (
|
||||
<Box>
|
||||
<ProgressBar
|
||||
percent={100}
|
||||
className="complete-progress-bar"
|
||||
label={t("Common:LoadingIsComplete")}
|
||||
/>
|
||||
<Text className="error-text">
|
||||
{t("Settings:NoUsersInBackup")}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isError ? (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onUploadToServer}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:UploadToServer")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
isSaving={showReminder}
|
||||
displaySettings
|
||||
saveButtonDisabled={showReminder}
|
||||
showReminder
|
||||
/>
|
||||
) : (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onNextStep}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
saveButtonDisabled={!showReminder}
|
||||
showReminder
|
||||
/>
|
||||
)}
|
||||
</ErrorBlock>
|
||||
)}
|
||||
|
||||
{cancelDialogVisible && (
|
||||
<CancelUploadDialog
|
||||
visible={cancelDialogVisible}
|
||||
// loading={isFileLoading}
|
||||
onClose={hideCancelDialog}
|
||||
cancelMigration={handleCancelMigration}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ dialogsStore, importAccountsStore }) => {
|
||||
const {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
multipleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
} = importAccountsStore;
|
||||
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
multipleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
cancelDialogVisible: cancelUploadDialogVisible,
|
||||
setCancelDialogVisible: setCancelUploadDialogVisible,
|
||||
};
|
||||
})(observer(SelectFileStep));
|
@ -1,57 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import UsersRowContent from "./UsersRowContent";
|
||||
|
||||
const UsersRow = (props) => {
|
||||
const { t, data, sectionWidth, isChecked, toggleAccount } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
data={data}
|
||||
checkbox
|
||||
checked={isChecked}
|
||||
onClick={toggleAccount}
|
||||
onSelect={toggleAccount}
|
||||
contextButtonSpacerWidth="0"
|
||||
>
|
||||
<UsersRowContent
|
||||
t={t}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
isDuplicate={data.isDuplicate}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersRow;
|
@ -1,99 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContent } from "@docspace/shared/components/row-content";
|
||||
|
||||
const StyledRowContent = styled(RowContent)`
|
||||
display: flex;
|
||||
|
||||
.rowMainContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.username {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 5px;
|
||||
`
|
||||
: css`
|
||||
margin-right: 5px;
|
||||
`}
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-existing {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.existingTextColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const UsersRowContent = ({
|
||||
t,
|
||||
data,
|
||||
sectionWidth,
|
||||
displayName,
|
||||
email,
|
||||
isDuplicate,
|
||||
}) => {
|
||||
const contentData = [
|
||||
<div key={data.key}>
|
||||
<Box displayProp="flex">
|
||||
<Text className="username">{displayName}</Text>
|
||||
{isDuplicate && (
|
||||
<Text className="user-existing">
|
||||
({t("Settings:AccountAlreadyExists")})
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Text className="user-email">{email}</Text>
|
||||
</div>,
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledRowContent sectionWidth={sectionWidth}>
|
||||
{contentData}
|
||||
</StyledRowContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersRowContent;
|
@ -1,192 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import UsersRow from "./UsersRow";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 348px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
|
||||
.row-header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 6px;
|
||||
`
|
||||
: css`
|
||||
margin-left: 6px;
|
||||
`}
|
||||
}
|
||||
|
||||
.row-header-title {
|
||||
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
.row_content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const checkedAccountType = "withEmail";
|
||||
|
||||
const RowView = (props) => {
|
||||
const {
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
checkedUsers,
|
||||
withEmailUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = props;
|
||||
|
||||
const toggleAll = (e) => {
|
||||
toggleAllAccounts(e.target.checked, withEmailUsers, checkedAccountType);
|
||||
};
|
||||
|
||||
const handleToggle = (user) => toggleAccount(user, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => setSearchValue("");
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withEmail.length > 0 &&
|
||||
checkedUsers.withEmail.length !== withEmailUsers.length;
|
||||
|
||||
const isChecked = checkedUsers.withEmail.length === withEmailUsers.length;
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow sectionWidth={sectionWidth}>
|
||||
<div className="row-header-item">
|
||||
{checkedUsers.withEmail.length > 0 && (
|
||||
<Checkbox
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
)}
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</div>
|
||||
</StyledRow>
|
||||
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
checkedUsers,
|
||||
withEmailUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
checkedUsers,
|
||||
withEmailUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(RowView));
|
@ -1,140 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { TableHeader } from "@docspace/shared/components/table";
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const TABLE_COLUMNS = `GoogleWorkspaceColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const UsersTableHeader = (props) => {
|
||||
const {
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
isIndeterminate,
|
||||
isChecked,
|
||||
toggleAll,
|
||||
} = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 180,
|
||||
checkbox: {
|
||||
value: isChecked,
|
||||
isIndeterminate,
|
||||
onChange: toggleAll,
|
||||
},
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Email",
|
||||
title: t("Common:Email"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Dublicate",
|
||||
title: t("Settings:DuplicateNoun"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(getColumns(defaultColumns));
|
||||
}, [isIndeterminate, isChecked]);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="48px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore }) => {
|
||||
return {
|
||||
userId: userStore.user.id,
|
||||
};
|
||||
})(observer(UsersTableHeader));
|
@ -1,100 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { TableRow } from "@docspace/shared/components/table";
|
||||
import { TableCell } from "@docspace/shared/components/table";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.table-container_cell {
|
||||
padding-right: 30px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
margin-right: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.not-existing {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-existing {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.existingTextColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const NOT_EXIST = "—";
|
||||
|
||||
const UsersTableRow = ({
|
||||
t,
|
||||
displayName,
|
||||
email,
|
||||
isDuplicate,
|
||||
isChecked,
|
||||
toggleAccount,
|
||||
}) => {
|
||||
return (
|
||||
<StyledTableRow checked={isChecked} onClick={toggleAccount}>
|
||||
<TableCell className="checkboxWrapper">
|
||||
<Checkbox isChecked={isChecked} onChange={toggleAccount} />
|
||||
<Text className="username">{displayName}</Text>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Text className="user-email">{email}</Text>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
{isDuplicate ? (
|
||||
<Text className="user-existing">{t("Settings:AccountAlreadyExists")}</Text>
|
||||
) : (
|
||||
<Text className="not-existing">{NOT_EXIST}</Text>
|
||||
)}
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersTableRow;
|
@ -1,244 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import UsersTableHeader from "./UsersTableHeader";
|
||||
import UsersTableRow from "./UsersTableRow";
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
import { TableBody } from "@docspace/shared/components/table";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledTableContainer = styled(TableContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 28px 0 15px;
|
||||
`
|
||||
: css`
|
||||
padding: 0px 15px 0 28px;
|
||||
`}
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding: 0;
|
||||
padding-inline-start: 8px;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
cursor: pointer;
|
||||
|
||||
padding-left: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
|
||||
.table-container_cell {
|
||||
margin-top: -1px;
|
||||
border-top: ${(props) =>
|
||||
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
|
||||
|
||||
margin-left: -24px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
margin-right: -20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-list-item:has(.selected-table-row) {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 618px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTableContainer.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const COLUMNS_SIZE = `googleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelGoogleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const checkedAccountType = "withEmail";
|
||||
|
||||
const TableView = (props) => {
|
||||
const {
|
||||
t,
|
||||
withEmailUsers,
|
||||
userId,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = props;
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const toggleAll = (e) => {
|
||||
toggleAllAccounts(e.target.checked, withEmailUsers, checkedAccountType);
|
||||
};
|
||||
|
||||
const handleToggle = (e, user) => {
|
||||
e.stopPropagation();
|
||||
toggleAccount(user, checkedAccountType);
|
||||
};
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withEmail.length > 0 &&
|
||||
checkedUsers.withEmail.length !== withEmailUsers.length;
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
userId={userId}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={checkedUsers.withEmail.length === withEmailUsers.length}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
isDuplicate={data.isDuplicate}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={(e) => handleToggle(e, data)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore, importAccountsStore }) => {
|
||||
const { id: userId } = userStore.user;
|
||||
const {
|
||||
withEmailUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
withEmailUsers,
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(TableView));
|
@ -1,62 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Consumer } from "@docspace/shared/utils/context";
|
||||
|
||||
import TableView from "./TableView";
|
||||
import RowView from "./RowView";
|
||||
|
||||
const AccountsTable = ({ t, viewAs, accountsData }) => {
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<TableView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
/>
|
||||
) : (
|
||||
<RowView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup }) => {
|
||||
const { viewAs } = setup;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
};
|
||||
})(withTranslation(["People"])(observer(AccountsTable)));
|
@ -1,185 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { SearchInput } from "@docspace/shared/components/search-input";
|
||||
|
||||
import AccountsTable from "./AccountsTable";
|
||||
import AccountsPaging from "../../../sub-components/AccountsPaging";
|
||||
import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
|
||||
|
||||
import { parseQuota } from "../../../utils";
|
||||
|
||||
const SelectUsersStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
setResultUsers,
|
||||
areCheckedUsersEmpty,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
}) => {
|
||||
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
|
||||
const [quota, setQuota] = useState({ used: 0, max: 0 });
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchValue("");
|
||||
setQuota(parseQuota(quotaCharacteristics[1]));
|
||||
}, []);
|
||||
|
||||
const handleDataChange = (leftBoundary, rightBoundary) => {
|
||||
setDataPortion(withEmailUsers.slice(leftBoundary, rightBoundary));
|
||||
};
|
||||
|
||||
const onChangeInput = (value) => {
|
||||
setSearchValue(value);
|
||||
};
|
||||
|
||||
const onClearSearchInput = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.lastName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleStepIncrement = () => {
|
||||
setResultUsers();
|
||||
onNextStep();
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
cancelMigration();
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
onPrevStep();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const totalUsedUsers =
|
||||
quota.used +
|
||||
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={handleStepIncrement}
|
||||
onCancelClick={goBack}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings={true}
|
||||
saveButtonDisabled={
|
||||
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
|
||||
}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
|
||||
{quota.max && (
|
||||
<UsersInfoBlock
|
||||
t={t}
|
||||
totalUsedUsers={totalUsedUsers}
|
||||
selectedUsers={checkedUsers.withEmail.length}
|
||||
totalUsers={withEmailUsers.length}
|
||||
totalLicenceLimit={quota.max}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SearchInput
|
||||
id="search-withEmailUsers-input"
|
||||
placeholder={t("Common:Search")}
|
||||
style={{ marginTop: "16px" }}
|
||||
value={searchValue}
|
||||
onChange={onChangeInput}
|
||||
refreshTimeout={100}
|
||||
onClearSearch={onClearSearchInput}
|
||||
/>
|
||||
|
||||
<AccountsTable t={t} accountsData={filteredAccounts} />
|
||||
|
||||
{withEmailUsers.length > 25 && filteredAccounts.length > 0 && (
|
||||
<AccountsPaging
|
||||
t={t}
|
||||
numberOfItems={withEmailUsers.length}
|
||||
setDataPortion={handleDataChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={handleStepIncrement}
|
||||
onCancelClick={goBack}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings={true}
|
||||
saveButtonDisabled={
|
||||
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
|
||||
}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore, currentQuotaStore }) => {
|
||||
const {
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
areCheckedUsersEmpty,
|
||||
setResultUsers,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
} = importAccountsStore;
|
||||
const { quotaCharacteristics } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
areCheckedUsersEmpty,
|
||||
setResultUsers,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
};
|
||||
})(observer(SelectUsersStep));
|
@ -1,69 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import UsersTypeRowContent from "./UsersTypeRowContent";
|
||||
|
||||
const UsersTypeRow = ({
|
||||
data,
|
||||
sectionWidth,
|
||||
typeOptions,
|
||||
isChecked,
|
||||
toggleAccount,
|
||||
}) => {
|
||||
const userTypeRef = useRef();
|
||||
|
||||
const handleAccountToggle = (e) => {
|
||||
userTypeRef.current?.contains(e.target) || toggleAccount();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
key={data.key}
|
||||
data={data}
|
||||
checked={isChecked}
|
||||
contextButtonSpacerWidth="0"
|
||||
onRowClick={handleAccountToggle}
|
||||
onSelect={handleAccountToggle}
|
||||
>
|
||||
<UsersTypeRowContent
|
||||
id={data.key}
|
||||
sectionWidth={sectionWidth}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
type={data.userType}
|
||||
typeOptions={typeOptions}
|
||||
userTypeRef={userTypeRef}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersTypeRow;
|
@ -1,153 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContent } from "@docspace/shared/components/row-content";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
|
||||
const StyledRowContent = styled(RowContent)`
|
||||
display: flex;
|
||||
|
||||
.row-main-container-wrapper {
|
||||
width: 100%;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 0px;
|
||||
`
|
||||
: css`
|
||||
margin-right: 0px;
|
||||
`}
|
||||
}
|
||||
|
||||
.rowMainContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-type {
|
||||
.combo-button {
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
justify-content: flex-end;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.combo-button-label {
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
flex: initial;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UsersRowContent = ({
|
||||
id,
|
||||
sectionWidth,
|
||||
displayName,
|
||||
email,
|
||||
typeOptions,
|
||||
roleSelectorRef,
|
||||
type,
|
||||
changeUserType,
|
||||
}) => {
|
||||
const onSelectUser = (e) => {
|
||||
changeUserType(id, e.key);
|
||||
};
|
||||
|
||||
const selectedOption =
|
||||
typeOptions.find((option) => option.key === type) || {};
|
||||
|
||||
const contentData = [
|
||||
<Box
|
||||
key={id}
|
||||
displayProp="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Box>
|
||||
<Text className="username">{displayName}</Text>
|
||||
<Text className="user-email">{email}</Text>
|
||||
</Box>
|
||||
|
||||
<div ref={roleSelectorRef}>
|
||||
<ComboBox
|
||||
className="user-type"
|
||||
selectedOption={selectedOption}
|
||||
options={typeOptions}
|
||||
onSelect={onSelectUser}
|
||||
scaled
|
||||
size="content"
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth="fit-content"
|
||||
/>
|
||||
</div>
|
||||
</Box>,
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledRowContent sectionWidth={sectionWidth}>
|
||||
{contentData}
|
||||
</StyledRowContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const { changeUserType } = importAccountsStore;
|
||||
|
||||
return {
|
||||
changeUserType,
|
||||
};
|
||||
})(observer(UsersRowContent));
|
@ -1,241 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import UsersTypeRow from "./UsersTypeRow";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableGroupMenu } from "@docspace/shared/components/table";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import ChangeTypeReactSvgUrl from "PUBLIC_DIR/images/change.type.react.svg?url";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.table-group-menu {
|
||||
height: 61px;
|
||||
position: sticky;
|
||||
z-index: 201;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -16px;
|
||||
`
|
||||
: css`
|
||||
margin-left: -16px;
|
||||
`}
|
||||
width: 100%;
|
||||
|
||||
margin-top: 20px;
|
||||
top: 61px;
|
||||
margin-bottom: -29.5px;
|
||||
|
||||
.table-container_group-menu {
|
||||
padding: 0px 16px;
|
||||
border-image-slice: 0;
|
||||
box-shadow: rgba(4, 15, 27, 0.07) 0px 15px 20px;
|
||||
}
|
||||
|
||||
.table-container_group-menu-checkbox {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 8px;
|
||||
`
|
||||
: css`
|
||||
margin-left: 8px;
|
||||
`}
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 348px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
box-sizing: border-box;
|
||||
min-height: 40px;
|
||||
|
||||
.row-header-title {
|
||||
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
.row_content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const checkedAccountType = "result";
|
||||
|
||||
const RowView = ({
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
}) => {
|
||||
const isIndeterminate =
|
||||
checkedUsers.result.length > 0 &&
|
||||
checkedUsers.result.length !== filteredUsers.length;
|
||||
|
||||
const toggleAll = (isChecked) =>
|
||||
toggleAllAccounts(isChecked, filteredUsers, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => setSearchValue("");
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
id: "change-type",
|
||||
key: "change-type",
|
||||
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
|
||||
disabled: false,
|
||||
withDropDown: true,
|
||||
options: typeOptions,
|
||||
iconUrl: ChangeTypeReactSvgUrl,
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
{checkedUsers.result.length > 0 && (
|
||||
<div className="table-group-menu">
|
||||
<TableGroupMenu
|
||||
sectionWidth={sectionWidth}
|
||||
headerMenu={headerMenu}
|
||||
withoutInfoPanelToggler
|
||||
withComboBox={false}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={checkedUsers.result.length === filteredUsers.length}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow key="Name" sectionWidth={sectionWidth}>
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</StyledRow>
|
||||
|
||||
{accountsData.map((data) => (
|
||||
<UsersTypeRow
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
typeOptions={typeOptions}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
};
|
||||
})(observer(RowView));
|
@ -1,137 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { TableHeader } from "@docspace/shared/components/table";
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const TABLE_COLUMNS = `GoogleWorkspaceColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const UsersTypeTableHeader = (props) => {
|
||||
const {
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
setHideColumns,
|
||||
isIndeterminate,
|
||||
isChecked,
|
||||
} = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 180,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Type",
|
||||
title: t("Common:Type"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
minWidth: 100,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Email",
|
||||
title: t("Common:Email"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(getColumns(defaultColumns));
|
||||
}, [isIndeterminate, isChecked]);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="48px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
setHideColumns={setHideColumns}
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore }) => {
|
||||
return {
|
||||
userId: userStore.user.id,
|
||||
};
|
||||
})(observer(UsersTypeTableHeader));
|
@ -1,300 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import UsersTypeTableHeader from "./UsersTypeTableHeader";
|
||||
import UsersTypeTableRow from "./UsersTypeTableRow";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableGroupMenu } from "@docspace/shared/components/table";
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
import { TableBody } from "@docspace/shared/components/table";
|
||||
import ChangeTypeReactSvgUrl from "PUBLIC_DIR/images/change.type.react.svg?url";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledTableContainer = styled(TableContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.table-group-menu {
|
||||
height: 69px;
|
||||
position: sticky;
|
||||
z-index: 201;
|
||||
width: calc(100% + 40px);
|
||||
margin-top: -33px;
|
||||
margin-left: -20px;
|
||||
top: 0;
|
||||
|
||||
margin-bottom: -36px;
|
||||
|
||||
.table-container_group-menu {
|
||||
border-image-slice: 0;
|
||||
border-image-source: none;
|
||||
border-bottom: ${(props) =>
|
||||
props.theme.client.settings.migration.workspaceBorder};
|
||||
box-shadow: rgba(4, 15, 27, 0.07) 0px 15px 20px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 28px 0 15px;
|
||||
`
|
||||
: css`
|
||||
padding: 0px 15px 0 28px;
|
||||
`}
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding: 0;
|
||||
padding-inline-start: 8px;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
cursor: pointer;
|
||||
|
||||
padding-left: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
|
||||
.table-container_cell {
|
||||
margin-top: -1px;
|
||||
border-top: ${(props) =>
|
||||
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
|
||||
|
||||
margin-left: -24px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
margin-right: -20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-list-item:has(.selected-table-row) {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 618px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTableContainer.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const COLUMNS_SIZE = `googleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelGoogleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const checkedAccountType = "result";
|
||||
|
||||
const TableView = ({
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
}) => {
|
||||
const tableRef = useRef(null);
|
||||
const [hideColumns, setHideColumns] = useState(false);
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.result.length > 0 &&
|
||||
checkedUsers.result.length !== filteredUsers.length;
|
||||
|
||||
const toggleAll = (isChecked) =>
|
||||
toggleAllAccounts(isChecked, filteredUsers, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
id: "change-type",
|
||||
key: "change-type",
|
||||
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
|
||||
disabled: false,
|
||||
withDropDown: true,
|
||||
options: typeOptions,
|
||||
iconUrl: ChangeTypeReactSvgUrl,
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
{checkedUsers.result.length > 0 && (
|
||||
<div className="table-group-menu">
|
||||
<TableGroupMenu
|
||||
checkboxOptions={[]}
|
||||
sectionWidth={sectionWidth}
|
||||
headerMenu={headerMenu}
|
||||
withoutInfoPanelToggler
|
||||
withComboBox={false}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={checkedUsers.result.length === filteredUsers.length}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTypeTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={checkedUsers.result.length === filteredUsers.length}
|
||||
toggleAll={toggleAll}
|
||||
setHideColumns={setHideColumns}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTypeTableRow
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
type={data.userType}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
typeOptions={typeOptions}
|
||||
hideColumns={hideColumns}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore, importAccountsStore }) => {
|
||||
const { id: userId } = userStore.user;
|
||||
const {
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
};
|
||||
})(observer(TableView));
|
@ -1,107 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Consumer } from "@docspace/shared/utils/context";
|
||||
|
||||
import TableView from "./TableView";
|
||||
import RowView from "./RowView";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const checkedAccountType = "result";
|
||||
|
||||
const AccountsTable = ({
|
||||
t,
|
||||
viewAs,
|
||||
accountsData,
|
||||
changeGroupType,
|
||||
UserTypes,
|
||||
toggleAllAccounts,
|
||||
}) => {
|
||||
const typeOptions = [
|
||||
{
|
||||
key: UserTypes.PortalAdmin,
|
||||
label: t("Common:PortalAdmin", { productName: PRODUCT_NAME }),
|
||||
onClick: () => {
|
||||
changeGroupType(UserTypes.PortalAdmin);
|
||||
toggleAllAccounts(false, [], checkedAccountType);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: UserTypes.RoomAdmin,
|
||||
label: t("Common:RoomAdmin"),
|
||||
onClick: () => {
|
||||
changeGroupType(UserTypes.RoomAdmin);
|
||||
toggleAllAccounts(false, [], checkedAccountType);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: UserTypes.User,
|
||||
label: t("Common:PowerUser"),
|
||||
onClick: () => {
|
||||
changeGroupType(UserTypes.User);
|
||||
toggleAllAccounts(false, [], checkedAccountType);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<TableView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
typeOptions={typeOptions}
|
||||
/>
|
||||
) : (
|
||||
<RowView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
typeOptions={typeOptions}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup, importAccountsStore }) => {
|
||||
const { viewAs } = setup;
|
||||
const { changeGroupType, UserTypes, toggleAllAccounts } = importAccountsStore;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
changeGroupType,
|
||||
UserTypes,
|
||||
toggleAllAccounts,
|
||||
};
|
||||
})(
|
||||
withTranslation(["ChangeUserTypeDialog", "People"])(observer(AccountsTable)),
|
||||
);
|
@ -1,142 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { SearchInput } from "@docspace/shared/components/search-input";
|
||||
import AccountsTable from "./AccountsTable";
|
||||
import AccountsPaging from "../../../sub-components/AccountsPaging";
|
||||
|
||||
const StyledSearchInput = styled(SearchInput)`
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
const SelectUsersTypeStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
showReminder,
|
||||
|
||||
users,
|
||||
checkedUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
}) => {
|
||||
const [boundaries, setBoundaries] = useState([0, 25]);
|
||||
const [dataPortion, setDataPortion] = useState(
|
||||
filteredUsers.slice(...boundaries),
|
||||
);
|
||||
|
||||
const handleDataChange = (leftBoundary, rightBoundary) => {
|
||||
setBoundaries([leftBoundary, rightBoundary]);
|
||||
setDataPortion(filteredUsers.slice(leftBoundary, rightBoundary));
|
||||
};
|
||||
|
||||
const onChangeInput = (value) => {
|
||||
setSearchValue(value);
|
||||
};
|
||||
|
||||
const onClearSearchInput = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.lastName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDataPortion(filteredUsers.slice(...boundaries));
|
||||
}, [users]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onNextStep}
|
||||
onCancelClick={onPrevStep}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
/>
|
||||
|
||||
{filteredUsers.length > 0 && (
|
||||
<>
|
||||
<StyledSearchInput
|
||||
id="search-users-type-input"
|
||||
placeholder={t("Common:Search")}
|
||||
value={searchValue}
|
||||
onChange={onChangeInput}
|
||||
refreshTimeout={100}
|
||||
onClearSearch={onClearSearchInput}
|
||||
/>
|
||||
|
||||
<AccountsTable t={t} accountsData={filteredAccounts} />
|
||||
|
||||
{filteredUsers.length > 25 && filteredAccounts.length > 0 && (
|
||||
<AccountsPaging
|
||||
t={t}
|
||||
numberOfItems={filteredUsers.length}
|
||||
setDataPortion={handleDataChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onNextStep}
|
||||
onCancelClick={onPrevStep}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const { users, checkedUsers, searchValue, setSearchValue, filteredUsers } =
|
||||
importAccountsStore;
|
||||
|
||||
return {
|
||||
users,
|
||||
checkedUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
filteredUsers,
|
||||
};
|
||||
})(observer(SelectUsersTypeStep));
|
@ -1,106 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import SelectFileStep from "./SelectFileStep";
|
||||
import SelectUsersStep from "./SelectUsersStep";
|
||||
import SelectUsersTypeStep from "./SelectUsersTypeStep";
|
||||
import ImportStep from "./ImportStep";
|
||||
import ImportProcessingStep from "./ImportProcessingStep";
|
||||
import ImportCompleteStep from "./ImportCompleteStep";
|
||||
|
||||
const StepContent = ({
|
||||
t,
|
||||
currentStep,
|
||||
showReminder,
|
||||
setShowReminder,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
}) => {
|
||||
const isFifthStep = currentStep === 5;
|
||||
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return (
|
||||
<SelectFileStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
setShowReminder={setShowReminder}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<SelectUsersStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
/>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<SelectUsersTypeStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
/>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<ImportStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
/>
|
||||
);
|
||||
case 5:
|
||||
return (
|
||||
<ImportProcessingStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
isFifthStep={isFifthStep}
|
||||
/>
|
||||
);
|
||||
case 6:
|
||||
return (
|
||||
<ImportCompleteStep
|
||||
t={t}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export default StepContent;
|
@ -0,0 +1,162 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
|
||||
import PeopleIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg?url";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
import SelectFileStep from "../../components/SelectFileStep";
|
||||
import SelectUsersStep from "../../components/SelectUsersStep";
|
||||
import SelectUsersTypeStep from "../../components/SelectUsersTypeStep";
|
||||
import ImportStep from "../../components/ImportStep";
|
||||
import ImportProcessingStep from "../../components/ImportProcessingStep";
|
||||
import ImportCompleteStep from "../../components/ImportCompleteStep";
|
||||
|
||||
import { TFunciton } from "../../types";
|
||||
|
||||
export const getStepsData = (
|
||||
t: TFunciton,
|
||||
isTypeSelectEmpty: boolean,
|
||||
organizationName: string,
|
||||
) => {
|
||||
return [
|
||||
{
|
||||
title: t("Common:SelectFiles"),
|
||||
description: t("Settings:SelectFileDescriptionGoogle"),
|
||||
component: (
|
||||
<SelectFileStep
|
||||
t={t}
|
||||
isMultipleUpload
|
||||
migratorName="GoogleWorkspace"
|
||||
acceptedExtensions={[".zip"]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("Settings:SelectUsers"),
|
||||
description: t("Settings:SelectUsersDescriptionGoogle", {
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
}),
|
||||
component: <SelectUsersStep t={t} canDisable shouldSetUsers />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:SelectUserTypes"),
|
||||
description: isTypeSelectEmpty ? (
|
||||
<>
|
||||
<b>{t("Settings:RolesAreSet")}</b>
|
||||
<div>
|
||||
{t("Settings:UsersAreRegistered", {
|
||||
productName: PRODUCT_NAME,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Trans
|
||||
t={t}
|
||||
ns="Settings"
|
||||
i18nKey="SelectUserTypesDescription"
|
||||
values={{
|
||||
productName: PRODUCT_NAME,
|
||||
}}
|
||||
components={{
|
||||
1: <b />,
|
||||
}}
|
||||
/>
|
||||
<HelpButton
|
||||
place="bottom"
|
||||
offsetRight={0}
|
||||
tooltipContent={
|
||||
<Text>
|
||||
<Trans
|
||||
i18nKey="TypesAndPrivileges"
|
||||
ns="Settings"
|
||||
t={t}
|
||||
values={{ productName: PRODUCT_NAME }}
|
||||
components={{
|
||||
1: <b />,
|
||||
2: <b />,
|
||||
3: <b />,
|
||||
4: <b />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
position: "relative",
|
||||
bottom: "-2px",
|
||||
margin: "0px 5px",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
component: <SelectUsersTypeStep t={t} />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImport"),
|
||||
description: t("Settings:ImportSectionDescription", {
|
||||
productName: PRODUCT_NAME,
|
||||
}),
|
||||
component: (
|
||||
<ImportStep
|
||||
t={t}
|
||||
serviceName="Google Workspace"
|
||||
usersExportDetails={{
|
||||
name: t("Common:Accounts"),
|
||||
icon: PeopleIcon,
|
||||
}}
|
||||
personalExportDetails={{
|
||||
name: t("Settings:GoogleDriveFiles"),
|
||||
}}
|
||||
sharedFilesExportDetails={{
|
||||
name: t("Settings:SharedFiles"),
|
||||
}}
|
||||
sharedFoldersExportDetails={{
|
||||
name: t("Settings:SharedFolders"),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImportProcessing"),
|
||||
description: t("Settings:ImportProcessingDescription"),
|
||||
component: <ImportProcessingStep t={t} migratorName="GoogleWorkspace" />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImportComplete"),
|
||||
description: t("Settings:ImportCompleteDescriptionGoogle", {
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
}),
|
||||
component: <ImportCompleteStep t={t} />,
|
||||
},
|
||||
];
|
||||
};
|
@ -1,294 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Trans, withTranslation } from "react-i18next";
|
||||
import { getStepTitle, getGoogleStepDescription } from "../../../utils";
|
||||
import {
|
||||
tablet,
|
||||
isMobile as isMobileBreakpoint,
|
||||
} from "@docspace/shared/utils/device";
|
||||
|
||||
import { isMobile } from "react-device-detect";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
import styled, { css } from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import StepContent from "./Stepper";
|
||||
import SelectFileLoader from "../sub-components/SelectFileLoader";
|
||||
import BreakpointWarning from "SRC_DIR/components/BreakpointWarning";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const STEP_LENGTH = 6;
|
||||
|
||||
const GoogleWrapper = styled.div`
|
||||
margin-top: 1px;
|
||||
|
||||
.workspace-subtitle {
|
||||
color: ${(props) => props.theme.client.settings.migration.descriptionColor};
|
||||
max-width: 700px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media ${tablet} {
|
||||
max-width: 675px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.step-counter {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 5px;
|
||||
`
|
||||
: css`
|
||||
margin-right: 5px;
|
||||
`}
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.step-description {
|
||||
max-width: 700px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 16px;
|
||||
line-height: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.stepDescriptionColor};
|
||||
|
||||
@media ${tablet} {
|
||||
max-width: 675px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const GoogleWorkspace = ({
|
||||
t,
|
||||
clearCheckedAccounts,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
organizationName,
|
||||
}) => {
|
||||
const [showReminder, setShowReminder] = useState(false);
|
||||
const [currentStep, setCurrentStep] = useState(1);
|
||||
const [shouldRender, setShouldRender] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onNextStep = () => {
|
||||
setCurrentStep((prev) => {
|
||||
const nextStep = prev < 6 ? prev + 1 : 6;
|
||||
return nextStep;
|
||||
});
|
||||
};
|
||||
|
||||
const onPrevStep = () => {
|
||||
if (currentStep !== 1) {
|
||||
setCurrentStep((prev) => prev - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const helpContent = () => (
|
||||
<Text fontSize="12px">
|
||||
<Trans
|
||||
i18nKey="TypesAndPrivileges"
|
||||
ns="Settings"
|
||||
t={t}
|
||||
values={{ productName: PRODUCT_NAME }}
|
||||
components={{
|
||||
1: <strong></strong>,
|
||||
2: <strong></strong>,
|
||||
3: <strong></strong>,
|
||||
4: <strong></strong>,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
|
||||
const tooltipStyle = {
|
||||
display: "inline-block",
|
||||
position: "relative",
|
||||
bottom: "-2px",
|
||||
margin: "0px 5px",
|
||||
};
|
||||
|
||||
const renderTooltip = (
|
||||
<HelpButton
|
||||
place="bottom"
|
||||
offsetRight={0}
|
||||
getContent={helpContent}
|
||||
style={tooltipStyle}
|
||||
/>
|
||||
);
|
||||
|
||||
useViewEffect({
|
||||
view: viewAs,
|
||||
setView: setViewAs,
|
||||
currentDeviceType,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
getMigrationStatus().then((res) => {
|
||||
if (
|
||||
!res ||
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length ===
|
||||
0
|
||||
) {
|
||||
setShouldRender(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.migratorName !== "GoogleWorkspace") {
|
||||
const workspacesEnum = {
|
||||
GoogleWorkspace: "google",
|
||||
Nextcloud: "nextcloud",
|
||||
Workspace: "onlyoffice",
|
||||
};
|
||||
const migratorName = res.parseResult.migratorName;
|
||||
|
||||
setShouldRender(true);
|
||||
navigate(
|
||||
`/portal-settings/data-import/migration/${workspacesEnum[migratorName]}?service=${migratorName}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
res.parseResult.operation === "parse" &&
|
||||
res.isCompleted &&
|
||||
res.error
|
||||
) {
|
||||
setUsers(res.parseResult);
|
||||
setCurrentStep(2);
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "migration" && !res.isCompleted) {
|
||||
setCurrentStep(5);
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "migration" && res.isCompleted) {
|
||||
setCurrentStep(6);
|
||||
}
|
||||
|
||||
setShouldRender(true);
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
|
||||
return clearCheckedAccounts;
|
||||
}, []);
|
||||
|
||||
if (isMobile || isMobileBreakpoint())
|
||||
return (
|
||||
<BreakpointWarning
|
||||
isMobileUnavailableOnly
|
||||
sectionName={t("Settings:DataImport")}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!shouldRender) return <SelectFileLoader />;
|
||||
|
||||
return (
|
||||
<GoogleWrapper>
|
||||
<Text className="workspace-subtitle">
|
||||
{t("Settings:AboutDataImport", {
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
})}
|
||||
</Text>
|
||||
<div className="step-container">
|
||||
<Box displayProp="flex" marginProp="0 0 8px">
|
||||
<Text className="step-counter">
|
||||
{currentStep}/{STEP_LENGTH}.
|
||||
</Text>
|
||||
<Text className="step-title">{getStepTitle(t, currentStep)}</Text>
|
||||
</Box>
|
||||
<Box className="step-description">
|
||||
{getGoogleStepDescription(
|
||||
t,
|
||||
currentStep,
|
||||
renderTooltip,
|
||||
Trans,
|
||||
filteredUsers.length === 0,
|
||||
organizationName,
|
||||
)}
|
||||
</Box>
|
||||
<StepContent
|
||||
t={t}
|
||||
currentStep={currentStep}
|
||||
onNextStep={onNextStep}
|
||||
onPrevStep={onPrevStep}
|
||||
showReminder={showReminder}
|
||||
setShowReminder={setShowReminder}
|
||||
/>
|
||||
</div>
|
||||
</GoogleWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const Component = inject(
|
||||
({ setup, settingsStore, importAccountsStore }) => {
|
||||
const {
|
||||
clearCheckedAccounts,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
} = importAccountsStore;
|
||||
const { viewAs, setViewAs } = setup;
|
||||
const { currentDeviceType, organizationName } = settingsStore;
|
||||
|
||||
return {
|
||||
clearCheckedAccounts,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
organizationName,
|
||||
};
|
||||
},
|
||||
)(withTranslation(["Common, Settings"])(observer(GoogleWorkspace)));
|
@ -0,0 +1,117 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useLayoutEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getStepsData } from "./Stepper";
|
||||
|
||||
import SelectFileLoader from "../sub-components/SelectFileLoader";
|
||||
import StepLayout from "../sub-components/StepLayout";
|
||||
|
||||
import { InjectedWorkspaceProps, WorkspaceProps, TFunciton } from "../types";
|
||||
|
||||
const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
const {
|
||||
theme,
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
organizationName,
|
||||
migratingWorkspace,
|
||||
migrationPhase,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
} = props as InjectedWorkspaceProps;
|
||||
|
||||
const { t, ready }: { t: TFunciton; ready: boolean } = useTranslation([
|
||||
"Common, SMTPSettings, Settings",
|
||||
]);
|
||||
|
||||
const StepsData = getStepsData(
|
||||
t,
|
||||
filteredUsers.length === 0,
|
||||
organizationName,
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (migratingWorkspace === "GoogleWorkspace" && !isMigrationInit) {
|
||||
if (migrationPhase === "setup") {
|
||||
setStep(2);
|
||||
} else if (migrationPhase === "migrating") {
|
||||
setStep(5);
|
||||
} else if (migrationPhase === "complete") {
|
||||
setStep(6);
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
||||
return (
|
||||
<StepLayout
|
||||
t={t}
|
||||
theme={theme}
|
||||
step={step}
|
||||
totalSteps={StepsData.length}
|
||||
title={StepsData[step - 1].title}
|
||||
description={StepsData[step - 1].description}
|
||||
component={StepsData[step - 1].component}
|
||||
organizationName={organizationName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Component = inject<TStore>(
|
||||
({ settingsStore, importAccountsStore }) => {
|
||||
const {
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
setWorkspace,
|
||||
migrationPhase,
|
||||
migratingWorkspace,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
} = importAccountsStore;
|
||||
const { theme, organizationName } = settingsStore;
|
||||
|
||||
return {
|
||||
theme,
|
||||
organizationName,
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
setWorkspace,
|
||||
migrationPhase,
|
||||
migratingWorkspace,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
};
|
||||
},
|
||||
)(observer(GoogleWorkspace));
|
@ -1,182 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
import { Wrapper } from "../StyledStepper";
|
||||
|
||||
const ErrorText = styled(Text)`
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
const InfoText = styled(Text)`
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
`;
|
||||
|
||||
const ImportCompleteStep = ({
|
||||
t,
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
}) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [importResult, setImportResult] = useState({
|
||||
succeedUsers: 0,
|
||||
failedUsers: 0,
|
||||
errors: [],
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const onDownloadLog = async () => {
|
||||
try {
|
||||
await getMigrationLog()
|
||||
.then((response) => new Blob([response]))
|
||||
.then((blob) => {
|
||||
let a = document.createElement("a");
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = "migration.log";
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeCheckbox = () => {
|
||||
setIsChecked((prev) => !prev);
|
||||
};
|
||||
|
||||
const onFinishClick = () => {
|
||||
if (isChecked) {
|
||||
sendWelcomeLetter({ isSendWelcomeEmail: true });
|
||||
}
|
||||
clearCheckedAccounts();
|
||||
clearMigration();
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
getMigrationStatus().then((res) =>
|
||||
setImportResult({
|
||||
succeedUsers: res.parseResult.successedUsers,
|
||||
failedUsers: res.parseResult.failedUsers,
|
||||
errors: res.parseResult.errors,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<InfoText>
|
||||
{t("Settings:ImportedUsers", {
|
||||
selectedUsers: importResult.succeedUsers,
|
||||
importedUsers: importResult.succeedUsers + importResult.failedUsers,
|
||||
})}
|
||||
</InfoText>
|
||||
|
||||
{importResult.failedUsers > 0 && (
|
||||
<ErrorText>
|
||||
{t("Settings:ErrorsWereFound", { errors: importResult.failedUsers })}
|
||||
</ErrorText>
|
||||
)}
|
||||
|
||||
{importResult.errors?.length > 0 && (
|
||||
<ErrorText>{t("Settings:ErrorOccuredDownloadLog")}</ErrorText>
|
||||
)}
|
||||
|
||||
<div className="sendLetterBlockWrapper">
|
||||
<Checkbox
|
||||
label={t("Settings:SendInviteLetter")}
|
||||
isChecked={isChecked}
|
||||
onChange={onChangeCheckbox}
|
||||
/>
|
||||
<HelpButton
|
||||
place="right"
|
||||
offsetRight={0}
|
||||
style={{ margin: "0px 5px" }}
|
||||
tooltipContent={
|
||||
<Text fontSize="12px">{t("Settings:InviteLetterTooltip")}</Text>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onFinishClick}
|
||||
onCancelClick={onDownloadLog}
|
||||
saveButtonLabel={t("Common:Finish")}
|
||||
cancelButtonLabel={t("Settings:DownloadLog")}
|
||||
displaySettings
|
||||
showReminder
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
};
|
||||
})(observer(ImportCompleteStep));
|
@ -1,161 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import ImportSection from "../../../sub-components/ImportSection";
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import PeopleIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg";
|
||||
import AccountsIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg";
|
||||
import DocumentsIcon from "PUBLIC_DIR/images/catalog.documents.react.svg";
|
||||
import RoomsIcon from "PUBLIC_DIR/images/catalog.rooms.react.svg";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const SectionsWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
margin-top: 16px;
|
||||
|
||||
.save-cancel-buttons {
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ImportStep = ({
|
||||
t,
|
||||
incrementStep,
|
||||
decrementStep,
|
||||
importOptions,
|
||||
setImportOptions,
|
||||
}) => {
|
||||
const onChange = (e, name) => {
|
||||
const checked = e.target.checked;
|
||||
setImportOptions({ [name]: checked });
|
||||
};
|
||||
|
||||
const serviceName = "NextCloud";
|
||||
const users =
|
||||
t("Settings:Employees")[0].toUpperCase() + t("Settings:Employees").slice(1);
|
||||
|
||||
return (
|
||||
<SectionsWrapper>
|
||||
<ImportSection
|
||||
isChecked
|
||||
sectionName={users}
|
||||
description={t("Settings:UsersSectionDescription")}
|
||||
exportSection={{ sectionName: users, workspace: serviceName }}
|
||||
importSection={{
|
||||
sectionName: t("Common:Accounts"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: PeopleIcon,
|
||||
}}
|
||||
isDisabled
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importGroups}
|
||||
onChange={(e) => onChange(e, "importGroups")}
|
||||
sectionName={t("Common:Groups")}
|
||||
description={t("Settings:GroupsDescription", { serviceName })}
|
||||
exportSection={{
|
||||
sectionName: t("Common:Groups"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Accounts"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: AccountsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importPersonalFiles}
|
||||
onChange={(e) => onChange(e, "importPersonalFiles")}
|
||||
sectionName={t("Settings:PersonalFiles")}
|
||||
description={t("Settings:PersonalFilesDescription")}
|
||||
exportSection={{
|
||||
sectionName: "Google Drive's Files",
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Documents"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: DocumentsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importSharedFiles}
|
||||
onChange={(e) => onChange(e, "importSharedFiles")}
|
||||
sectionName={t("Settings:SharedFiles")}
|
||||
description={t("Settings:SharedFilesDescription")}
|
||||
exportSection={{
|
||||
sectionName: t("Settings:SharedFiles"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Documents"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: DocumentsIcon,
|
||||
}}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importSharedFolders}
|
||||
onChange={(e) => onChange(e, "importSharedFolders")}
|
||||
sectionName={t("Settings:SharedFolders")}
|
||||
description={t("Settings:SharedFoldersDescription")}
|
||||
exportSection={{
|
||||
sectionName: t("Settings:SharedFolders"),
|
||||
workspace: serviceName,
|
||||
}}
|
||||
importSection={{
|
||||
sectionName: t("Common:Rooms"),
|
||||
workspace: PRODUCT_NAME,
|
||||
SectionIcon: RoomsIcon,
|
||||
}}
|
||||
/>
|
||||
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={incrementStep}
|
||||
onCancelClick={decrementStep}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
showReminder
|
||||
/>
|
||||
</SectionsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const { importOptions, setImportOptions } = importAccountsStore;
|
||||
|
||||
return {
|
||||
importOptions,
|
||||
setImportOptions,
|
||||
};
|
||||
})(observer(ImportStep));
|
@ -1,417 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
|
||||
import { isTablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { FileInput } from "@docspace/shared/components/file-input";
|
||||
import { ProgressBar } from "@docspace/shared/components/progress-bar";
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 700px;
|
||||
margin-top: 16px;
|
||||
|
||||
.choose-backup-file {
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.upload-backup-input {
|
||||
height: 32px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.icon-button_svg {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.client.settings.migration.fileInputIconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-back-buttons {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.select-file-progress-text {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.select-file-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FileUploadContainer = styled.div`
|
||||
max-width: 350px;
|
||||
`;
|
||||
|
||||
const ErrorBlock = styled.div`
|
||||
max-width: 700px;
|
||||
|
||||
.complete-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
|
||||
}
|
||||
|
||||
.save-cancel-buttons {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FAILS_TRIES = 1;
|
||||
|
||||
const SelectFileStep = ({
|
||||
t,
|
||||
incrementStep,
|
||||
cancelDialogVisible,
|
||||
setCancelDialogVisibile,
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
}) => {
|
||||
const [isSaveDisabled, setIsSaveDisabled] = useState(true);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [isFileError, setIsFileError] = useState(false);
|
||||
const [fileName, setFileName] = useState(null);
|
||||
const [searchParams] = useSearchParams();
|
||||
const isAbort = useRef(false);
|
||||
const uploadInterval = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [failTries, setFailsTries] = useState(FAILS_TRIES);
|
||||
|
||||
const goBack = () => {
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
};
|
||||
|
||||
const checkMigrationStatusAndUpdate = async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res || res.parseResult.migratorName !== "Nextcloud") {
|
||||
clearInterval(uploadInterval.current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "parse" && !res.isCompleted) {
|
||||
setProgress(res.progress);
|
||||
setIsFileLoading(true);
|
||||
} else {
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
|
||||
setIsFileError(false);
|
||||
setIsSaveDisabled(false);
|
||||
|
||||
if (res.parseResult.files?.length > 0) {
|
||||
setFileName(res.parseResult.files.join(", "));
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
toastr.error(res.error);
|
||||
setIsFileError(true);
|
||||
setIsSaveDisabled(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
} else if (res.isCompleted || res.progress === 100) {
|
||||
setUsers(res.parseResult);
|
||||
setIsSaveDisabled(true);
|
||||
incrementStep && incrementStep();
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(error.message);
|
||||
setIsFileError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setIsSaveDisabled(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
|
||||
uploadInterval.current = setInterval(() => {
|
||||
checkMigrationStatusAndUpdate();
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(uploadInterval.current);
|
||||
}, []);
|
||||
|
||||
const onUploadToServer = () => {
|
||||
setIsSaveDisabled(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
};
|
||||
|
||||
const onUploadFile = async (file) => {
|
||||
setIsVisible(true);
|
||||
try {
|
||||
if (Array.isArray(file)) throw new Error(t("Common:SomethingWentWrong"));
|
||||
|
||||
await singleFileUploading(file, setProgress, isAbort);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
await initMigrationName(searchParams.get("service"));
|
||||
|
||||
uploadInterval.current = setInterval(async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res && failTries) {
|
||||
setFailsTries((tries) => tries - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
toastr.error(res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.parseResult.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsFileLoading(false);
|
||||
setIsVisible(false);
|
||||
setUsers(res.parseResult);
|
||||
setIsSaveDisabled(true);
|
||||
}
|
||||
|
||||
setProgress(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(error || error.message);
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
setIsError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
} finally {
|
||||
isAbort.current = false;
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
toastr.error(error || error.message);
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectFile = (file) => {
|
||||
setProgress(0);
|
||||
setIsFileError(false);
|
||||
setIsSaveDisabled(false);
|
||||
setIsFileLoading(true);
|
||||
setFailsTries(FAILS_TRIES);
|
||||
try {
|
||||
onUploadFile(file);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDownloadArchives = async () => {
|
||||
try {
|
||||
await getMigrationStatus()
|
||||
.then(
|
||||
(res) =>
|
||||
new Blob([res.parseResult.failedArchives], {
|
||||
type: "text/csv;charset=utf-8",
|
||||
}),
|
||||
)
|
||||
.then((blob) => {
|
||||
let a = document.createElement("a");
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = "unsupported_files";
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setCancelDialogVisibile(true);
|
||||
};
|
||||
|
||||
const handleCancelMigration = () => {
|
||||
isAbort.current = true;
|
||||
setProgress(0);
|
||||
setIsFileLoading(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
cancelMigration();
|
||||
};
|
||||
|
||||
const hideCancelDialog = () => setCancelDialogVisibile(false);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<FileUploadContainer>
|
||||
<Text className="choose-backup-file">
|
||||
{t("Settings:ChooseBackupFile")}
|
||||
</Text>
|
||||
<FileInput
|
||||
scale
|
||||
onInput={onSelectFile}
|
||||
className="upload-backup-input"
|
||||
placeholder={fileName || t("Settings:BackupFile")}
|
||||
isDisabled={isFileLoading}
|
||||
accept={[".zip"]}
|
||||
/>
|
||||
</FileUploadContainer>
|
||||
{isFileLoading ? (
|
||||
<FileUploadContainer>
|
||||
<ProgressBar
|
||||
percent={progress}
|
||||
isInfiniteProgress={isVisible}
|
||||
className="select-file-progress-bar"
|
||||
label={t("Settings:BackupFileUploading")}
|
||||
/>
|
||||
<Button
|
||||
size={isTablet() ? "medium" : "small"}
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</FileUploadContainer>
|
||||
) : (
|
||||
<ErrorBlock>
|
||||
{isFileError && (
|
||||
<Box>
|
||||
<ProgressBar
|
||||
percent={100}
|
||||
className="complete-progress-bar"
|
||||
label={t("Common:LoadingIsComplete")}
|
||||
/>
|
||||
<Text className="error-text">
|
||||
{t("Settings:UnsupportedFilesDescription")}
|
||||
</Text>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered
|
||||
fontWeight={600}
|
||||
onClick={onDownloadArchives}
|
||||
>
|
||||
{t("Settings:CheckUnsupportedFiles")}
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isError ? (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onUploadToServer}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:UploadToServer")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
isSaving={isSaveDisabled}
|
||||
displaySettings
|
||||
saveButtonDisabled={isSaveDisabled}
|
||||
showReminder
|
||||
/>
|
||||
) : (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={incrementStep}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
saveButtonDisabled={!isSaveDisabled}
|
||||
showReminder
|
||||
/>
|
||||
)}
|
||||
</ErrorBlock>
|
||||
)}
|
||||
|
||||
{cancelDialogVisible && (
|
||||
<CancelUploadDialog
|
||||
visible={cancelDialogVisible}
|
||||
// loading={isFileLoading}
|
||||
onClose={hideCancelDialog}
|
||||
cancelMigration={handleCancelMigration}
|
||||
/>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ dialogsStore, importAccountsStore }) => {
|
||||
const {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
} = importAccountsStore;
|
||||
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
cancelDialogVisible: cancelUploadDialogVisible,
|
||||
setCancelDialogVisibile: setCancelUploadDialogVisible,
|
||||
};
|
||||
})(observer(SelectFileStep));
|
@ -1,187 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { SearchInput } from "@docspace/shared/components/search-input";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import AccountsTable from "./AccountsTable";
|
||||
import AccountsPaging from "../../../sub-components/AccountsPaging";
|
||||
|
||||
import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
|
||||
import { Wrapper } from "../StyledStepper";
|
||||
|
||||
import { parseQuota } from "../../../utils";
|
||||
|
||||
const SelectUsersStep = (props) => {
|
||||
const {
|
||||
t,
|
||||
incrementStep,
|
||||
decrementStep,
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
users,
|
||||
} = props;
|
||||
|
||||
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
|
||||
const [quota, setQuota] = useState({ used: 0, max: 0 });
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchValue("");
|
||||
setQuota(parseQuota(quotaCharacteristics[1]));
|
||||
}, []);
|
||||
|
||||
const handleDataChange = (leftBoundary, rightBoundary) => {
|
||||
setDataPortion(withEmailUsers.slice(leftBoundary, rightBoundary));
|
||||
};
|
||||
|
||||
const onChangeInput = (value) => {
|
||||
setSearchValue(value);
|
||||
};
|
||||
|
||||
const onClearSearchInput = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.lastName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
const goBack = () => {
|
||||
cancelMigration();
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
decrementStep();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const numberOfSelectedUsers =
|
||||
checkedUsers.withEmail.length + checkedUsers.withoutEmail.length;
|
||||
|
||||
const totalUsedUsers =
|
||||
quota.used +
|
||||
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length +
|
||||
checkedUsers.withoutEmail.length;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{withEmailUsers.length > 0 ? (
|
||||
<>
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={incrementStep}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
showReminder
|
||||
displaySettings
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
|
||||
{quota.max && (
|
||||
<UsersInfoBlock
|
||||
t={t}
|
||||
totalUsedUsers={totalUsedUsers}
|
||||
selectedUsers={numberOfSelectedUsers}
|
||||
totalUsers={withEmailUsers.length + users.withoutEmail.length}
|
||||
totalLicenceLimit={quota.max}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SearchInput
|
||||
id="search-users-input"
|
||||
placeholder={t("Common:Search")}
|
||||
value={searchValue}
|
||||
onChange={onChangeInput}
|
||||
refreshTimeout={100}
|
||||
onClearSearch={onClearSearchInput}
|
||||
/>
|
||||
|
||||
<AccountsTable t={t} accountsData={filteredAccounts} />
|
||||
|
||||
{withEmailUsers.length > 25 && filteredAccounts.length > 0 && (
|
||||
<AccountsPaging
|
||||
t={t}
|
||||
numberOfItems={withEmailUsers.length}
|
||||
setDataPortion={handleDataChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:AddEmailsWarning")}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={incrementStep}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
showReminder
|
||||
displaySettings
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore, currentQuotaStore }) => {
|
||||
const {
|
||||
users,
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
} = importAccountsStore;
|
||||
const { quotaCharacteristics } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
users,
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
};
|
||||
})(observer(SelectUsersStep));
|
@ -1,137 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { TableHeader } from "@docspace/shared/components/table";
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const TABLE_COLUMNS = `nextcloudFourthColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const UsersTableHeader = (props) => {
|
||||
const {
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
setHideColumns,
|
||||
isIndeterminate,
|
||||
isChecked,
|
||||
} = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 180,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Type",
|
||||
title: t("Common:Type"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
minWidth: 100,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Email",
|
||||
title: t("Common:Email"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(getColumns(defaultColumns));
|
||||
}, [isIndeterminate, isChecked]);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="48px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
setHideColumns={setHideColumns}
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore }) => {
|
||||
return {
|
||||
userId: userStore.user.id,
|
||||
};
|
||||
})(observer(UsersTableHeader));
|
@ -1,147 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { TableRow } from "@docspace/shared/components/table";
|
||||
import { TableCell } from "@docspace/shared/components/table";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.table-container_cell {
|
||||
padding-right: 30px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
margin-right: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-type {
|
||||
.combo-button {
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
justify-content: flex-start;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.combo-button-label {
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.comboBoxLabelColor};
|
||||
}
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
flex: initial;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.client.settings.migration.comboBoxLabelColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UsersTypeTableRow = ({
|
||||
id,
|
||||
displayName,
|
||||
email,
|
||||
typeOptions,
|
||||
isChecked,
|
||||
toggleAccount,
|
||||
type,
|
||||
changeUserType,
|
||||
}) => {
|
||||
const userTypeRef = useRef();
|
||||
|
||||
const onSelectUser = (e) => {
|
||||
changeUserType(id, e.key);
|
||||
};
|
||||
|
||||
const selectedOption =
|
||||
typeOptions.find((option) => option.key === type) || {};
|
||||
|
||||
const handleAccountToggle = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.target.closest(".dropdown-container") ||
|
||||
userTypeRef.current?.contains(e.target) ||
|
||||
toggleAccount();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTableRow checked={isChecked} onClick={handleAccountToggle}>
|
||||
<TableCell className="checkboxWrapper">
|
||||
<Checkbox isChecked={isChecked} onChange={handleAccountToggle} />
|
||||
<Text className="username">{displayName}</Text>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<div ref={userTypeRef}>
|
||||
<ComboBox
|
||||
className="user-type"
|
||||
selectedOption={selectedOption}
|
||||
options={typeOptions}
|
||||
onSelect={onSelectUser}
|
||||
scaled
|
||||
size="content"
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth="fit-content"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Text className="user-email">{email}</Text>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const { changeUserType } = importAccountsStore;
|
||||
|
||||
return {
|
||||
changeUserType,
|
||||
};
|
||||
})(observer(UsersTypeTableRow));
|
@ -1,206 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { isMobile as isMobileBreakpoint } from "@docspace/shared/utils/device";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { getStepsData } from "./Stepper";
|
||||
import BreakpointWarning from "SRC_DIR/components/BreakpointWarning";
|
||||
import SelectFileLoader from "../sub-components/SelectFileLoader";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const NextcloudWrapper = styled.div`
|
||||
max-width: 700px;
|
||||
|
||||
.data-import-counter {
|
||||
margin-top: 19px;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.data-import-section-description {
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
`;
|
||||
|
||||
const NextcloudWorkspace = (props) => {
|
||||
const {
|
||||
t,
|
||||
tReady,
|
||||
theme,
|
||||
clearCheckedAccounts,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
organizationName,
|
||||
} = props;
|
||||
const [currentStep, setCurrentStep] = useState(1);
|
||||
const [shouldRender, setShouldRender] = useState(false);
|
||||
const StepsData = getStepsData(
|
||||
t,
|
||||
currentStep,
|
||||
setCurrentStep,
|
||||
filteredUsers.length === 0,
|
||||
organizationName,
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useViewEffect({
|
||||
view: viewAs,
|
||||
setView: setViewAs,
|
||||
currentDeviceType,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
getMigrationStatus().then((res) => {
|
||||
if (
|
||||
!res ||
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length ===
|
||||
0
|
||||
) {
|
||||
setShouldRender(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.migratorName !== "Nextcloud") {
|
||||
const workspacesEnum = {
|
||||
GoogleWorkspace: "google",
|
||||
Nextcloud: "nextcloud",
|
||||
Workspace: "onlyoffice",
|
||||
};
|
||||
const migratorName = res.parseResult.migratorName;
|
||||
|
||||
setShouldRender(true);
|
||||
navigate(
|
||||
`/portal-settings/data-import/migration/${workspacesEnum[migratorName]}?service=${migratorName}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "parse" && res.isCompleted) {
|
||||
setUsers(res.parseResult);
|
||||
setCurrentStep(2);
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "migration" && !res.isCompleted) {
|
||||
setCurrentStep(6);
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "migration" && res.isCompleted) {
|
||||
setCurrentStep(7);
|
||||
}
|
||||
|
||||
setShouldRender(true);
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
|
||||
return clearCheckedAccounts;
|
||||
}, []);
|
||||
|
||||
if (isMobile || isMobileBreakpoint())
|
||||
return (
|
||||
<BreakpointWarning
|
||||
isMobileUnavailableOnly
|
||||
sectionName={t("Settings:DataImport")}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!tReady || !shouldRender) return <SelectFileLoader />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextcloudWrapper>
|
||||
<Text
|
||||
className="data-import-description"
|
||||
lineHeight="20px"
|
||||
color={theme.isBase ? "#657077" : "#ADADAD"}
|
||||
>
|
||||
{t("Settings:AboutDataImport", {
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
})}
|
||||
</Text>
|
||||
<Text
|
||||
className="data-import-counter"
|
||||
fontSize="16px"
|
||||
fontWeight={700}
|
||||
lineHeight="22px"
|
||||
>
|
||||
{currentStep}/{StepsData.length}. {StepsData[currentStep - 1].title}
|
||||
</Text>
|
||||
<div className="data-import-section-description">
|
||||
{StepsData[currentStep - 1].description}
|
||||
</div>
|
||||
</NextcloudWrapper>
|
||||
{StepsData[currentStep - 1].component}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Component = inject(
|
||||
({ setup, settingsStore, importAccountsStore }) => {
|
||||
const {
|
||||
clearCheckedAccounts,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
} = importAccountsStore;
|
||||
const { initSettings, viewAs, setViewAs } = setup;
|
||||
const { currentDeviceType, organizationName } = settingsStore;
|
||||
|
||||
return {
|
||||
initSettings,
|
||||
theme: settingsStore.theme,
|
||||
clearCheckedAccounts,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
filteredUsers,
|
||||
organizationName,
|
||||
};
|
||||
},
|
||||
)(
|
||||
withTranslation(["Common, SMTPSettings, Settings"])(
|
||||
observer(NextcloudWorkspace),
|
||||
),
|
||||
);
|
@ -24,42 +24,28 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import SelectFileStep from "./SelectFileStep";
|
||||
import SelectUsersStep from "./SelectUsersStep";
|
||||
import AddEmailsStep from "./AddEmailsStep";
|
||||
import SelectUsersTypeStep from "./SelectUsersTypeStep";
|
||||
import ImportStep from "./ImportStep";
|
||||
import ImportProcessingStep from "./ImportProcessingStep";
|
||||
import ImportCompleteStep from "./ImportCompleteStep";
|
||||
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { Trans } from "react-i18next";
|
||||
import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
|
||||
import PeopleIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg?url";
|
||||
import SelectFileStep from "../../components/SelectFileStep";
|
||||
import SelectUsersStep from "../../components/SelectUsersStep";
|
||||
import AddEmailsStep from "../../components/AddEmailsStep";
|
||||
import SelectUsersTypeStep from "../../components/SelectUsersTypeStep";
|
||||
import ImportStep from "../../components/ImportStep";
|
||||
import ImportProcessingStep from "../../components/ImportProcessingStep";
|
||||
import ImportCompleteStep from "../../components/ImportCompleteStep";
|
||||
|
||||
import { TFunciton } from "../../types";
|
||||
|
||||
export const getStepsData = (
|
||||
t,
|
||||
currentStep,
|
||||
setCurrentStep,
|
||||
isTypeSelectEmpty,
|
||||
organizationName,
|
||||
t: TFunciton,
|
||||
isTypeSelectEmpty: boolean,
|
||||
organizationName: string,
|
||||
) => {
|
||||
const isSixthStep = currentStep === 6;
|
||||
|
||||
const incrementStep = () => {
|
||||
setCurrentStep((prev) => {
|
||||
const nextStep = prev < 7 ? prev + 1 : 7;
|
||||
return nextStep;
|
||||
});
|
||||
};
|
||||
|
||||
const decrementStep = () => {
|
||||
if (currentStep !== 1) {
|
||||
setCurrentStep((prev) => prev - 1);
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
title: t("Common:SelectFile"),
|
||||
@ -67,8 +53,9 @@ export const getStepsData = (
|
||||
component: (
|
||||
<SelectFileStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
isMultipleUpload={false}
|
||||
migratorName="Nextcloud"
|
||||
acceptedExtensions={[".zip"]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -79,11 +66,7 @@ export const getStepsData = (
|
||||
organizationName,
|
||||
}),
|
||||
component: (
|
||||
<SelectUsersStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
/>
|
||||
<SelectUsersStep t={t} canDisable={false} shouldSetUsers={false} />
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -92,13 +75,7 @@ export const getStepsData = (
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
}),
|
||||
component: (
|
||||
<AddEmailsStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
/>
|
||||
),
|
||||
component: <AddEmailsStep t={t} />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:SelectUserTypes"),
|
||||
@ -132,10 +109,10 @@ export const getStepsData = (
|
||||
t={t}
|
||||
values={{ productName: PRODUCT_NAME }}
|
||||
components={{
|
||||
1: <b></b>,
|
||||
2: <b></b>,
|
||||
3: <b></b>,
|
||||
4: <b></b>,
|
||||
1: <b />,
|
||||
2: <b />,
|
||||
3: <b />,
|
||||
4: <b />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
@ -149,13 +126,7 @@ export const getStepsData = (
|
||||
/>
|
||||
</>
|
||||
),
|
||||
component: (
|
||||
<SelectUsersTypeStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
/>
|
||||
),
|
||||
component: <SelectUsersTypeStep t={t} />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImport"),
|
||||
@ -165,22 +136,27 @@ export const getStepsData = (
|
||||
component: (
|
||||
<ImportStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
serviceName="Nextcloud"
|
||||
usersExportDetails={{
|
||||
name: t("Common:Accounts"),
|
||||
icon: PeopleIcon,
|
||||
}}
|
||||
personalExportDetails={{
|
||||
name: t("Settings:UsersFiles"),
|
||||
}}
|
||||
sharedFilesExportDetails={{
|
||||
name: t("Settings:SharedFiles"),
|
||||
}}
|
||||
sharedFoldersExportDetails={{
|
||||
name: t("Settings:SharedFolders"),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImportProcessing"),
|
||||
description: t("Settings:ImportProcessingDescription"),
|
||||
component: (
|
||||
<ImportProcessingStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
isSixthStep={isSixthStep}
|
||||
/>
|
||||
),
|
||||
component: <ImportProcessingStep t={t} migratorName="Nextcloud" />,
|
||||
},
|
||||
{
|
||||
title: t("Settings:DataImportComplete"),
|
||||
@ -188,13 +164,7 @@ export const getStepsData = (
|
||||
productName: PRODUCT_NAME,
|
||||
organizationName,
|
||||
}),
|
||||
component: (
|
||||
<ImportCompleteStep
|
||||
t={t}
|
||||
incrementStep={incrementStep}
|
||||
decrementStep={decrementStep}
|
||||
/>
|
||||
),
|
||||
component: <ImportCompleteStep t={t} />,
|
||||
},
|
||||
];
|
||||
};
|
@ -0,0 +1,117 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useLayoutEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getStepsData } from "./Stepper";
|
||||
|
||||
import SelectFileLoader from "../sub-components/SelectFileLoader";
|
||||
import StepLayout from "../sub-components/StepLayout";
|
||||
|
||||
import { InjectedWorkspaceProps, WorkspaceProps, TFunciton } from "../types";
|
||||
|
||||
const NextcloudWorkspace = (props: WorkspaceProps) => {
|
||||
const {
|
||||
theme,
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
organizationName,
|
||||
migratingWorkspace,
|
||||
migrationPhase,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
} = props as InjectedWorkspaceProps;
|
||||
|
||||
const { t, ready }: { t: TFunciton; ready: boolean } = useTranslation([
|
||||
"Common, SMTPSettings, Settings",
|
||||
]);
|
||||
|
||||
const StepsData = getStepsData(
|
||||
t,
|
||||
filteredUsers.length === 0,
|
||||
organizationName,
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (migratingWorkspace === "Nextcloud" && !isMigrationInit) {
|
||||
if (migrationPhase === "setup") {
|
||||
setStep(2);
|
||||
} else if (migrationPhase === "migrating") {
|
||||
setStep(6);
|
||||
} else if (migrationPhase === "complete") {
|
||||
setStep(7);
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
||||
return (
|
||||
<StepLayout
|
||||
t={t}
|
||||
theme={theme}
|
||||
step={step}
|
||||
totalSteps={StepsData.length}
|
||||
title={StepsData[step - 1].title}
|
||||
description={StepsData[step - 1].description}
|
||||
component={StepsData[step - 1].component}
|
||||
organizationName={organizationName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Component = inject<TStore>(
|
||||
({ settingsStore, importAccountsStore }) => {
|
||||
const {
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
setWorkspace,
|
||||
migratingWorkspace,
|
||||
migrationPhase,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
} = importAccountsStore;
|
||||
const { theme, organizationName } = settingsStore;
|
||||
|
||||
return {
|
||||
theme,
|
||||
filteredUsers,
|
||||
step,
|
||||
setStep,
|
||||
setWorkspace,
|
||||
organizationName,
|
||||
migratingWorkspace,
|
||||
migrationPhase,
|
||||
isMigrationInit,
|
||||
setIsMigrationInit,
|
||||
};
|
||||
},
|
||||
)(observer(NextcloudWorkspace));
|
@ -1,195 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 16px 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.checkbox-text {
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const InfoText = styled(Text)`
|
||||
margin-top: -8px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
`;
|
||||
|
||||
const ErrorText = styled(Text)`
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const ImportCompleteStep = ({
|
||||
t,
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
}) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [importResult, setImportResult] = useState({
|
||||
succeedUsers: 0,
|
||||
failedUsers: 0,
|
||||
errors: [],
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const onDownloadLog = async () => {
|
||||
try {
|
||||
await getMigrationLog()
|
||||
.then((response) => new Blob([response]))
|
||||
.then((blob) => {
|
||||
let a = document.createElement("a");
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = "migration.log";
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeCheckbox = () => {
|
||||
setIsChecked((prev) => !prev);
|
||||
};
|
||||
|
||||
const onFinishClick = () => {
|
||||
if (isChecked) {
|
||||
sendWelcomeLetter({ isSendWelcomeEmail: true });
|
||||
}
|
||||
clearMigration();
|
||||
clearCheckedAccounts();
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
navigate("/portal-settings/data-import/migration")
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
getMigrationStatus().then((res) =>
|
||||
setImportResult({
|
||||
succeedUsers: res.parseResult.successedUsers,
|
||||
failedUsers: res.parseResult.failedUsers,
|
||||
errors: res.parseResult.errors,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InfoText>
|
||||
{t("Settings:ImportedUsers", {
|
||||
selectedUsers: importResult.succeedUsers,
|
||||
importedUsers: importResult.succeedUsers + importResult.failedUsers,
|
||||
})}
|
||||
</InfoText>
|
||||
|
||||
{importResult.failedUsers > 0 && (
|
||||
<ErrorText>
|
||||
{t("Settings:ErrorsWereFound", {
|
||||
errors: importResult.failedUsers,
|
||||
})}
|
||||
</ErrorText>
|
||||
)}
|
||||
|
||||
{importResult.errors?.length > 0 && (
|
||||
<ErrorText>{t("Settings:ErrorOccuredDownloadLog")}</ErrorText>
|
||||
)}
|
||||
|
||||
<Wrapper>
|
||||
<Checkbox
|
||||
label={t("Settings:SendInviteLetter")}
|
||||
isChecked={isChecked}
|
||||
onChange={onChangeCheckbox}
|
||||
/>
|
||||
<HelpButton
|
||||
place="right"
|
||||
offsetRight={0}
|
||||
style={{ margin: "0px 5px" }}
|
||||
tooltipContent={
|
||||
<Text fontSize="12px">{t("Settings:InviteLetterTooltip")}</Text>
|
||||
}
|
||||
/>
|
||||
</Wrapper>
|
||||
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onFinishClick}
|
||||
onCancelClick={onDownloadLog}
|
||||
saveButtonLabel={t("Common:Finish")}
|
||||
cancelButtonLabel={t("Settings:DownloadLog")}
|
||||
displaySettings
|
||||
showReminder
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
getMigrationLog,
|
||||
clearCheckedAccounts,
|
||||
sendWelcomeLetter,
|
||||
clearMigration,
|
||||
getMigrationStatus,
|
||||
};
|
||||
})(observer(ImportCompleteStep));
|
@ -1,150 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isTablet } from "@docspace/shared/utils/device";
|
||||
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { ProgressBar } from "@docspace/shared/components/progress-bar";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.data-import-progress-bar {
|
||||
margin-top: -8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ImportProcessingStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
isFifthStep,
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
}) => {
|
||||
const [percent, setPercent] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const uploadInterval = useRef(null);
|
||||
|
||||
const handleFileMigration = async () => {
|
||||
setIsLoading(true);
|
||||
setPercent(0);
|
||||
setIsVisible(true);
|
||||
try {
|
||||
await proceedFileMigration("Workspace");
|
||||
|
||||
uploadInterval.current = setInterval(async () => {
|
||||
const res = await getMigrationStatus();
|
||||
setPercent(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsLoading(false);
|
||||
setIsVisible(false);
|
||||
setPercent(100);
|
||||
setTimeout(() => {
|
||||
onNextStep();
|
||||
}, 1000);
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// const hideCancelDialog = () => setIsVisible(false);
|
||||
|
||||
// const onCancel = () => {
|
||||
// setIsVisible(true);
|
||||
// };
|
||||
|
||||
// const handleCancelMigration = () => {
|
||||
// setIsLoading(false);
|
||||
// cancelMigration();
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
handleFileMigration();
|
||||
|
||||
return () => clearInterval(uploadInterval.current);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={percent}
|
||||
isInfiniteProgress={isVisible}
|
||||
className="data-import-progress-bar"
|
||||
/>
|
||||
{/* <Button
|
||||
size={isTablet() ? "medium" : "small"}
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
|
||||
{isVisible && (
|
||||
<CancelUploadDialog
|
||||
visible={isVisible}
|
||||
loading={false}
|
||||
isFifthStep={isFifthStep}
|
||||
cancelMigration={handleCancelMigration}
|
||||
onClose={hideCancelDialog}
|
||||
/>
|
||||
)} */}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
cancelMigration,
|
||||
getMigrationStatus,
|
||||
};
|
||||
})(observer(ImportProcessingStep));
|
@ -1,420 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
|
||||
import { isTablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { FileInput } from "@docspace/shared/components/file-input";
|
||||
import { ProgressBar } from "@docspace/shared/components/progress-bar";
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.select-file-title {
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-bottom: 4px;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.select-file-input {
|
||||
height: 32px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.icon-button_svg {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.client.settings.migration.fileInputIconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-file-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
width: 350px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ErrorBlock = styled.div`
|
||||
max-width: 700px;
|
||||
|
||||
.complete-progress-bar {
|
||||
margin: 12px 0 16px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
color: ${(props) => props.theme.client.settings.migration.errorTextColor};
|
||||
}
|
||||
|
||||
.save-cancel-buttons {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FAILS_TRIES = 1;
|
||||
|
||||
const SelectFileStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
showReminder,
|
||||
setShowReminder,
|
||||
cancelDialogVisble,
|
||||
setCancelDialogVisible,
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
}) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [showErrorText, setShowErrorText] = useState(false);
|
||||
const [isFileError, setIsFileError] = useState(false);
|
||||
const [fileName, setFileName] = useState(null);
|
||||
const [searchParams] = useSearchParams();
|
||||
const isAbort = useRef(false);
|
||||
const uploadInterval = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [failTries, setFailsTries] = useState(FAILS_TRIES);
|
||||
|
||||
const goBack = () => {
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
};
|
||||
|
||||
const checkMigrationStatusAndUpdate = async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res || res.parseResult.migratorName !== "Workspace") {
|
||||
clearInterval(uploadInterval.current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.operation === "parse" && !res.isCompleted) {
|
||||
setProgress(res.progress);
|
||||
setIsFileLoading(true);
|
||||
} else {
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
|
||||
setIsFileError(false);
|
||||
setShowReminder(true);
|
||||
|
||||
if (res.parseResult.files?.length > 0) {
|
||||
setFileName(res.parseResult.files.join(", "));
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
setIsFileError(false);
|
||||
setShowReminder(false);
|
||||
setFileName(null);
|
||||
clearInterval(uploadInterval.current);
|
||||
} else if (res.isCompleted || res.progress === 100) {
|
||||
setUsers(res.parseResult);
|
||||
setShowReminder(true);
|
||||
onNextStep && onNextStep();
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(error.message || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setShowReminder(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
|
||||
uploadInterval.current = setInterval(() => {
|
||||
checkMigrationStatusAndUpdate();
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(uploadInterval.current);
|
||||
}, []);
|
||||
|
||||
const onUploadToServer = () => {
|
||||
setShowReminder(false);
|
||||
checkMigrationStatusAndUpdate();
|
||||
};
|
||||
|
||||
const onUploadFile = async (file) => {
|
||||
setIsVisible(true);
|
||||
try {
|
||||
if (Array.isArray(file)) {
|
||||
setShowErrorText(true);
|
||||
throw new Error(t("Common:SomethingWentWrong"));
|
||||
}
|
||||
|
||||
await singleFileUploading(file, setProgress, isAbort);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
await initMigrationName(searchParams.get("service"));
|
||||
|
||||
uploadInterval.current = setInterval(async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
|
||||
if (!res && failTries) {
|
||||
setFailsTries((tries) => tries - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
toastr.error(res.error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.parseResult.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsFileLoading(false);
|
||||
setIsVisible(false);
|
||||
setUsers(res.parseResult);
|
||||
setShowReminder(true);
|
||||
}
|
||||
|
||||
setProgress(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
|
||||
if (res.error) {
|
||||
setShowErrorText(true);
|
||||
} else {
|
||||
setShowErrorText(false);
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
setIsError(true);
|
||||
clearInterval(uploadInterval.current);
|
||||
} finally {
|
||||
isAbort.current = false;
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectFile = (file) => {
|
||||
setProgress(0);
|
||||
setIsFileError(false);
|
||||
setShowReminder(false);
|
||||
setIsFileLoading(true);
|
||||
setFailsTries(FAILS_TRIES);
|
||||
try {
|
||||
onUploadFile(file);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
setIsFileLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDownloadArchives = async () => {
|
||||
try {
|
||||
await getMigrationStatus()
|
||||
.then(
|
||||
(res) =>
|
||||
new Blob([res.parseResult.failedArchives], {
|
||||
type: "text/csv;charset=utf-8",
|
||||
}),
|
||||
)
|
||||
.then((blob) => {
|
||||
let a = document.createElement("a");
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = "unsupported_files";
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setCancelDialogVisible(true);
|
||||
};
|
||||
|
||||
const handleCancelMigration = () => {
|
||||
isAbort.current = true;
|
||||
setProgress(0);
|
||||
setIsFileLoading(false);
|
||||
clearInterval(uploadInterval.current);
|
||||
cancelMigration();
|
||||
};
|
||||
|
||||
const hideCancelDialog = () => setCancelDialogVisible(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper>
|
||||
<Text className="select-file-title">
|
||||
{t("Settings:ChooseBackupFile")}
|
||||
</Text>
|
||||
<FileInput
|
||||
scale
|
||||
onInput={onSelectFile}
|
||||
className="select-file-input"
|
||||
placeholder={fileName || t("Settings:BackupFile")}
|
||||
isDisabled={isFileLoading}
|
||||
accept={[".zip", ".tar", ".tar.gz"]}
|
||||
/>
|
||||
</Wrapper>
|
||||
|
||||
{isFileLoading ? (
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={progress}
|
||||
isInfiniteProgress={isVisible}
|
||||
className="select-file-progress-bar"
|
||||
label={t("Settings:BackupFileUploading")}
|
||||
/>
|
||||
<Button
|
||||
size={isTablet() ? "medium" : "small"}
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</Wrapper>
|
||||
) : (
|
||||
<ErrorBlock>
|
||||
{isFileError && (
|
||||
<Box>
|
||||
<ProgressBar
|
||||
percent={100}
|
||||
className="complete-progress-bar"
|
||||
label={t("Common:LoadingIsComplete")}
|
||||
/>
|
||||
<Text className="error-text">
|
||||
{showErrorText
|
||||
? t("Settings:UnsupportedFilesDescription")
|
||||
: t("Settings:UnsupportedFilesWithUploadDesc")}
|
||||
</Text>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered
|
||||
fontWeight={600}
|
||||
onClick={onDownloadArchives}
|
||||
>
|
||||
{t("Settings:CheckUnsupportedFiles")}
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isError ? (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onUploadToServer}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:UploadToServer")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
isSaving={showReminder}
|
||||
displaySettings
|
||||
saveButtonDisabled={showReminder}
|
||||
showReminder
|
||||
/>
|
||||
) : (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={onNextStep}
|
||||
onCancelClick={goBack}
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
saveButtonDisabled={!showReminder}
|
||||
showReminder
|
||||
/>
|
||||
)}
|
||||
</ErrorBlock>
|
||||
)}
|
||||
|
||||
{cancelDialogVisble && (
|
||||
<CancelUploadDialog
|
||||
visible={cancelDialogVisble}
|
||||
// loading={isFileLoading}
|
||||
onClose={hideCancelDialog}
|
||||
cancelMigration={handleCancelMigration}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ dialogsStore, importAccountsStore }) => {
|
||||
const {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
} = importAccountsStore;
|
||||
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
isFileLoading,
|
||||
setIsFileLoading,
|
||||
cancelMigration,
|
||||
cancelDialogVisble: cancelUploadDialogVisible,
|
||||
setCancelDialogVisible: setCancelUploadDialogVisible,
|
||||
};
|
||||
})(observer(SelectFileStep));
|
@ -1,57 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import UsersRowContent from "./UsersRowContent";
|
||||
|
||||
const UsersRow = (props) => {
|
||||
const { t, data, sectionWidth, isChecked, toggleAccount } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
data={data}
|
||||
checkbox
|
||||
checked={isChecked}
|
||||
onClick={toggleAccount}
|
||||
onSelect={toggleAccount}
|
||||
contextButtonSpacerWidth="0"
|
||||
>
|
||||
<UsersRowContent
|
||||
t={t}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
isDuplicate={data.isDuplicate}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersRow;
|
@ -1,99 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContent } from "@docspace/shared/components/row-content";
|
||||
|
||||
const StyledRowContent = styled(RowContent)`
|
||||
display: flex;
|
||||
|
||||
.rowMainContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.username {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 5px;
|
||||
`
|
||||
: css`
|
||||
margin-right: 5px;
|
||||
`}
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-existing {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.existingTextColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const UsersRowContent = ({
|
||||
t,
|
||||
data,
|
||||
sectionWidth,
|
||||
displayName,
|
||||
email,
|
||||
isDuplicate,
|
||||
}) => {
|
||||
const contentData = [
|
||||
<div key={data.key}>
|
||||
<Box displayProp="flex">
|
||||
<Text className="username">{displayName}</Text>
|
||||
{isDuplicate && (
|
||||
<Text className="user-existing">
|
||||
({t("Settings:AccountAlreadyExists")})
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Text className="user-email">{email}</Text>
|
||||
</div>,
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledRowContent sectionWidth={sectionWidth}>
|
||||
{contentData}
|
||||
</StyledRowContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersRowContent;
|
@ -1,195 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import UsersRow from "./UsersRow";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 348px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
|
||||
.row-header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 6px;
|
||||
`
|
||||
: css`
|
||||
margin-left: 6px;
|
||||
`}
|
||||
}
|
||||
|
||||
.row-header-title {
|
||||
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
.row_content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const checkedAccountType = "withEmail";
|
||||
|
||||
const RowView = ({
|
||||
t,
|
||||
withEmailUsers,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
}) => {
|
||||
const rowRef = useRef(null);
|
||||
|
||||
const toggleAll = (e) => {
|
||||
toggleAllAccounts(e.target.checked, withEmailUsers, checkedAccountType);
|
||||
};
|
||||
|
||||
const handleToggle = (user) => toggleAccount(user, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withEmail.length > 0 &&
|
||||
checkedUsers.withEmail.length !== withEmailUsers.length;
|
||||
|
||||
const isChecked = checkedUsers.withEmail.length === withEmailUsers.length;
|
||||
|
||||
return (
|
||||
<StyledRowContainer forwardedRef={rowRef} useReactWindow={false}>
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow sectionWidth={sectionWidth}>
|
||||
<div className="row-header-item">
|
||||
{checkedUsers.withEmail.length > 0 && (
|
||||
<Checkbox
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
)}
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</div>
|
||||
</StyledRow>
|
||||
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
withEmailUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
withEmailUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(RowView));
|
@ -1,140 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { TableHeader } from "@docspace/shared/components/table";
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const TABLE_COLUMNS = `GoogleWorkspaceColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const UsersTableHeader = (props) => {
|
||||
const {
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
isIndeterminate,
|
||||
isChecked,
|
||||
toggleAll,
|
||||
} = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 180,
|
||||
checkbox: {
|
||||
value: isChecked,
|
||||
isIndeterminate,
|
||||
onChange: toggleAll,
|
||||
},
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Email",
|
||||
title: t("Common:Email"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Dublicate",
|
||||
title: t("Settings:DuplicateNoun"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setColumns(getColumns(defaultColumns));
|
||||
}, [isIndeterminate, isChecked]);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="48px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore }) => {
|
||||
return {
|
||||
userId: userStore.user.id,
|
||||
};
|
||||
})(observer(UsersTableHeader));
|
@ -1,100 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { TableRow } from "@docspace/shared/components/table";
|
||||
import { TableCell } from "@docspace/shared/components/table";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.table-container_cell {
|
||||
padding-right: 30px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.client.settings.migration.subtitleColor};
|
||||
}
|
||||
|
||||
.user-email {
|
||||
margin-right: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.not-existing {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.user-existing {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.existingTextColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const NOT_EXIST = "—";
|
||||
|
||||
const UsersTableRow = ({
|
||||
t,
|
||||
displayName,
|
||||
email,
|
||||
isDuplicate,
|
||||
isChecked,
|
||||
toggleAccount,
|
||||
}) => {
|
||||
return (
|
||||
<StyledTableRow checked={isChecked} onClick={toggleAccount}>
|
||||
<TableCell className="checkboxWrapper">
|
||||
<Checkbox isChecked={isChecked} onChange={toggleAccount} />
|
||||
<Text className="username">{displayName}</Text>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Text className="user-email">{email}</Text>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
{isDuplicate ? (
|
||||
<Text className="user-existing">{t("Settings:AccountAlreadyExists")}</Text>
|
||||
) : (
|
||||
<Text className="not-existing">{NOT_EXIST}</Text>
|
||||
)}
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersTableRow;
|
@ -1,246 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import UsersTableHeader from "./UsersTableHeader";
|
||||
import UsersTableRow from "./UsersTableRow";
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
import { TableBody } from "@docspace/shared/components/table";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledTableContainer = styled(TableContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 28px 0 15px;
|
||||
`
|
||||
: css`
|
||||
padding: 0px 15px 0 28px;
|
||||
`}
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding: 0;
|
||||
padding-inline-start: 8px;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
cursor: pointer;
|
||||
|
||||
padding-left: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
|
||||
.table-container_cell {
|
||||
margin-top: -1px;
|
||||
border-top: ${(props) =>
|
||||
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
|
||||
|
||||
margin-left: -24px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
margin-right: -20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-list-item:has(.selected-table-row) {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 618px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTableContainer.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const COLUMNS_SIZE = `googleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelGoogleWorkspaceColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const checkedAccountType = "withEmail";
|
||||
|
||||
const TableView = ({
|
||||
t,
|
||||
withEmailUsers,
|
||||
userId,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
}) => {
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const toggleAll = (e) => {
|
||||
toggleAllAccounts(e.target.checked, withEmailUsers, checkedAccountType);
|
||||
};
|
||||
|
||||
const handleToggle = (e, user) => {
|
||||
e.stopPropagation();
|
||||
toggleAccount(user, checkedAccountType);
|
||||
};
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withEmail.length > 0 &&
|
||||
checkedUsers.withEmail.length !== withEmailUsers.length;
|
||||
|
||||
const isChecked = checkedUsers.withEmail.length === withEmailUsers.length;
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
userId={userId}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
isDuplicate={data.isDuplicate}
|
||||
data={data}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={(e) => handleToggle(e, data)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore, importAccountsStore }) => {
|
||||
const { id: userId } = userStore.user;
|
||||
const {
|
||||
withEmailUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
withEmailUsers,
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(TableView));
|
@ -1,62 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Consumer } from "@docspace/shared/utils/context";
|
||||
|
||||
import TableView from "./TableView";
|
||||
import RowView from "./RowView";
|
||||
|
||||
const AccountsTable = ({ t, viewAs, accountsData }) => {
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<TableView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
/>
|
||||
) : (
|
||||
<RowView
|
||||
t={t}
|
||||
sectionWidth={context.sectionWidth}
|
||||
accountsData={accountsData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup }) => {
|
||||
const { viewAs } = setup;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
};
|
||||
})(withTranslation(["People"])(observer(AccountsTable)));
|
@ -1,185 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
|
||||
import { SearchInput } from "@docspace/shared/components/search-input";
|
||||
|
||||
import AccountsTable from "./AccountsTable";
|
||||
import AccountsPaging from "../../../sub-components/AccountsPaging";
|
||||
import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
|
||||
|
||||
import { parseQuota } from "../../../utils";
|
||||
|
||||
const SelectUsersStep = ({
|
||||
t,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
withEmailUsers,
|
||||
setResultUsers,
|
||||
areCheckedUsersEmpty,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
}) => {
|
||||
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
|
||||
const [quota, setQuota] = useState({ used: 0, max: 0 });
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchValue("");
|
||||
setQuota(parseQuota(quotaCharacteristics[1]));
|
||||
}, []);
|
||||
|
||||
const handleDataChange = (leftBoundary, rightBoundary) => {
|
||||
setDataPortion(withEmailUsers.slice(leftBoundary, rightBoundary));
|
||||
};
|
||||
|
||||
const onChangeInput = (value) => {
|
||||
setSearchValue(value);
|
||||
};
|
||||
|
||||
const onClearSearchInput = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.lastName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleStepIncrement = () => {
|
||||
setResultUsers();
|
||||
onNextStep();
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
cancelMigration();
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
onPrevStep();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const totalUsedUsers =
|
||||
quota.used +
|
||||
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={handleStepIncrement}
|
||||
onCancelClick={goBack}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
saveButtonDisabled={
|
||||
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
|
||||
}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
|
||||
{quota.max && (
|
||||
<UsersInfoBlock
|
||||
t={t}
|
||||
totalUsedUsers={totalUsedUsers}
|
||||
selectedUsers={checkedUsers.withEmail.length}
|
||||
totalUsers={withEmailUsers.length}
|
||||
totalLicenceLimit={quota.max}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SearchInput
|
||||
id="search-users-input"
|
||||
placeholder={t("Common:Search")}
|
||||
style={{ marginTop: "20px" }}
|
||||
value={searchValue}
|
||||
onChange={onChangeInput}
|
||||
refreshTimeout={100}
|
||||
onClearSearch={onClearSearchInput}
|
||||
/>
|
||||
|
||||
<AccountsTable t={t} accountsData={filteredAccounts} />
|
||||
|
||||
{withEmailUsers.length > 25 && filteredAccounts.length > 0 && (
|
||||
<AccountsPaging
|
||||
t={t}
|
||||
numberOfItems={withEmailUsers.length}
|
||||
setDataPortion={handleDataChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && (
|
||||
<SaveCancelButtons
|
||||
className="save-cancel-buttons"
|
||||
onSaveClick={handleStepIncrement}
|
||||
onCancelClick={goBack}
|
||||
showReminder
|
||||
saveButtonLabel={t("Settings:NextStep")}
|
||||
cancelButtonLabel={t("Common:Back")}
|
||||
displaySettings
|
||||
saveButtonDisabled={
|
||||
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
|
||||
}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore, currentQuotaStore }) => {
|
||||
const {
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
setResultUsers,
|
||||
areCheckedUsersEmpty,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
} = importAccountsStore;
|
||||
const { quotaCharacteristics } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
setResultUsers,
|
||||
areCheckedUsersEmpty,
|
||||
withEmailUsers,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
cancelMigration,
|
||||
checkedUsers,
|
||||
quotaCharacteristics,
|
||||
};
|
||||
})(observer(SelectUsersStep));
|
@ -1,67 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useRef } from "react";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import UsersTypeRowContent from "./UsersTypeRowContent";
|
||||
|
||||
const UsersTypeRow = ({
|
||||
data,
|
||||
sectionWidth,
|
||||
typeOptions,
|
||||
isChecked,
|
||||
toggleAccount,
|
||||
}) => {
|
||||
const roleSelectorRef = useRef();
|
||||
|
||||
const handleAccountToggle = (e) => {
|
||||
roleSelectorRef.current?.contains(e.target) || toggleAccount();
|
||||
};
|
||||
|
||||
return (
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
key={data.key}
|
||||
data={data}
|
||||
checked={isChecked}
|
||||
contextButtonSpacerWidth="0"
|
||||
onRowClick={handleAccountToggle}
|
||||
onSelect={handleAccountToggle}
|
||||
>
|
||||
<UsersTypeRowContent
|
||||
id={data.key}
|
||||
sectionWidth={sectionWidth}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
type={data.userType}
|
||||
typeOptions={typeOptions}
|
||||
roleSelectorRef={roleSelectorRef}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersTypeRow;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user