Merge branch 'develop' into feature/update-react-router-dom

This commit is contained in:
Timofey Boyko 2024-07-17 15:22:40 +03:00
commit e04a925303
200 changed files with 5002 additions and 9993 deletions

View File

@ -146,7 +146,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "إعدادات اللغة والمنطقة الزمنية هي طريقة لتغير لغة البوابة بأكملها لجميع مستخدمين البوابة ولتهيئة المنطقة الزمنية حيث يمكنهم متابعة أحداث البوابة المعروضة بالوقت والتاريخ الصحيح.",
"LastUpdate": "اخر تحديث: {{date}}",
"LicenseLimitCounter": "حد الترخيص للمدراء/المستخدمين ذوي الصلاحيات:",
"LicenseLimitDescription": "يتكون عداد حد الترخيص من: الحسابات الموجودة بالفعل في {{productName}} والمستخدمين الجدد الذين تريد استيرادهم. إذا قمت باستيراد مستخدمين لديهم حساب {{productName}} بالفعل، فلن يتم احتسابهم مرة أخرى في العداد. يسمح لك ترخيص {{productName}} الخاص بك بالحصول على 100 مستخدم كحد أقصى.",
"LicenseLimitDescription": "يتكون عداد حد الترخيص من: الحسابات الموجودة بالفعل في {{productName}} والمستخدمين الجدد الذين تريد استيرادهم. إذا قمت باستيراد مستخدمين لديهم حساب {{productName}} بالفعل، فلن يتم احتسابهم مرة أخرى في العداد. يسمح لك ترخيص {{productName}} الخاص بك بالحصول على {{maxLimit}} مستخدم كحد أقصى.",
"Lifetime": "الوقت المتبقي (دقيقة)",
"LimitThemesTooltip": "يمكنك فقط إنشاء 3 قوالب مخصصة. لإنشاء واحدة جديدة ، يجب عليك حذف أحد القوالب السابقة.",
"LocalFile": "ملف محلي",

View File

@ -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",

View File

@ -147,7 +147,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Настройките за Език и Часова зона са начин за промяна на езика на целия портал за всички потребители на портала и за конфигуриране на часовата зона, така че всички събития на портала да се показват с правилните дата и час.",
"LastUpdate": "Последна актуализация: {{date}}",
"LicenseLimitCounter": "Ограничение на лиценза Администратори/Мощност:",
"LicenseLimitDescription": "Броячът за ограничение на лиценза се състои от: вече съществуващи профили в {{productName}} и нови потребители, които искате да входирате. Ако входирате потребители, които вече имат профил в {{productName}}, те няма да бъдат преброени отново в брояча. Вашият лиценз за {{productName}} ви позволява да имате максимум 100 потребителя.",
"LicenseLimitDescription": "Броячът за ограничение на лиценза се състои от: вече съществуващи профили в {{productName}} и нови потребители, които искате да входирате. Ако входирате потребители, които вече имат профил в {{productName}}, те няма да бъдат преброени отново в брояча. Вашият лиценз за {{productName}} ви позволява да имате максимум {{maxLimit}} потребителя.",
"Lifetime": "Живот (мин.)",
"LimitThemesTooltip": "Можете да създадете само 3 персонализирани теми. За да създадете нова, трябва да изтриете една от предишните теми.",
"LocalFile": "Локален файл",

View File

@ -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",

View File

@ -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",

View File

