Merge branch 'develop' into feature/room-share-optimization
This commit is contained in:
commit
32f1c003de
@ -405,6 +405,19 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"logocolors":[
|
||||
{"r":255, "g":102, "b":128},
|
||||
{"r":255, "g":143, "b":64},
|
||||
{"r":242, "g":210, "b":48},
|
||||
{"r":97, "g":192, "b":89},
|
||||
{"r":112, "g":224, "b":150},
|
||||
{"r":31, "g":206, "b":203},
|
||||
{"r":92, "g":195, "b":247},
|
||||
{"r":97, "g":145, "b":242},
|
||||
{"r":119, "g":87, "b":217},
|
||||
{"r":182, "g":121, "b":242},
|
||||
{"r":255, "g":127, "b":212}
|
||||
],
|
||||
"radicale": {
|
||||
"admin": "",
|
||||
"path": ""
|
||||
|
3960
i18next/common.babel
3960
i18next/common.babel
File diff suppressed because it is too large
Load Diff
7161
migrations/mysql/SaaS/MigrationContext/20230907123747_MigrationContext_Upgrade4.Designer.cs
generated
Normal file
7161
migrations/mysql/SaaS/MigrationContext/20230907123747_MigrationContext_Upgrade4.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MigrationContext_Upgrade4 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "color",
|
||||
table: "files_folder",
|
||||
type: "char(6)",
|
||||
nullable: true,
|
||||
collation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "color",
|
||||
table: "files_folder");
|
||||
}
|
||||
}
|
||||
}
|
@ -6258,6 +6258,12 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasColumnType("char(6)")
|
||||
.HasColumnName("color")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<string>("CreateBy")
|
||||
.IsRequired()
|
||||
.HasColumnType("char(38)")
|
||||
|
@ -9,7 +9,8 @@
|
||||
"clean": "shx rm -rf dist",
|
||||
"deploy": "shx --silent mkdir -p ../../build/deploy/client && shx cp -r dist/* ../../build/deploy/client",
|
||||
"start": "yarn build:translations && NODE_OPTIONS=--openssl-legacy-provider webpack-cli serve",
|
||||
"start-prod": "serve dist -s -p 5001"
|
||||
"start-prod": "serve dist -s -p 5001",
|
||||
"analyze": "webpack --profile --json --env mode=analyze && webpack-bundle-analyzer bundle/output/path/stats.json"
|
||||
},
|
||||
"old-scripts": {
|
||||
"build:test": "webpack --env minimize=false --mode production",
|
||||
@ -84,6 +85,7 @@
|
||||
"typescript": "^4.9.5",
|
||||
"use-resize-observer": "^9.1.0",
|
||||
"webpack": "5.76.3",
|
||||
"webpack-bundle-analyzer": "^4.9.1",
|
||||
"webpack-cli": "4.10.0",
|
||||
"webpack-dev-server": "4.13.1"
|
||||
},
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Avtomatik yedəkləməni aktivləşdirin.",
|
||||
"EnableAutomaticBackupDescription": "Sahə məlumatlarını yedəkləmək üçün bu seçimdən istifadə edin.",
|
||||
"EnterTitle": "Başlığı daxil edin",
|
||||
"EveryDay": "Hər gün",
|
||||
"EveryMonth": "Hər ay",
|
||||
"EveryWeek": "Hər həftə",
|
||||
"ForcePathStyle": "Məcburi Keçid Üslubu",
|
||||
"IntegrationRequest": "ONLYOFFICE DocSpace-də faydalı inteqrasiya və ya komponent yoxdur? Komandamıza bir sorğu buraxın və biz buna baxacağıq.",
|
||||
"IPSecurity": "IP Təhlükəsizliyi",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Активиране на автоматично архивиране.",
|
||||
"EnableAutomaticBackupDescription": "Използвайте тази опция, за да архивирате данните на портала.",
|
||||
"EnterTitle": "Въведете заглавие",
|
||||
"EveryDay": "Всеки ден",
|
||||
"EveryMonth": "Всеки месец",
|
||||
"EveryWeek": "Всяка седмица",
|
||||
"ForcePathStyle": "Стил на Force Path",
|
||||
"IntegrationRequest": "Липсва Ви полезна интеграция или компонент в ONLYOFFICE DocSpace? Направете заявка към нашия екип и ние ще я разгледаме.",
|
||||
"IPSecurity": "IP сигурност",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Povolit automatické zálohování.",
|
||||
"EnableAutomaticBackupDescription": "Pomocí této možnosti zálohujte data prostoru.",
|
||||
"EnterTitle": "Zadejte titulek",
|
||||
"EveryDay": "Každý den",
|
||||
"EveryMonth": "Každý měsíc",
|
||||
"EveryWeek": "Každý týden",
|
||||
"ForcePathStyle": "Vynutit styl cesty",
|
||||
"IntegrationRequest": "Chybí vám v ONLYOFFICE DocSpace užitečná integrace nebo komponenta? Zanechte požadavek našemu týmu a my se na něj podíváme.",
|
||||
"IPSecurity": "Zabezpečení IP",
|
||||
|
@ -104,9 +104,6 @@
|
||||
"EnterTime": "Zeit eingeben",
|
||||
"EnterTitle": "Titel eingeben",
|
||||
"ErrorMessageBruteForceProtection": "Das angegebene Argument lag außerhalb des gültigen Wertebereichs.",
|
||||
"EveryDay": "Jeden Tag",
|
||||
"EveryMonth": "Jeden Monat",
|
||||
"EveryWeek": "Jede Woche",
|
||||
"ForcePathStyle": "Erzwinge Pfad-Stil",
|
||||
"IntegrationRequest": "Brauchen Sie eine nützliche Integration oder Komponente in ONLYOFFICE DocSpace? Hinterlassen Sie eine Anfrage an unser Team und wir werden uns darum kümmern.",
|
||||
"IPSecurity": "IP-Sicherheit",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Ενεργοποίηση αυτόματης δημιουργίας αντιγράφων ασφαλείας.",
|
||||
"EnableAutomaticBackupDescription": "Χρησιμοποιήστε αυτήν την επιλογή για να δημιουργήσετε αντίγραφα ασφαλείας των δεδομένων της DocSpace.",
|
||||
"EnterTitle": "Εισαγωγή τίτλου",
|
||||
"EveryDay": "Κάθε μέρα",
|
||||
"EveryMonth": "Κάθε μήνα",
|
||||
"EveryWeek": "Κάθε εβδομάδα",
|
||||
"ForcePathStyle": "Στυλ διαδρομής δύναμης",
|
||||
"IntegrationRequest": "Σας λείπει μια χρήσιμη ενσωμάτωση ή ένα στοιχείο στο DocSpace του ONLYOFFICE; Αφήστε ένα αίτημα στην ομάδα μας και θα το εξετάσουμε.",
|
||||
"IPSecurity": "Ασφάλεια IP",
|
||||
|
@ -111,9 +111,6 @@
|
||||
"EnterTime": "Enter time",
|
||||
"EnterTitle": "Enter title",
|
||||
"ErrorMessageBruteForceProtection": "Specified argument was out of the range of valid values.",
|
||||
"EveryDay": "Every day",
|
||||
"EveryMonth": "Every month",
|
||||
"EveryWeek": "Every week",
|
||||
"ForcePathStyle": "Force Path Style",
|
||||
"IntegrationRequest": "Missing a useful integration or component in ONLYOFFICE DocSpace? Leave a request to our team and we will look into that.",
|
||||
"IPSecurity": "IP Security",
|
||||
|
@ -96,9 +96,6 @@
|
||||
"EnableAutomaticBackup": "Active copia de seguridad automática.",
|
||||
"EnableAutomaticBackupDescription": "Utilice esta opción para hacer una copia de seguridad de los datos del espacio.",
|
||||
"EnterTitle": "Introduzca título",
|
||||
"EveryDay": "Cada día",
|
||||
"EveryMonth": "Cada mes",
|
||||
"EveryWeek": "Cada semana",
|
||||
"ForcePathStyle": "Estilo de Force Path",
|
||||
"IntegrationRequest": "¿Falta una integración o componente útil en ONLYOFFICE DocSpace? Envíe una solicitud a nuestro equipo y la analizaremos.",
|
||||
"IPSecurity": "Seguridad IP",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Ota automaattinen varmuuskopiointi käyttöön.",
|
||||
"EnableAutomaticBackupDescription": "Tämän vaihtoehdon avulla voit varmuuskopioida tilan tiedot.",
|
||||
"EnterTitle": "Syötä otsikko",
|
||||
"EveryDay": "Joka päivä",
|
||||
"EveryMonth": "Kuukausittain",
|
||||
"EveryWeek": "Joka viikko",
|
||||
"ForcePathStyle": "Pakota polku -tyyli",
|
||||
"IntegrationRequest": "Puuttuuko ONLYOFFICE DocSpacesta käytännöllinen integraatio tai komponentti? Jätä pyyntö tiimillemme, niin tutkimme asiaa.",
|
||||
"IPSecurity": "IP-suojaus",
|
||||
|
@ -96,9 +96,6 @@
|
||||
"EnableAutomaticBackup": "Activer la sauvegarde automatique.",
|
||||
"EnableAutomaticBackupDescription": "Utilisez cette option pour sauvegarder les données de l’espace.",
|
||||
"EnterTitle": "Saisir un titre",
|
||||
"EveryDay": "Chaque jour",
|
||||
"EveryMonth": "Chaque mois",
|
||||
"EveryWeek": "Chaque semaine",
|
||||
"ForcePathStyle": "Force le style de chemin",
|
||||
"IntegrationRequest": "Il manque une intégration ou un composant utile dans ONLYOFFICE DocSpace ? Laissez une demande à notre équipe et nous nous en occuperons.",
|
||||
"IPSecurity": "Sécurité IP",
|
||||
|
@ -96,9 +96,6 @@
|
||||
"EnableAutomaticBackup": "Միացնել ավտոմատ կրկնօրինակումը:",
|
||||
"EnableAutomaticBackupDescription": "Օգտագործեք այս տարբերակը՝ կայքէջի տվյալները պահուստավորելու համար:",
|
||||
"EnterTitle": "Մուտքագրեք անվանումը",
|
||||
"EveryDay": "Ամեն օր",
|
||||
"EveryMonth": "Ամեն ամիս",
|
||||
"EveryWeek": "Ամեն շաբաթ",
|
||||
"ForcePathStyle": "Պարտադրել ուղու ոճ",
|
||||
"IntegrationRequest": "Բացակայո՞ւմ եք օգտակար ինտեգրում կամ բաղադրիչ ONLYOFFICE DocSpace-ում: Խնդրանք թողեք մեր թիմին, և մենք կուսումնասիրենք դա:",
|
||||
"IPSecurity": "IP անվտանգություն",
|
||||
|
@ -104,9 +104,6 @@
|
||||
"EnterTime": "Inserisci l'ora",
|
||||
"EnterTitle": "Inserisci il titolo",
|
||||
"ErrorMessageBruteForceProtection": "L'argomento specificato non rientra nell'intervallo di valori validi.",
|
||||
"EveryDay": "Ogni giorno",
|
||||
"EveryMonth": "Ogni mese",
|
||||
"EveryWeek": "Ogni settimana",
|
||||
"ForcePathStyle": "Forza percorso stile",
|
||||
"IntegrationRequest": "Ti manca un'integrazione o un componente utile in ONLYOFFICE DocSpace? Lascia la tua richiesta al nostro team e lo studieremo.",
|
||||
"IPSecurity": "Sicurezza IP",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "自動バックアップを有効にする",
|
||||
"EnableAutomaticBackupDescription": "スペースのデータをバックアップする場合は、このオプションを使用してください。",
|
||||
"EnterTitle": "タイトルを入力",
|
||||
"EveryDay": "毎日",
|
||||
"EveryMonth": "毎月",
|
||||
"EveryWeek": "毎週",
|
||||
"ForcePathStyle": "強制パススタイル",
|
||||
"IntegrationRequest": "ONLYOFFICE DocSpaceの便利な統合やコンポーネントをお探しですか?私たちのチームにリクエストをしてください。",
|
||||
"IPSecurity": "IPセキュリティ",
|
||||
|
@ -94,9 +94,6 @@
|
||||
"EnableAutomaticBackup": "자동 백업을 활성화하세요.",
|
||||
"EnableAutomaticBackupDescription": "스페이스 데이터를 백업하기 위해 이 옵션을 사용하세요.",
|
||||
"EnterTitle": "제목을 입력하세요",
|
||||
"EveryDay": "매일",
|
||||
"EveryMonth": "매월",
|
||||
"EveryWeek": "매주",
|
||||
"ForcePathStyle": "경로 스타일 강제",
|
||||
"IntegrationRequest": "ONLYOFFICE DocSpace에 유용한 통합 또는 구성 요소가 부족한가요? 저희 팀이 검토할 수 있도록 요청을 남겨주세요.",
|
||||
"IPSecurity": "IP 보안",
|
||||
|
@ -94,9 +94,6 @@
|
||||
"EnableAutomaticBackup": "ເປີດໃຊ້ງານ ອັດຕະໂນມັດ ສຳຮອງ .",
|
||||
"EnableAutomaticBackupDescription": "ໃຊ້ຕົວເລືອກນີ້ເພື່ອສຳຮອງຂໍ້ມູນປະຕູ.",
|
||||
"EnterTitle": "ໃສ່ຊື່",
|
||||
"EveryDay": "ທຸກໆ ມື້",
|
||||
"EveryMonth": "ທຸກໆ ເດືອນ",
|
||||
"EveryWeek": "ທຸກໆ ອາທິດ",
|
||||
"ForcePathStyle": "ບັງຄັບ ເສັ້ນທາງ ແບບ",
|
||||
"IntegrationRequest": "ຂາດການເຊື່ອມໂຍງ ຫຼືອົງປະກອບທີ່ເປັນປະໂຫຍດໃນ ONLYOFFICE DocSpace ບໍ? ອອກຈາກການຮ້ອງຂໍໃຫ້ທີມງານຂອງພວກເຮົາແລະພວກເຮົາຈະກວດເບິ່ງວ່າ.",
|
||||
"IPSecurity": "ຄວາມປອດໄພ IP",
|
||||
|
@ -94,9 +94,6 @@
|
||||
"EnableAutomaticBackup": "Iespējojiet automātisko dublēšanu.",
|
||||
"EnableAutomaticBackupDescription": "Izmantojiet šo opciju, lai dublētu portāla datus.",
|
||||
"EnterTitle": "Ievadiet nosaukums",
|
||||
"EveryDay": "Katru dienu",
|
||||
"EveryMonth": "Katru mēnesi",
|
||||
"EveryWeek": "Katru nedēļu",
|
||||
"ForcePathStyle": "Uzspiest ceļa stilu",
|
||||
"IntegrationRequest": "Vai ONLYOFFICE DocSpace trūkst noderīgas integrācijas vai komponenta? Atstājiet pieprasījumu mūsu komandai, un mēs to izskatīsim.",
|
||||
"IPSecurity": "IP drošība",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Automatische back-up inschakelen.",
|
||||
"EnableAutomaticBackupDescription": "Gebruik deze optie om een back-up te maken van de ruimtegegevens.",
|
||||
"EnterTitle": "Voer titel in",
|
||||
"EveryDay": "Elke dag",
|
||||
"EveryMonth": "Elke maand",
|
||||
"EveryWeek": "Elke week",
|
||||
"ForcePathStyle": "Forceer Path Style",
|
||||
"IntegrationRequest": "Mist u een handige integratie of component in ONLYOFFICE DocSpace? Laat een verzoek achter bij ons team en wij zullen ernaar kijken.",
|
||||
"IPSecurity": "IP Beveiliging",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Włącz automatyczne tworzenie kopii zapasowych.",
|
||||
"EnableAutomaticBackupDescription": "Użyj tej opcji do tworzenia kopii zapasowych danych portalu.",
|
||||
"EnterTitle": "Wpisz tytuł",
|
||||
"EveryDay": "Codziennie",
|
||||
"EveryMonth": "Każdego miesiąca",
|
||||
"EveryWeek": "Każdy tydzień",
|
||||
"ForcePathStyle": "Wymuś styl ścieżki",
|
||||
"IntegrationRequest": "Brakuje Ci przydatnej integracji lub innego ważnego komponentu w ONLYOFFICE DocSpace? Zostaw nam wiadomość, a niezwłocznie przyjrzymy się sprawie.",
|
||||
"IPSecurity": "IP Security",
|
||||
|
@ -96,9 +96,6 @@
|
||||
"EnableAutomaticBackup": "Ativar backup automático.",
|
||||
"EnableAutomaticBackupDescription": "Use esta opção para fazer backup dos dados do espaço.",
|
||||
"EnterTitle": "Insira o título",
|
||||
"EveryDay": "Todos os dias",
|
||||
"EveryMonth": "Todos os meses",
|
||||
"EveryWeek": "Todas as semanas",
|
||||
"ForcePathStyle": "Estilo de Caminho de Força",
|
||||
"IntegrationRequest": "Falta uma integração ou componente útil no ONLYOFFICE DocSpace? Deixe uma solicitação para nossa equipe e analisaremos isso.",
|
||||
"IPSecurity": "Segurança IP",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Ativar Criação Automática de Cópias de Segurança.",
|
||||
"EnableAutomaticBackupDescription": "Utilize esta opção para criar cópias de segurança dos dados do espaço.",
|
||||
"EnterTitle": "Introduza o título",
|
||||
"EveryDay": "Todos os dias",
|
||||
"EveryMonth": "Todos os meses",
|
||||
"EveryWeek": "Todas as semanas",
|
||||
"ForcePathStyle": "Forçar a Formatação do Destino",
|
||||
"IntegrationRequest": "Está a faltar uma integração ou componente útil no ONLYOFFICE DocSpace? Deixe um pedido à nossa equipa e analisaremos o assunto.",
|
||||
"IPSecurity": "Segurança IP",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Activează copiere de rezervă automată.",
|
||||
"EnableAutomaticBackupDescription": "Utilizați această opțiune pentru a crea o copie de rezervă a datelor din portal.",
|
||||
"EnterTitle": "Introduceți titlul",
|
||||
"EveryDay": "Zilnic",
|
||||
"EveryMonth": "Lunar",
|
||||
"EveryWeek": "Saptamanal",
|
||||
"ForcePathStyle": "Tip cale forțat",
|
||||
"IntegrationRequest": "Vă lipsește o soluție de integrare sau o componentă utilă în spațiul DocSpace? Trimiteți o solicitare către echipa noastră și vom analiza propunerea dvs.",
|
||||
"IPSecurity": "Securitate IP",
|
||||
|
@ -104,9 +104,6 @@
|
||||
"EnterTime": "Ввести время",
|
||||
"EnterTitle": "Укажите название",
|
||||
"ErrorMessageBruteForceProtection": "Указанный аргумент находился вне диапазона допустимых значений.",
|
||||
"EveryDay": "Каждый день",
|
||||
"EveryMonth": "Каждый месяц",
|
||||
"EveryWeek": "Каждую неделю",
|
||||
"ForcePathStyle": "Принудительно использовать стиль пути",
|
||||
"IntegrationRequest": "В ONLYOFFICE DocSpace отсутствует полезная интеграция или компонент? Отправьте запрос нашей команде, и мы его рассмотрим.",
|
||||
"IPSecurity": "IP-безопасность",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Povoliť automatické zálohovanie údajov.",
|
||||
"EnableAutomaticBackupDescription": "Použiť túto voľbu na zálohovanie údajov priestoru.",
|
||||
"EnterTitle": "Zadajte názov",
|
||||
"EveryDay": "Každý deň",
|
||||
"EveryMonth": "Každý mesiac",
|
||||
"EveryWeek": "Každý týždeň",
|
||||
"ForcePathStyle": "Zapnúť Path-Style",
|
||||
"IntegrationRequest": "Chýba vám v ONLYOFFICE DocSpace užitočná integrácia alebo komponent? Napíšte nášmu tímu svoju požiadavku a my sa na ňu pozrieme.",
|
||||
"IPSecurity": "IP Bezpečnosť",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Omogoči samodejno varnostno kopiranje.",
|
||||
"EnableAutomaticBackupDescription": "Uporabite to možnost za varnostno kopiranje podatkov prostora.",
|
||||
"EnterTitle": "Vnesite naslov",
|
||||
"EveryDay": "Vsak dan",
|
||||
"EveryMonth": "Vsak mesec",
|
||||
"EveryWeek": "Vsak teden",
|
||||
"ForcePathStyle": "Stil vsiljene poti",
|
||||
"IntegrationRequest": "Pogrešate uporabno integracijo ali komponento v ONLYOFFICE DocSpace? Zaupajte zahtevo naši ekipi in preučili jo bomo.",
|
||||
"IPSecurity": "IP varnost",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Otomatik yedeklemeyi etkinleştir.",
|
||||
"EnableAutomaticBackupDescription": "Alan verilerini yedeklemek için bu seçeneği kullanın.",
|
||||
"EnterTitle": "Başlık girin",
|
||||
"EveryDay": "Her gün",
|
||||
"EveryMonth": "Her ay",
|
||||
"EveryWeek": "Her hafta",
|
||||
"ForcePathStyle": "Zorla Yol Stili",
|
||||
"IntegrationRequest": "ONLYOFFICE DocSpace'te yararlı bir entegrasyon veya bileşen mi eksik? Ekibimize bir talep bırakın, bununla ilgilenelim.",
|
||||
"IPSecurity": "IP Güvenliği",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Увімкнути автоматичне резервне копіювання.",
|
||||
"EnableAutomaticBackupDescription": "Використовуйте цей параметр для резервного копіювання даних порталу.",
|
||||
"EnterTitle": "Введіть заголовок",
|
||||
"EveryDay": "Кожного дня",
|
||||
"EveryMonth": "Кожного місяця",
|
||||
"EveryWeek": "Кожного тижня",
|
||||
"ForcePathStyle": "Примусово використати стиль шляху",
|
||||
"IntegrationRequest": "Не вистачає корисної інтеграції чи компонента в ONLYOFFICE DocSpace? Залиште заявку нашій команді, і ми її розглянемо.",
|
||||
"IPSecurity": "IP-безпека",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "Bật sao lưu tự động.",
|
||||
"EnableAutomaticBackupDescription": "Sử dụng tùy chọn này để sao lưu dữ liệu không gian.",
|
||||
"EnterTitle": "Nhập tiêu đề",
|
||||
"EveryDay": "Hàng ngày",
|
||||
"EveryMonth": "Hàng tháng",
|
||||
"EveryWeek": "Hàng tuần",
|
||||
"ForcePathStyle": "Kiểu đường dẫn bắt buộc",
|
||||
"IntegrationRequest": "Bạn thấy thiếu một tích hợp hoặc thành phần hữu ích trong ONLYOFFICE DocSpace? Xin hãy gửi yêu cầu cho nhóm của chúng tôi và chúng tôi sẽ xem xét điều đó.",
|
||||
"IPSecurity": "Bảo mật IP",
|
||||
|
@ -95,9 +95,6 @@
|
||||
"EnableAutomaticBackup": "启用自动备份",
|
||||
"EnableAutomaticBackupDescription": "使用该选项备份协作空间的数据。",
|
||||
"EnterTitle": "请输入标题",
|
||||
"EveryDay": "每天",
|
||||
"EveryMonth": "每个月",
|
||||
"EveryWeek": "每周",
|
||||
"ForcePathStyle": "力路风格",
|
||||
"IntegrationRequest": "在 ONLYOFFICE 协作空间中缺少有用的集成或组件?请向我们的团队提出申请,我们将对此进行研究。",
|
||||
"IPSecurity": "IP 安全性",
|
||||
|
@ -4,6 +4,7 @@ import { inject, observer } from "mobx-react";
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "@docspace/components/themes/base";
|
||||
import NoUserSelect from "@docspace/components/utils/commonStyles";
|
||||
import RoomIcon from "./RoomIcon";
|
||||
|
||||
const StyledIcon = styled.img`
|
||||
${NoUserSelect}
|
||||
@ -29,9 +30,7 @@ const IconWrapper = styled.div`
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.default ? "none" : props.theme.itemIcon.borderColor};
|
||||
border: 1px solid ${(props) => props.theme.itemIcon.borderColor};
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -46,7 +45,7 @@ const EncryptedFileIcon = styled.div`
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
margin-top: 14px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 12px;
|
||||
@ -56,22 +55,31 @@ const EncryptedFileIcon = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
const ItemIcon = ({ icon, fileExst, isPrivacy, isRoom, defaultRoomIcon }) => {
|
||||
const [showDefaultIcon, setShowDefaultIcon] = React.useState(isRoom);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isRoom || defaultRoomIcon === icon) return;
|
||||
setShowDefaultIcon(false);
|
||||
}, [isRoom, defaultRoomIcon, icon, setShowDefaultIcon]);
|
||||
const ItemIcon = ({
|
||||
icon,
|
||||
fileExst,
|
||||
isPrivacy,
|
||||
isRoom,
|
||||
title,
|
||||
logo,
|
||||
color,
|
||||
isArchive,
|
||||
}) => {
|
||||
const isLoadedRoomIcon = !!logo?.medium;
|
||||
const showDefaultRoomIcon = !isLoadedRoomIcon && isRoom;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconWrapper isRoom={isRoom} default={showDefaultIcon}>
|
||||
<IconWrapper isRoom={isRoom}>
|
||||
{showDefaultRoomIcon ? (
|
||||
<RoomIcon color={color} title={title} isArchive={isArchive} />
|
||||
) : (
|
||||
<StyledIcon
|
||||
className={`react-svg-icon`}
|
||||
isRoom={isRoom}
|
||||
src={showDefaultIcon ? defaultRoomIcon : icon}
|
||||
src={isRoom ? logo?.medium : icon}
|
||||
/>
|
||||
)}
|
||||
</IconWrapper>
|
||||
{isPrivacy && fileExst && <EncryptedFileIcon isEdit={false} />}
|
||||
</>
|
||||
|
66
packages/client/src/components/RoomIcon.js
Normal file
66
packages/client/src/components/RoomIcon.js
Normal file
@ -0,0 +1,66 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "@docspace/components/themes/base";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
const StyledIcon = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: ${(props) => props.size};
|
||||
width: ${(props) => props.size};
|
||||
|
||||
.room-background {
|
||||
height: ${(props) => props.size};
|
||||
width: ${(props) => props.size};
|
||||
border-radius: ${(props) => props.radius};
|
||||
vertical-align: middle;
|
||||
background: ${(props) =>
|
||||
props.isArchive
|
||||
? props.theme.roomIcon.backgroundArchive
|
||||
: `#` + props.color};
|
||||
position: absolute;
|
||||
opacity: ${(props) => props.theme.roomIcon.opacityBackground};
|
||||
}
|
||||
|
||||
.room-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
${(props) =>
|
||||
!props.theme.isBase &&
|
||||
!props.isArchive &&
|
||||
css`
|
||||
color: ${(props) => `#` + props.color};
|
||||
`};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledIcon.defaultProps = { theme: Base };
|
||||
|
||||
const RoomIcon = ({
|
||||
title,
|
||||
isArchive,
|
||||
color,
|
||||
size = "32px",
|
||||
radius = "6px",
|
||||
}) => {
|
||||
const titleWithoutSpaces = title.replace(/\s+/g, " ").trim();
|
||||
const indexAfterLastSpace = titleWithoutSpaces.lastIndexOf(" ");
|
||||
const secondCharacter =
|
||||
indexAfterLastSpace === -1
|
||||
? ""
|
||||
: titleWithoutSpaces[indexAfterLastSpace + 1];
|
||||
|
||||
const roomTitle = (title[0] + secondCharacter).toUpperCase();
|
||||
|
||||
return (
|
||||
<StyledIcon color={color} size={size} radius={radius} isArchive={isArchive}>
|
||||
<div className="room-background" />
|
||||
<Text className="room-title">{roomTitle}</Text>
|
||||
</StyledIcon>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoomIcon;
|
@ -86,14 +86,27 @@ export const QuotaBarTypes = Object.freeze({
|
||||
export const BINDING_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";
|
||||
export const BINDING_REDIRECT =
|
||||
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";
|
||||
export const SSO_NAME_ID_FORMAT =
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient";
|
||||
export const SSO_NAME_ID_FORMAT = [
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos",
|
||||
];
|
||||
export const SSO_GIVEN_NAME = "givenName";
|
||||
export const SSO_SN = "sn";
|
||||
export const SSO_EMAIL = "email";
|
||||
export const SSO_LOCATION = "location";
|
||||
export const SSO_TITLE = "title";
|
||||
export const SSO_PHONE = "phone";
|
||||
export const SSO_SIGNING = "signing";
|
||||
export const SSO_ENCRYPT = "encrypt";
|
||||
export const SSO_SIGNING_ENCRYPT = "signing and encrypt";
|
||||
|
||||
export const DEFAULT_SELECT_TIMEZONE = {
|
||||
key: "UTC",
|
||||
|
@ -34,6 +34,12 @@ const StyledNoThumbnail = styled.div`
|
||||
outline: 1px solid
|
||||
${(props) => props.theme.infoPanel.details.customLogoBorderColor};
|
||||
}
|
||||
|
||||
.room-title {
|
||||
font-size: 41px;
|
||||
font-weight: 700;
|
||||
line-height: 56px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledThumbnail.defaultProps = { theme: Base };
|
||||
|
@ -8,6 +8,7 @@ import { Text } from "@docspace/components";
|
||||
import ItemContextOptions from "./ItemContextOptions";
|
||||
|
||||
import { StyledTitle } from "../../styles/common";
|
||||
import RoomIcon from "@docspace/client/src/components/RoomIcon";
|
||||
|
||||
const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
const itemTitleRef = useRef();
|
||||
@ -15,15 +16,25 @@ const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
if (isSeveralItems) return <></>;
|
||||
|
||||
const icon = selection.icon;
|
||||
const isLoadedRoomIcon = !!selection.logo?.medium;
|
||||
const showDefaultRoomIcon = !isLoadedRoomIcon && selection.isRoom;
|
||||
|
||||
return (
|
||||
<StyledTitle ref={itemTitleRef}>
|
||||
<div className="item-icon">
|
||||
{showDefaultRoomIcon ? (
|
||||
<RoomIcon
|
||||
color={selection.logo.color}
|
||||
title={selection.title}
|
||||
isArchive={selection.isArchive}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
className={`icon ${selection.isRoom && "is-room"}`}
|
||||
src={icon}
|
||||
alt="thumbnail-icon"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Text className="text">{selection.title}</Text>
|
||||
{selection && (
|
||||
|
@ -9,7 +9,7 @@ import Text from "@docspace/components/text";
|
||||
import DetailsHelper from "../../helpers/DetailsHelper.js";
|
||||
import { StyledNoThumbnail, StyledThumbnail } from "../../styles/details.js";
|
||||
import { StyledProperties, StyledSubtitle } from "../../styles/common.js";
|
||||
|
||||
import RoomIcon from "@docspace/client/src/components/RoomIcon";
|
||||
const Details = ({
|
||||
t,
|
||||
selection,
|
||||
@ -67,6 +67,9 @@ const Details = ({
|
||||
|
||||
//console.log("InfoPanel->Details render", { selection });
|
||||
|
||||
const isLoadedRoomIcon = !!selection.logo?.large;
|
||||
const showDefaultRoomIcon = !isLoadedRoomIcon && selection.isRoom;
|
||||
|
||||
return (
|
||||
<>
|
||||
{selection.thumbnailUrl && !isThumbnailError ? (
|
||||
@ -86,6 +89,15 @@ const Details = ({
|
||||
</StyledThumbnail>
|
||||
) : (
|
||||
<StyledNoThumbnail>
|
||||
{showDefaultRoomIcon ? (
|
||||
<RoomIcon
|
||||
color={selection.logo.color}
|
||||
title={selection.title}
|
||||
isArchive={selection.isArchive}
|
||||
size="96px"
|
||||
radius="16px"
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
className={`no-thumbnail-img ${selection.isRoom && "is-room"} ${
|
||||
selection.isRoom &&
|
||||
@ -96,6 +108,7 @@ const Details = ({
|
||||
src={currentIcon}
|
||||
alt="thumbnail-icon-big"
|
||||
/>
|
||||
)}
|
||||
</StyledNoThumbnail>
|
||||
)}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import { tablet } from "@docspace/components/utils/device";
|
||||
import CursorPalmReactSvgUrl from "PUBLIC_DIR/images/cursor.palm.react.svg?url";
|
||||
import { classNames } from "@docspace/components/utils/classNames";
|
||||
const checkedStyle = css`
|
||||
background: ${props => props.theme.filesSection.rowView.checkedBackground};
|
||||
background: ${(props) => props.theme.filesSection.rowView.checkedBackground};
|
||||
${marginStyles}
|
||||
`;
|
||||
|
||||
@ -28,10 +28,10 @@ const StyledWrapper = styled.div`
|
||||
`;
|
||||
|
||||
const StyledSimpleFilesRow = styled(Row)`
|
||||
${props => (props.checked || props.isActive) && checkedStyle};
|
||||
${(props) => (props.checked || props.isActive) && checkedStyle};
|
||||
height: 56px;
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
!isMobile &&
|
||||
!props.isDragging &&
|
||||
css`
|
||||
@ -39,25 +39,25 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
cursor: pointer;
|
||||
${checkedStyle}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
!props.showHotkeyBorder &&
|
||||
css`
|
||||
margin-top: -2px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
border-top: ${props =>
|
||||
border-top: ${(props) =>
|
||||
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
|
||||
border-bottom: ${props =>
|
||||
border-bottom: ${(props) =>
|
||||
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
|
||||
`}
|
||||
}
|
||||
`};
|
||||
position: unset;
|
||||
cursor: ${props =>
|
||||
cursor: ${(props) =>
|
||||
!props.isThirdPartyFolder &&
|
||||
(props.checked || props.isActive) &&
|
||||
`url(${CursorPalmReactSvgUrl}), auto`};
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.inProgress &&
|
||||
css`
|
||||
pointer-events: none;
|
||||
@ -66,7 +66,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
|
||||
margin-top: 0px;
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.showHotkeyBorder &&
|
||||
css`
|
||||
border-top: 1px solid #2da7db !important;
|
||||
@ -77,7 +77,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
padding-right: 24px;
|
||||
`}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.isHighlight &&
|
||||
css`
|
||||
${marginStyles}
|
||||
@ -85,16 +85,16 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
margin-top: -2px;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
border-top: ${props =>
|
||||
border-top: ${(props) =>
|
||||
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
|
||||
border-bottom: ${props =>
|
||||
border-bottom: ${(props) =>
|
||||
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
|
||||
|
||||
animation: Highlight 2s 1;
|
||||
|
||||
@keyframes Highlight {
|
||||
0% {
|
||||
background: ${props => props.theme.filesSection.animationColor};
|
||||
background: ${(props) => props.theme.filesSection.animationColor};
|
||||
}
|
||||
|
||||
100% {
|
||||
@ -105,7 +105,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
|
||||
|
||||
::after {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.showHotkeyBorder &&
|
||||
css`
|
||||
background: #2da7db;
|
||||
@ -123,7 +123,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
`}
|
||||
}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
(!props.contextOptions || props.isEdit) &&
|
||||
`
|
||||
& > div:last-child {
|
||||
@ -136,7 +136,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
|
||||
.styled-element {
|
||||
height: 32px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 7px;
|
||||
@ -147,7 +147,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.row_content {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.sectionWidth > 500 && `max-width: fit-content;`}//min-width: auto
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.badge {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 8px;
|
||||
@ -169,7 +169,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.badge:last-child {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 0px;
|
||||
@ -180,7 +180,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.lock-file {
|
||||
cursor: ${props => (props.withAccess ? "pointer" : "default")};
|
||||
cursor: ${(props) => (props.withAccess ? "pointer" : "default")};
|
||||
svg {
|
||||
height: 12px;
|
||||
}
|
||||
@ -203,11 +203,11 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.badges {
|
||||
margin-top: ${props =>
|
||||
margin-top: ${(props) =>
|
||||
props.isSmallContainer ? "1px" : props.isRooms ? "4px" : "2px"};
|
||||
margin-bottom: 0px;
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.isSmallContainer &&
|
||||
css`
|
||||
.tablet-pinned {
|
||||
@ -221,13 +221,15 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.badge {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: ${props => (props.isSmallContainer ? "8px" : "24px")};
|
||||
margin-left: ${(props) =>
|
||||
props.isSmallContainer ? "8px" : "24px"};
|
||||
`
|
||||
: css`
|
||||
margin-right: ${props => (props.isSmallContainer ? "8px" : "24px")};
|
||||
margin-right: ${(props) =>
|
||||
props.isSmallContainer ? "8px" : "24px"};
|
||||
`}
|
||||
}
|
||||
|
||||
@ -238,21 +240,21 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
}
|
||||
|
||||
.expandButton {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: ${props => (!props.folderCategory ? "6px" : "0")};
|
||||
margin-right: ${(props) => (!props.folderCategory ? "6px" : "0")};
|
||||
`
|
||||
: css`
|
||||
margin-left: ${props => (!props.folderCategory ? "6px" : "0")};
|
||||
margin-left: ${(props) => (!props.folderCategory ? "6px" : "0")};
|
||||
`}
|
||||
padding-top: 0px;
|
||||
}
|
||||
.expandButton > div:first-child {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.folderCategory &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-right: 0 !important;
|
||||
@ -266,7 +268,7 @@ const StyledSimpleFilesRow = styled(Row)`
|
||||
|
||||
StyledSimpleFilesRow.defaultProps = { theme: Base };
|
||||
|
||||
const SimpleFilesRow = props => {
|
||||
const SimpleFilesRow = (props) => {
|
||||
const {
|
||||
item,
|
||||
sectionWidth,
|
||||
@ -309,11 +311,14 @@ const SimpleFilesRow = props => {
|
||||
icon={item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
defaultRoomIcon={item.defaultRoomIcon}
|
||||
title={item.title}
|
||||
logo={item.logo}
|
||||
color={item.logo?.color}
|
||||
isArchive={item.isArchive}
|
||||
/>
|
||||
);
|
||||
|
||||
const onDragOver = dragOver => {
|
||||
const onDragOver = (dragOver) => {
|
||||
if (dragOver !== isDragOver) {
|
||||
setIsDragOver(dragOver);
|
||||
}
|
||||
@ -347,7 +352,8 @@ const SimpleFilesRow = props => {
|
||||
: checkedProps || isActive
|
||||
? "row-selected"
|
||||
: ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<DragAndDrop
|
||||
data-title={item.title}
|
||||
value={value}
|
||||
@ -357,7 +363,8 @@ const SimpleFilesRow = props => {
|
||||
dragging={dragging && isDragging}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
style={dragStyles}>
|
||||
style={dragStyles}
|
||||
>
|
||||
<StyledSimpleFilesRow
|
||||
key={item.id}
|
||||
data={item}
|
||||
@ -390,7 +397,8 @@ const SimpleFilesRow = props => {
|
||||
isSmallContainer={isSmallContainer}
|
||||
isRooms={isRooms}
|
||||
folderCategory={folderCategory}
|
||||
isHighlight={isHighlight}>
|
||||
isHighlight={isHighlight}
|
||||
>
|
||||
<FilesRowContent
|
||||
item={item}
|
||||
sectionWidth={sectionWidth}
|
||||
|
@ -49,7 +49,10 @@ const FilesTableRow = (props) => {
|
||||
icon={item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
defaultRoomIcon={item.defaultRoomIcon}
|
||||
title={item.title}
|
||||
logo={item.logo}
|
||||
color={item.logo?.color}
|
||||
isArchive={item.isArchive}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -75,9 +75,10 @@ const FileTile = (props) => {
|
||||
icon={item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
defaultRoomIcon={
|
||||
item.isRoom && item.icon ? item.icon : item.defaultRoomIcon
|
||||
}
|
||||
title={item.title}
|
||||
logo={item.logo}
|
||||
color={item.logo?.color}
|
||||
isArchive={item.isArchive}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -60,7 +60,7 @@ const StyledContainer = styled.div`
|
||||
min-height: 33px;
|
||||
|
||||
.table-container_group-menu {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin: 0 -20px 0 0;
|
||||
@ -75,7 +75,7 @@ const StyledContainer = styled.div`
|
||||
|
||||
@media ${tablet} {
|
||||
height: 60px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin: 0 -16px 0 0;
|
||||
@ -89,7 +89,7 @@ const StyledContainer = styled.div`
|
||||
${isMobile &&
|
||||
css`
|
||||
height: 60px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin: 0 -16px 0 0;
|
||||
@ -103,7 +103,7 @@ css`
|
||||
@media ${mobile} {
|
||||
height: 52px;
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin: 0 -16px 0 0;
|
||||
@ -117,7 +117,7 @@ css`
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
height: 52px;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin: 0 -16px 0 0;
|
||||
@ -132,7 +132,7 @@ css`
|
||||
.header-container {
|
||||
min-height: 33px;
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.hideContextMenuInsideArchiveRoom &&
|
||||
`.option-button {
|
||||
display: none;}`}
|
||||
@ -143,7 +143,7 @@ css`
|
||||
}
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = props => {
|
||||
const SectionHeaderContent = (props) => {
|
||||
const {
|
||||
currentFolderId,
|
||||
setSelectFileDialogVisible,
|
||||
@ -244,7 +244,7 @@ const SectionHeaderContent = props => {
|
||||
const isAccountsPage = location.pathname.includes("accounts");
|
||||
const isSettingsPage = location.pathname.includes("settings");
|
||||
|
||||
const onCreate = format => {
|
||||
const onCreate = (format) => {
|
||||
const event = new Event(Events.CREATE);
|
||||
|
||||
const payload = {
|
||||
@ -286,7 +286,7 @@ const SectionHeaderContent = props => {
|
||||
const createFolder = () => onCreate();
|
||||
|
||||
// TODO: add privacy room check for files
|
||||
const onUploadAction = type => {
|
||||
const onUploadAction = (type) => {
|
||||
const element =
|
||||
type === "file"
|
||||
? document.getElementById("customFileInput")
|
||||
@ -438,7 +438,7 @@ const SectionHeaderContent = props => {
|
||||
const pluginOptions = getMainButtonItems();
|
||||
|
||||
if (pluginOptions) {
|
||||
pluginOptions.forEach(option => {
|
||||
pluginOptions.forEach((option) => {
|
||||
options.splice(option.value.position, 0, {
|
||||
key: option.key,
|
||||
...option.value,
|
||||
@ -474,7 +474,7 @@ const SectionHeaderContent = props => {
|
||||
setBufferSelection(currentFolderId);
|
||||
setIsFolderActions(true);
|
||||
downloadAction(t("Translations:ArchivingData"), [currentFolderId]).catch(
|
||||
err => toastr.error(err)
|
||||
(err) => toastr.error(err)
|
||||
);
|
||||
};
|
||||
|
||||
@ -496,7 +496,7 @@ const SectionHeaderContent = props => {
|
||||
setIsFolderActions(true);
|
||||
|
||||
if (confirmDelete || isThirdPartySelection) {
|
||||
getFolderInfo(currentFolderId).then(data => {
|
||||
getFolderInfo(currentFolderId).then((data) => {
|
||||
setBufferSelection(data);
|
||||
setDeleteDialogVisible(true);
|
||||
});
|
||||
@ -508,7 +508,7 @@ const SectionHeaderContent = props => {
|
||||
FolderRemoved: t("Files:FolderRemoved"),
|
||||
};
|
||||
|
||||
deleteAction(translations, [currentFolderId], true).catch(err =>
|
||||
deleteAction(translations, [currentFolderId], true).catch((err) =>
|
||||
toastr.error(err)
|
||||
);
|
||||
}
|
||||
@ -614,10 +614,10 @@ const SectionHeaderContent = props => {
|
||||
|
||||
const isDisabled = isRecycleBinFolder || isRoom;
|
||||
|
||||
const links = externalLinks.filter(l => !l.sharedTo.disabled);
|
||||
const links = externalLinks.filter((l) => !l.sharedTo.disabled);
|
||||
const isMultiExternalLink = links.length > 1;
|
||||
|
||||
const roomLinks = links.map(link => {
|
||||
const roomLinks = links.map((link) => {
|
||||
return {
|
||||
// id: "option_move-to",
|
||||
key: `external-link_${link.sharedTo.id}`,
|
||||
@ -768,7 +768,7 @@ const SectionHeaderContent = props => {
|
||||
key: "archive-room",
|
||||
label: t("MoveToArchive"),
|
||||
icon: RoomArchiveSvgUrl,
|
||||
onClick: e => onClickArchive(e),
|
||||
onClick: (e) => onClickArchive(e),
|
||||
disabled: !isRoom || !security?.Move,
|
||||
"data-action": "archive",
|
||||
action: "archive",
|
||||
@ -822,7 +822,7 @@ const SectionHeaderContent = props => {
|
||||
];
|
||||
};
|
||||
|
||||
const onSelect = e => {
|
||||
const onSelect = (e) => {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
|
||||
isAccountsPage ? setAccountsSelected(key) : setSelected(key);
|
||||
@ -835,7 +835,7 @@ const SectionHeaderContent = props => {
|
||||
const getMenuItems = () => {
|
||||
const checkboxOptions = isAccountsPage ? (
|
||||
<>
|
||||
{accountsCbMenuItems.map(key => {
|
||||
{accountsCbMenuItems.map((key) => {
|
||||
const label = getAccountsCheckboxItemLabel(t, key);
|
||||
const id = getAccountsMenuItemId(key);
|
||||
return (
|
||||
@ -851,7 +851,7 @@ const SectionHeaderContent = props => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{cbMenuItems.map(key => {
|
||||
{cbMenuItems.map((key) => {
|
||||
const label = getCheckboxItemLabel(t, key);
|
||||
const id = getCheckboxItemId(key);
|
||||
return (
|
||||
@ -870,7 +870,7 @@ const SectionHeaderContent = props => {
|
||||
return checkboxOptions;
|
||||
};
|
||||
|
||||
const onChange = checked => {
|
||||
const onChange = (checked) => {
|
||||
isAccountsPage
|
||||
? setAccountsSelected(checked ? "all" : "none")
|
||||
: setSelected(checked ? "all" : "none");
|
||||
@ -898,7 +898,7 @@ const SectionHeaderContent = props => {
|
||||
|
||||
filter.folder = id;
|
||||
|
||||
const itemIdx = selectedFolder.navigationPath.findIndex(v => v.id === id);
|
||||
const itemIdx = selectedFolder.navigationPath.findIndex((v) => v.id === id);
|
||||
|
||||
const state = {
|
||||
title: selectedFolder.navigationPath[itemIdx]?.title || "",
|
||||
@ -912,7 +912,7 @@ const SectionHeaderContent = props => {
|
||||
window.DocSpace.navigate(`${path}?${filter.toUrlParams()}`, { state });
|
||||
};
|
||||
|
||||
const onInvite = e => {
|
||||
const onInvite = (e) => {
|
||||
const type = e.item["data-type"];
|
||||
|
||||
if (isGracePeriod) {
|
||||
@ -933,7 +933,7 @@ const SectionHeaderContent = props => {
|
||||
.then(() =>
|
||||
toastr.success(t("PeopleTranslations:SuccessSentMultipleInvitatios"))
|
||||
)
|
||||
.catch(err => toastr.error(err));
|
||||
.catch((err) => toastr.error(err));
|
||||
}, [resendInvitesAgain]);
|
||||
|
||||
const headerMenu = isAccountsPage
|
||||
@ -956,7 +956,7 @@ const SectionHeaderContent = props => {
|
||||
tableGroupMenuVisible =
|
||||
isAccountsHeaderVisible &&
|
||||
tableGroupMenuVisible &&
|
||||
headerMenu.some(x => !x.disabled);
|
||||
headerMenu.some((x) => !x.disabled);
|
||||
tableGroupMenuProps.isChecked = isAccountsHeaderChecked;
|
||||
tableGroupMenuProps.isIndeterminate = isAccountsHeaderIndeterminate;
|
||||
tableGroupMenuProps.withoutInfoPanelToggler = false;
|
||||
@ -1002,10 +1002,11 @@ const SectionHeaderContent = props => {
|
||||
|
||||
return (
|
||||
<Consumer key="header">
|
||||
{context => (
|
||||
{(context) => (
|
||||
<StyledContainer
|
||||
isRecycleBinFolder={isRecycleBinFolder}
|
||||
hideContextMenuInsideArchiveRoom={hideContextMenuInsideArchiveRoom}>
|
||||
hideContextMenuInsideArchiveRoom={hideContextMenuInsideArchiveRoom}
|
||||
>
|
||||
{tableGroupMenuVisible ? (
|
||||
<TableGroupMenu {...tableGroupMenuProps} />
|
||||
) : (
|
||||
@ -1112,10 +1113,14 @@ export default inject(
|
||||
categoryType,
|
||||
} = filesStore;
|
||||
|
||||
const { setIsSectionFilterLoading, showHeaderLoader, isLoading } =
|
||||
clientLoadingStore;
|
||||
const {
|
||||
setIsSectionFilterLoading,
|
||||
showHeaderLoader,
|
||||
|
||||
const setIsLoading = param => {
|
||||
isLoading,
|
||||
} = clientLoadingStore;
|
||||
|
||||
const setIsLoading = (param) => {
|
||||
setIsSectionFilterLoading(param);
|
||||
};
|
||||
|
||||
@ -1208,7 +1213,7 @@ export default inject(
|
||||
let folderPath = navigationPath;
|
||||
|
||||
if (isFrame && !!pathParts) {
|
||||
folderPath = navigationPath.filter(item => !item.isRootRoom);
|
||||
folderPath = navigationPath.filter((item) => !item.isRootRoom);
|
||||
}
|
||||
|
||||
const isRoot = isFrame
|
||||
|
@ -248,6 +248,7 @@ const PureHome = (props) => {
|
||||
primaryProgressDataVisible || secondaryProgressDataStoreVisible;
|
||||
|
||||
sectionProps.isEmptyPage = isEmptyPage;
|
||||
sectionProps.isTrashFolder = isRecycleBinFolder;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,9 @@ const Certificates = (props) => {
|
||||
spEncryptAlgorithm,
|
||||
spDecryptAlgorithm,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
} = props;
|
||||
|
||||
let prefix = "";
|
||||
@ -139,7 +142,7 @@ const Certificates = (props) => {
|
||||
{provider === "IdentityProvider" && (
|
||||
<>
|
||||
<SsoComboBox
|
||||
isDisabled={idpCertificates.length === 0}
|
||||
isDisabled={isDisabledIdpSigning}
|
||||
labelText={t("idpSigningAlgorithm")}
|
||||
name="idpVerifyAlgorithm"
|
||||
options={verifyAlgorithmsOptions}
|
||||
@ -152,7 +155,7 @@ const Certificates = (props) => {
|
||||
{provider === "ServiceProvider" && (
|
||||
<>
|
||||
<SsoComboBox
|
||||
isDisabled={spCertificates.length === 0}
|
||||
isDisabled={isDisabledSpSigning}
|
||||
labelText={t("spSigningAlgorithm")}
|
||||
name="spSigningAlgorithm"
|
||||
options={verifyAlgorithmsOptions}
|
||||
@ -161,7 +164,7 @@ const Certificates = (props) => {
|
||||
/>
|
||||
|
||||
<SsoComboBox
|
||||
isDisabled={spCertificates.length === 0}
|
||||
isDisabled={isDisabledSpEncrypt}
|
||||
labelText={t("StandardDecryptionAlgorithm")}
|
||||
name={"spEncryptAlgorithm"}
|
||||
options={decryptAlgorithmsOptions}
|
||||
@ -193,6 +196,9 @@ export default inject(({ ssoStore }) => {
|
||||
spEncryptAlgorithm,
|
||||
spDecryptAlgorithm,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
} = ssoStore;
|
||||
|
||||
return {
|
||||
@ -207,5 +213,8 @@ export default inject(({ ssoStore }) => {
|
||||
spEncryptAlgorithm,
|
||||
spDecryptAlgorithm,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
};
|
||||
})(observer(Certificates));
|
||||
|
@ -27,8 +27,8 @@ const CertificatesTable = (props) => {
|
||||
console.log(prefix, index);
|
||||
const onEdit = () => {
|
||||
prefix === "sp"
|
||||
? setSpCertificate(certificate, index)
|
||||
: setIdpCertificate(certificate);
|
||||
? setSpCertificate(certificate, index, true)
|
||||
: setIdpCertificate(certificate, index, true);
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
|
@ -44,11 +44,15 @@ const CheckboxSet = (props) => {
|
||||
spSignLogoutRequests,
|
||||
spSignLogoutResponses,
|
||||
spEncryptAssertions,
|
||||
enableSso,
|
||||
setCheckbox,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
} = props;
|
||||
|
||||
const isDisabled =
|
||||
prefix === "sp" ? isDisabledSpSigning : isDisabledIdpSigning;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Checkbox
|
||||
@ -58,7 +62,7 @@ const CheckboxSet = (props) => {
|
||||
: "sp-sign-auth-requests"
|
||||
}
|
||||
className="checkbox-input"
|
||||
isDisabled={!enableSso || isLoadingXml}
|
||||
isDisabled={isDisabled}
|
||||
onChange={setCheckbox}
|
||||
label={prefix === "idp" ? t("idpAuthRequest") : t("spAuthRequest")}
|
||||
name={checkboxesNames[prefix][0]}
|
||||
@ -74,7 +78,7 @@ const CheckboxSet = (props) => {
|
||||
: "sp-sign-logout-requests"
|
||||
}
|
||||
className="checkbox-input"
|
||||
isDisabled={!enableSso || isLoadingXml}
|
||||
isDisabled={isDisabled}
|
||||
onChange={setCheckbox}
|
||||
label={
|
||||
prefix === "idp" ? t("idpSignExitRequest") : t("spSignExitRequest")
|
||||
@ -92,7 +96,7 @@ const CheckboxSet = (props) => {
|
||||
: "sp-sign-logout-responses"
|
||||
}
|
||||
className="checkbox-input"
|
||||
isDisabled={!enableSso || isLoadingXml}
|
||||
isDisabled={isDisabled}
|
||||
onChange={setCheckbox}
|
||||
label={
|
||||
prefix === "idp"
|
||||
@ -112,7 +116,7 @@ const CheckboxSet = (props) => {
|
||||
<Checkbox
|
||||
id="sp-encrypt-assertions"
|
||||
className="checkbox-input"
|
||||
isDisabled={!enableSso || isLoadingXml}
|
||||
isDisabled={isDisabledSpEncrypt}
|
||||
onChange={setCheckbox}
|
||||
label={t("spDecryptStatements")}
|
||||
name={checkboxesNames[prefix][3]}
|
||||
@ -133,9 +137,10 @@ export default inject(({ ssoStore }) => {
|
||||
spSignLogoutRequests,
|
||||
spSignLogoutResponses,
|
||||
spEncryptAssertions,
|
||||
enableSso,
|
||||
setCheckbox,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
} = ssoStore;
|
||||
|
||||
return {
|
||||
@ -146,8 +151,9 @@ export default inject(({ ssoStore }) => {
|
||||
spSignLogoutRequests,
|
||||
spSignLogoutResponses,
|
||||
spEncryptAssertions,
|
||||
enableSso,
|
||||
setCheckbox,
|
||||
isLoadingXml,
|
||||
isDisabledSpSigning,
|
||||
isDisabledSpEncrypt,
|
||||
isDisabledIdpSigning,
|
||||
};
|
||||
})(observer(CheckboxSet));
|
||||
|
@ -66,7 +66,7 @@ class ClientLoadingStore {
|
||||
return;
|
||||
}
|
||||
this.startLoadingTime.header = new Date();
|
||||
if (withTimer) {
|
||||
if (withTimer && !this.firstLoad) {
|
||||
return (this.sectionHeaderTimer = setTimeout(() => {
|
||||
this.updateIsSectionHeaderLoading(isSectionHeaderLoading);
|
||||
}, SHOW_LOADER_TIMER));
|
||||
@ -107,7 +107,7 @@ class ClientLoadingStore {
|
||||
return;
|
||||
}
|
||||
this.startLoadingTime.filter = new Date();
|
||||
if (withTimer) {
|
||||
if (withTimer && !this.firstLoad) {
|
||||
return (this.sectionFilterTimer = setTimeout(() => {
|
||||
this.updateIsSectionFilterLoading(isSectionFilterLoading);
|
||||
}, SHOW_LOADER_TIMER));
|
||||
@ -153,7 +153,7 @@ class ClientLoadingStore {
|
||||
return;
|
||||
}
|
||||
this.startLoadingTime.body = new Date();
|
||||
if (withTimer) {
|
||||
if (withTimer && !this.firstLoad) {
|
||||
return (this.sectionBodyTimer = setTimeout(() => {
|
||||
this.updateIsSectionBodyLoading(isSectionBodyLoading);
|
||||
}, SHOW_LOADER_TIMER));
|
||||
|
@ -108,7 +108,7 @@ class FilesStore {
|
||||
mainButtonMobileVisible = true;
|
||||
filesIsLoading = false;
|
||||
|
||||
isEmptyPage = false;
|
||||
isEmptyPage = true;
|
||||
isLoadedFetchFiles = false;
|
||||
|
||||
tempActionFilesIds = [];
|
||||
@ -1390,7 +1390,7 @@ class FilesStore {
|
||||
authorType ||
|
||||
roomId ||
|
||||
search ||
|
||||
!withSubfolders ||
|
||||
withSubfolders ||
|
||||
filterType ||
|
||||
searchInContent;
|
||||
|
||||
@ -2805,6 +2805,8 @@ class FilesStore {
|
||||
|
||||
if (items.length > 0 && this.isEmptyPage) {
|
||||
this.setIsEmptyPage(false);
|
||||
} else if (items.length === 0 && !this.isEmptyPage) {
|
||||
this.setIsEmptyPage(true);
|
||||
}
|
||||
|
||||
const newItem = items.map((item) => {
|
||||
|
@ -19,6 +19,9 @@ import {
|
||||
SSO_TITLE,
|
||||
SSO_PHONE,
|
||||
SSO_NAME_ID_FORMAT,
|
||||
SSO_SIGNING,
|
||||
SSO_ENCRYPT,
|
||||
SSO_SIGNING_ENCRYPT,
|
||||
} from "../helpers/constants";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
@ -41,11 +44,11 @@ class SsoFormStore {
|
||||
sloUrlPost = "";
|
||||
sloUrlRedirect = "";
|
||||
sloBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";
|
||||
nameIdFormat = SSO_NAME_ID_FORMAT;
|
||||
nameIdFormat = SSO_NAME_ID_FORMAT[0];
|
||||
|
||||
idpCertificate = "";
|
||||
idpPrivateKey = null;
|
||||
idpAction = "signing";
|
||||
idpAction = SSO_SIGNING;
|
||||
idpCertificates = [];
|
||||
|
||||
// idpCertificateAdvanced
|
||||
@ -59,7 +62,7 @@ class SsoFormStore {
|
||||
|
||||
spCertificate = "";
|
||||
spPrivateKey = "";
|
||||
spAction = "signing";
|
||||
spAction = SSO_SIGNING;
|
||||
spCertificates = [];
|
||||
|
||||
// spCertificateAdvanced
|
||||
@ -126,6 +129,7 @@ class SsoFormStore {
|
||||
|
||||
defaultSettings = null;
|
||||
editIndex = 0;
|
||||
isEdit = false;
|
||||
|
||||
isInit = false;
|
||||
|
||||
@ -191,6 +195,8 @@ class SsoFormStore {
|
||||
closeIdpModal = () => {
|
||||
this.idpCertificate = "";
|
||||
this.idpPrivateKey = "";
|
||||
this.editIndex = 0;
|
||||
this.isEdit = false;
|
||||
this.idpIsModalVisible = false;
|
||||
};
|
||||
|
||||
@ -199,6 +205,7 @@ class SsoFormStore {
|
||||
this.spPrivateKey = "";
|
||||
this.spIsModalVisible = false;
|
||||
this.editIndex = 0;
|
||||
this.isEdit = false;
|
||||
};
|
||||
|
||||
setComboBoxOption = (option) => {
|
||||
@ -638,33 +645,77 @@ class SsoFormStore {
|
||||
return array.filter((item, index, array) => array.indexOf(item) == index);
|
||||
};
|
||||
|
||||
setSpCertificate = (certificate, index) => {
|
||||
setSpCertificate = (certificate, index, isEdit) => {
|
||||
this.spCertificate = certificate.crt;
|
||||
this.spPrivateKey = certificate.key;
|
||||
this.spAction = certificate.action;
|
||||
this.editIndex = index;
|
||||
this.isEdit = isEdit;
|
||||
this.spIsModalVisible = true;
|
||||
};
|
||||
|
||||
setIdpCertificate = (certificate) => {
|
||||
setIdpCertificate = (certificate, index, isEdit) => {
|
||||
this.idpCertificate = certificate.crt;
|
||||
this.idpPrivateKey = certificate.key;
|
||||
this.idpAction = certificate.action;
|
||||
this.editIndex = index;
|
||||
this.isEdit = isEdit;
|
||||
this.idpIsModalVisible = true;
|
||||
};
|
||||
|
||||
resetSpCheckboxes = (action) => {
|
||||
if (action === SSO_SIGNING_ENCRYPT) {
|
||||
this.spSignAuthRequests = false;
|
||||
this.spSignLogoutRequests = false;
|
||||
this.spSignLogoutResponses = false;
|
||||
this.spEncryptAssertions = false;
|
||||
}
|
||||
if (action === SSO_SIGNING) {
|
||||
this.spSignAuthRequests = false;
|
||||
this.spSignLogoutRequests = false;
|
||||
this.spSignLogoutResponses = false;
|
||||
}
|
||||
if (action === SSO_ENCRYPT) {
|
||||
this.spEncryptAssertions = false;
|
||||
}
|
||||
};
|
||||
|
||||
resetIdpCheckboxes = () => {
|
||||
this.idpVerifyAuthResponsesSign = false;
|
||||
this.idpVerifyLogoutRequestsSign = false;
|
||||
this.idpVerifyLogoutResponsesSign = false;
|
||||
};
|
||||
|
||||
delSpCertificate = (action) => {
|
||||
this.resetSpCheckboxes(action);
|
||||
this.spCertificates = this.spCertificates.filter(
|
||||
(certificate) => certificate.action !== action
|
||||
);
|
||||
};
|
||||
|
||||
delIdpCertificate = (cert) => {
|
||||
this.resetIdpCheckboxes();
|
||||
this.idpCertificates = this.idpCertificates.filter(
|
||||
(certificate) => certificate.crt !== cert
|
||||
);
|
||||
};
|
||||
|
||||
checkSpCertificateExist = () => {
|
||||
if (
|
||||
this.spAction === SSO_SIGNING_ENCRYPT &&
|
||||
this.spCertificates.length > 0 &&
|
||||
!this.isEdit
|
||||
)
|
||||
return true;
|
||||
|
||||
return this.spCertificates.find(
|
||||
(item) =>
|
||||
(item.action === this.spAction ||
|
||||
item.action === SSO_SIGNING_ENCRYPT) &&
|
||||
!this.isEdit
|
||||
);
|
||||
};
|
||||
|
||||
addSpCertificate = async (t) => {
|
||||
const data = [
|
||||
{
|
||||
@ -674,12 +725,7 @@ class SsoFormStore {
|
||||
},
|
||||
];
|
||||
|
||||
if (
|
||||
this.spCertificates.find(
|
||||
(item, index) =>
|
||||
item.action === this.spAction && this.editIndex !== index
|
||||
)
|
||||
) {
|
||||
if (this.checkSpCertificateExist()) {
|
||||
toastr.error(t("CertificateExist"));
|
||||
return;
|
||||
}
|
||||
@ -693,10 +739,16 @@ class SsoFormStore {
|
||||
return;
|
||||
}
|
||||
const newCertificates = res.data;
|
||||
if (this.isEdit) {
|
||||
this.spCertificates[this.editIndex] = newCertificates[0];
|
||||
this.checkedSpBoxes(newCertificates[0]);
|
||||
} else {
|
||||
newCertificates.map((cert) => {
|
||||
this.spCertificates = [...this.spCertificates, cert];
|
||||
this.checkedSpBoxes(cert);
|
||||
});
|
||||
}
|
||||
|
||||
this.isCertificateLoading = false;
|
||||
this.closeSpModal();
|
||||
} catch (err) {
|
||||
@ -707,14 +759,14 @@ class SsoFormStore {
|
||||
};
|
||||
|
||||
checkedSpBoxes = (cert) => {
|
||||
if (cert.action === "signing") {
|
||||
if (cert.action === SSO_SIGNING) {
|
||||
this.spSignAuthRequests = true;
|
||||
this.spSignLogoutRequests = true;
|
||||
}
|
||||
if (cert.action === "encrypt") {
|
||||
if (cert.action === SSO_ENCRYPT) {
|
||||
this.spEncryptAssertions = true;
|
||||
}
|
||||
if (cert.action === "signing and encrypt") {
|
||||
if (cert.action === SSO_SIGNING_ENCRYPT) {
|
||||
this.spSignAuthRequests = true;
|
||||
this.spSignLogoutRequests = true;
|
||||
this.spEncryptAssertions = true;
|
||||
@ -730,7 +782,11 @@ class SsoFormStore {
|
||||
},
|
||||
];
|
||||
|
||||
if (this.idpCertificates.find((item) => item.crt === this.idpCertificate)) {
|
||||
if (
|
||||
this.idpCertificates.find(
|
||||
(item) => item.crt === this.idpCertificate && !this.isEdit
|
||||
)
|
||||
) {
|
||||
toastr.error(t("CertificateExist"));
|
||||
return;
|
||||
}
|
||||
@ -744,10 +800,15 @@ class SsoFormStore {
|
||||
return;
|
||||
}
|
||||
const newCertificates = res.data;
|
||||
if (this.isEdit) {
|
||||
this.idpCertificates[this.editIndex] = newCertificates[0];
|
||||
this.checkedIdpBoxes(newCertificates[0]);
|
||||
} else {
|
||||
newCertificates.map((cert) => {
|
||||
this.idpCertificates = [...this.idpCertificates, cert];
|
||||
this.checkedIdpBoxes(cert);
|
||||
});
|
||||
}
|
||||
this.isCertificateLoading = false;
|
||||
this.closeIdpModal();
|
||||
} catch (err) {
|
||||
@ -824,6 +885,27 @@ class SsoFormStore {
|
||||
const currentSettings = this.getSettings();
|
||||
return !isEqual(currentSettings, this.defaultSettings);
|
||||
}
|
||||
|
||||
get isDisabledIdpSigning() {
|
||||
if (!this.enableSso || this.isLoadingXml) return true;
|
||||
return this.idpCertificates.length === 0;
|
||||
}
|
||||
|
||||
get isDisabledSpSigning() {
|
||||
if (!this.enableSso || this.isLoadingXml) return true;
|
||||
return !this.spCertificates.some(
|
||||
(cert) =>
|
||||
cert.action === SSO_SIGNING || cert.action === SSO_SIGNING_ENCRYPT
|
||||
);
|
||||
}
|
||||
|
||||
get isDisabledSpEncrypt() {
|
||||
if (!this.enableSso || this.isLoadingXml) return true;
|
||||
return !this.spCertificates.some(
|
||||
(cert) =>
|
||||
cert.action === SSO_ENCRYPT || cert.action === SSO_SIGNING_ENCRYPT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SsoFormStore;
|
||||
|
@ -4,6 +4,8 @@ const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const ModuleFederationPlugin =
|
||||
require("webpack").container.ModuleFederationPlugin;
|
||||
const DefinePlugin = require("webpack").DefinePlugin;
|
||||
const BundleAnalyzerPlugin =
|
||||
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
||||
|
||||
const ExternalTemplateRemotesPlugin = require("external-remotes-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
@ -362,5 +364,9 @@ module.exports = (env, argv) => {
|
||||
|
||||
config.plugins.push(new DefinePlugin(defines));
|
||||
|
||||
if (env.mode === "analyze") {
|
||||
config.plugins.push(new BundleAnalyzerPlugin());
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
@ -57,6 +57,7 @@ const Navigation = ({
|
||||
burgerLogo,
|
||||
isPublicRoom,
|
||||
titleIcon,
|
||||
|
||||
...rest
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
@ -221,6 +222,7 @@ const Navigation = ({
|
||||
isDesktopClient={isDesktopClient}
|
||||
isInfoPanelVisible={isInfoPanelVisible}
|
||||
withLogo={!!withLogo}
|
||||
className="navigation-container"
|
||||
>
|
||||
{withLogo && (
|
||||
<NavigationLogo
|
||||
@ -253,10 +255,14 @@ const Navigation = ({
|
||||
onPlusClick={onPlusClick}
|
||||
isFrame={isFrame}
|
||||
isPublicRoom={isPublicRoom}
|
||||
isTrashFolder={isTrashFolder}
|
||||
/>
|
||||
</StyledContainer>
|
||||
{isTrashFolder && !isEmptyPage && (
|
||||
<TrashWarning title={titles.trashWarning} />
|
||||
<TrashWarning
|
||||
title={titles.trashWarning}
|
||||
isTabletView={isTabletView}
|
||||
/>
|
||||
)}
|
||||
{infoPanelIsVisible && !hideInfoPanel && (
|
||||
<ToggleInfoPanelButton
|
||||
|
@ -10,7 +10,7 @@ const ContextButton = (props) => {
|
||||
const ref = useRef(null);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const { className, getData, withMenu, ...rest } = props;
|
||||
const { className, getData, withMenu, isTrashFolder, ...rest } = props;
|
||||
|
||||
const toggle = (e, isOpen) => {
|
||||
isOpen ? menuRef.current.show(e) : menuRef.current.hide(e);
|
||||
@ -42,7 +42,7 @@ const ContextButton = (props) => {
|
||||
ref={menuRef}
|
||||
onHide={onHide}
|
||||
scaled={false}
|
||||
leftOffset={isDesktop() ? 150 : 0}
|
||||
leftOffset={isTrashFolder ? 188 : isDesktop() ? 150 : 0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -164,6 +164,7 @@ const ControlButtons = ({
|
||||
onPlusClick,
|
||||
isFrame,
|
||||
isPublicRoom,
|
||||
isTrashFolder,
|
||||
}) => {
|
||||
const toggleInfoPanelAction = () => {
|
||||
toggleInfoPanel && toggleInfoPanel();
|
||||
@ -205,6 +206,7 @@ const ControlButtons = ({
|
||||
withMenu={withMenu}
|
||||
//onPlusClick={onPlusClick}
|
||||
title={titles?.actions}
|
||||
isTrashFolder={isTrashFolder}
|
||||
/>
|
||||
|
||||
{!isDesktop && (
|
||||
|
@ -20,21 +20,6 @@ const StyledTrashWarning = styled.div`
|
||||
color: ${({ theme }) => theme.section.header.trashErasureLabelText};
|
||||
background: ${({ theme }) =>
|
||||
theme.section.header.trashErasureLabelBackground};
|
||||
|
||||
${({ isTabletView }) =>
|
||||
!isTabletView
|
||||
? css`
|
||||
@media ${tablet} {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
: css`
|
||||
margin-bottom: 16px;
|
||||
display: none;
|
||||
@media ${tablet} {
|
||||
display: flex;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const TrashWarning = ({ title, isTabletView }) => {
|
||||
|
@ -49,6 +49,7 @@ const Section = (props) => {
|
||||
isInfoPanelVisible,
|
||||
isInfoPanelScrollLocked,
|
||||
isEmptyPage,
|
||||
isTrashFolder,
|
||||
} = props;
|
||||
|
||||
const [sectionSize, setSectionSize] = React.useState({
|
||||
@ -169,6 +170,7 @@ const Section = (props) => {
|
||||
viewAs={viewAs}
|
||||
showText={showText}
|
||||
isEmptyPage={isEmptyPage}
|
||||
isTrashFolder={isTrashFolder}
|
||||
>
|
||||
{sectionHeaderContent
|
||||
? sectionHeaderContent.props.children
|
||||
@ -206,6 +208,7 @@ const Section = (props) => {
|
||||
showText={showText}
|
||||
settingsStudio={settingsStudio}
|
||||
isEmptyPage={isEmptyPage}
|
||||
isTrashFolder={isTrashFolder}
|
||||
>
|
||||
{sectionHeaderContent
|
||||
? sectionHeaderContent.props.children
|
||||
|
@ -13,13 +13,14 @@ import {
|
||||
desktop,
|
||||
smallTablet,
|
||||
mobile,
|
||||
hugeMobile,
|
||||
} from "@docspace/components/utils/device";
|
||||
|
||||
const settingsStudioStyles = css`
|
||||
${({ settingsStudio }) =>
|
||||
settingsStudio &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0 20px 16px 7px;
|
||||
@ -29,7 +30,7 @@ const settingsStudioStyles = css`
|
||||
`}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0 24px 16px 0;
|
||||
@ -40,7 +41,7 @@ const settingsStudioStyles = css`
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 8px 24px 16px 0;
|
||||
@ -51,7 +52,7 @@ const settingsStudioStyles = css`
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0 24px 16px 0;
|
||||
@ -64,7 +65,7 @@ const settingsStudioStyles = css`
|
||||
`;
|
||||
|
||||
const paddingStyles = css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 19px 20px 16px 3px;
|
||||
@ -76,9 +77,20 @@ const paddingStyles = css`
|
||||
|
||||
${settingsStudioStyles};
|
||||
|
||||
@media ${tablet} {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 19px 24px 16px 0;
|
||||
`
|
||||
: css`
|
||||
padding: 19px 0 16px 24px;
|
||||
`}
|
||||
}
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0 23px 16px 0 !important;
|
||||
@ -88,9 +100,20 @@ const paddingStyles = css`
|
||||
`}
|
||||
`};
|
||||
|
||||
@media ${hugeMobile} {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 24px 16px 0;
|
||||
`
|
||||
: css`
|
||||
padding: 0px 0 16px 24px;
|
||||
`}
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 24px 16px 0 !important;
|
||||
@ -104,16 +127,16 @@ const paddingStyles = css`
|
||||
const commonStyles = css`
|
||||
flex-grow: 1;
|
||||
|
||||
${props => (props.isDesktop ? "height: auto" : "height: 100%")};
|
||||
${(props) => (props.isDesktop ? "height: auto" : "height: 100%")};
|
||||
|
||||
${props => !props.withScroll && `height: 100%;`}
|
||||
${(props) => !props.withScroll && `height: 100%;`}
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
|
||||
.section-wrapper {
|
||||
height: 100%;
|
||||
${props =>
|
||||
${(props) =>
|
||||
!props.withScroll &&
|
||||
css`
|
||||
display: flex;
|
||||
@ -121,17 +144,17 @@ const commonStyles = css`
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
`};
|
||||
${props => !props.withScroll && paddingStyles}
|
||||
${(props) => !props.withScroll && paddingStyles}
|
||||
}
|
||||
|
||||
.section-wrapper-content {
|
||||
${paddingStyles}
|
||||
flex: 1 0 auto;
|
||||
outline: none;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.viewAs == "tile" &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-right: 20px;
|
||||
@ -141,7 +164,7 @@ const commonStyles = css`
|
||||
`}
|
||||
`}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
(props.viewAs == "settings" || props.viewAs == "profile") &&
|
||||
css`
|
||||
padding-top: 0;
|
||||
@ -171,7 +194,7 @@ const commonStyles = css`
|
||||
`}
|
||||
|
||||
@media ${desktop} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.viewAs === "row" &&
|
||||
css`
|
||||
margin-top: -15px;
|
||||
@ -187,10 +210,10 @@ const StyledSectionBody = styled.div`
|
||||
|
||||
${commonStyles};
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.withScroll &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -20px;
|
||||
@ -200,7 +223,7 @@ const StyledSectionBody = styled.div`
|
||||
`}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -24px;
|
||||
@ -213,7 +236,7 @@ const StyledSectionBody = styled.div`
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -24px;
|
||||
@ -242,10 +265,10 @@ const StyledDropZoneBody = styled(DragAndDrop)`
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.withScroll &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -20px;
|
||||
@ -255,7 +278,7 @@ const StyledDropZoneBody = styled(DragAndDrop)`
|
||||
`}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -24px;
|
||||
@ -267,7 +290,7 @@ const StyledDropZoneBody = styled(DragAndDrop)`
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -24px;
|
||||
@ -339,13 +362,15 @@ class SectionBody extends React.Component {
|
||||
isLoaded={isLoaded}
|
||||
isDesktop={isDesktop}
|
||||
settingsStudio={settingsStudio}
|
||||
className="section-body">
|
||||
className="section-body"
|
||||
>
|
||||
{withScroll ? (
|
||||
!isMobileOnly ? (
|
||||
<Scrollbar
|
||||
id="sectionScroll"
|
||||
scrollclass="section-scroll"
|
||||
stype="mediumBlack">
|
||||
stype="mediumBlack"
|
||||
>
|
||||
<div className="section-wrapper">
|
||||
<div className="section-wrapper-content" {...focusProps}>
|
||||
{children}
|
||||
@ -374,13 +399,15 @@ class SectionBody extends React.Component {
|
||||
withScroll={withScroll}
|
||||
isLoaded={isLoaded}
|
||||
isDesktop={isDesktop}
|
||||
settingsStudio={settingsStudio}>
|
||||
settingsStudio={settingsStudio}
|
||||
>
|
||||
{withScroll ? (
|
||||
!isMobileOnly ? (
|
||||
<Scrollbar
|
||||
id="sectionScroll"
|
||||
scrollclass="section-scroll"
|
||||
stype="mediumBlack">
|
||||
stype="mediumBlack"
|
||||
>
|
||||
<div className="section-wrapper">
|
||||
<div className="section-wrapper-content" {...focusProps}>
|
||||
{children}
|
||||
|
@ -18,6 +18,30 @@ const StyledSectionHeader = styled.div`
|
||||
height: 61px;
|
||||
min-height: 61px;
|
||||
|
||||
${({ isTrashFolder, isEmptyPage }) =>
|
||||
isTrashFolder &&
|
||||
!isEmptyPage &&
|
||||
css`
|
||||
height: 109px;
|
||||
min-height: 109px;
|
||||
|
||||
.header-container {
|
||||
flex-direction: column;
|
||||
height: 109px !important;
|
||||
min-height: 109px !important;
|
||||
|
||||
.navigation-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
.trash-warning {
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
.header-container {
|
||||
margin-bottom: 1px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
@ -28,29 +52,91 @@ const StyledSectionHeader = styled.div`
|
||||
css`
|
||||
height: 61px;
|
||||
min-height: 61px;
|
||||
|
||||
${({ isTrashFolder, isEmptyPage }) =>
|
||||
isTrashFolder &&
|
||||
!isEmptyPage &&
|
||||
css`
|
||||
height: 109px;
|
||||
min-height: 109px;
|
||||
|
||||
.header-container {
|
||||
flex-direction: column;
|
||||
height: 109px !important;
|
||||
min-height: 109px !important;
|
||||
|
||||
.navigation-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
.trash-warning {
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
height: 53px;
|
||||
min-height: 53px;
|
||||
|
||||
${({ isTrashFolder, isEmptyPage }) =>
|
||||
isTrashFolder &&
|
||||
!isEmptyPage &&
|
||||
css`
|
||||
height: 101px;
|
||||
min-height: 101px;
|
||||
|
||||
.header-container {
|
||||
flex-direction: column;
|
||||
height: 101px !important;
|
||||
min-height: 101px !important;
|
||||
|
||||
.navigation-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
.trash-warning {
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
height: 53px;
|
||||
min-height: 53px;
|
||||
`}
|
||||
|
||||
${({ isTrashFolder, isEmptyPage }) =>
|
||||
isTrashFolder &&
|
||||
!isEmptyPage &&
|
||||
css`
|
||||
@media ${tablet} {
|
||||
height: 109px;
|
||||
min-height: 109px;
|
||||
height: 101px;
|
||||
min-height: 101px;
|
||||
|
||||
.header-container {
|
||||
flex-direction: column;
|
||||
height: 101px !important;
|
||||
min-height: 101px !important;
|
||||
|
||||
.navigation-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
.trash-warning {
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
${props =>
|
||||
`}
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-left: 20px;
|
||||
@ -74,7 +160,7 @@ const StyledSectionHeader = styled.div`
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-left: 16px;
|
||||
@ -88,7 +174,7 @@ const StyledSectionHeader = styled.div`
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-left: 0 !important;
|
||||
@ -101,7 +187,7 @@ const StyledSectionHeader = styled.div`
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 0px;
|
||||
@ -115,7 +201,7 @@ const StyledSectionHeader = styled.div`
|
||||
css`
|
||||
width: 100vw !important;
|
||||
max-width: 100vw !important;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-left: 16px !important;
|
||||
@ -124,24 +210,23 @@ const StyledSectionHeader = styled.div`
|
||||
padding-right: 16px !important;
|
||||
`}
|
||||
|
||||
margin-bottom: ${props => (props.settingsStudio ? "8px !important" : "0")};
|
||||
margin-bottom: ${(props) =>
|
||||
props.settingsStudio ? "8px !important" : "0"};
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledSectionHeader.defaultProps = { theme: Base };
|
||||
|
||||
const SectionHeader = props => {
|
||||
const SectionHeader = (props) => {
|
||||
const {
|
||||
viewAs,
|
||||
settingsStudio = false,
|
||||
className,
|
||||
isEmptyPage,
|
||||
isTrashFolder,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
const isTrashFolder = pathname.indexOf("trash") !== -1;
|
||||
|
||||
return (
|
||||
<StyledSectionHeader
|
||||
className={`section-header ${className}`}
|
||||
|
@ -0,0 +1,18 @@
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import React, { Suspense } from "react";
|
||||
|
||||
import i18n from "../i18n";
|
||||
|
||||
const i18nextStoryDecorator = (Story) => {
|
||||
return (
|
||||
// here catches the suspense from components not yet ready (still loading translations)
|
||||
// alternative set useSuspense false on i18next.options.react when initializing i18next
|
||||
<Suspense fallback={<div>loading translations...</div>}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Story />
|
||||
</I18nextProvider>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default i18nextStoryDecorator;
|
31
packages/components/.storybook/i18n.js
Normal file
31
packages/components/.storybook/i18n.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import HttpBackend from "i18next-http-backend";
|
||||
import i18n from "i18next";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
newInstance
|
||||
.use(HttpBackend)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
load: "currentOnly",
|
||||
ns: ["Common"],
|
||||
defaultNS: "Common",
|
||||
backend: {
|
||||
backendOptions: [
|
||||
{
|
||||
loadPath: "../../client/public/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
{
|
||||
loadPath: "../../../public/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
],
|
||||
},
|
||||
lng: "en",
|
||||
fallbackLng: "en",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default newInstance;
|
@ -136,7 +136,7 @@ class ComboBox extends React.Component {
|
||||
forceCloseClickOutside,
|
||||
withoutBackground,
|
||||
} = this.props;
|
||||
const { tabIndex, ...props } = this.props;
|
||||
const { tabIndex, onClickSelectedItem, ...props } = this.props;
|
||||
const { isOpen, selectedOption } = this.state;
|
||||
|
||||
const dropDownMaxHeightProp = dropDownMaxHeight
|
||||
@ -258,6 +258,7 @@ class ComboBox extends React.Component {
|
||||
disabled={disabled}
|
||||
backgroundColor={option.backgroundColor}
|
||||
onClick={this.optionClick.bind(this, option)}
|
||||
onClickSelectedItem={() => onClickSelectedItem?.(option)}
|
||||
fillIcon={fillIcon}
|
||||
isModern={noBorder}
|
||||
isActive={isActive}
|
||||
|
7
packages/components/cron/Cron.props.ts
Normal file
7
packages/components/cron/Cron.props.ts
Normal file
@ -0,0 +1,7 @@
|
||||
interface CronProps {
|
||||
value?: string;
|
||||
setValue: (value: string) => void;
|
||||
onError?: (error?: Error) => void;
|
||||
}
|
||||
|
||||
export default CronProps;
|
140
packages/components/cron/Cron.stories.tsx
Normal file
140
packages/components/cron/Cron.stories.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import React, { FC, useEffect, useMemo, useState } from "react";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18nextStoryDecorator from "../.storybook/decorators/i18nextStoryDecorator";
|
||||
|
||||
import Cron, { getNextSynchronization } from ".";
|
||||
import TextInput from "../text-input/text-input";
|
||||
import Button from "../button";
|
||||
import CronProps from "./Cron.props";
|
||||
|
||||
type CronType = FC<{ locale: string } & CronProps>;
|
||||
|
||||
type Story = StoryObj<CronType>;
|
||||
|
||||
const locales = [
|
||||
"az",
|
||||
"ar-SA",
|
||||
"zh-cn",
|
||||
"cs",
|
||||
"nl",
|
||||
"en",
|
||||
"fi",
|
||||
"fr",
|
||||
"de",
|
||||
"de-ch",
|
||||
"el",
|
||||
"it",
|
||||
"ja",
|
||||
"ko",
|
||||
"lv",
|
||||
"pl",
|
||||
"pt",
|
||||
"pt-br",
|
||||
"ru",
|
||||
"sk",
|
||||
"sl",
|
||||
"es",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
];
|
||||
|
||||
const meta: Meta<CronType> = {
|
||||
title: "Components/Cron",
|
||||
component: Cron,
|
||||
argTypes: {
|
||||
value: {
|
||||
description: "Cron value",
|
||||
},
|
||||
setValue: {
|
||||
description: "Set the cron value, similar to onChange.",
|
||||
},
|
||||
onError: {
|
||||
description:
|
||||
"Triggered when the cron component detects an error with the value.",
|
||||
},
|
||||
locale: { control: "select", options: locales },
|
||||
},
|
||||
decorators: [i18nextStoryDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
locale: "en",
|
||||
},
|
||||
|
||||
render: ({ value: defaultValue, locale }) => {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const [input, setInput] = useState(defaultValue);
|
||||
|
||||
const [cron, setCron] = useState(defaultValue);
|
||||
const [error, setError] = useState<Error>();
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(locale);
|
||||
}, [locale]);
|
||||
|
||||
const onError = (error?: Error) => {
|
||||
setError(error);
|
||||
};
|
||||
|
||||
const setValue = (cron?: string) => {
|
||||
setInput(cron);
|
||||
setCron(cron);
|
||||
};
|
||||
|
||||
const onClick = () => {
|
||||
setCron(input);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue);
|
||||
}, [defaultValue]);
|
||||
|
||||
const date = useMemo(() => cron && getNextSynchronization(cron), [cron]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "6px",
|
||||
alignItems: "baseline",
|
||||
maxWidth: "max-content",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
hasError={!!error}
|
||||
scale={false}
|
||||
/>
|
||||
{/*@ts-ignore*/}
|
||||
<Button size="small" primary label={"Set value"} onClick={onClick} />
|
||||
</div>
|
||||
|
||||
<Cron value={cron} setValue={setValue} onError={onError} />
|
||||
<p>
|
||||
<strong>Cron string: </strong> {cron}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Error message: </strong> {error?.message ?? "undefined"}
|
||||
</p>
|
||||
{date && (
|
||||
<p>
|
||||
<strong>Next synchronization: </strong>{" "}
|
||||
{date
|
||||
.toUTC()
|
||||
.setLocale(locale ?? "en")
|
||||
.toFormat("DDDD tt")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
14
packages/components/cron/Cron.styled.ts
Normal file
14
packages/components/cron/Cron.styled.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const CronWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
export const Suffix = styled.span`
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
font-weight: 400;
|
||||
`;
|
139
packages/components/cron/Cron.tsx
Normal file
139
packages/components/cron/Cron.tsx
Normal file
@ -0,0 +1,139 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import React, { useState, useEffect, useRef, useMemo } from "react";
|
||||
|
||||
import { MonthDays, Months, Period, WeekDays, Hours, Minutes } from "./Field";
|
||||
|
||||
import { getCronStringFromValues, stringToArray } from "./part";
|
||||
import { defaultCronString, defaultPeriod } from "./constants";
|
||||
import { getPeriodFromCronParts, getUnits } from "./util";
|
||||
|
||||
import { CronWrapper, Suffix } from "./Cron.styled";
|
||||
|
||||
import type CronProps from "./Cron.props";
|
||||
import type { PeriodType } from "./types";
|
||||
|
||||
function Cron({ value = defaultCronString, setValue, onError }: CronProps) {
|
||||
const { t } = useTranslation("Common");
|
||||
|
||||
const valueRef = useRef<string>(value);
|
||||
|
||||
const [period, setPeriod] = useState<PeriodType>(defaultPeriod);
|
||||
|
||||
const [hours, setHours] = useState<number[]>([]);
|
||||
const [months, setMonths] = useState<number[]>([]);
|
||||
const [minutes, setMinutes] = useState<number[]>([]);
|
||||
const [weekDays, setWeekDays] = useState<number[]>([]);
|
||||
const [monthDays, setMonthDays] = useState<number[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
onError?.(undefined); // reset error state
|
||||
if (valueRef.current !== value) init();
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const cornString = getCronStringFromValues(
|
||||
period,
|
||||
months,
|
||||
monthDays,
|
||||
weekDays,
|
||||
hours,
|
||||
minutes
|
||||
);
|
||||
|
||||
setValue(cornString);
|
||||
valueRef.current = cornString;
|
||||
|
||||
onError?.(undefined);
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
}
|
||||
}, [period, hours, months, minutes, weekDays, monthDays]);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
const init = () => {
|
||||
try {
|
||||
const cronParts = stringToArray(value);
|
||||
const period = getPeriodFromCronParts(cronParts);
|
||||
|
||||
const [minutes, hours, monthDays, months, weekDays] = cronParts;
|
||||
|
||||
setMinutes(minutes);
|
||||
setHours(hours);
|
||||
setMonthDays(monthDays);
|
||||
setMonths(months);
|
||||
setWeekDays(weekDays);
|
||||
|
||||
setPeriod(period);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
onError?.(error);
|
||||
}
|
||||
};
|
||||
|
||||
const { isYear, isMonth, isWeek, isHour, isMinute } = useMemo(() => {
|
||||
const isYear = period === "Year";
|
||||
const isMonth = period === "Month";
|
||||
const isWeek = period === "Week";
|
||||
const isHour = period === "Hour";
|
||||
const isMinute = period == "Minute";
|
||||
|
||||
return {
|
||||
isYear,
|
||||
isMonth,
|
||||
isWeek,
|
||||
isHour,
|
||||
isMinute,
|
||||
};
|
||||
}, [period]);
|
||||
|
||||
const units = useMemo(() => getUnits(t), [t]);
|
||||
|
||||
return (
|
||||
<CronWrapper>
|
||||
<Period t={t} period={period} setPeriod={setPeriod} />
|
||||
{isYear && (
|
||||
<Months unit={units[3]} t={t} months={months} setMonths={setMonths} />
|
||||
)}
|
||||
{(isYear || isMonth) && (
|
||||
<MonthDays
|
||||
t={t}
|
||||
unit={units[2]}
|
||||
weekDays={weekDays}
|
||||
monthDays={monthDays}
|
||||
setMonthDays={setMonthDays}
|
||||
/>
|
||||
)}
|
||||
{(isYear || isMonth || isWeek) && (
|
||||
<WeekDays
|
||||
t={t}
|
||||
unit={units[4]}
|
||||
isWeek={isWeek}
|
||||
period={period}
|
||||
monthDays={monthDays}
|
||||
weekDays={weekDays}
|
||||
setWeekDays={setWeekDays}
|
||||
/>
|
||||
)}
|
||||
{!isHour && !isMinute && (
|
||||
<Hours unit={units[1]} t={t} hours={hours} setHours={setHours} />
|
||||
)}
|
||||
|
||||
{!isMinute && (
|
||||
<Minutes
|
||||
t={t}
|
||||
unit={units[0]}
|
||||
period={period}
|
||||
minutes={minutes}
|
||||
setMinutes={setMinutes}
|
||||
/>
|
||||
)}
|
||||
<Suffix>UTC</Suffix>
|
||||
</CronWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default Cron;
|
9
packages/components/cron/Field/Hours/Hours.props.ts
Normal file
9
packages/components/cron/Field/Hours/Hours.props.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { FieldProps } from "../../types";
|
||||
|
||||
interface HoursProps extends FieldProps {
|
||||
hours: number[];
|
||||
setHours: Dispatch<SetStateAction<number[]>>;
|
||||
}
|
||||
|
||||
export default HoursProps;
|
19
packages/components/cron/Field/Hours/Hours.tsx
Normal file
19
packages/components/cron/Field/Hours/Hours.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import Select from "../../Select";
|
||||
|
||||
import type HoursProps from "./Hours.props";
|
||||
|
||||
function Hours({ hours, setHours, unit, t }: HoursProps) {
|
||||
return (
|
||||
<Select
|
||||
value={hours}
|
||||
setValue={setHours}
|
||||
placeholder={t("EveryHour")}
|
||||
unit={unit}
|
||||
prefix={t("At")}
|
||||
dropDownMaxHeight={300}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Hours;
|
10
packages/components/cron/Field/Minutes/Minutes.props.ts
Normal file
10
packages/components/cron/Field/Minutes/Minutes.props.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { PeriodType, FieldProps } from "../../types";
|
||||
|
||||
interface MinutesProps extends FieldProps {
|
||||
minutes: number[];
|
||||
setMinutes: Dispatch<SetStateAction<number[]>>;
|
||||
period: PeriodType;
|
||||
}
|
||||
|
||||
export default MinutesProps;
|
22
packages/components/cron/Field/Minutes/Minutes.tsx
Normal file
22
packages/components/cron/Field/Minutes/Minutes.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import Select from "../../Select";
|
||||
|
||||
import type MinutesProps from "./Minutes.props";
|
||||
|
||||
function Minutes({ minutes, setMinutes, period, t, unit }: MinutesProps) {
|
||||
const isHour = period === "Hour";
|
||||
const prefix = isHour ? t("At") : ":";
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={minutes}
|
||||
setValue={setMinutes}
|
||||
placeholder={t("EveryMinute")}
|
||||
unit={unit}
|
||||
prefix={prefix}
|
||||
dropDownMaxHeight={300}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Minutes;
|
10
packages/components/cron/Field/MonthDay/MonthDays.props.ts
Normal file
10
packages/components/cron/Field/MonthDay/MonthDays.props.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { FieldProps } from "../../types";
|
||||
|
||||
interface MonthDaysProps extends FieldProps {
|
||||
monthDays: number[];
|
||||
weekDays: number[];
|
||||
setMonthDays: Dispatch<SetStateAction<number[]>>;
|
||||
}
|
||||
|
||||
export default MonthDaysProps;
|
30
packages/components/cron/Field/MonthDay/MonthDays.tsx
Normal file
30
packages/components/cron/Field/MonthDay/MonthDays.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React, { useMemo } from "react";
|
||||
import Select from "../../Select";
|
||||
|
||||
import type MonthDaysProps from "./MonthDays.props";
|
||||
|
||||
function MonthDays({
|
||||
weekDays,
|
||||
monthDays,
|
||||
unit,
|
||||
setMonthDays,
|
||||
t,
|
||||
}: MonthDaysProps) {
|
||||
const placeholder = useMemo(() => {
|
||||
const isEmpty = weekDays.length === 0;
|
||||
|
||||
return isEmpty ? t("EveryDayOfTheMonth") : t("DayOfTheMonth");
|
||||
}, [weekDays.length]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={monthDays}
|
||||
setValue={setMonthDays}
|
||||
placeholder={placeholder}
|
||||
unit={unit}
|
||||
prefix={t("On")}
|
||||
dropDownMaxHeight={300}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default MonthDays;
|
9
packages/components/cron/Field/Months/Months.props.ts
Normal file
9
packages/components/cron/Field/Months/Months.props.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { FieldProps } from "../../types";
|
||||
|
||||
interface MonthsProps extends FieldProps {
|
||||
months: number[];
|
||||
setMonths: Dispatch<SetStateAction<number[]>>;
|
||||
}
|
||||
|
||||
export default MonthsProps;
|
20
packages/components/cron/Field/Months/Months.tsx
Normal file
20
packages/components/cron/Field/Months/Months.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
|
||||
import Select from "../../Select";
|
||||
|
||||
import type MonthsProps from "./Months.props";
|
||||
|
||||
function Months({ months, unit, setMonths, t }: MonthsProps) {
|
||||
return (
|
||||
<Select
|
||||
value={months}
|
||||
setValue={setMonths}
|
||||
placeholder={t("EveryMonth")}
|
||||
unit={unit}
|
||||
prefix={t("In")}
|
||||
dropDownMaxHeight={300}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Months;
|
42
packages/components/cron/Field/Period/Period.helper.ts
Normal file
42
packages/components/cron/Field/Period/Period.helper.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import type { PeriodOptionType } from "./Period.props";
|
||||
import type { PeriodType, TFunction } from "../../types";
|
||||
|
||||
export const getOptions = (t: TFunction): PeriodOptionType[] => [
|
||||
{
|
||||
key: "Year",
|
||||
label: getLabel("Year", t),
|
||||
},
|
||||
{
|
||||
key: "Month",
|
||||
label: getLabel("Month", t),
|
||||
},
|
||||
{
|
||||
key: "Week",
|
||||
label: getLabel("Week", t),
|
||||
},
|
||||
{
|
||||
key: "Day",
|
||||
label: getLabel("Day", t),
|
||||
},
|
||||
{
|
||||
key: "Hour",
|
||||
label: getLabel("Hour", t),
|
||||
},
|
||||
];
|
||||
|
||||
export const getLabel = (period: PeriodType, t: TFunction) => {
|
||||
switch (period) {
|
||||
case "Year":
|
||||
return t("EveryYear");
|
||||
case "Month":
|
||||
return t("EveryMonth");
|
||||
case "Week":
|
||||
return t("EveryWeek");
|
||||
case "Day":
|
||||
return t("EveryDay");
|
||||
case "Hour":
|
||||
return t("EveryHour");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
15
packages/components/cron/Field/Period/Period.props.ts
Normal file
15
packages/components/cron/Field/Period/Period.props.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { PeriodType, TFunction } from "../../types";
|
||||
|
||||
interface PeriodProps {
|
||||
t: TFunction;
|
||||
period?: PeriodType;
|
||||
setPeriod: Dispatch<SetStateAction<PeriodType>>;
|
||||
}
|
||||
|
||||
export type PeriodOptionType = {
|
||||
key: PeriodType;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export default PeriodProps;
|
33
packages/components/cron/Field/Period/Period.tsx
Normal file
33
packages/components/cron/Field/Period/Period.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
import ComboBox from "../../../combobox";
|
||||
import { getLabel, getOptions } from "./Period.helper";
|
||||
|
||||
import PeriodProps, { type PeriodOptionType } from "./Period.props";
|
||||
|
||||
function Period({ period = "Hour", setPeriod, t }: PeriodProps) {
|
||||
const onSelect = (arg: PeriodOptionType) => {
|
||||
setPeriod(arg.key);
|
||||
};
|
||||
|
||||
const options = useMemo(() => getOptions(t), [t]);
|
||||
const selectedOption = useMemo(
|
||||
() => ({ key: period, label: getLabel(period, t) }),
|
||||
[period, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<ComboBox
|
||||
scaledOptions
|
||||
size="content"
|
||||
scaled={false}
|
||||
noBorder={false}
|
||||
options={options}
|
||||
showDisabledItems
|
||||
onSelect={onSelect}
|
||||
selectedOption={selectedOption}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Period;
|
12
packages/components/cron/Field/WeekDays/WeekDays.props.ts
Normal file
12
packages/components/cron/Field/WeekDays/WeekDays.props.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { PeriodType, FieldProps } from "../../types";
|
||||
|
||||
interface WeekDaysProps extends FieldProps {
|
||||
isWeek: boolean;
|
||||
period: PeriodType;
|
||||
weekDays: number[];
|
||||
monthDays: number[];
|
||||
setWeekDays: Dispatch<SetStateAction<number[]>>;
|
||||
}
|
||||
|
||||
export default WeekDaysProps;
|
36
packages/components/cron/Field/WeekDays/WeekDays.tsx
Normal file
36
packages/components/cron/Field/WeekDays/WeekDays.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
import Select from "../../Select";
|
||||
|
||||
import type WeekDaysProps from "./WeekDays.props";
|
||||
|
||||
function WeekDays({
|
||||
setWeekDays,
|
||||
unit,
|
||||
isWeek,
|
||||
weekDays,
|
||||
monthDays,
|
||||
period,
|
||||
t,
|
||||
}: WeekDaysProps) {
|
||||
const prefix = period === "Week" ? t("On") : t("And");
|
||||
|
||||
const placeholder = useMemo(() => {
|
||||
const isEmpty = monthDays.length === 0;
|
||||
|
||||
return isEmpty || isWeek ? t("EveryDayOfTheWeek") : t("DayOfTheWeek");
|
||||
}, [monthDays.length, isWeek]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={weekDays}
|
||||
setValue={setWeekDays}
|
||||
placeholder={placeholder}
|
||||
unit={unit}
|
||||
prefix={prefix}
|
||||
dropDownMaxHeight={300}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default WeekDays;
|
6
packages/components/cron/Field/index.ts
Normal file
6
packages/components/cron/Field/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as Period } from "./Period/Period";
|
||||
export { default as Months } from "./Months/Months";
|
||||
export { default as MonthDays } from "./MonthDay/MonthDays";
|
||||
export { default as WeekDays } from "./WeekDays/WeekDays";
|
||||
export { default as Hours } from "./Hours/Hours";
|
||||
export { default as Minutes } from "./Minutes/Minutes";
|
12
packages/components/cron/Select/Select.props.ts
Normal file
12
packages/components/cron/Select/Select.props.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import { Unit } from "./../types";
|
||||
interface SelectProps {
|
||||
unit: Unit;
|
||||
value: number[];
|
||||
placeholder: string;
|
||||
setValue: Dispatch<SetStateAction<number[]>>;
|
||||
prefix: string;
|
||||
dropDownMaxHeight?: number;
|
||||
}
|
||||
|
||||
export default SelectProps;
|
11
packages/components/cron/Select/Select.styled.ts
Normal file
11
packages/components/cron/Select/Select.styled.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const SelectWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
& > span {
|
||||
font-size: 13px;
|
||||
}
|
||||
`;
|
83
packages/components/cron/Select/Select.tsx
Normal file
83
packages/components/cron/Select/Select.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
import ComboBox from "../../combobox";
|
||||
import { Option } from "../types";
|
||||
import { fixFormatValue } from "../util";
|
||||
import { SelectWrapper } from "./Select.styled";
|
||||
import SelectProps from "./Select.props";
|
||||
|
||||
function Select({
|
||||
unit,
|
||||
value,
|
||||
placeholder,
|
||||
setValue,
|
||||
prefix,
|
||||
dropDownMaxHeight,
|
||||
}: SelectProps) {
|
||||
const options = useMemo(() => {
|
||||
const { altWithTranslation } = unit;
|
||||
|
||||
if (altWithTranslation) {
|
||||
return altWithTranslation.map((item, index) => {
|
||||
const number = unit.min === 0 ? index : index + 1;
|
||||
|
||||
return {
|
||||
key: number,
|
||||
label: item,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return [...Array(unit.total)].map((_, index) => {
|
||||
const number = unit.min === 0 ? index : index + 1;
|
||||
|
||||
return {
|
||||
key: number,
|
||||
label: fixFormatValue(number),
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const selectedOption = useMemo(() => {
|
||||
const isEmpty = value.length === 0;
|
||||
|
||||
return {
|
||||
key: isEmpty ? -1 : value[0],
|
||||
label: isEmpty
|
||||
? placeholder
|
||||
: unit.altWithTranslation
|
||||
? unit.altWithTranslation[value[0] - unit.min]
|
||||
: fixFormatValue(value[0]),
|
||||
};
|
||||
}, [value, placeholder]);
|
||||
|
||||
const onSelect = (option: Option<number, string>) => {
|
||||
setValue([option.key]);
|
||||
};
|
||||
|
||||
const onReset = (option: Option<number, string>) => {
|
||||
if (option.key === value[0]) {
|
||||
setValue([]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectWrapper>
|
||||
<span>{prefix}</span>
|
||||
<ComboBox
|
||||
scaledOptions
|
||||
size="content"
|
||||
scaled={false}
|
||||
noBorder={false}
|
||||
showDisabledItems
|
||||
options={options}
|
||||
onSelect={onSelect}
|
||||
onClickSelectedItem={onReset}
|
||||
selectedOption={selectedOption}
|
||||
dropDownMaxHeight={dropDownMaxHeight}
|
||||
/>
|
||||
</SelectWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default Select;
|
1
packages/components/cron/Select/index.ts
Normal file
1
packages/components/cron/Select/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from "./Select";
|
11
packages/components/cron/constants.ts
Normal file
11
packages/components/cron/constants.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { Options, PeriodType, Unit } from "./types";
|
||||
|
||||
export const defaultPeriod: PeriodType = "Hour";
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
outputHashes: false,
|
||||
outputMonthNames: false,
|
||||
outputWeekdayNames: false,
|
||||
};
|
||||
|
||||
export const defaultCronString = "* * * * *";
|
2
packages/components/cron/index.ts
Normal file
2
packages/components/cron/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default } from "./Cron";
|
||||
export { getNextSynchronization } from "./part";
|
88
packages/components/cron/part.ts
Normal file
88
packages/components/cron/part.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { DateTime } from "luxon";
|
||||
import { Options, PeriodType } from "./types";
|
||||
import { defaultOptions } from "./constants";
|
||||
import {
|
||||
arrayToStringPart,
|
||||
assertValidArray,
|
||||
findDate,
|
||||
stringToArrayPart,
|
||||
getUnits,
|
||||
} from "./util";
|
||||
|
||||
const units = getUnits();
|
||||
|
||||
export const stringToArray = (str: string, full = false) => {
|
||||
if (typeof str !== "string") {
|
||||
throw new Error("Invalid cron string");
|
||||
}
|
||||
const parts = str.replace(/\s+/g, " ").trim().split(" ");
|
||||
if (parts.length !== 5) {
|
||||
throw new Error("Invalid cron string format");
|
||||
} else {
|
||||
return parts.map((str, idx) => stringToArrayPart(str, units[idx], full));
|
||||
}
|
||||
};
|
||||
|
||||
export function arrayToString(arr: number[][], options?: Partial<Options>) {
|
||||
assertValidArray(arr);
|
||||
const parts = arr.map((part, idx) =>
|
||||
arrayToStringPart(part, units[idx], { ...defaultOptions, ...options })
|
||||
);
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
export function getCronStringFromValues(
|
||||
period: PeriodType,
|
||||
months: number[] | undefined,
|
||||
monthDays: number[] | undefined,
|
||||
weekDays: number[] | undefined,
|
||||
hours: number[] | undefined,
|
||||
minutes: number[] | undefined
|
||||
) {
|
||||
const newMonths = period === "Year" && months ? months : [];
|
||||
const newMonthDays =
|
||||
(period === "Year" || period === "Month") && monthDays ? monthDays : [];
|
||||
const newWeekDays =
|
||||
(period === "Year" || period === "Month" || period === "Week") && weekDays
|
||||
? weekDays
|
||||
: [];
|
||||
const newHours =
|
||||
period !== "Minute" && period !== "Hour" && hours ? hours : [];
|
||||
const newMinutes = period !== "Minute" && minutes ? minutes : [];
|
||||
|
||||
const parsedArray = arrayToString([
|
||||
newMinutes,
|
||||
newHours,
|
||||
newMonthDays,
|
||||
newMonths,
|
||||
newWeekDays,
|
||||
]);
|
||||
|
||||
return parsedArray;
|
||||
}
|
||||
|
||||
export const getNextSynchronization = (
|
||||
cronString: string,
|
||||
timezone?: string
|
||||
) => {
|
||||
try {
|
||||
const cron = stringToArray(cronString, true);
|
||||
assertValidArray(cron);
|
||||
let date = DateTime.now();
|
||||
|
||||
if (timezone) date = date.setZone(timezone);
|
||||
|
||||
if (!date.isValid) {
|
||||
throw new Error("Invalid timezone provided");
|
||||
}
|
||||
|
||||
if (date.second > 0) {
|
||||
// plus a minute to the date to prevent returning dates in the past
|
||||
date = date.plus({ minute: 1 });
|
||||
}
|
||||
|
||||
return findDate(cron, date);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
30
packages/components/cron/types.ts
Normal file
30
packages/components/cron/types.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { TFunction as TranslationFunction } from "react-i18next";
|
||||
|
||||
export type PeriodType = "Year" | "Month" | "Week" | "Day" | "Hour" | "Minute";
|
||||
export type Unit = {
|
||||
name: "minute" | "hour" | "day" | "month" | "weekday";
|
||||
min: number;
|
||||
max: number;
|
||||
alt?: ReadonlyArray<string>;
|
||||
fullLabel?: ReadonlyArray<string>;
|
||||
total: number;
|
||||
altWithTranslation?: ReadonlyArray<string>;
|
||||
};
|
||||
|
||||
export type Options = {
|
||||
outputHashes: boolean;
|
||||
outputWeekdayNames: boolean;
|
||||
outputMonthNames: boolean;
|
||||
};
|
||||
|
||||
export type Option<K = unknown, L = unknown> = {
|
||||
key: K;
|
||||
label: L;
|
||||
};
|
||||
|
||||
export type TFunction = TranslationFunction<"translation", undefined>;
|
||||
|
||||
export interface FieldProps {
|
||||
t: TFunction;
|
||||
unit: Unit;
|
||||
}
|
497
packages/components/cron/util.ts
Normal file
497
packages/components/cron/util.ts
Normal file
@ -0,0 +1,497 @@
|
||||
import { DateTime } from "luxon";
|
||||
import { Options, PeriodType, TFunction, Unit } from "./types";
|
||||
|
||||
export const parseNumber = (value: unknown) => {
|
||||
if (typeof value === "string") {
|
||||
const str: string = value.trim();
|
||||
if (/^\d+$/.test(str)) {
|
||||
const num = Number(str);
|
||||
if (!isNaN(num) && isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
} else if (typeof value === "number") {
|
||||
if (!isNaN(value) && isFinite(value) && value === Math.floor(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const assertValidArray = (arr: unknown): void | never => {
|
||||
if (
|
||||
arr === undefined ||
|
||||
!Array.isArray(arr) ||
|
||||
arr.length !== 5 ||
|
||||
arr.some((element) => !Array.isArray(element))
|
||||
) {
|
||||
throw new Error("Invalid cron array");
|
||||
}
|
||||
};
|
||||
|
||||
export const range = (start: number, end: number): number[] => {
|
||||
const array: number[] = [];
|
||||
for (let i = start; i <= end; i++) {
|
||||
array.push(i);
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
export const sort = (array: number[]) => [...array].sort((a, b) => a - b);
|
||||
|
||||
export const flatten = (arrays: number[][]) =>
|
||||
([] as number[]).concat.apply([], arrays);
|
||||
|
||||
export const dedup = (array: number[]) => {
|
||||
const result: number[] = [];
|
||||
array.forEach((i) => {
|
||||
if (result.indexOf(i) < 0) {
|
||||
result.push(i);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getPeriodFromCronParts = (cronParts: number[][]): PeriodType => {
|
||||
if (cronParts[3].length > 0) {
|
||||
return "Year";
|
||||
} else if (cronParts[2].length > 0) {
|
||||
return "Month";
|
||||
} else if (cronParts[4].length > 0) {
|
||||
return "Week";
|
||||
} else if (cronParts[1].length > 0) {
|
||||
return "Day";
|
||||
} else if (cronParts[0].length > 0) {
|
||||
return "Hour";
|
||||
}
|
||||
// return "minute";
|
||||
return "Hour";
|
||||
};
|
||||
|
||||
export const fixFormatValue = (value: number) => {
|
||||
let result = value.toString();
|
||||
|
||||
if (value < 10) {
|
||||
result = result.padStart(2, "0");
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const arrayToStringPart = (
|
||||
arr: number[],
|
||||
unit: Unit,
|
||||
options: Options
|
||||
) => {
|
||||
const values = sort(
|
||||
dedup(
|
||||
fixSunday(
|
||||
arr.map((value) => {
|
||||
const parsedValue = parseNumber(value);
|
||||
if (parsedValue === undefined) {
|
||||
throw getError(`Invalid value "${value}"`, unit);
|
||||
}
|
||||
return parsedValue;
|
||||
}),
|
||||
unit
|
||||
)
|
||||
)
|
||||
);
|
||||
assertInRange(values, unit);
|
||||
return toString(values, unit, options);
|
||||
};
|
||||
|
||||
export const stringToArrayPart = (str: string, unit: Unit, full = false) => {
|
||||
if ((str === "*" || str === "*/1") && !full) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const values = sort(
|
||||
dedup(
|
||||
fixSunday(
|
||||
flatten(
|
||||
replaceAlternatives(str, unit)
|
||||
.split(",")
|
||||
.map((value: string) => {
|
||||
const valueParts = value.split("/");
|
||||
if (valueParts.length > 2) {
|
||||
throw getError(`Invalid value "${str}"`, unit);
|
||||
}
|
||||
let parsedValues: number[];
|
||||
const left = valueParts[0];
|
||||
const right = valueParts[1];
|
||||
if (left === "*") {
|
||||
parsedValues = range(unit.min, unit.max);
|
||||
} else {
|
||||
parsedValues = parseRange(left, str, unit);
|
||||
}
|
||||
const step = parseStep(right, unit);
|
||||
return applyInterval(parsedValues, step);
|
||||
})
|
||||
),
|
||||
unit
|
||||
)
|
||||
)
|
||||
);
|
||||
assertInRange(values, unit);
|
||||
return values;
|
||||
};
|
||||
|
||||
const toRanges = (values: number[]) => {
|
||||
const retval: number[][] = [];
|
||||
let startPart: number | undefined = undefined;
|
||||
values.forEach(function (value, index, self) {
|
||||
if (value !== self[index + 1] - 1) {
|
||||
if (startPart !== undefined) {
|
||||
retval.push([startPart, value]);
|
||||
startPart = undefined;
|
||||
} else {
|
||||
retval.push([value]);
|
||||
}
|
||||
} else if (startPart === undefined) {
|
||||
startPart = value;
|
||||
}
|
||||
});
|
||||
return retval;
|
||||
};
|
||||
|
||||
const toString = (values: number[], unit: Unit, options: Options) => {
|
||||
let retval = "";
|
||||
if (isFull(values, unit) || values.length === 0) {
|
||||
if (options.outputHashes) {
|
||||
retval = "H";
|
||||
} else {
|
||||
retval = "*";
|
||||
}
|
||||
} else {
|
||||
const step = getStep(values);
|
||||
if (step && isInterval(values, step)) {
|
||||
if (isFullInterval(values, unit, step)) {
|
||||
if (options.outputHashes) {
|
||||
retval = `H/${step}`;
|
||||
} else {
|
||||
retval = `*/${step}`;
|
||||
}
|
||||
} else {
|
||||
const min = values[0];
|
||||
const max = values[values.length - 1];
|
||||
const range =
|
||||
formatValue(min, unit, options) +
|
||||
"-" +
|
||||
formatValue(max, unit, options);
|
||||
if (options.outputHashes) {
|
||||
retval = `H(${range})/${step}`;
|
||||
} else {
|
||||
retval = `${range}/${step}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retval = toRanges(values)
|
||||
.map((range) => {
|
||||
if (range.length === 1) {
|
||||
return formatValue(range[0], unit, options);
|
||||
} else {
|
||||
return (
|
||||
formatValue(range[0], unit, options) +
|
||||
"-" +
|
||||
formatValue(range[1], unit, options)
|
||||
);
|
||||
}
|
||||
})
|
||||
.join(",");
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
const formatValue = (value: number, unit: Unit, options: Options) => {
|
||||
if (
|
||||
(options.outputWeekdayNames && unit.name === "weekday") ||
|
||||
(options.outputMonthNames && unit.name === "month")
|
||||
) {
|
||||
if (unit.alt) {
|
||||
return unit.alt[value - unit.min];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const getError = (error: string, unit: Unit) =>
|
||||
new Error(`${error} for ${unit.name}`);
|
||||
|
||||
const parseRange = (rangeString: string, context: string, unit: Unit) => {
|
||||
const subparts = rangeString.split("-");
|
||||
if (subparts.length === 1) {
|
||||
const value = parseNumber(subparts[0]);
|
||||
if (value === undefined) {
|
||||
throw getError(`Invalid value "${context}"`, unit);
|
||||
}
|
||||
return [value];
|
||||
} else if (subparts.length === 2) {
|
||||
const minValue = parseNumber(subparts[0]);
|
||||
const maxValue = parseNumber(subparts[1]);
|
||||
if (minValue === undefined || maxValue === undefined) {
|
||||
throw getError(`Invalid value "${context}"`, unit);
|
||||
}
|
||||
if (maxValue < minValue) {
|
||||
throw getError(
|
||||
`Max range is less than min range in "${rangeString}"`,
|
||||
unit
|
||||
);
|
||||
}
|
||||
return range(minValue, maxValue);
|
||||
} else {
|
||||
throw getError(`Invalid value "${rangeString}"`, unit);
|
||||
}
|
||||
};
|
||||
|
||||
const parseStep = (step: string, unit: Unit) => {
|
||||
if (step !== undefined) {
|
||||
const parsedStep = parseNumber(step);
|
||||
if (parsedStep === undefined) {
|
||||
throw getError(`Invalid interval step value "${step}"`, unit);
|
||||
}
|
||||
return parsedStep;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const applyInterval = (values: number[], step: number) => {
|
||||
if (step) {
|
||||
const minVal = values[0];
|
||||
values = values.filter(
|
||||
(value) => value % step === minVal % step || value === minVal
|
||||
);
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const fixSunday = (values: number[], unit: Unit) => {
|
||||
if (unit.name === "weekday") {
|
||||
values = values.map((value) => {
|
||||
if (value === 7) {
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const replaceAlternatives = (str: string, unit: Unit) => {
|
||||
if (unit.alt) {
|
||||
str = str.toUpperCase();
|
||||
for (let i = 0; i < unit.alt.length; i++) {
|
||||
str = str.replace(unit.alt[i], String(i + unit.min));
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
const assertInRange = (values: number[], unit: Unit) => {
|
||||
const first = values[0];
|
||||
const last = values[values.length - 1];
|
||||
if (first < unit.min) {
|
||||
throw getError(`Value "${first}" out of range`, unit);
|
||||
} else if (last > unit.max) {
|
||||
throw getError(`Value "${last}" out of range`, unit);
|
||||
}
|
||||
};
|
||||
|
||||
const isInterval = (values: number[], step: number) => {
|
||||
for (let i = 1; i < values.length; i++) {
|
||||
const prev = values[i - 1];
|
||||
const value = values[i];
|
||||
if (value - prev !== step) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const isFullInterval = (values: number[], unit: Unit, step: number) => {
|
||||
const min = values[0];
|
||||
const max = values[values.length - 1];
|
||||
const haveAllValues = values.length === (max - min) / step + 1;
|
||||
if (min === unit.min && max + step > unit.max && haveAllValues) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const getStep = (values: number[]) => {
|
||||
if (values.length > 2) {
|
||||
const step = values[1] - values[0];
|
||||
if (step > 1) {
|
||||
return step;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const isFull = (values: number[], unit: Unit) => {
|
||||
return values.length === unit.max - unit.min + 1;
|
||||
};
|
||||
|
||||
const shiftMonth = (arr: number[][], date: DateTime) => {
|
||||
while (arr[3].indexOf(date.month) === -1) {
|
||||
date = date.plus({ months: 1 }).startOf("month");
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
const shiftDay = (arr: number[][], date: DateTime): [DateTime, boolean] => {
|
||||
const currentMonth = date.month;
|
||||
while (
|
||||
arr[2].indexOf(date.day) === -1 ||
|
||||
// luxon uses 1-7 for weekdays, but we use 0-6
|
||||
arr[4].indexOf(date.weekday === 7 ? 0 : date.weekday) === -1
|
||||
) {
|
||||
date = date.plus({ days: 1 }).startOf("day");
|
||||
if (currentMonth !== date.month) {
|
||||
return [date, true];
|
||||
}
|
||||
}
|
||||
return [date, false];
|
||||
};
|
||||
|
||||
const shiftHour = (arr: number[][], date: DateTime): [DateTime, boolean] => {
|
||||
const currentDay = date.day;
|
||||
while (arr[1].indexOf(date.hour) === -1) {
|
||||
date = date.plus({ hours: 1 }).startOf("hour");
|
||||
if (currentDay !== date.day) {
|
||||
return [date, true];
|
||||
}
|
||||
}
|
||||
return [date, false];
|
||||
};
|
||||
|
||||
const shiftMinute = (arr: number[][], date: DateTime): [DateTime, boolean] => {
|
||||
const currentHour = date.hour;
|
||||
while (arr[0].indexOf(date.minute) === -1) {
|
||||
date = date.plus({ minutes: 1 }).startOf("minute");
|
||||
if (currentHour !== date.hour) {
|
||||
return [date, true];
|
||||
}
|
||||
}
|
||||
return [date, false];
|
||||
};
|
||||
|
||||
export const findDate = (arr: number[][], date: DateTime) => {
|
||||
let retry = 24;
|
||||
let monthChanged: boolean;
|
||||
let dayChanged: boolean;
|
||||
let hourChanged: boolean;
|
||||
|
||||
while (--retry) {
|
||||
date = shiftMonth(arr, date);
|
||||
[date, monthChanged] = shiftDay(arr, date);
|
||||
if (!monthChanged) {
|
||||
[date, dayChanged] = shiftHour(arr, date);
|
||||
if (!dayChanged) {
|
||||
[date, hourChanged] = shiftMinute(arr, date);
|
||||
if (!hourChanged) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!retry) {
|
||||
throw new Error("Unable to find execution time for schedule");
|
||||
}
|
||||
return date.set({ second: 0, millisecond: 0 });
|
||||
};
|
||||
|
||||
export const getUnits = (t?: TFunction) => {
|
||||
const units: ReadonlyArray<Unit> = Object.freeze([
|
||||
{
|
||||
name: "minute",
|
||||
min: 0,
|
||||
max: 59,
|
||||
total: 60,
|
||||
},
|
||||
{
|
||||
name: "hour",
|
||||
min: 0,
|
||||
max: 23,
|
||||
total: 24,
|
||||
},
|
||||
{
|
||||
name: "day",
|
||||
min: 1,
|
||||
max: 31,
|
||||
total: 31,
|
||||
},
|
||||
{
|
||||
name: "month",
|
||||
min: 1,
|
||||
max: 12,
|
||||
total: 12,
|
||||
alt: [
|
||||
"JAN",
|
||||
"FEB",
|
||||
"MAR",
|
||||
"APR",
|
||||
"MAY",
|
||||
"JUN",
|
||||
"JUL",
|
||||
"AUG",
|
||||
"SEP",
|
||||
"OCT",
|
||||
"NOV",
|
||||
"DEC",
|
||||
],
|
||||
altWithTranslation: t
|
||||
? [
|
||||
t("JAN"),
|
||||
t("FEB"),
|
||||
t("MAR"),
|
||||
t("APR"),
|
||||
t("MAY"),
|
||||
t("JUN"),
|
||||
t("JUL"),
|
||||
t("AUG"),
|
||||
t("SEP"),
|
||||
t("OCT"),
|
||||
t("NOV"),
|
||||
t("DEC"),
|
||||
]
|
||||
: undefined,
|
||||
fullLabel: [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "weekday",
|
||||
min: 0,
|
||||
max: 6,
|
||||
total: 7,
|
||||
alt: ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"],
|
||||
altWithTranslation: t
|
||||
? [
|
||||
t("Common:SUN"),
|
||||
t("Common:MON"),
|
||||
t("Common:TUE"),
|
||||
t("Common:WED"),
|
||||
t("Common:THU"),
|
||||
t("Common:FRI"),
|
||||
t("Common:SAT"),
|
||||
]
|
||||
: undefined,
|
||||
},
|
||||
]);
|
||||
|
||||
return units;
|
||||
};
|
@ -31,10 +31,11 @@ const DropDownItem = (props) => {
|
||||
isActiveDescendant,
|
||||
} = props;
|
||||
|
||||
const { withToggle, checked, onClick, ...rest } = props;
|
||||
const { withToggle, checked, onClick, onClickSelectedItem, ...rest } = props;
|
||||
|
||||
const onClickAction = (e) => {
|
||||
onClick && !disabled && onClick(e);
|
||||
onClickSelectedItem && isSelected && onClickSelectedItem();
|
||||
};
|
||||
|
||||
const stopPropagation = (event) => {
|
||||
|
@ -25,6 +25,7 @@
|
||||
"framer-motion": "^4.1.17",
|
||||
"html-to-react": "^1.5.0",
|
||||
"lodash": "4.17.21",
|
||||
"luxon": "^3.4.0",
|
||||
"moment": "^2.29.4",
|
||||
"prop-types": "^15.8.1",
|
||||
"punycode": "^2.3.0",
|
||||
@ -77,6 +78,7 @@
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@types/jest": "^24.9.1",
|
||||
"@types/luxon": "^3.3.1",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "^0.4.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
@ -94,6 +96,7 @@
|
||||
"eslint-plugin-jest": "^26.9.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-storybook": "^0.6.12",
|
||||
"i18next-http-backend": "^2.2.2",
|
||||
"jest": "^24.9.0",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-junit": "^10.0.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import commonInputStyles from "./common-input-styles";
|
||||
import Input from "./input";
|
||||
import Base from "../themes/base";
|
||||
@ -24,6 +24,9 @@ const StyledTextInput = styled(Input).attrs((props) => ({
|
||||
${commonInputStyles}
|
||||
-webkit-appearance: ${(props) => props.theme.textInput.appearance};
|
||||
|
||||
${(props) =>
|
||||
!props.isDisabled &&
|
||||
css`
|
||||
background-color: ${(props) => props.theme.input.backgroundColor};
|
||||
-webkit-text-fill-color: ${(props) =>
|
||||
props?.value.length > 0
|
||||
@ -33,6 +36,7 @@ const StyledTextInput = styled(Input).attrs((props) => ({
|
||||
-webkit-background-clip: text !important;
|
||||
box-shadow: inset 0 0 20px 20px
|
||||
${(props) => props.theme.input.backgroundColor} !important;
|
||||
`}
|
||||
|
||||
display: ${(props) => props.theme.textInput.display};
|
||||
font-family: ${(props) => props.theme.fontFamily};
|
||||
|
@ -3140,6 +3140,11 @@ const Base = {
|
||||
headerColor: "#333",
|
||||
descriptionColor: "#555F65",
|
||||
},
|
||||
|
||||
roomIcon: {
|
||||
backgroundArchive: "#A3A9AE",
|
||||
opacityBackground: "1",
|
||||
},
|
||||
};
|
||||
|
||||
export default Base;
|
||||
|
@ -3143,6 +3143,11 @@ const Dark = {
|
||||
headerColor: "#FFF",
|
||||
descriptionColor: "#ADADAD",
|
||||
},
|
||||
|
||||
roomIcon: {
|
||||
backgroundArchive: "#FFFFFF",
|
||||
opacityBackground: "0.1",
|
||||
},
|
||||
};
|
||||
|
||||
export default Dark;
|
||||
|
5
packages/login/index.d.ts
vendored
5
packages/login/index.d.ts
vendored
@ -108,6 +108,10 @@ declare global {
|
||||
message: string | undefined;
|
||||
}
|
||||
|
||||
interface ISSOSettings {
|
||||
hideAuthPage: boolean;
|
||||
}
|
||||
|
||||
interface IInitialState {
|
||||
portalSettings?: IPortalSettings;
|
||||
buildInfo?: IBuildInfo;
|
||||
@ -116,6 +120,7 @@ declare global {
|
||||
match?: MatchType;
|
||||
currentColorScheme?: ITheme;
|
||||
isAuth?: boolean;
|
||||
ssoSettings?: ISSOSettings;
|
||||
logoUrls: ILogoUrl[];
|
||||
error?: IError;
|
||||
}
|
||||
|
@ -59,6 +59,13 @@ app.get("*", async (req: ILoginRequest, res: Response, next) => {
|
||||
|
||||
try {
|
||||
initialState = await getInitialState(query);
|
||||
const hideAuthPage = initialState?.ssoSettings?.hideAuthPage;
|
||||
const ssoUrl = initialState?.capabilities?.ssoUrl;
|
||||
|
||||
if (hideAuthPage && ssoUrl && query.skipssoredirect !== "true") {
|
||||
res.redirect(ssoUrl);
|
||||
return next();
|
||||
}
|
||||
|
||||
if (initialState.isAuth && url !== "/login/error") {
|
||||
res.redirect("/");
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
getCapabilities,
|
||||
getAppearanceTheme,
|
||||
getLogoUrls,
|
||||
getCurrentSsoSettings
|
||||
} from "@docspace/common/api/settings";
|
||||
import { checkIsAuthenticated } from "@docspace/common/api/user";
|
||||
import { TenantStatus } from "@docspace/common/constants";
|
||||
@ -51,8 +52,9 @@ export const getInitialState = async (
|
||||
providers: ProvidersType,
|
||||
capabilities: ICapabilities,
|
||||
availableThemes: IThemes,
|
||||
isAuth: any,
|
||||
logoUrls: any;
|
||||
isAuth: boolean,
|
||||
logoUrls: ILogoUrl[],
|
||||
ssoSettings: ISSOSettings;
|
||||
|
||||
const baseSettings = [
|
||||
getSettings(),
|
||||
@ -65,6 +67,7 @@ export const getInitialState = async (
|
||||
getAuthProviders(),
|
||||
getCapabilities(),
|
||||
checkIsAuthenticated(),
|
||||
getCurrentSsoSettings(),
|
||||
];
|
||||
|
||||
[portalSettings, buildInfo, availableThemes, logoUrls] = await Promise.all(
|
||||
@ -72,7 +75,7 @@ export const getInitialState = async (
|
||||
);
|
||||
|
||||
if (portalSettings.tenantStatus !== TenantStatus.PortalRestore)
|
||||
[providers, capabilities, isAuth] = await Promise.all(settings);
|
||||
[providers, capabilities, isAuth, ssoSettings] = await Promise.all(settings);
|
||||
|
||||
const currentColorScheme = availableThemes.themes.find((theme) => {
|
||||
return availableThemes.selected === theme.id;
|
||||
@ -87,6 +90,7 @@ export const getInitialState = async (
|
||||
currentColorScheme,
|
||||
isAuth,
|
||||
logoUrls,
|
||||
ssoSettings
|
||||
};
|
||||
|
||||
return initialState;
|
||||
|
@ -401,6 +401,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
|
||||
toUpdate.ModifiedOn = _tenantUtil.DateTimeToUtc(folder.ModifiedOn);
|
||||
toUpdate.ModifiedBy = folder.ModifiedBy;
|
||||
toUpdate.HasLogo = folder.HasLogo;
|
||||
toUpdate.Color = folder.Color;
|
||||
|
||||
await filesDbContext.SaveChangesAsync();
|
||||
|
||||
@ -423,6 +424,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
|
||||
ModifiedBy = folder.ModifiedBy,
|
||||
FolderType = folder.FolderType,
|
||||
Private = folder.Private,
|
||||
Color = folder.Color,
|
||||
TenantId = TenantID
|
||||
};
|
||||
|
||||
|
@ -45,6 +45,7 @@ public class DbFolder : IDbFile, IDbSearch, ISearchItem
|
||||
public int FilesCount { get; set; }
|
||||
public bool Private { get; set; }
|
||||
public bool HasLogo { get; set; }
|
||||
public string Color { get; set; }
|
||||
|
||||
public DbTenant Tenant { get; set; }
|
||||
|
||||
@ -145,6 +146,12 @@ public static class DbFolderExtension
|
||||
.HasDefaultValueSql("'0'");
|
||||
|
||||
entity.Property(e => e.HasLogo).HasColumnName("has_logo");
|
||||
|
||||
entity.Property(e => e.Color)
|
||||
.HasColumnName("color")
|
||||
.HasColumnType("char(6)")
|
||||
.HasCharSet("utf8")
|
||||
.UseCollation("utf8_general_ci");
|
||||
});
|
||||
}
|
||||
public static void PgSqlAddDbFolder(this ModelBuilder modelBuilder)
|
||||
|
@ -75,6 +75,7 @@ public class Folder<T> : FileEntry<T>, IFolder
|
||||
public bool Pinned { get; set; }
|
||||
public bool Private { get; set; }
|
||||
public bool HasLogo { get; set; }
|
||||
public string Color { get; set; }
|
||||
public override bool IsNew
|
||||
{
|
||||
get => Convert.ToBoolean(NewForMe);
|
||||
|
@ -29,7 +29,6 @@ namespace ASC.Web.Files.Services.WCFService;
|
||||
[Scope]
|
||||
public class FileStorageService //: IFileStorageService
|
||||
{
|
||||
private static readonly FileEntrySerializer _serializer = new FileEntrySerializer();
|
||||
private readonly CompressToArchive _compressToArchive;
|
||||
private readonly OFormRequestManager _oFormRequestManager;
|
||||
private readonly ThirdPartySelector _thirdPartySelector;
|
||||
@ -46,7 +45,6 @@ public class FileStorageService //: IFileStorageService
|
||||
private readonly FilesLinkUtility _filesLinkUtility;
|
||||
private readonly BaseCommonLinkUtility _baseCommonLinkUtility;
|
||||
private readonly CoreBaseSettings _coreBaseSettings;
|
||||
private readonly CustomNamingPeople _customNamingPeople;
|
||||
private readonly DisplayUserSettingsHelper _displayUserSettingsHelper;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly PathProvider _pathProvider;
|
||||
@ -88,6 +86,7 @@ public class FileStorageService //: IFileStorageService
|
||||
private readonly QuotaSocketManager _quotaSocketManager;
|
||||
private readonly ExternalShare _externalShare;
|
||||
private readonly TenantUtil _tenantUtil;
|
||||
private readonly RoomLogoManager _roomLogoManager;
|
||||
|
||||
public FileStorageService(
|
||||
Global global,
|
||||
@ -100,7 +99,6 @@ public class FileStorageService //: IFileStorageService
|
||||
FilesLinkUtility filesLinkUtility,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
CoreBaseSettings coreBaseSettings,
|
||||
CustomNamingPeople customNamingPeople,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILoggerProvider optionMonitor,
|
||||
@ -147,7 +145,8 @@ public class FileStorageService //: IFileStorageService
|
||||
TenantQuotaFeatureStatHelper tenantQuotaFeatureStatHelper,
|
||||
QuotaSocketManager quotaSocketManager,
|
||||
ExternalShare externalShare,
|
||||
TenantUtil tenantUtil)
|
||||
TenantUtil tenantUtil,
|
||||
RoomLogoManager roomLogoManager)
|
||||
{
|
||||
_global = global;
|
||||
_globalStore = globalStore;
|
||||
@ -159,7 +158,6 @@ public class FileStorageService //: IFileStorageService
|
||||
_filesLinkUtility = filesLinkUtility;
|
||||
_baseCommonLinkUtility = baseCommonLinkUtility;
|
||||
_coreBaseSettings = coreBaseSettings;
|
||||
_customNamingPeople = customNamingPeople;
|
||||
_displayUserSettingsHelper = displayUserSettingsHelper;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_pathProvider = pathProvider;
|
||||
@ -207,6 +205,7 @@ public class FileStorageService //: IFileStorageService
|
||||
_quotaSocketManager = quotaSocketManager;
|
||||
_externalShare = externalShare;
|
||||
_tenantUtil = tenantUtil;
|
||||
_roomLogoManager = roomLogoManager;
|
||||
}
|
||||
|
||||
public async Task<Folder<T>> GetFolderAsync<T>(T folderId)
|
||||
@ -614,7 +613,7 @@ public class FileStorageService //: IFileStorageService
|
||||
newFolder.ParentId = parent.Id;
|
||||
newFolder.FolderType = folderType;
|
||||
newFolder.Private = parent.Private ? parent.Private : privacy;
|
||||
|
||||
newFolder.Color = _roomLogoManager.GetRandomColour();
|
||||
var folderId = await folderDao.SaveFolderAsync(newFolder);
|
||||
var folder = await folderDao.GetFolderAsync(folderId);
|
||||
|
||||
@ -634,6 +633,8 @@ public class FileStorageService //: IFileStorageService
|
||||
{
|
||||
throw GenerateException(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<Folder<T>> FolderRenameAsync<T>(T folderId, string title)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user