@ -145,7 +145,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Οι Ρυθμίσεις γλώσσας και ζώνης ώρας είναι ένας τρόπος για να αλλάξετε τη γλώσσα ολόκληρης της πύλης για όλους τους χρήστες της πύλης και να ρυθμίσετε τη ζώνη ώρας, ώστε όλα τα συμβάντα της πύλης να εμφανίζονται με τη σωστή ημερομηνία και ώρα.",
"LastUpdate": "Τελευταία ενημέρωση: {{date}}",
"LicenseLimitCounter": "Όριο άδειας χρήσης Διαχειριστές/Ισχύς:",
"LicenseLimitDescription": "Ο μετρητής του ορίου αδειών χρήσης αποτελείται από: τους ήδη υπάρχοντες λογαριασμούς στο {{productName}} και τους νέους χρήστες που θέλετε να εισαγάγετε. Εάν εισαγάγετε χρήστες που έχουν ήδη λογαριασμό στο {{productName}}, δεν θα υπολογίζονται ξανά στον μετρητή. Η άδεια χρήσης του {{productName}} σάς επιτρέπει να έχετε το πολύ 100 χρήστες.",
"LicenseLimitDescription": "Ο μετρητής του ορίου αδειών χρήσης αποτελείται από: τους ήδη υπάρχοντες λογαριασμούς στο {{productName}} και τους νέους χρήστες που θέλετε να εισαγάγετε. Εάν εισαγάγετε χρήστες που έχουν ήδη λογαριασμό στο {{productName}}, δεν θα υπολογίζονται ξανά στον μετρητή. Η άδεια χρήσης του {{productName}} σάς επιτρέπει να έχετε το πολύ {{maxLimit}} χρήστες.",
"Lifetime": "Διάρκεια ζωής (λεπτά)",
"LimitThemesTooltip": "Μπορείτε να δημιουργήσετε μόνο 3 προσαρμοσμένα θέματα. Για να δημιουργήσετε ένα νέο, πρέπει να διαγράψετε ένα από τα προηγούμενα θέματα.",
"LocalFile": "Τοπικό αρχείο",

View File

@ -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",

View File

@ -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": "Users 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",

View File

@ -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",

View File

@ -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",

View File

@ -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 lun des thèmes précédents.",
"LocalFile": "Fichier local",

View File

@ -147,7 +147,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Լեզվի և ժամային գոտու կարգավորումները պորտալի բոլոր օգտագործողների համար ամբողջ պորտալի լեզուն փոխելու և ժամային գոտին այնպես կարգավորելու միջոց է, որպեսզի պորտալի բոլոր իրադարձությունները ցուցադրվեն ճիշտ ամսաթվով և ժամով:",
"LastUpdate": "Վերջին թարմացում: {{date}}",
"LicenseLimitCounter": "Լիցենզիայի սահմանաչափ ադմիններ/հզորություն.",
"LicenseLimitDescription": "Լիցենզիայի սահմանաչափը բաղկացած է {{productName}}-ում արդեն գոյություն ունեցող հաշիվներից և նոր օգտվողներից, որոնք ցանկանում եք ներմուծել: Եթե ներմուծում եք օգտատերեր, ովքեր արդեն ունեն {{productName}} հաշիվ, նրանք կրկին չեն հաշվվելու հաշվիչում: Ձեր {{productName}} լիցենզիան թույլ է տալիս ունենալ առավելագույնը 100 օգտվող:",
"LicenseLimitDescription": "Լիցենզիայի սահմանաչափը բաղկացած է {{productName}}-ում արդեն գոյություն ունեցող հաշիվներից և նոր օգտվողներից, որոնք ցանկանում եք ներմուծել: Եթե ներմուծում եք օգտատերեր, ովքեր արդեն ունեն {{productName}} հաշիվ, նրանք կրկին չեն հաշվվելու հաշվիչում: Ձեր {{productName}} լիցենզիան թույլ է տալիս ունենալ առավելագույնը {{maxLimit}} օգտվող:",
"Lifetime": "Աշխատաժամ (րոպե)",
"LimitThemesTooltip": "Դուք կարող եք ստեղծել միայն 3 հատուկ թեմա: Նորը ստեղծելու համար դուք պետք է ջնջեք նախորդ թեմաներից մեկը:",
"LocalFile": "Տեղական ֆայլ",

View File

@ -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",

View File

@ -146,7 +146,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "言語とタイムゾーンの設定は、すべてのポータルユーザーのためにポータル全体の言語を変更し、ポータルのすべてのイベントが正しい日付と時刻で表示されるようにタイムゾーンを設定する方法です。",
"LastUpdate": "最終更新日:{{date}} ",
"LicenseLimitCounter": "ライセンス制限 管理者/パワー:",
"LicenseLimitDescription": "ライセンス制限カウンターは、{{productName}}に既に存在するアカウントと、インポートする新規ユーザーで構成されます。既に{{productName}}アカウントを持っているユーザーをインポートした場合、そのユーザーはカウントされません。現在の{{productName}}ライセンスでは、最大100人のユーザーを持つことができます。",
"LicenseLimitDescription": "ライセンス制限カウンターは、{{productName}}に既に存在するアカウントと、インポートする新規ユーザーで構成されます。既に{{productName}}アカウントを持っているユーザーをインポートした場合、そのユーザーはカウントされません。現在の{{productName}}ライセンスでは、最大{{maxLimit}}人のユーザーを持つことができます。",
"Lifetime": "有効期間(分)",
"LimitThemesTooltip": "カスタムテーマは3つまでしか作成できません。新しく作成する場合は、以前のテーマを1つ削除する必要があります。",
"LocalFile": "ローカルファイル",

View File

@ -147,7 +147,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "언어 및 시간대 설정을 통해 모든 포털 사용자에 대해 전체 포털의 언어를 변경하고 포털의 모든 이벤트가 올바른 날짜와 시간으로 표시되도록 시간대를 구성할 수 있습니다.",
"LastUpdate": "마지막 업데이트: {{date}}",
"LicenseLimitCounter": "라이센스 제한 관리자/권한:",
"LicenseLimitDescription": "라이센스 제한 카운터는 {{productName}}의 기존 계정과 가져오기하려는 새 사용자로 구성됩니다. 이미 {{productName}} 계정이 있는 사용자를 가져오기하면 해당 사용자는 카운터에 다시 계산되지 않습니다.{{productName}} 라이센스를 사용하면 최대 100명의 사용자를 보유할 수 있습니다.",
"LicenseLimitDescription": "라이센스 제한 카운터는 {{productName}}의 기존 계정과 가져오기하려는 새 사용자로 구성됩니다. 이미 {{productName}} 계정이 있는 사용자를 가져오기하면 해당 사용자는 카운터에 다시 계산되지 않습니다.{{productName}} 라이센스를 사용하면 최대 {{maxLimit}}명의 사용자를 보유할 수 있습니다.",
"Lifetime": "수명(분)",
"LimitThemesTooltip": "3개의 사용자 지정 테마만 생성할 수 있습니다. 새 테마를 생성하려면 이미 생성한 테마 중 하나를 삭제해야 합니다.",
"LocalFile": "로컬 파일",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -149,7 +149,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Настройки языка и часового пояса позволяют изменить язык всего портала для всех пользователей и настроить часовой пояс, чтобы все события на портале отображались с корректной датой и временем.",
"LastUpdate": "Последнее обновление: {{date}}",
"LicenseLimitCounter": "Ограничение лицензии Администраторы/Опытные пользователи:",
"LicenseLimitDescription": "Счетчик лимита лицензии состоит из: уже существующих учетных записей в {{productName}} и новых пользователей, которых вы хотите импортировать. Если вы импортируете пользователей, у которых уже есть учетная запись {{productName}}, они больше не будут учитываться в счетчике. Ваша лицензия {{productName}} позволяет вам иметь максимум 100 пользователей.",
"LicenseLimitDescription": "Счетчик лимита лицензии состоит из: уже существующих учетных записей в {{productName}} и новых пользователей, которых вы хотите импортировать. Если вы импортируете пользователей, у которых уже есть учетная запись {{productName}}, они больше не будут учитываться в счетчике. Ваша лицензия {{productName}} позволяет вам иметь максимум {{maxLimit}} пользователей.",
"Lifetime": "Срок службы (мин)",
"LimitThemesTooltip": "Вы можете создать только 3 пользовательские темы. Чтобы создать новую, необходимо удалить одну из предыдущих тем.",
"LocalFile": "Локальный файл",

View File

@ -136,7 +136,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "භාෂාව සහ වේලා කලාපයේ සැකසුම් මගින් අවකාශයේ භාෂාව සහ වේලා කලාපය සැකසීමට හැකිය. එමඟින්, සියළුම ක්‍රියාකාරකම්වල දිනය සහ වේලාව නිවැරදිව පෙන්වනු ඇත. පරිශ්‍රීලකයින්ට තමන්ගේ පැතිකඩ හරහා ද රිසි පරිදි මෙම සැකසුම් වෙනස් කිරීමට හැකිය.",
"LastUpdate": "අවසාන යාවත්කාලය: {{date}}",
"LicenseLimitCounter": "බලපත්‍රයේ සීමාව පරිපාලකයින්/බලවත්:",
"LicenseLimitDescription": "බලපත්‍රයේ සීමාව ගණනය වන්නේ {{productName}} හි දැනටමත් පවතින ගිණුම් සහ ඔබට ආයාත කිරීමට වුවමනා නව පරිශ්‍රීලකයින් අනුව වේ. ඔබ දැනටමත් {{productName}} ගිණුම් තිබෙන පරිශ්‍රීලකයින් ආයාත කළහොත් ඔවුන් නැවත ගණනය නොවේ. ඔබගේ {{productName}} බලපත්‍රය සඳහා උපරිම පරිශ්‍රීලකයින් 100 ක් ලද හැකිය.",
"LicenseLimitDescription": "බලපත්‍රයේ සීමාව ගණනය වන්නේ {{productName}} හි දැනටමත් පවතින ගිණුම් සහ ඔබට ආයාත කිරීමට වුවමනා නව පරිශ්‍රීලකයින් අනුව වේ. ඔබ දැනටමත් {{productName}} ගිණුම් තිබෙන පරිශ්‍රීලකයින් ආයාත කළහොත් ඔවුන් නැවත ගණනය නොවේ. ඔබගේ {{productName}} බලපත්‍රය සඳහා උපරිම පරිශ්‍රීලකයින් {{maxLimit}} ක් ලද හැකිය.",
"Lifetime": "ආයුකාලය (විනාඩි)",
"LimitThemesTooltip": "ඔබට අභිරුචි තේමා 𑇣 (3) ක් සෑදීමට හැකිය. නව එකක් සෑදීමට, කලින් සෑදූ තේමාවක් මකා දැමිය යුතුය.",
"LocalFile": "ස්ථානීය ගොනුව",

View File

@ -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",

View File

@ -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",

View File

@ -148,7 +148,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Поставке jезика и временске зоне омогућавају промену језика целог портала за све кориснике портала и конфигурацију временске зоне тако да ће сви догађаји на порталу бити приказани са тачним датумом и временом.",
"LastUpdate": "Последње ажурирање: {{date}}",
"LicenseLimitCounter": "Ограничење лиценце за Админе/Напредни корисници",
"LicenseLimitDescription": "Бројач ограничења лиценце састоји се од: већ постојећих налога у {{productName}}-у и нових корисника које желите да увезете. Ако увезете кориснике који већ имају налог у {{productName}}-у, неће бити поново бројани у бројачу. Ваша {{productName}} лиценца вам дозвољава да имате максимално 100 корисника.",
"LicenseLimitDescription": "Бројач ограничења лиценце састоји се од: већ постојећих налога у {{productName}}-у и нових корисника које желите да увезете. Ако увезете кориснике који већ имају налог у {{productName}}-у, неће бити поново бројани у бројачу. Ваша {{productName}} лиценца вам дозвољава да имате максимално {{maxLimit}} корисника.",
"Lifetime": "Време трајања (мин)",
"LimitThemesTooltip": "Можете само креирати 3 прилагођене теме. Да бисте креитрали нову, морате обрисати једну од претходних.",
"LocalFile": "Локална датотека",

View File

@ -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",

View File

@ -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",

View File

@ -148,7 +148,7 @@
"LanguageAndTimeZoneSettingsNavDescription": "Параметри мови та часового поясу — це спосіб змінити мову усього порталу для всіх користувачів порталу, а також налаштувати часові пояси, щоб усі події порталу показувалися із правильною датою та часом.",
"LastUpdate": "Останнє оновлення: {{date}}",
"LicenseLimitCounter": "Ліцензійне обмеження адміністраторів/досвідчених користувачів:",
"LicenseLimitDescription": "Лічильник ліцензійних обмежень враховує облікові записи, що вже існують у просторі {{productName}}, та нових користувачів, яких ви хочете імпортувати. Якщо ви імпортуєте користувачів, які вже мають обліковий запис {{productName}}, лічильник не буде враховувати їх повторно. Ваша ліцензія {{productName}} дозволяє мати максимум 100 користувачів.",
"LicenseLimitDescription": "Лічильник ліцензійних обмежень враховує облікові записи, що вже існують у просторі {{productName}}, та нових користувачів, яких ви хочете імпортувати. Якщо ви імпортуєте користувачів, які вже мають обліковий запис {{productName}}, лічильник не буде враховувати їх повторно. Ваша ліцензія {{productName}} дозволяє мати максимум {{maxLimit}} користувачів.",
"Lifetime": "Час життя (хв.)",
"LimitThemesTooltip": "Ви можете створити лише 3 власні теми. Щоб створити нову тему, необхідно видалити одну з попередніх тем.",
"LocalFile": "Локальний файл",

View File

@ -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ộ",

View File

@ -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",

View File

@ -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;

View File

@ -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,

View File

@ -59,7 +59,7 @@ const Main = (props) => {
window.addEventListener("resize", onResize);
return () => {
window.addEventListener("resize", onResize);
window.removeEventListener("resize", onResize);
clearTimeout(updateSizeRef.current);
};

View File

@ -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 = ({

View File

@ -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));

View File

@ -47,7 +47,7 @@ const StyledWrapper = styled.div`
}
.hidden {
visibility: hidden;
display: none;
}
`;

View File

@ -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>
)}

View File

@ -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;

View File

@ -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}

View File

@ -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;
`}
}

View File

@ -462,6 +462,7 @@ const EmbeddingPanelComponent = (props: EmbeddingPanelProps) => {
selectedOption={selectedLink as TOption}
displaySelectedOption
directionY="bottom"
withLabel={false}
/>
</>
)}

View File

@ -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>
</>
);
};

View File

@ -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;
}
}
}
}
`;

View File

@ -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,

View File

@ -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) {

View File

@ -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>
);

View File

@ -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");

View File

@ -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>
);

View File

@ -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);
}

View File

@ -122,7 +122,7 @@ export const HistoryItemList = ({
iconName={FolderLocationReactSvgUrl}
size="16"
isFill
onClick={() => checkAndOpenLocationAction!(item)}
onClick={() => checkAndOpenLocationAction!(item, actionType)}
title={t("Files:OpenLocation")}
/>
</StyledHistoryBlockFile>

View File

@ -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 =

View File

@ -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) => {

View File

@ -209,6 +209,7 @@ const StyledTileContainer = styled.div`
}
@media ${tablet} {
margin-top: 16px;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`

View File

@ -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,

View File

@ -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:

View File

@ -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),

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -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));

View File

@ -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)));

View File

@ -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));

View File

@ -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;

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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)),
);

View File

@ -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));

View File

@ -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;

View File

@ -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} />,
},
];
};

View File

@ -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)));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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),
),
);

View File

@ -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} />,
},
];
};

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -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));

View File

@ -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)));

View File

@ -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));

View File

@ -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