Merge branch 'develop' into feature/rewrite-error-boundary

This commit is contained in:
Akmal Isomadinov 2024-02-13 14:28:52 +05:00
commit 3cb31f417a
229 changed files with 12290 additions and 3037 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "docspace",
"version": "2.0.2",
"version": "2.0.3",
"private": true,
"workspaces": {
"packages": [

View File

@ -0,0 +1,15 @@
module.exports = {
extends: "../shared/.eslintrc.cjs",
parserOptions: {
project: "tsconfig.eslint.json",
tsconfigRootDir: __dirname,
sourceType: "module",
},
ignorePatterns: [
"./tsconfig.json",
"coverage/**",
"storybook-static/**",
"*.js",
],
};

View File

@ -0,0 +1,3 @@
{
"extends": "../shared/.prettierrc.json"
}

View File

@ -1,5 +1,5 @@
interface Window {
zESettings?: any;
zESettings?: unknown;
zE?: {
apply: Function;
};

View File

@ -1,6 +1,6 @@
{
"name": "@docspace/client",
"version": "2.0.2",
"version": "2.0.3",
"private": true,
"homepage": "",
"scripts": {
@ -59,6 +59,7 @@
},
"devDependencies": {
"@babel/core": "^7.21.3",
"@babel/eslint-parser": "^7.21.8",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-export-default-from": "^7.18.10",
"@babel/plugin-transform-runtime": "^7.21.0",
@ -66,16 +67,30 @@
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
"@svgr/webpack": "^5.5.0",
"@types/eslint": "^8.44.7",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"babel-loader": "^8.3.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.1.0",
"css-loader": "^6.7.3",
"eslint": "^8.54.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^0.6.15",
"external-remotes-plugin": "^1.0.0",
"file-loader": "^6.2.0",
"html-loader": "^4.2.0",
"html-webpack-plugin": "5.5.0",
"json-loader": "^0.5.7",
"playwright": "^1.32.0",
"prettier": "^3.1.0",
"sass": "^1.59.3",
"sass-loader": "^12.6.0",
"serve": "14.2.0",

View File

@ -35,7 +35,7 @@
"MobileWin": "حمّل ONLYOFFICE Desktop Editors لنظام ويندوز",
"MoveHere": "انقل إلى هنا",
"MoveToOperation": "جارٍ النقل",
"NewForm": "قوالب جاهزة",
"NewForm": "نموذج PDF",
"Other": "آخر",
"OwnerChange": "تغيير المالك",
"Presentations": "العروض التقديمية",

View File

@ -35,7 +35,7 @@
"MobileWin": "Windows üçün ONLYOFFICE Masaüstü Redaktorları endirin",
"MoveHere": "Bura köçürün",
"MoveToOperation": "Yer dəyişdirmə",
"NewForm": "Forma şablonu",
"NewForm": "PDF forma",
"Other": "Digər",
"OwnerChange": "Sahibi dəyiş",
"Presentations": "Təqdimatlar",

View File

@ -35,7 +35,7 @@
"MobileWin": "Изтеглете ONLYOFFICE настолни редактори за Windows",
"MoveHere": "Премести се тук",
"MoveToOperation": "Преместване",
"NewForm": "Шаблон на формуляр",
"NewForm": "PDF формуляр",
"Other": "Друг",
"OwnerChange": "Смени собственик",
"Presentations": "Презентации",

View File

@ -35,7 +35,7 @@
"MobileWin": "Stáhnout ONLYOFFICE Desktop Editors pro Windows",
"MoveHere": "Přesunout zde",
"MoveToOperation": "Přesouvám",
"NewForm": "Šablona formuláře",
"NewForm": "PDF formulář",
"Other": "Jiný",
"OwnerChange": "Změnit vlastníka",
"Presentations": "Prezentace",

View File

@ -35,7 +35,7 @@
"MobileWin": "Download ONLYOFFICE Desktop Editors für Windows",
"MoveHere": "Verschieben",
"MoveToOperation": "Wird verschoben",
"NewForm": "Formularvorlage",
"NewForm": "PDF-Formular",
"Other": "Sonstiges",
"OwnerChange": "Besitzer ändern",
"Presentations": "Präsentationen",

View File

@ -35,7 +35,7 @@
"MobileWin": "Λήψη ONLYOFFICE Desktop Editors για Windows",
"MoveHere": "Μετακίνηση εδώ",
"MoveToOperation": "Μετακίνηση",
"NewForm": "Πρότυπο φόρμας",
"NewForm": "Φόρμα PDF",
"Other": "Άλλο",
"OwnerChange": "Αλλαγή κατόχου",
"Presentations": "Παρουσιάσεις",

View File

@ -14,9 +14,9 @@
"ArchiveEmptyScreenUser": "Rooms that have been archived will appear here.",
"Archives": "Archives",
"AssignOwner": "Assign Owner",
"BackToParentFolderButton": "Back to parent folder",
"BadgeAlertDescription": "Several roles are specified in this form. Forms with multiple roles are not available for filling in this type of room",
"BadgeMyDraftTitle": "My draft",
"BackToParentFolderButton": "Back to parent folder",
"ByAuthor": "Author",
"ByCreation": "Created",
"ByErasure": "Erasure",
@ -27,20 +27,20 @@
"CollaborationRooms": "Collaboration",
"ContainsSpecCharacter": "The title can't contain any of the following characters: *+:\"<>?|/",
"Convert": "Convert",
"CopySharedLink": "Copy shared link",
"CopyItem": "<strong>{{title}}</strong> copied",
"CopyItems": "<strong>{{qty}}</strong> elements copied",
"CopyLink": "Copy link",
"CopyLinkPassword": "Copy link password",
"CopyPassword": "Copy password",
"CopySharedLink": "Copy shared link",
"CreateAndCopy": "Create and copy",
"CreateNewLink": "Create new link",
"CreateRoom": "Create room",
"CustomRooms": "Custom",
"DaysRemaining": "Days remaining: {{daysRemaining}}",
"DeleteSharedLink": "The previous link will become unavailable. A new shared link will be created.",
"DeleteLink": "Delete link",
"DeleteLinkNote": "The link will be deleted permanently. You will not be able to undo this action.",
"DeleteSharedLink": "The previous link will become unavailable. A new shared link will be created.",
"DisableDownload": "Restrict file content copy, file download and printing",
"DisableLink": "Disable link",
"DisableNotifications": "Disable notifications",
@ -48,9 +48,9 @@
"DocumentEdited": "Cannot perform the action because the document is being edited.",
"DownloadAll": "Download all",
"EditAdditionalLink": "Edit additional link",
"EditSharedLink": "Edit shared link",
"EditLink": "Edit link",
"EditRoom": "Edit room",
"EditSharedLink": "Edit shared link",
"EmbeddingSettings": "Embedding settings",
"EmptyFile": "Empty file",
"EmptyFilterDescriptionText": "No files or folders match this filter. Try a different one or clear filter to view all files. ",
@ -58,12 +58,12 @@
"EmptyFolderDecription": "Drop files here or create new ones",
"EmptyFolderDescriptionUser": "Files and folders uploaded by admins will appear here.",
"EmptyFolderHeader": "No files in this folder",
"EmptyFormFolderDoneHeaderText": "No finished forms yet",
"EmptyFormFolderDoneDescriptionText": "This section will display all forms that are complete.",
"EmptyFormFolderProgressHeaderText": "Nothing here yet",
"EmptyFormFolderDoneHeaderText": "No finished forms yet",
"EmptyFormFolderProgressDescriptionText": "This section will display the forms in progress.",
"EmptyFormSubFolderHeaderText": "No files in this folder yet",
"EmptyFormFolderProgressHeaderText": "Nothing here yet",
"EmptyFormSubFolderDoneDescriptionText": "Here you will find forms which have been completely filled out by other users.",
"EmptyFormSubFolderHeaderText": "No files in this folder yet",
"EmptyFormSubFolderProgressDescriptionText": "Here you will find forms in progress, i.e. forms which users have started to fill out but haven't completed. ",
"EmptyRecycleBin": "Empty Trash",
"EmptyRootRoomHeader": "Welcome to DocSpace",
@ -73,8 +73,10 @@
"ExcludeSubfolders": "Exclude subfolders",
"FavoritesEmptyContainerDescription": "To mark files as favorites or remove them from this list, use the context menu.",
"FileContents": "File contents",
"FileDownloadingIsRestricted": "File downloading is restricted in this room.",
"FileRemoved": "File moved to Trash",
"FileRenamed": "The document '{{oldTitle}}' is renamed to '{{newTitle}}'",
"FilesWillAppearHere": "Files and folders added to the room will appear here.",
"FillingFormRooms": "Filling form",
"Filter": "Filter",
"FinalizeVersion": "Finalize version",
@ -84,7 +86,6 @@
"FormRoom": "Form room",
"Forms": "Forms",
"FormsTemplates": "Forms templates",
"SharedLinks": "Shared links",
"GeneralLinkDeletedSuccessfully": "New general link created successfully",
"GoToMyRooms": "Go to rooms",
"GoToPersonal": "Go to My Documents",
@ -99,6 +100,7 @@
"LinkEditedSuccessfully": "Link successfully edited and copied",
"LinkEnabledSuccessfully": "Link enabled successfully",
"LinkForPortalUsers": "Link for DocSpace users",
"LinkSettings": "Link settings",
"LinkSuccessfullyCopied": "Link successfully copied to clipboard",
"LinkSuccessfullyCreatedAndCopied": "Link successfully created and copied to clipboard",
"LinkValidUntil": "This link will be valid until",
@ -139,8 +141,8 @@
"PrivateRoomHeader": "Welcome to ONLYOFFICE private room where every symbol you type is encrypted",
"PrivateRoomSupport": "Work in Private Room is available via {{organizationName}} desktop app. <3>Instructions</3>",
"PublicRoom": "Public room",
"RecentlyAccessible": "Recently accessible via link",
"RecentEmptyContainerDescription": "Your last viewed or edited docs will be displayed in this section.",
"RecentlyAccessible": "Recently accessible via link",
"RecentViaLinkEmptyContainerDescription": "Here you will find a list of the recently opened files shared with you via an external link.",
"RecycleBinAction": "Empty trash",
"RemovedFromFavorites": "Removed from favorites",
@ -150,6 +152,7 @@
"RevokeLink": "Revoke link",
"RoomAvailableViaExternalLink": "Room available via external link",
"RoomCreated": "Room created",
"RoomEmptyAtTheMoment": "This room is empty at the moment.",
"RoomEmptyContainerDescription": "Please create the first room.",
"RoomEmptyContainerDescriptionUser": "Rooms shared with you will appear here.",
"RoomNotificationsDisabled": "Room notifications disabled",
@ -165,6 +168,7 @@
"SelectorEmptyScreenHeader": "No files and folders here yet",
"SendByEmail": "Send by email",
"Share": "Share",
"SharedLinks": "Shared links",
"ShareFolder": "Share folder",
"ShareFolderDescription": "A new room will be created and all the contents of the selected folder will be copied there. Afterwards, you can invite other users to collaborate on the files within a room.",
"ShareRoom": "Share room",
@ -190,9 +194,5 @@
"WantToRestoreTheRoom": "All shared links in this room will become active, and its contents will be available to everyone with the link. Do you want to restore the room?",
"WantToRestoreTheRooms": "All shared links in restored rooms will become active, and their contents will be available to everyone with the room links. Do you want to restore the rooms?",
"WithSubfolders": "With subfolders",
"YouLeftTheRoom": "You have left the room",
"LinkSettings": "Link settings",
"FileDownloadingIsRestricted": "File downloading is restricted in this room.",
"RoomEmptyAtTheMoment": "This room is empty at the moment.",
"FilesWillAppearHere": "Files and folders added to the room will appear here."
"YouLeftTheRoom": "You have left the room"
}

View File

@ -12,5 +12,6 @@
"InviteViaLinkDescriptionRoom": "Create a universal link for self-authorization in the room",
"LinkCopySuccess": "Link has been copied",
"ResetChange": "Reset change",
"SendInvitation": "Send invitation"
"SendInvitation": "Send invitation",
"UsersAlreadyAdded": "Some users have already been added"
}

View File

@ -1,38 +1,100 @@
{
"ActionButton": "Action button",
"ActionButtonDescription": "You can disable the Action and + buttons in the current section header to limit creation of files, folders, and rooms.",
"AddWatermarks": "Add watermarks to documents",
"AdvancedDisplay": "Advanced display settings",
"APILink": "API library",
"Ascending": "Ascending",
"AllTypes": "All types",
"ButtonColor": "Button color",
"ButtonCustomization": "Button customization",
"ButtonText": "Button text",
"CancelButtonText": "Cancel button text",
"Code": "Code to insert",
"CopyWindowCode": "Copy window embed code",
"CreateSampleHeader": "Create sample DocSpace embed",
"Chat": "Chat",
"CSPDescription": "To safely embed DocSpace as an iframe in a website, add its URL to your allow list.",
"CSPHeader": "Embed DocSpace as iframe",
"CSPHelp": "This setting is a security mechanism that can be used to protect against content injection attacks. The CSP describes secure resource download sources. Downloading from resources not included in the `white list` is blocked. Specify the domains (together with the protocol) with which it will work.",
"CSPInputPlaceholder": "Enter URL like this: https://example.com",
"CustomizingDisplay": "Customizing the display",
"DefaultColumnsOption": "Default (Quantity depends on screen width)",
"DataDisplay": "Data display settings",
"Descending": "Descending",
"DisplayColumns": "Displaying columns in a file row",
"EmbedCodeSuccessfullyCopied": "Embed code successfully copied to clipboard",
"Editor": "Editor",
"EditorDescription": "Allows you to open the SDK as a document editor for editing by specifying the id parameter for a file.",
"EditorPresetDescription": "This mode allows you to open the SDK as a document editor for editing by specifying the id parameter for a file.",
"EnterCount": "Enter count",
"EnterHeight": "Enter height",
"EnterId": "Enter id",
"EnterPage": "Enter page number",
"EnterWidth": "Enter width",
"ElementItself": "The element itself",
"ElementCalledAfterClicking": "The element will be called after clicking",
"FeedbackAndSupport": "Feedback&Support",
"Filter": "Search, Filter and Sort",
"FileSelector": "File selector",
"FileSelectorDescription": "Opens the file selector and allows you to select a file from a list of available files.",
"FileSelectorPresetDescription": "Use this mode to display the file selector. It allows selecting a file from the list of the available ones.",
"FileTypeDisplay": "File type display",
"FrameId": "Frame id",
"FileId": "File ID",
"GetCode": "Get code to insert",
"Header": "Header",
"HeaderDescription": "You can disable header in the mobile version to limit access to the DocSpace sections (just like disabling the left menu in the desktop version).",
"InterfaceElements": "Interface elements",
"InitializeSDK": "Initialize the SDK in the following modes",
"InLeftPanel": "in the left panel",
"ItemsCount": "Items count on one page",
"ItemsCountDescription": "You can specify the number of files / folders displayed on one page, as well as specify which page to start displaying",
"JavascriptSdk": "Javascript SDK",
"Manager": "Manager",
"ManagerDescription": "Displays a list of entities depending on the specified rootPath. It allows you to create rooms, folders, and files and work with them.",
"ManagerPresetDescription": "Use this mode to display a list of entities depending on the specified rootPath. It allows creating and working with rooms, folders and files.",
"MainElementParameter": "Main element parameter",
"Menu": "Left menu",
"MenuDescription": "You can disable the left menu if users don't need to navigate to other sections.",
"MobileOnly": "only mobile devices",
"Page": "Display page (number)",
"RoomDescription": "You can select the room you want to display",
"RoomOrFolder": "Room or Folder",
"RoomOrFolderDescription": "You can select the section, room or folder you want to display",
"SDKDescription": "Using JavaScript SDK, you can embed a room or a folder from ONLYOFFICE DocSpace into your web interface as an iframe. Here, you can find settings for creating a sample iframe and configuring CSP. To use the complete SDK, please refer to the ",
"RoomSelector": "Room selector",
"RoomSelectorDescription": "Opens the room selector and allows you to select a room from a list of the available rooms.",
"RoomSelectorPresetDescription": "Use this mode to display the room selector. It allows selecting a room from the list of the available ones.",
"RoomTypeDisplay": "Room type display",
"Rotate": "Rotate",
"RightPanelCollapsed": "Right panel collapsed",
"Scale": "Scale",
"SDKDescription": "Using JavaScript SDK, you can embed one of the available ONLYOFFICE DocSpace modes into your web interface as an iframe (file manager, room or file selector, editor and viewer). Here, you can find settings for creating a sample iframe using modes and configuring CSP. To use the complete SDK, please refer to the ",
"SearchBlock": "Search block",
"ManagerSearchBlockDescription": "You can disable the search, filter and sort options.",
"FilesSearchDescription": "File search within the opened folder/room.",
"SearchTerm": "Search term",
"SelectToDocSpace": "Select to DocSpace",
"SelectImage": "Select image",
"SettingUpColumns": "Setting up Columns",
"SettingUpColumnsDescription": "You can disable the ability for users to manage and customize file information columns in list view.",
"SetItUp": "Set it up",
"SelectButtonText": "Select Button text",
"SelectFile": "Select a file",
"SelectRoom": "Select a room",
"SelectTypes": "Select types",
"SelectorPreview": "Selector preview",
"SortOrder": "Sort order",
"Title": "Navigate and Title"
"SetUp": "SET UP",
"SimpleRoom": "Simple Room",
"SimpleRoomDescription": "Opens the room selector and allows you to select a room from a list of the available rooms.",
"SimpleRoomPresetDescription": "Use this mode to display a list of entities depending on the specified rootPath. It allows creating and working with rooms, folders and files.",
"Subtitle": "Subtitle",
"SubtitleDescription": "Subtitle with additional comments or descriptions for the current directory.",
"TabPlugins": "Tab Plugins",
"Title": "Navigate and Title",
"ManagerTitleDescription": "You can disable the title of the current section/room/folder.",
"Viewer": "Viewer",
"ViewerDescription": "Allows you to open the SDK as a document editor for viewing by specifying the id parameter for a file.",
"ViewerPresetDescription": "Allows you to open the SDK as a document editor for viewing by specifying the id parameter for a file."
}

View File

@ -1,7 +1,7 @@
{
"AddGroupsForSharingButton": "Add groups",
"AddShareMessage": "Add message",
"AdditionalLinkRemove": "Additional link remove",
"AddShareMessage": "Add message",
"AnyoneWithLink": "Anyone with the link",
"Comment": "Comment",
"CopyExternalLink": "Copy external link",
@ -19,16 +19,16 @@
"GeneralAccessLinkRemove": "General access link remove",
"InternalLink": "Internal link",
"LinkAccessDenied": "Access to the link has been denied",
"LinkName": "Link name",
"LinkExpireAfter": "The link will expire after <1>{{date}}</1>",
"LinkExpired": "The link has expired.",
"LinkIsValid": "The link is valid for <1>{{date}}</1>",
"LinkName": "Link name",
"Notify users": "Notify users",
"ReadOnly": "Read only",
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}.",
"ShareEmailSubject": "You have been granted access to the {{itemName}} document",
"ShareDocument": "Share this document",
"ShareDocumentDescription": "Provide access to the document and set the permission levels.",
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}.",
"ShareEmailSubject": "You have been granted access to the {{itemName}} document",
"ShareVia": "Share via",
"SharingSettingsTitle": "Sharing settings"
}

View File

@ -35,7 +35,7 @@
"MobileWin": "Download ONLYOFFICE Desktop Editors for Windows",
"MoveHere": "Move here",
"MoveToOperation": "Moving",
"NewForm": "Form template",
"NewForm": "PDF Form",
"Other": "Other",
"OwnerChange": "Change owner",
"Presentations": "Presentations",

View File

@ -35,7 +35,7 @@
"MobileWin": "Descargar los editores de escritorio ONLYOFFICE para Windows",
"MoveHere": "Mover aquí",
"MoveToOperation": "Moviendo",
"NewForm": "Plantilla de formulario",
"NewForm": "Formulario PDF",
"Other": "Otro",
"OwnerChange": "Cambiar propietario",
"Presentations": "Presentaciones",

View File

@ -35,7 +35,7 @@
"MobileWin": "Lataa ONLYOFFICE Desktop Editors Windowsille",
"MoveHere": "Siirrä tänne",
"MoveToOperation": "Siirretään",
"NewForm": "Lomakemalli",
"NewForm": "PDF-lomake",
"Other": "Muu",
"OwnerChange": "Vaihda omistaja",
"Presentations": "Esitykset",

View File

@ -35,7 +35,7 @@
"MobileWin": "Télécharger les applications de bureau ONLYOFFICE pour Windows",
"MoveHere": "Déplacer ici",
"MoveToOperation": "Déplacement",
"NewForm": "Modèle de formulaire",
"NewForm": "Formulaire PDF",
"Other": "Autre",
"OwnerChange": "Changer le propriétaire",
"Presentations": "Présentations",

View File

@ -35,7 +35,7 @@
"MobileWin": "Ներբեռնել ONLYOFFICE Desktop Editors Windows-ի համար",
"MoveHere": "Տեղափոխել այստեղ",
"MoveToOperation": "Տեղափոխություն",
"NewForm": "Ձևանմուշ",
"NewForm": "PDF Ձև",
"Other": "Այլ",
"OwnerChange": "Փոխել տնօրինողին",
"Presentations": "Ներկայացումներ",

View File

@ -35,7 +35,7 @@
"MobileWin": "Scarica ONLYOFFICE Desktop Editors per Windows",
"MoveHere": "Sposta qui",
"MoveToOperation": "Sta spostando",
"NewForm": "Modello di modulo",
"NewForm": "Modulo PDF",
"Other": "Altro",
"OwnerChange": "Cambiare proprietario",
"Presentations": "Presentazioni",

View File

@ -35,7 +35,7 @@
"MobileWin": "Windows用のONLYOFFICEデスクトップエディタをダウンロードする",
"MoveHere": "ここに移動",
"MoveToOperation": "移動中",
"NewForm": "フォーム テンプレート",
"NewForm": "PDFフォーム",
"Other": "その他",
"OwnerChange": "オーナー変更",
"Presentations": "プレゼンテーション",

View File

@ -35,7 +35,7 @@
"MobileWin": "Windows용 ONLYOFFICE Desktop Editors를 다운로드 받으세요",
"MoveHere": "여기로 이동",
"MoveToOperation": "이동 중",
"NewForm": "양식 템플릿",
"NewForm": "PDF 양식",
"Other": "기타",
"OwnerChange": "소유자 변경",
"Presentations": "프레젠테이션",

View File

@ -34,7 +34,7 @@
"MobileWin": "ດາວໂຫລດ ONLYOFFICE Desktop Editors ສໍາລັບ Windows",
"MoveHere": "ຍ້າຍ ນີ້",
"MoveToOperation": "ກຳລັງເຄື່ອນຍ້າຍ",
"NewForm": "ແບບຟອມ",
"NewForm": "ແບບຟອມ PDF",
"Other": "ອື່ນໆ",
"OwnerChange": "ປ່ຽນເຈົ້າຂອງ",
"Presentations": "ບົດສະເຫນີ",

View File

@ -35,7 +35,7 @@
"MobileWin": "Lejupielādēt ONLYOFFICE darbvirsmas redaktorus operētājsistēmai Windows",
"MoveHere": "Pārvietoties šeit",
"MoveToOperation": "Pārvieto",
"NewForm": "Veidlapas veidne",
"NewForm": "PDF veidlapa",
"Other": "Cits",
"OwnerChange": "Mainīt īpašnieku",
"Presentations": "Prezentācijas",

View File

@ -35,7 +35,7 @@
"MobileWin": "Download ONLYOFFICE Desktop Editors voor Windows",
"MoveHere": "Hierheen verplaatsen",
"MoveToOperation": "Verplaatsen",
"NewForm": "Formulier sjabloon",
"NewForm": "PDF-formulier",
"Other": "Overige",
"OwnerChange": "Wijzig eigenaar",
"Presentations": "Presentaties",

View File

@ -35,7 +35,7 @@
"MobileWin": "Pobierz ONLYOFFICE Desktop Editors dla Windows",
"MoveHere": "Przenieś tutaj",
"MoveToOperation": "Przenoszenie",
"NewForm": "Szablon formularza",
"NewForm": "Formularz PDF",
"Other": "Inny",
"OwnerChange": "Zmień właściciela",
"Presentations": "Prezentacje",

View File

@ -35,7 +35,7 @@
"MobileWin": "Baixar ONLYOFFICE Desktop Editors para Windows",
"MoveHere": "Mover aqui",
"MoveToOperation": "Movendo",
"NewForm": "Modelo de formulário",
"NewForm": "Formulário PDF",
"Other": "Outro",
"OwnerChange": "Alterar proprietário",
"Presentations": "Apresentações ",

View File

@ -35,7 +35,7 @@
"MobileWin": "Faça o download do ONLYOFFICE Desktop Editors para Windows",
"MoveHere": "Mover para aqui",
"MoveToOperation": "A mover",
"NewForm": "Modelo de Formulário",
"NewForm": "Formulário PDF",
"Other": "Outros",
"OwnerChange": "Alterar dono",
"Presentations": "Apresentações ",

View File

@ -35,7 +35,7 @@
"MobileWin": "Descarcă ONLYOFFICE Desktop Editors pentru Windows",
"MoveHere": "Mutare în acest loc",
"MoveToOperation": "Mutare",
"NewForm": "Șablon formă,",
"NewForm": "Formular PDF",
"Other": "Altă",
"OwnerChange": "Schimbare proprietar",
"Presentations": "Prezentări",

View File

@ -35,7 +35,7 @@
"MobileWin": "Скачать десктопные редакторы ONLYOFFICE для Windows",
"MoveHere": "Переместить сюда",
"MoveToOperation": "Перемещение",
"NewForm": "Шаблон формы",
"NewForm": "PDF-форма",
"Other": "Другой",
"OwnerChange": "Сменить владельца",
"Presentations": "Презентации",

View File

@ -35,7 +35,7 @@
"MobileWin": "Stiahnite si ONLYOFFICE Desktop Editors pre Windows",
"MoveHere": "Posunúť sem",
"MoveToOperation": "Presúva sa",
"NewForm": "Šablóna formulára",
"NewForm": "Formulár PDF",
"Other": "Iný",
"OwnerChange": "Zmeniť vlastníka",
"Presentations": "Prezentácie",

View File

@ -35,7 +35,7 @@
"MobileWin": "Prenesi ONLYOFFICE namizne urejevalnike za Windows",
"MoveHere": "Premakni sem",
"MoveToOperation": "Premikanje",
"NewForm": "Predloga obrazca",
"NewForm": "PDF obrazec",
"Other": "Drugo",
"OwnerChange": "Spremeni lastnika",
"Presentations": "Predstavitve",

View File

@ -35,7 +35,7 @@
"MobileWin": "Windows için ONLYOFFICE Masaüstü Düzenleyicilerini İndirin",
"MoveHere": "Buraya taşı",
"MoveToOperation": "Taşınıyor",
"NewForm": "Form şablonu",
"NewForm": "PDF formu",
"Other": "Diğer",
"OwnerChange": "Sahibi değiştir",
"Presentations": "Sunumlar",

View File

@ -35,7 +35,7 @@
"MobileWin": "Завантажити настільні редактори ONLYOFFICE для Windows",
"MoveHere": "Перемістити сюди",
"MoveToOperation": "Переміщення",
"NewForm": "Шаблон форми",
"NewForm": "Форма PDF",
"Other": "Інше",
"OwnerChange": "Змінити власника",
"Presentations": "Презентації",

View File

@ -35,7 +35,7 @@
"MobileWin": "Tải xuống Trình chỉnh sửa ONLYOFFICE cho máy tính Windows",
"MoveHere": "Chuyển vào đây",
"MoveToOperation": "Đang di chuyển",
"NewForm": "Mẫu biểu mẫu",
"NewForm": "Mẫu PDF",
"Other": "Khác",
"OwnerChange": "Thay đổi chủ sở hữu",
"Presentations": "Bản trình bày",

View File

@ -35,7 +35,7 @@
"MobileWin": "下载适用于Windows的ONLYOFFICE桌面编辑器",
"MoveHere": "移到这里",
"MoveToOperation": "移动中",
"NewForm": "表单模板",
"NewForm": "PDF表格",
"Other": "其他",
"OwnerChange": "变更所有者",
"Presentations": "演示文稿",

View File

@ -134,6 +134,9 @@ const ArticleMainButtonContent = (props) => {
isGracePeriod,
setInviteUsersWarningDialogVisible,
currentDeviceType,
isFrame,
disableActionButton,
} = props;
const navigate = useNavigate();
@ -492,7 +495,9 @@ const ArticleMainButtonContent = (props) => {
? t("Common:Invite")
: t("Common:Actions");
const isDisabled = isSettingsPage
const isDisabled = isFrame
? disableActionButton
: isSettingsPage
? isSettingsPage
: isAccountsPage
? !isAccountsPage
@ -638,6 +643,8 @@ export default inject(
const { setOformFromFolderId, oformsFilter } = oformsStore;
const { mainButtonItemsList } = pluginStore;
const { frameConfig, isFrame } = settingsStore
return {
isGracePeriod,
setInviteUsersWarningDialogVisible,
@ -680,6 +687,9 @@ export default inject(
versionHistoryPanelVisible,
security,
currentDeviceType,
isFrame,
disableActionButton: frameConfig?.disableActionButton,
};
}
)(

View File

@ -130,7 +130,7 @@ const Badges = ({
const iconForm =
sizeBadge === "medium" ? FormFillRectSvgUrl : AccessEditFormReactSvgUrl;
const iconEdit = !isForm ? FileActionsConvertEditDocReactSvgUrl : iconForm;
const iconEdit = !isPdf ? FileActionsConvertEditDocReactSvgUrl : iconForm;
const iconRefresh = desktopView ? Refresh12ReactSvgUrl : RefreshReactSvgUrl;
@ -206,7 +206,7 @@ const Badges = ({
/>
</BadgeWrapper>
)}
{isEditing && !isVisitor && !isPdf && !(isRecentTab && !canEditing) && (
{isEditing && !isVisitor && !(isRecentTab && !canEditing) && (
<ColorTheme
themeId={ThemeId.IconButton}
isEditing={isEditing}
@ -215,7 +215,7 @@ const Badges = ({
size={sizeBadge}
onClick={onFilesClick}
hoverColor={theme.filesBadges.hoverIconColor}
title={isForm ? t("Common:FillFormButton") : t("Common:EditButton")}
title={isPdf ? t("Common:FillFormButton") : t("Common:EditButton")}
/>
)}
{item.viewAccessibility?.MustConvert &&

View File

@ -142,6 +142,10 @@ export type FilesSelectorProps = {
onClose?: () => void;
withSearch: boolean;
withBreadCrumbs: boolean;
withSubtitle: boolean;
isMove?: boolean;
isCopy?: boolean;
isRestore: boolean;
@ -226,6 +230,8 @@ export type FilesSelectorProps = {
embedded: boolean;
withHeader: boolean;
withCancelButton: boolean;
cancelButtonLabel: string;
acceptButtonLabel: string;
settings: any;
roomsFolderId?: number;

View File

@ -46,6 +46,10 @@ const FilesSelector = ({
onClose,
withSearch = true,
withBreadCrumbs = true,
withSubtitle = true,
isMove,
isCopy,
isRestore,
@ -76,7 +80,7 @@ const FilesSelector = ({
onSelectFolder,
onSetBaseFolderPath,
//onSetNewFolderPath,
// onSetNewFolderPath,
setIsDataReady,
onSelectTreeNode,
onSave,
@ -103,6 +107,8 @@ const FilesSelector = ({
embedded,
withHeader,
withCancelButton = true,
cancelButtonLabel,
acceptButtonLabel,
getIcon,
isRoomBackup,
@ -162,8 +168,8 @@ const FilesSelector = ({
} = useLoadersHelper({ items });
useEffect(() => {
setIsDataReady && setIsDataReady(!showLoader);
}, [showLoader]);
setIsDataReady?.(!showLoader);
}, [showLoader, setIsDataReady]);
const { isRoot, setIsRoot, getRootData } = useRootHelper({
setIsBreadCrumbsLoading,
@ -265,24 +271,31 @@ const FilesSelector = ({
}, [selectedItemId, isRoot]);
React.useEffect(() => {
const sessionPath = window.sessionStorage.getItem("filesSelectorPath");
let folderId = currentFolderId
? currentFolderId
: sessionPath && (isMove || isCopy || isRestore || isRestoreAll)
? +sessionPath
: fromFolderId;
const getRoomSettings = () => {
setSelectedItemType("rooms");
getRoomList(0, true);
};
const needRoomList = isRoomsOnly && !currentFolderId;
const needRoomList = isRoomsOnly && !folderId;
if (needRoomList) {
getRoomSettings();
return;
}
if (!currentFolderId) {
if (!folderId) {
getRootData();
return;
}
setSelectedItemId(currentFolderId);
setSelectedItemId(folderId);
if (
needRoomList ||
@ -296,8 +309,8 @@ const FilesSelector = ({
}
setSelectedItemType("files");
getFileList(0, currentFolderId, true);
}, []);
getFileList(0, folderId, true);
}, [currentFolderId]);
const onClickBreadCrumb = (item: BreadCrumb) => {
if (!isFirstLoad) {
@ -312,7 +325,7 @@ const FilesSelector = ({
setItems(null);
const idx = breadCrumbs.findIndex(
(value) => value.id.toString() === item.id.toString()
(value) => value.id.toString() === item.id.toString(),
);
const maxLength = breadCrumbs.length - 1;
@ -321,7 +334,7 @@ const FilesSelector = ({
const newBreadCrumbs = breadCrumbs.map((item, index) => {
if (!foundParentId) {
currentFolderIndex = disabledItems.findIndex(
(id) => id === item?.id
(id) => id === item?.id,
);
}
@ -405,7 +418,7 @@ const FilesSelector = ({
items: any,
accessRights: any,
fileName: string,
isChecked: boolean
isChecked: boolean,
) => {
const isPublic =
breadCrumbs.findIndex((f: any) => f.roomType === RoomsType.PublicRoom) >
@ -502,10 +515,10 @@ const FilesSelector = ({
isMove,
isSelect,
filterParam,
isRestore
isRestore,
);
const acceptButtonLabel = getAcceptButtonLabel(
const defaultAcceptButtonLabel = getAcceptButtonLabel(
t,
isEditorDialog,
isCopy,
@ -513,7 +526,7 @@ const FilesSelector = ({
isMove,
isSelect,
filterParam,
isRestore
isRestore,
);
const isDisabled = getIsDisabled(
@ -530,7 +543,7 @@ const FilesSelector = ({
filterParam,
!!selectedFileInfo,
includeFolder,
isRestore
isRestore,
);
const SelectorBody = (
@ -544,10 +557,10 @@ const FilesSelector = ({
onClearSearch={onClearSearchAction}
items={items ? items : []}
onSelect={onSelectAction}
acceptButtonLabel={acceptButtonLabel}
acceptButtonLabel={acceptButtonLabel || defaultAcceptButtonLabel}
onAccept={onAcceptAction}
withCancelButton={withCancelButton}
cancelButtonLabel={t("Common:CancelButton")}
cancelButtonLabel={cancelButtonLabel || t("Common:CancelButton")}
onCancel={onCloseAction}
emptyScreenImage={
theme.isBase ? EmptyScreenAltSvgUrl : EmptyScreenAltSvgDarkUrl
@ -561,12 +574,12 @@ const FilesSelector = ({
}
searchEmptyScreenHeader={t("Common:NotFoundTitle")}
searchEmptyScreenDescription={t("EmptyFilterDescriptionText")}
withBreadCrumbs
withBreadCrumbs={withBreadCrumbs}
breadCrumbs={breadCrumbs}
onSelectBreadCrumb={onClickBreadCrumb}
isLoading={showLoader}
isBreadCrumbsLoading={showBreadCrumbsLoader}
withSearch={!isRoot && items ? items.length > 0 : !isRoot && isFirstLoad}
withSearch={withSearch && !isRoot && items ? items.length > 0 : !isRoot && isFirstLoad}
rowLoader={
<RowLoader
isMultiSelect={false}
@ -590,7 +603,7 @@ const FilesSelector = ({
currentFooterInputValue={currentFooterInputValue}
footerCheckboxLabel={footerCheckboxLabel}
descriptionText={
!filterParam || filterParam === "ALL"
!withSubtitle || !filterParam || filterParam === "ALL"
? ""
: descriptionText ?? t("Common:SelectDOCXFormat")
}
@ -644,7 +657,7 @@ export default inject(
filesStore,
infoPanelStore,
}: any,
{ isCopy, isRestoreAll, isMove, isRestore, isPanelVisible, id }: any
{ isCopy, isRestoreAll, isMove, isRestore, isPanelVisible, id }: any,
) => {
const { id: selectedId, parentId, rootFolderType } = selectedFolderStore;
@ -652,8 +665,6 @@ export default inject(
filesActionsStore;
const { itemOperationToFolder, clearActiveOperations } = uploadDataStore;
const sessionPath = window.sessionStorage.getItem("filesSelectorPath");
const { treeFolders, roomsFolderId } = treeFoldersStore;
const {
@ -727,13 +738,8 @@ export default inject(
? parentId
: selectedId;
const currentFolderId =
sessionPath && (isMove || isCopy || isRestore || isRestoreAll)
? +sessionPath
: fromFolderId;
return {
currentFolderId,
fromFolderId,
parentId,
rootFolderType,
@ -770,5 +776,5 @@ export default inject(
roomsFolderId,
};
}
},
)(observer(FilesSelector));

View File

@ -123,9 +123,9 @@ const ContactContainer = (props) => {
);
};
export default inject(({ authStore, settingsStore, payments }) => {
export default inject(({ authStore, settingsStore, paymentStore }) => {
const { isCommunity } = authStore;
const { helpUrl, salesEmail } = payments;
const { helpUrl, salesEmail } = paymentStore;
const { theme } = settingsStore;
return { helpUrl, salesEmail, theme, isCommunity };
})(observer(ContactContainer));

View File

@ -43,7 +43,9 @@ const AvatarEditorDialog = (props) => {
const { visible, onClose, profile, updateCreatedAvatar, setHasAvatar } =
props;
const [avatar, setAvatar] = useState({
uploadedFile: profile.hasAvatar ? profile.avatarMax : DefaultUserAvatarMax,
uploadedFile: profile.hasAvatar
? profile.avatarOriginal
: DefaultUserAvatarMax,
x: 0.5,
y: 0.5,
zoom: 1,
@ -63,7 +65,6 @@ const AvatarEditorDialog = (props) => {
onClose();
return;
}
const file = await dataUrlToFile(preview);
const avatarData = new FormData();

View File

@ -106,8 +106,8 @@ ChangePricingPlanDialog.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default inject(({ payments, currentQuotaStore }) => {
const { managersCount, allowedStorageSizeByQuota } = payments;
export default inject(({ paymentStore, currentQuotaStore }) => {
const { managersCount, allowedStorageSizeByQuota } = paymentStore;
const { addedManagersCount, usedTotalStorageSizeCount } = currentQuotaStore;
return {

View File

@ -7,7 +7,7 @@ import { Portal } from "@docspace/shared/components/portal";
import { Base } from "@docspace/shared/themes";
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
import { PluginComponents } from "SRC_DIR/helpers/plugins/enums";
import { messageActions } from "SRC_DIR/helpers/plugins/utils";
const StyledFullScreen = styled.div`
@ -83,7 +83,7 @@ const PluginDialog = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};
@ -111,7 +111,7 @@ const PluginDialog = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};

View File

@ -198,8 +198,8 @@ SalesDepartmentRequestDialog.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default inject(({ payments }) => {
const { sendPaymentRequest } = payments;
export default inject(({ paymentStore }) => {
const { sendPaymentRequest } = paymentStore;
return {
sendPaymentRequest,

View File

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import { Button } from "@docspace/shared/components/button";
import { ModalDialog } from "@docspace/shared/components/modal-dialog";
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
import { PluginComponents } from "SRC_DIR/helpers/plugins/enums";
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
import Header from "./sub-components/Header";

View File

@ -7,7 +7,7 @@ import { Text } from "@docspace/shared/components/text";
import { Link } from "@docspace/shared/components/link";
import { getCorrectDate, getCookie } from "@docspace/shared/utils";
import { PluginStatus } from "SRC_DIR/helpers/plugins/constants";
import { PluginStatus } from "SRC_DIR/helpers/plugins/enums";
import { Base } from "@docspace/shared/themes";
const StyledContainer = styled.div`
@ -121,7 +121,7 @@ const Info = ({ t, plugin, withDelete, withSeparator }) => {
lineHeight={"20px"}
noSelect
>
{plugin.createBy}
{plugin.createBy.displayName}
</Text>
</>
)}

View File

@ -53,7 +53,7 @@ const AddUsersPanel = ({
const onBackClick = () => onClose();
const getFilterWithOutDisabledUser = useCallback(
() => Filter.getFilterWithOutDisabledUser(),
[]
[],
);
const onKeyPress = (e) => {
@ -88,6 +88,8 @@ const AddUsersPanel = ({
avatar: item.avatar,
isOwner: item.isOwner,
isAdmin: item.isAdmin,
isVisitor: item.isVisitor,
isCollaborator: item.isCollaborator,
};
items.push(newItem);
}
@ -100,7 +102,7 @@ const AddUsersPanel = ({
};
const selectedAccess = accessOptions.filter(
(access) => access.access === accessRight
(access) => access.access === accessRight,
)[0];
const [itemsList, setItemsList] = useState(null);
@ -110,11 +112,11 @@ const AddUsersPanel = ({
const [total, setTotal] = useState(0);
const [isLoading, setIsLoading] = useLoadingWithTimeout(
LOADER_TIMEOUT,
false
false,
);
const [isLoadingSearch, setIsLoadingSearch] = useLoadingWithTimeout(
LOADER_TIMEOUT,
false
false,
);
useEffect(() => {
@ -321,7 +323,7 @@ export default inject(({ settingsStore }) => {
})(
observer(
withTranslation(["SharingPanel", "PeopleTranslations", "Common"])(
withLoader(AddUsersPanel)(<Loaders.DialogAsideLoader isPanel />)
)
)
withLoader(AddUsersPanel)(<Loaders.DialogAsideLoader isPanel />),
),
),
);

View File

@ -292,6 +292,28 @@ const StyledDropDown = styled(DropDown)`
text-overflow: ellipsis;
overflow: hidden;
}
.email-list_avatar {
display: flex;
align-items: center;
gap: 8px;
overflow: hidden;
}
.email-list_add-button {
display: flex;
margin-left: auto;
align-items: center;
gap: 4px;
p {
color: #4781d1;
}
svg path {
fill: #4781d1;
}
}
}
`;

View File

@ -8,7 +8,7 @@ import { Text } from "@docspace/shared/components/text";
import { TextInput } from "@docspace/shared/components/text-input";
import { DropDownItem } from "@docspace/shared/components/drop-down-item";
import { toastr } from "@docspace/shared/components/toast";
import { parseAddresses } from "@docspace/shared/utils";
import { parseAddresses, getParts } from "@docspace/shared/utils";
import { ComboBox } from "@docspace/shared/components/combobox";
import Filter from "@docspace/shared/api/people/filter";
@ -34,6 +34,9 @@ import {
ResetLink,
} from "../StyledInvitePanel";
import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url";
import ArrowIcon from "PUBLIC_DIR/images/arrow.right.react.svg";
const minSearchValue = 2;
const InviteInput = ({
@ -60,10 +63,10 @@ const InviteInput = ({
const [inputValue, setInputValue] = useState("");
const [usersList, setUsersList] = useState([]);
const [isChangeLangMail, setIsChangeLangMail] = useState(false);
const [searchPanelVisible, setSearchPanelVisible] = useState(false);
const [isAddEmailPanelBlocked, setIsAddEmailPanelBlocked] = useState(true);
const [selectedAccess, setSelectedAccess] = useState(defaultAccess);
const [dropDownWidth, setDropDownWidth] = useState(0);
const searchRef = useRef();
@ -74,6 +77,13 @@ const InviteInput = ({
isBeta: isBetaLanguage(language),
};
useEffect(() => {
setTimeout(() => {
const width = searchRef?.current?.offsetWidth ?? 0;
if (width !== dropDownWidth) setDropDownWidth(width);
}, 0);
});
useEffect(() => {
!culture.key &&
setInviteLanguage({
@ -95,6 +105,7 @@ const InviteInput = ({
access: selectedAccess,
displayName: address.email,
errors: address.parseErrors,
isEmailInvite: true,
};
});
}
@ -105,32 +116,34 @@ const InviteInput = ({
access: selectedAccess,
displayName: addresses[0].email,
errors: addresses[0].parseErrors,
isEmailInvite: true,
};
};
const searchByQuery = async (value) => {
const query = value.trim();
if (query.length >= minSearchValue) {
if (query.length > minSearchValue) {
const filter = Filter.getFilterWithOutDisabledUser();
filter.search = query;
const users = await getMembersList(roomId, filter);
setUsersList(users.items);
setIsAddEmailPanelBlocked(false);
if (users.total) setIsAddEmailPanelBlocked(false);
}
if (!query) {
closeInviteInputPanel();
setInputValue("");
setUsersList([]);
setIsAddEmailPanelBlocked(true);
}
};
const debouncedSearch = useCallback(
debounce((value) => searchByQuery(value), 300),
[]
[],
);
const onChange = (e) => {
@ -145,20 +158,22 @@ const InviteInput = ({
return;
}
if (
(!!usersList.length || clearValue.length >= minSearchValue) &&
!searchPanelVisible
) {
openInviteInputPanel();
}
if (roomId !== -1) {
debouncedSearch(clearValue);
return;
}
setIsAddEmailPanelBlocked(false);
const regex =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{0,}))$/g;
const parts = getParts(value);
for (let i = 0; i < parts.length; i += 1) {
if (regex.test(parts[i])) {
setIsAddEmailPanelBlocked(false);
return;
}
}
setIsAddEmailPanelBlocked(true);
};
const removeExist = (items) => {
@ -167,8 +182,7 @@ const InviteInput = ({
return unique;
}, []);
if (items.length > filtered.length)
toastr.warning("Some users have already been added");
if (items.length > filtered.length) toastr.warning(t("UsersAlreadyAdded"));
return filtered;
};
@ -179,22 +193,25 @@ const InviteInput = ({
item.access = selectedAccess;
const addUser = () => {
if (item.isOwner || item.isAdmin)
item.access = ShareAccessRights.RoomManager;
if (shared) {
toastr.warning(t("UsersAlreadyAdded"));
} else {
if (item.isOwner || item.isAdmin)
item.access = ShareAccessRights.RoomManager;
const items = removeExist([item, ...inviteItems]);
const items = removeExist([item, ...inviteItems]);
setInviteItems(items);
}
setInviteItems(items);
closeInviteInputPanel();
setInputValue("");
setUsersList([]);
setIsAddEmailPanelBlocked(true);
};
return (
<DropDownItem
key={id}
onClick={addUser}
disabled={shared}
height={48}
heightTablet={48}
className="list-item"
@ -220,8 +237,8 @@ const InviteInput = ({
const filtered = removeExist(newItems);
setInviteItems(filtered);
closeInviteInputPanel();
setInputValue("");
setIsAddEmailPanelBlocked(true);
setUsersList([]);
};
@ -231,7 +248,6 @@ const InviteInput = ({
const filtered = removeExist(items);
setInviteItems(filtered);
closeInviteInputPanel();
setInputValue("");
setUsersList([]);
};
@ -241,33 +257,35 @@ const InviteInput = ({
const openUsersPanel = () => {
setInputValue("");
setAddUsersPanelVisible(true);
setIsAddEmailPanelBlocked(true);
};
const closeUsersPanel = () => {
setAddUsersPanelVisible(false);
};
const openInviteInputPanel = (e) => {
setSearchPanelVisible(true);
};
const closeInviteInputPanel = (e) => {
if (e?.target?.tagName?.toUpperCase() === "INPUT") return;
setSearchPanelVisible(false);
};
const foundUsers = usersList.map((user) => getItemContent(user));
const addEmailPanel = (
<DropDownItem
className="add-item"
className="list-item"
style={{ width: "inherit" }}
textOverflow
onClick={addEmail}
height={48}
>
{t("Common:AddButton")} «{inputValue}»
<div className="email-list_avatar">
<Avatar size="min" role="user" source={AtReactSvgUrl} />
<Text truncate fontSize="14px" fontWeight={600}>
{inputValue}
</Text>
</div>
<div className="email-list_add-button">
<Text fontSize="13px" fontWeight={600}>
{t("Common:AddButton")}
</Text>
<ArrowIcon />
</div>
</DropDownItem>
);
@ -278,7 +296,7 @@ const InviteInput = ({
};
const onKeyPress = (e) => {
if (e.key === "Enter" && !!!usersList.length && inputValue.length > 2) {
if (e.key === "Enter") {
addEmail();
}
};
@ -401,28 +419,27 @@ const InviteInput = ({
: t("InviteRoomSearchPlaceholder")
}
value={inputValue}
onFocus={openInviteInputPanel}
isAutoFocussed={true}
onKeyDown={onKeyDown}
/>
</StyledInviteInput>
{inputValue.length >= minSearchValue &&
(isAddEmailPanelBlocked ? (
<></>
) : (
<StyledDropDown
width={searchRef?.current?.offsetWidth}
isDefaultMode={false}
open={searchPanelVisible}
manualX="16px"
showDisabledItems
clickOutsideAction={closeInviteInputPanel}
eventTypes="click"
{...dropDownMaxHeight}
>
{!!usersList.length ? foundUsers : addEmailPanel}
</StyledDropDown>
))}
{isAddEmailPanelBlocked ? (
<></>
) : (
<StyledDropDown
width={dropDownWidth}
isDefaultMode={false}
open
manualX="16px"
showDisabledItems
eventTypes="click"
withBackdrop={false}
zIndex={399}
{...dropDownMaxHeight}
>
{!!usersList.length ? foundUsers : addEmailPanel}
</StyledDropDown>
)}
<AccessSelector
className="add-manually-access"

View File

@ -3,11 +3,10 @@ import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url";
import React, { useState, useEffect } from "react";
import { Avatar } from "@docspace/shared/components/avatar";
import { Text } from "@docspace/shared/components/text";
import { capitalize } from "lodash";
import { parseAddresses } from "@docspace/shared/utils";
import { getAccessOptions } from "../utils";
import { getUserRole } from "@docspace/shared/utils/common";
import { getUserRole, getUserTypeLabel } from "@docspace/shared/utils/common";
import {
StyledEditInput,
@ -33,25 +32,37 @@ const Item = ({
inputsRef,
setIsOpenItemAccess,
isMobileView,
standalone,
}) => {
const { avatar, displayName, email, id, errors, access } = item;
const name = !!avatar ? (displayName !== "" ? displayName : email) : email;
const source = !!avatar ? avatar : AtReactSvgUrl;
const role = getUserRole(item);
const [edit, setEdit] = useState(false);
const [inputValue, setInputValue] = useState(name);
const [parseErrors, setParseErrors] = useState(errors);
const accesses = getAccessOptions(t, roomType, true, true, isOwner);
const accesses = getAccessOptions(
t,
roomType,
true,
true,
isOwner,
standalone,
);
const filteredAccesses = filterUserRoleOptions(accesses, item, true);
const defaultAccess = filteredAccesses.find(
(option) => option.access === +access
(option) => option.access === +access,
);
const role = getUserRole(item);
const typeLabel = item?.isEmailInvite
? getUserTypeLabel(defaultAccess.type, t)
: getUserTypeLabel(role, t);
const errorsInList = () => {
const hasErrors = inviteItems.some((item) => !!item.errors?.length);
setHasErrors(hasErrors);
@ -133,7 +144,7 @@ const Item = ({
color="#A3A9AE"
truncate
>
{`${capitalize(role)} | ${email}`}
{item.userName ? `${typeLabel} | ${email}` : `${typeLabel}`}
</Text>
</StyledInviteUserBody>
@ -167,6 +178,7 @@ const Item = ({
setIsOpenItemAccess={setIsOpenItemAccess}
isMobileView={isMobileView}
noBorder
standalone={standalone}
/>
)}
</>

View File

@ -22,6 +22,7 @@ const Row = memo(({ data, index, style }) => {
inputsRef,
setIsOpenItemAccess,
isMobileView,
standalone,
} = data;
if (inviteItems === undefined) return;
@ -42,6 +43,7 @@ const Row = memo(({ data, index, style }) => {
inputsRef={inputsRef}
setIsOpenItemAccess={setIsOpenItemAccess}
isMobileView={isMobileView}
standalone={standalone}
/>
</StyledRow>
);
@ -60,6 +62,7 @@ const ItemsList = ({
inputsRef,
invitePanelBodyRef,
isMobileView,
standalone,
}) => {
const [bodyHeight, setBodyHeight] = useState(0);
const [offsetTop, setOffsetTop] = useState(0);
@ -146,6 +149,7 @@ const ItemsList = ({
setIsOpenItemAccess,
isMobileView,
t,
standalone,
}}
outerElementType={!scrollAllPanelContent && CustomScrollbarsVirtualList}
>
@ -155,14 +159,16 @@ const ItemsList = ({
);
};
export default inject(({ userStore, dialogsStore }) => {
export default inject(({ userStore, dialogsStore, settingsStore }) => {
const { setInviteItems, inviteItems, changeInviteItem } = dialogsStore;
const { isOwner } = userStore.user;
const { standalone } = settingsStore;
return {
setInviteItems,
inviteItems,
changeInviteItem,
isOwner,
standalone,
};
})(observer(ItemsList));

View File

@ -10,7 +10,7 @@ export const getAccessOptions = (
withRemove = false,
withSeparator = false,
isOwner = false,
standalone = false
standalone = false,
) => {
let options = [];
const accesses = {
@ -22,6 +22,7 @@ export const getAccessOptions = (
color: "#EDC409",
access:
roomType === -1 ? EmployeeType.Admin : ShareAccessRights.FullAccess,
type: "admin",
},
roomAdmin: {
key: "roomAdmin",
@ -31,6 +32,7 @@ export const getAccessOptions = (
color: "#EDC409",
access:
roomType === -1 ? EmployeeType.User : ShareAccessRights.RoomManager,
type: "manager",
},
collaborator: {
key: "collaborator",
@ -42,42 +44,49 @@ export const getAccessOptions = (
roomType === -1
? EmployeeType.Collaborator
: ShareAccessRights.Collaborator,
type: "collaborator",
},
user: {
key: "user",
label: t("Common:User"),
description: t("Translations:RoleUserDescription"),
access: EmployeeType.Guest,
type: "user",
},
editor: {
key: "editor",
label: t("Translations:RoleEditor"),
description: t("Translations:RoleEditorDescription"),
access: ShareAccessRights.Editing,
type: "user",
},
formFiller: {
key: "formFiller",
label: t("Translations:RoleFormFiller"),
description: t("Translations:RoleFormFillerDescription"),
access: ShareAccessRights.FormFilling,
type: "user",
},
reviewer: {
key: "reviewer",
label: t("Translations:RoleReviewer"),
description: t("Translations:RoleReviewerDescription"),
access: ShareAccessRights.Review,
type: "user",
},
commentator: {
key: "commentator",
label: t("Translations:RoleCommentator"),
description: t("Translations:RoleCommentatorDescription"),
access: ShareAccessRights.Comment,
type: "user",
},
viewer: {
key: "viewer",
label: t("Translations:RoleViewer"),
description: t("Translations:RoleViewerDescription"),
access: ShareAccessRights.ReadOnly,
type: "user",
},
};

View File

@ -93,7 +93,7 @@ const ConfirmRoute = ({
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
"/error"
"/error",
);
break;
case ValidationResult.Expired:
@ -104,7 +104,7 @@ const ConfirmRoute = ({
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
"/error"
"/error",
);
break;
case ValidationResult.TariffLimit:
@ -115,7 +115,7 @@ const ConfirmRoute = ({
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
"/error?messageKey=20"
"/error?messageKey=20",
);
break;
default:
@ -126,7 +126,7 @@ const ConfirmRoute = ({
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
"/error"
"/error",
);
break;
}
@ -145,7 +145,7 @@ const ConfirmRoute = ({
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
"/error"
"/error",
);
});
}, [getData, doAuthenticated, isAuthenticated, storeIsLoaded, logout]);

View File

@ -13,7 +13,7 @@ import { Button } from "@docspace/shared/components/button";
import { ToggleButton } from "@docspace/shared/components/toggle-button";
import { ComboBox } from "@docspace/shared/components/combobox";
import { PluginComponents } from "./constants";
import { PluginComponents } from "./enums";
import { messageActions } from "./utils";
@ -74,7 +74,7 @@ const ComponentPure = ({
component={item}
pluginName={pluginName}
/>
)
),
);
return <Box {...elementProps}>{childrenComponents}</Box>;
@ -110,7 +110,7 @@ const ComponentPure = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};
@ -139,7 +139,7 @@ const ComponentPure = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};
@ -168,11 +168,11 @@ const ComponentPure = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};
return <TextArea {...elementProps} onChange={onChangeAction} />;
return <Textarea {...elementProps} onChange={onChangeAction} />;
}
case PluginComponents.input: {
@ -197,7 +197,7 @@ const ComponentPure = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};
@ -247,7 +247,7 @@ const ComponentPure = ({
updateEventListenerItems,
updateFileItems,
updatePlugin
updatePlugin,
);
setIsRequestRunning && setIsRequestRunning(false);
@ -306,7 +306,7 @@ const ComponentPure = ({
updateMainButtonItems,
updateProfileMenuItems,
updateEventListenerItems,
updateFileItems
updateFileItems,
);
};

View File

@ -5,7 +5,7 @@ import PluginHeader from "./PluginHeader";
import PluginInfo from "./PluginInfo";
import PluginSettings from "./PluginSettings";
import { PluginScopes } from "../constants";
import { PluginScopes } from "../enums";
const StyledPlugin = styled.div`
display: flex;
@ -79,7 +79,7 @@ const Plugin = ({
author={author}
status={status}
description={description}
createBy={createBy}
createBy={createBy.displayName}
createOn={createOn}
homePage={homePage}
url={url}

View File

@ -4,7 +4,7 @@ import styled from "styled-components";
import { Text } from "@docspace/shared/components/text";
import { getCorrectDate } from "@docspace/shared/utils";
import { PluginStatus } from "SRC_DIR/helpers/plugins/constants";
import { PluginStatus } from "SRC_DIR/helpers/plugins/enums";
import { Link } from "@docspace/shared/components/link";
import { getPluginUrl } from "../utils";

View File

@ -3,7 +3,7 @@ import styled from "styled-components";
import WrappedComponent from "../WrappedComponent";
import { PluginComponents } from "../constants";
import { PluginComponents } from "../enums";
const StyledPluginSettings = styled.div`
.settings-header {

View File

@ -1,87 +0,0 @@
export const PluginFileType = Object.freeze({
Files: "file",
Folders: "folder",
Rooms: "room",
Image: "image",
Video: "video",
});
export const PluginScopes = Object.freeze({
API: "API",
Settings: "Settings",
ContextMenu: "ContextMenu",
InfoPanel: "InfoPanel",
MainButton: "MainButton",
ProfileMenu: "ProfileMenu",
EventListener: "EventListener",
File: "File",
});
export const PluginStatus = Object.freeze({
active: "active",
hide: "hide",
});
export const PluginActions = Object.freeze({
updateProps: "update-props",
updateContext: "update-context",
updateStatus: "update-status",
showToast: "show-toast",
// showSettingsModal: "show-settings-modal",
// closeSettingsModal: "close-settings-modal",
showCreateDialogModal: "show-create-dialog-modal",
showModal: "show-modal",
closeModal: "close-modal",
updateContextMenuItems: "update-context-menu-items",
updateInfoPanelItems: "update-info-panel-items",
updateMainButtonItems: "update-main-button-items",
updateProfileMenuItems: "update-profile-menu-items",
updateFileItems: "update-file-items",
updateEventListenerItems: "update-event-listener-items",
sendPostMessage: "send-post-message",
saveSettings: "save-settings",
});
export const PluginToastType = Object.freeze({
success: "success",
error: "error",
warning: "warning",
info: "info",
});
export const PluginComponents = Object.freeze({
box: "box",
button: "button",
checkbox: "checkbox",
input: "input",
label: "label",
text: "text",
textArea: "textArea",
toggleButton: "toggleButton",
img: "img",
iFrame: "iFrame",
comboBox: "comboBox",
skeleton: "skeleton",
});
export const PluginUsersType = Object.freeze({
owner: "Owner",
docSpaceAdmin: "DocSpaceAdmin",
roomAdmin: "RoomAdmin",
collaborator: "Collaborator",
user: "User",
});
export const PluginDevices = Object.freeze({
mobile: "mobile",
tablet: "tablet",
desktop: "desktop",
});

View File

@ -0,0 +1,97 @@
export const enum PluginFileType {
Files = "file",
Folders = "folder",
Rooms = "room",
Image = "image",
Video = "video",
}
export const enum PluginEvents {
CREATE = "create",
RENAME = "rename",
ROOM_CREATE = "create_room",
ROOM_EDIT = "edit_room",
CHANGE_COLUMN = "change_column",
CHANGE_USER_TYPE = "change_user_type",
CREATE_PLUGIN_FILE = "create_plugin_file",
}
export const enum PluginScopes {
API = "API",
Settings = "Settings",
ContextMenu = "ContextMenu",
InfoPanel = "InfoPanel",
MainButton = "MainButton",
ProfileMenu = "ProfileMenu",
EventListener = "EventListener",
File = "File",
}
export const enum PluginStatus {
active = "active",
hide = "hide",
}
export const enum PluginActions {
updateProps = "update-props",
updateContext = "update-context",
updateStatus = "update-status",
showToast = "show-toast",
// showSettingsModal= "show-settings-modal",
// closeSettingsModal= "close-settings-modal",
showCreateDialogModal = "show-create-dialog-modal",
showModal = "show-modal",
closeModal = "close-modal",
updateContextMenuItems = "update-context-menu-items",
updateInfoPanelItems = "update-info-panel-items",
updateMainButtonItems = "update-main-button-items",
updateProfileMenuItems = "update-profile-menu-items",
updateFileItems = "update-file-items",
updateEventListenerItems = "update-event-listener-items",
sendPostMessage = "send-post-message",
saveSettings = "save-settings",
}
export const enum PluginToastType {
success = "success",
error = "error",
warning = "warning",
info = "info",
}
export const enum PluginComponents {
box = "box",
button = "button",
checkbox = "checkbox",
input = "input",
label = "label",
text = "text",
textArea = "textArea",
toggleButton = "toggleButton",
img = "img",
iFrame = "iFrame",
comboBox = "comboBox",
skeleton = "skeleton",
}
export const enum PluginUsersType {
owner = "Owner",
docSpaceAdmin = "DocSpaceAdmin",
roomAdmin = "RoomAdmin",
collaborator = "Collaborator",
user = "User",
}
export const enum PluginDevices {
mobile = "mobile",
tablet = "tablet",
desktop = "desktop",
}

View File

@ -0,0 +1,275 @@
import { TCreatedBy } from "@docspace/shared/types";
import { ButtonProps } from "@docspace/shared/components/button/Button.types";
import { BoxProps } from "@docspace/shared/components/box/Box.types";
import { TextInputProps } from "@docspace/shared/components/text-input";
import { CheckboxProps } from "@docspace/shared/components/checkbox/Checkbox.types";
import { ToggleButtonProps } from "@docspace/shared/components/toggle-button/ToggleButton.types";
import { TextareaProps } from "@docspace/shared/components/textarea/Textarea.types";
import {
ComboboxProps,
TOption,
} from "@docspace/shared/components/combobox/Combobox.types";
import { ToastProps } from "@docspace/shared/components/toast/Toast.type";
import { ModalDialogProps } from "@docspace/shared/components/modal-dialog/ModalDialog.types";
import { TextProps } from "@docspace/shared/components/text/Text.types";
import { RectangleSkeletonProps } from "@docspace/shared/skeletons";
import { LabelProps } from "@docspace/shared/components/label/Label.types";
import {
TFile,
TFileSecurity,
TFolderSecurity,
} from "@docspace/shared/api/files/types";
import { TRoomSecurity } from "@docspace/shared/api/rooms/types";
import {
PluginActions,
PluginComponents,
PluginDevices,
PluginEvents,
PluginFileType,
PluginStatus,
PluginUsersType,
} from "./enums";
export interface IPostMessage {
frameId: string;
message: { [key: string]: unknown };
}
export interface IFrame {
src: string;
width?: string;
height?: string;
name?: string;
sandbox?: string;
id?: string;
style?: { [key: string]: string };
}
export interface ICreateDialog {
title: string;
startValue: string;
visible: boolean;
options?: TOption[];
selectedOption?: TOption;
onSelect?: (option: TOption) => IMessage | void;
onSave?: (
e: unknown,
value: string,
) => Promise<IMessage> | Promise<void> | IMessage | void;
onCancel?: (e: unknown) => void;
onClose?: (e: unknown) => void;
isCreateDialog: boolean;
extension?: string;
}
export interface IImage {
src: string;
alt: string;
width?: string;
height?: string;
name?: string;
id?: string;
style?: { [key: string]: string };
}
export interface IMessage {
actions?: PluginActions[];
newProps?:
| TextInputProps
| CheckboxProps
| ToggleButtonProps
| ButtonProps
| TextareaProps
| ComboboxProps;
toastProps?: ToastProps[];
contextProps?: {
name: string;
props:
| BoxProps
| ButtonProps
| CheckboxProps
| ComboboxProps
| IFrame
| IImage
| TextInputProps
| LabelProps
| RectangleSkeletonProps
| TextProps
| TextareaProps
| ToggleButtonProps;
}[];
createDialogProps?: ICreateDialog;
modalDialogProps?: ModalDialogProps;
postMessage?: IPostMessage;
settings?: string;
}
type TButtonGroup = {
component: PluginComponents.button;
props: ButtonProps;
contextName?: string;
};
export interface ISettings {
settings: BoxProps;
saveButton: TButtonGroup;
isLoading?: boolean;
onLoad?: () => Promise<{ settings: BoxProps; saveButton?: TButtonGroup }>;
}
export interface IContextMenuItem {
key: string;
label: string;
icon: string;
onClick: (id: number) => Promise<IMessage> | Promise<void> | IMessage | void;
withActiveItem?: boolean;
fileExt?: string[];
fileType?: PluginFileType[];
usersTypes?: PluginUsersType[];
devices?: PluginDevices[];
security?: (
| keyof TRoomSecurity
| keyof TFileSecurity
| keyof TFolderSecurity
)[];
pluginName?: string;
}
export interface IEventListenerItem {
key: string;
eventType: PluginEvents;
eventHandler: () => Promise<IMessage> | Promise<void> | IMessage | void;
usersTypes?: PluginUsersType[];
devices?: PluginDevices[];
pluginName?: string;
}
export interface IFileItem {
extension: string;
onClick: (item: TFile) => Promise<IMessage> | Promise<void> | IMessage | void;
usersType?: PluginUsersType[];
devices?: PluginDevices[];
fileTypeName?: string;
fileRowIcon?: string;
fileTileIcon?: string;
fileIcon?: string;
fileIconTile?: string;
pluginName?: string;
}
export interface IInfoPanelSubMenu {
name: string;
onClick?: (id: number) => Promise<IMessage> | Promise<void> | IMessage | void;
}
export interface IInfoPanelItem {
key: string;
subMenu: IInfoPanelSubMenu;
body: BoxProps;
onLoad: () => Promise<{ body: BoxProps }>;
filesType?: PluginFileType[];
filesExsts?: string[];
usersTypes?: PluginUsersType[];
devices?: PluginDevices[];
pluginName?: string;
}
export interface IMainButtonItem {
key: string;
label: string;
icon: string;
onClick?: (
id: number | string,
) => Promise<IMessage> | Promise<void> | IMessage | void;
usersType?: PluginUsersType[];
items?: IMainButtonItem[] | null;
devices?: PluginDevices[];
pluginName?: string;
}
export interface IProfileMenuItem {
key: string;
label: string;
icon: string;
onClick: () => Promise<IMessage> | Promise<void> | IMessage | void;
usersType?: PluginUsersType[];
devices?: PluginDevices[];
pluginName?: string;
}
export interface IframeWindow extends Window {
Plugins: { [key: string]: TPlugin };
}
export type TPlugin = {
name: string;
version: string;
description: string;
license: string;
author: string;
homePage: string;
pluginName: string;
scopes: string | string[];
image: string;
createBy: TCreatedBy;
createOn: Date;
enabled: boolean;
system: boolean;
url: string;
settings: string;
iconUrl: string;
status: PluginStatus;
onLoadCallback: () => Promise<void>;
updateStatus: (status: PluginStatus) => void;
getStatus: () => PluginStatus;
setOnLoadCallback: (callback: () => Promise<void>) => void;
adminPluginSettings?: ISettings | null;
setAdminPluginSettings?: (settings: ISettings | null) => void;
setAdminPluginSettingsValue?: (settings: string | null) => void;
getAdminPluginSettings?: () => ISettings | null;
origin?: string;
proxy?: string;
prefix?: string;
setOrigin?: (origin: string) => void;
setProxy?: (proxy: string) => void;
setPrefix?: (prefix: string) => void;
getOrigin?: () => string;
getProxy?: () => string;
getPrefix?: () => string;
setAPI?: (origin: string, proxy: string, prefix: string) => void;
getAPI?: () => { origin: string; proxy: string; prefix: string };
contextMenuItems: Map<string, IContextMenuItem>;
addContextMenuItem(item: IContextMenuItem): void;
getContextMenuItems(): Map<string, IContextMenuItem>;
getContextMenuItemsKeys(): string[];
updateContextMenuItem(item: IContextMenuItem): void;
eventListenerItems?: Map<string, IEventListenerItem>;
addEventListenerItem?: (item: IEventListenerItem) => void;
getEventListenerItems?: () => Map<string, IEventListenerItem>;
fileItems?: Map<string, IFileItem>;
addFileItem?: (item: IFileItem) => void;
getFileItems?: () => Map<string, IFileItem>;
updateFileItem?: (item: IFileItem) => void;
infoPanelItems?: Map<string, IInfoPanelItem>;
addInfoPanelItem?: (item: IInfoPanelItem) => void;
getInfoPanelItems?: () => Map<string, IInfoPanelItem>;
updateInfoPanelItem?: (item: IInfoPanelItem) => void;
mainButtonItems?: Map<string, IMainButtonItem>;
addMainButtonItem?: (item: IMainButtonItem) => void;
getMainButtonItems?: () => Map<string, IMainButtonItem>;
updateMainButtonItem?: (item: IMainButtonItem) => void;
profileMenuItems?: Map<string, IProfileMenuItem>;
addProfileMenuItem?: (item: IProfileMenuItem) => void;
getProfileMenuItems?: () => Map<string, IProfileMenuItem>;
updateProfileMenuItem?: (item: IProfileMenuItem) => void;
};

View File

@ -3,7 +3,7 @@ import { toastr } from "@docspace/shared/components/toast";
import config from "PACKAGE_FILE";
import { PluginActions, PluginToastType } from "./constants";
import { PluginActions, PluginToastType } from "./enums";
import { Events } from "@docspace/shared/enums";
export const messageActions = (
@ -26,7 +26,7 @@ export const messageActions = (
updateEventListenerItems,
updateFileItems,
updatePlugin
updatePlugin,
) => {
if (!message || !message.actions || message.actions.length === 0) return;
@ -152,7 +152,7 @@ export const messageActions = (
if (frame) {
frame.contentWindow.postMessage(
JSON.stringify(postMessage.message),
"*"
"*",
);
}
@ -177,6 +177,6 @@ export const getPluginUrl = (url, file) => {
window.DocSpaceConfig?.proxy?.url,
config.homepage,
path,
file
file,
);
};

View File

@ -7,7 +7,7 @@ import { FolderType, ShareAccessRights } from "@docspace/shared/enums";
import { translations } from "./autoGeneratedTranslations";
// import router from "SRC_DIR/router";
export const setDocumentTitle = (subTitle = null) => {
export const setDocumentTitle = (subTitle = "") => {
const { isAuthenticated, product: currentModule } = authStore;
const { organizationName } = settingsStore;

View File

@ -32,8 +32,8 @@ const Bonus = ({ standaloneInit, isInitPaymentPage }) => {
);
};
export default inject(({ payments }) => {
const { standaloneInit, isInitPaymentPage } = payments;
export default inject(({ paymentStore }) => {
const { standaloneInit, isInitPaymentPage } = paymentStore;
return {
standaloneInit,

View File

@ -35,28 +35,26 @@ export default function withLoader(WrappedComponent) {
type === "EmpInvite") &&
!passwordSettings
) {
axios
.all([getSettings(), getPortalPasswordSettings(confirmHeader)])
.catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
getPortalPasswordSettings(confirmHeader).catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error(errorMessage);
navigate(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
`/login/error?message=${errorMessage}`
)
);
});
console.error(errorMessage);
navigate(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
`/login/error?message=${errorMessage}`,
),
);
});
}
}, [passwordSettings]);
@ -77,8 +75,8 @@ export default function withLoader(WrappedComponent) {
navigate(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
`/login/error?message=${errorMessage}`
)
`/login/error?message=${errorMessage}`,
),
);
});
}

View File

@ -10,6 +10,7 @@ import { ComboBox } from "@docspace/shared/components/combobox";
import { getUserStatus } from "SRC_DIR/helpers/people-helpers";
import { StyledAccountContent } from "../../styles/accounts";
import { getUserTypeLabel } from "@docspace/shared/utils/common";
const Accounts = (props) => {
const {
@ -46,21 +47,6 @@ const Accounts = (props) => {
}
}, [infoPanelSelection]);
const getUserTypeLabel = React.useCallback((role) => {
switch (role) {
case "owner":
return t("Common:Owner");
case "admin":
return t("Common:DocSpaceAdmin");
case "manager":
return t("Common:RoomAdmin");
case "collaborator":
return t("Common:PowerUser");
case "user":
return t("Common:User");
}
}, []);
const getTypesOptions = React.useCallback(() => {
const options = [];
@ -128,10 +114,10 @@ const Accounts = (props) => {
setIsLoading(false);
}
},
[infoPanelSelection, changeUserType, t]
[infoPanelSelection, changeUserType, t],
);
const typeLabel = getUserTypeLabel(role);
const typeLabel = React.useCallback(() => getUserTypeLabel(role, t), [])();
const renderTypeData = () => {
const typesOptions = getTypesOptions();
@ -248,7 +234,7 @@ export default inject(
getPeopleListItem: usersStore.getPeopleListItem,
setInfoPanelSelection,
};
}
},
)(
withTranslation([
"People",
@ -263,7 +249,7 @@ export default inject(
"Translations",
])(
withLoader(observer(Accounts))(
<Loaders.InfoPanelViewLoader view="accounts" />
)
)
<Loaders.InfoPanelViewLoader view="accounts" />,
),
),
);

View File

@ -9,9 +9,8 @@ import { toastr } from "@docspace/shared/components/toast";
import { isMobileOnly, isMobile } from "react-device-detect";
import { decode } from "he";
import { filterUserRoleOptions } from "SRC_DIR/helpers";
import { capitalize } from "lodash";
import { getUserRole } from "@docspace/shared/utils/common";
import { getUserRole, getUserTypeLabel } from "@docspace/shared/utils/common";
import { Text } from "@docspace/shared/components/text";
import EmailPlusReactSvgUrl from "PUBLIC_DIR/images/e-mail+.react.svg?url";
import { StyledUserTypeHeader } from "../../styles/members";
@ -48,7 +47,7 @@ const User = ({
const fullRoomRoleOptions = membersHelper.getOptionsByRoomType(
infoPanelSelection.roomType,
canChangeUserRole
canChangeUserRole,
);
const userRole = membersHelper.getOptionByUserAccess(user.access, user);
@ -57,7 +56,7 @@ const User = ({
const onRepeatInvitation = async () => {
resendEmailInvitations(infoPanelSelection.id, true)
.then(() =>
toastr.success(t("PeopleTranslations:SuccessSentMultipleInvitatios"))
toastr.success(t("PeopleTranslations:SuccessSentMultipleInvitatios")),
)
.catch((err) => toastr.error(err));
};
@ -77,10 +76,10 @@ const User = ({
const newMembers = {
users: infoPanelMembers.users?.filter((m) => m.id !== user.id),
administrators: infoPanelMembers.administrators?.filter(
(m) => m.id !== user.id
(m) => m.id !== user.id,
),
expected: infoPanelMembers.expected?.filter(
(m) => m.id !== user.id
(m) => m.id !== user.id,
),
};
@ -129,13 +128,13 @@ const User = ({
setInfoPanelMembers({
roomId: infoPanelSelection.id,
users: infoPanelMembers.users?.map((m) =>
m.id === user.id ? { ...m, access: option.access } : m
m.id === user.id ? { ...m, access: option.access } : m,
),
administrators: infoPanelMembers.administrators?.map((m) =>
m.id === user.id ? { ...m, access: option.access } : m
m.id === user.id ? { ...m, access: option.access } : m,
),
expected: infoPanelMembers.expected?.map((m) =>
m.id === user.id ? { ...m, access: option.access } : m
m.id === user.id ? { ...m, access: option.access } : m,
),
});
}
@ -182,6 +181,8 @@ const User = ({
const onToggle = (e, isOpen) => {
// setIsScrollLocked(isOpen);
};
const role = getUserRole(user);
const typeLabel = getUserTypeLabel(role, t);
const getTooltipContent = () => (
<div>
@ -197,15 +198,13 @@ const User = ({
color="#A3A9AE !important"
dir="auto"
>
{`${capitalize(role)} | ${user.email}`}
{`${typeLabel} | ${user.email}`}
</Text>
</div>
);
const userAvatar = user.hasAvatar ? user.avatar : DefaultUserPhotoUrl;
const role = getUserRole(user);
const withTooltip = user.isOwner || user.isAdmin;
const uniqueTooltipId = `userTooltip_${Math.random()}`;
@ -269,7 +268,7 @@ const User = ({
color="#A3A9AE"
dir="auto"
>
{`${capitalize(role)} | ${user.email}`}
{`${typeLabel} | ${user.email}`}
</Text>
</div>
</div>

View File

@ -2,7 +2,7 @@ import React from "react";
import { inject, observer } from "mobx-react";
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
import { PluginComponents } from "SRC_DIR/helpers/plugins/enums";
const Plugin = ({ boxProps, pluginName }) => {
return (

View File

@ -15,7 +15,7 @@ import {
import { StyledInfoPanelHeader } from "./styles/common";
import { PluginFileType } from "SRC_DIR/helpers/plugins/constants";
import { PluginFileType } from "SRC_DIR/helpers/plugins/enums";
const InfoPanelHeaderContent = (props) => {
const {
@ -245,12 +245,12 @@ export default inject(
enablePlugins,
};
}
},
)(
withTranslation(["Common", "InfoPanel"])(
InfoPanelHeaderContent
InfoPanelHeaderContent,
// withLoader(observer(InfoPanelHeaderContent))(
// <Loaders.InfoPanelHeaderLoader />
// )
)
),
);

View File

@ -4,7 +4,7 @@ import { withTranslation } from "react-i18next";
import { useNavigate, useLocation } from "react-router-dom";
import queryString from "query-string";
import { PluginFileType } from "SRC_DIR/helpers/plugins/constants";
import { PluginFileType } from "SRC_DIR/helpers/plugins/enums";
import { MEDIA_VIEW_URL } from "@docspace/shared/constants";
import { combineUrl } from "@docspace/shared/utils/combineUrl";
@ -118,7 +118,7 @@ const FilesMediaViewer = (props) => {
setCurrentId(id);
navigate(url);
},
[setCurrentId, navigate]
[setCurrentId, navigate],
);
const resetSelection = () => {
@ -159,7 +159,7 @@ const FilesMediaViewer = (props) => {
// try to fix with one check later (see deleteAction)
const isActiveFile = activeFiles.find((elem) => elem.id === file.id);
const isActiveFolder = activeFolders.find(
(elem) => elem.id === file.id
(elem) => elem.id === file.id,
);
if (isActiveFile || isActiveFolder) return;
@ -169,7 +169,14 @@ const FilesMediaViewer = (props) => {
}
}
},
[files, t, activeFiles, activeFolders, setRemoveMediaItem, deleteItemAction]
[
files,
t,
activeFiles,
activeFolders,
setRemoveMediaItem,
deleteItemAction,
],
);
const onDownloadMediaFile = useCallback(
@ -179,7 +186,7 @@ const FilesMediaViewer = (props) => {
return window.open(viewUrlFile, "_self");
}
},
[playlist]
[playlist],
);
const onMediaViewerClose = useCallback(
@ -227,7 +234,7 @@ const FilesMediaViewer = (props) => {
setToPreviewFile,
setMediaViewerData,
setBufferSelection,
]
],
);
useEffect(() => {
if (playlist.length === 0 && isOpenMediaViewer) onMediaViewerClose();
@ -422,5 +429,5 @@ export default inject(
pluginContextMenuItems,
currentDeviceType,
};
}
},
)(withTranslation(["Files", "Translations"])(observer(FilesMediaViewer)));

View File

@ -20,6 +20,21 @@ const StyledBadgesContainer = styled.div`
display: flex;
align-items: center;
${(props) =>
props.infoPanelVisible &&
css`
.accounts-badge:last-child {
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-left: 12px;
`
: css`
margin-right: 12px;
`}
}
`}
`;
const StyledPaidBadge = styled(Badge)`
@ -52,7 +67,7 @@ const Badges = ({
withoutPaid,
isPaid = false,
filter,
infoPanelVisible,
isSSO = false,
}) => {
const navigate = useNavigate();
@ -74,9 +89,13 @@ const Badges = ({
};
return (
<StyledBadgesContainer className="badges additional-badges">
<StyledBadgesContainer
className="badges additional-badges"
infoPanelVisible={infoPanelVisible}
>
{isSSO && (
<Badge
className="accounts-badge"
label={SSO_LABEL}
color={"#FFFFFF"}
backgroundColor="#22C386"
@ -89,7 +108,7 @@ const Badges = ({
)}
{!withoutPaid && isPaid && (
<StyledPaidBadge
className="paid-badge"
className="paid-badge accounts-badge"
label={t("Paid")}
backgroundColor={"#EDC409"}
fontSize={"9px"}
@ -101,10 +120,16 @@ const Badges = ({
/>
)}
{statusType === "pending" && (
<StyledSendClockIcon className="pending-badge" size="small" />
<StyledSendClockIcon
className="pending-badge accounts-badge"
size="small"
/>
)}
{statusType === "disabled" && (
<StyledCatalogSpamIcon className="disabled-badge" size="small" />
<StyledCatalogSpamIcon
className="disabled-badge accounts-badge"
size="small"
/>
)}
</StyledBadgesContainer>
);

View File

@ -123,6 +123,8 @@ const Table = ({
canChangeUserType,
isFiltered,
currentDeviceType,
typeAccountsColumnIsEnabled,
emailAccountsColumnIsEnabled,
}) => {
const ref = useRef(null);
const [hideColumns, setHideColumns] = React.useState(false);
@ -173,6 +175,9 @@ const Table = ({
canChangeUserType={canChangeUserType}
hideColumns={hideColumns}
itemIndex={index}
typeAccountsColumnIsEnabled={typeAccountsColumnIsEnabled}
emailAccountsColumnIsEnabled={emailAccountsColumnIsEnabled}
infoPanelVisible={infoPanelVisible}
/>
))}
</TableBody>
@ -189,6 +194,7 @@ export default inject(
accessRightsStore,
infoPanelStore,
userStore,
tableStore,
}) => {
const {
usersStore,
@ -205,6 +211,8 @@ export default inject(
const { isAdmin, isOwner, id: userId } = userStore.user;
const { canChangeUserType } = accessRightsStore;
const { typeAccountsColumnIsEnabled, emailAccountsColumnIsEnabled } =
tableStore;
return {
peopleList,
@ -224,6 +232,8 @@ export default inject(
canChangeUserType,
isFiltered,
currentDeviceType,
typeAccountsColumnIsEnabled,
emailAccountsColumnIsEnabled,
};
}
},
)(observer(Table));

View File

@ -28,7 +28,7 @@ class PeopleTableHeader extends React.Component {
{
key: "Type",
title: t("Common:Type"),
enable: true,
enable: props.typeAccountsColumnIsEnabled,
sortBy: "type",
resizable: true,
onChange: this.onColumnChange,
@ -44,7 +44,7 @@ class PeopleTableHeader extends React.Component {
{
key: "Mail",
title: t("Common:Email"),
enable: true,
enable: props.emailAccountsColumnIsEnabled,
resizable: true,
sortBy: "email",
onChange: this.onColumnChange,
@ -52,38 +52,19 @@ class PeopleTableHeader extends React.Component {
},
];
const columns = this.getColumns(defaultColumns);
const columns = props.getColumns(defaultColumns);
this.state = { columns };
}
getColumns = (defaultColumns) => {
const storageColumns = localStorage.getItem(
`${TABLE_COLUMNS}=${this.props.userId}`
);
const columns = [];
if (storageColumns) {
const splitColumns = storageColumns.split(",");
for (let col of defaultColumns) {
const column = splitColumns.find((key) => key === col.key);
column ? (col.enable = true) : (col.enable = false);
columns.push(col);
}
return columns;
} else {
return defaultColumns;
}
};
onColumnChange = (key, e) => {
const { columns } = this.state;
const columnIndex = columns.findIndex((c) => c.key === key);
if (columnIndex === -1) return;
this.props.setColumnEnable(key);
columns[columnIndex].enable = !columns[columnIndex].enable;
this.setState({ columns });
@ -179,6 +160,7 @@ export default inject(
infoPanelStore,
clientLoadingStore,
userStore,
tableStore
}) => {
const { filterStore } = peopleStore;
@ -186,14 +168,25 @@ export default inject(
const { isVisible: infoPanelVisible } = infoPanelStore;
const { withPaging } = settingsStore;
const {
getColumns,
setColumnEnable,
typeAccountsColumnIsEnabled,
emailAccountsColumnIsEnabled,
} = tableStore;
return {
filter,
setIsLoading: clientLoadingStore.setIsSectionBodyLoading,
userId: userStore.user?.id,
infoPanelVisible,
withPaging,
getColumns,
setColumnEnable,
typeAccountsColumnIsEnabled,
emailAccountsColumnIsEnabled,
};
}
)(

View File

@ -14,6 +14,7 @@ import withContent from "SRC_DIR/HOCs/withPeopleContent";
import Badges from "../Badges";
import { Base } from "@docspace/shared/themes";
import { getUserTypeLabel } from "@docspace/shared/utils/common";
const StyledWrapper = styled.div`
display: contents;
@ -229,6 +230,9 @@ const PeopleTableRow = (props) => {
hideColumns,
value,
standalone,
typeAccountsColumnIsEnabled,
emailAccountsColumnIsEnabled,
infoPanelVisible,
} = props;
const {
@ -308,7 +312,7 @@ const PeopleTableRow = (props) => {
setIsLoading(false);
}
},
[item, changeUserType]
[item, changeUserType],
);
// const getRoomsOptions = React.useCallback(() => {
@ -328,22 +332,7 @@ const PeopleTableRow = (props) => {
// return <>{options.map((option) => option)}</>;
// }, []);
const getUserTypeLabel = React.useCallback((role) => {
switch (role) {
case "owner":
return t("Common:Owner");
case "admin":
return t("Common:DocSpaceAdmin");
case "manager":
return t("Common:RoomAdmin");
case "collaborator":
return t("Common:PowerUser");
case "user":
return t("Common:User");
}
}, []);
const typeLabel = getUserTypeLabel(role);
const typeLabel = React.useCallback(() => getUserTypeLabel(role, t), [])();
const isChecked = checkedProps.checked;
@ -466,10 +455,19 @@ const PeopleTableRow = (props) => {
? displayName
: email}
</Link>
<Badges statusType={statusType} isPaid={isPaidUser} isSSO={isSSO} />
<Badges
statusType={statusType}
isPaid={isPaidUser}
isSSO={isSSO}
infoPanelVisible={infoPanelVisible}
/>
</TableCell>
<TableCell className={"table-cell_type"}>{typeCell}</TableCell>
{typeAccountsColumnIsEnabled ? (
<TableCell className={"table-cell_type"}>{typeCell}</TableCell>
) : (
<div />
)}
{/* <TableCell className="table-cell_room">
{!rooms?.length ? (
@ -512,25 +510,29 @@ const PeopleTableRow = (props) => {
)}
</TableCell> */}
<TableCell>
<Link
type="page"
title={email}
fontSize="13px"
fontWeight={600}
color={sideInfoColor}
onClick={onEmailClick}
isTextOverflow
enableUserSelect
>
{email}
</Link>
</TableCell>
{emailAccountsColumnIsEnabled ? (
<TableCell>
<Link
type="page"
title={email}
fontSize="13px"
fontWeight={600}
color={sideInfoColor}
onClick={onEmailClick}
isTextOverflow
enableUserSelect
>
{email}
</Link>
</TableCell>
) : (
<div />
)}
</StyledPeopleRow>
</StyledWrapper>
);
};
export default withTranslation(["People", "Common", "Settings"])(
withContent(PeopleTableRow)
withContent(PeopleTableRow),
);

View File

@ -422,6 +422,7 @@ class FilesTableHeader extends React.Component {
tagRef,
setHideColumns,
isFrame,
showSettings,
} = this.props;
const {
@ -452,7 +453,7 @@ class FilesTableHeader extends React.Component {
tagRef={tagRef}
setHideColumns={setHideColumns}
settingsTitle={t("Files:TableSettingsTitle")}
showSettings={!isFrame}
showSettings={isFrame ? showSettings : true}
/>
);
}
@ -573,6 +574,7 @@ export default inject(
isFrame,
frameTableColumns: frameConfig?.viewTableColumns,
showSettings: frameConfig?.showSettings,
};
}
)(

View File

@ -247,6 +247,8 @@ const SectionHeaderContent = (props) => {
moveToPublicRoom,
currentDeviceType,
isFrame,
showTitle,
hideInfoPanel,
onClickArchive,
setLeaveRoomDialogVisible,
inRoom,
@ -1126,7 +1128,7 @@ const SectionHeaderContent = (props) => {
onPlusClick={onCreateRoom}
isEmptyPage={isEmptyPage}
isRoom={isCurrentRoom || isAccountsPage}
hideInfoPanel={isSettingsPage || isPublicRoom}
hideInfoPanel={hideInfoPanel || isSettingsPage || isPublicRoom}
withLogo={isPublicRoom && logo}
burgerLogo={isPublicRoom && burgerLogo}
isPublicRoom={isPublicRoom}
@ -1136,6 +1138,7 @@ const SectionHeaderContent = (props) => {
showRootFolderTitle={insideTheRoom}
currentDeviceType={currentDeviceType}
isFrame={isFrame}
showTitle={isFrame ? showTitle : true}
navigationButtonLabel={navigationButtonLabel}
onNavigationButtonClick={onNavigationButtonClick}
tariffBar={<TariffBar />}
@ -1271,6 +1274,7 @@ export default inject(
enablePlugins,
theme,
whiteLabelLogoUrls,
frameConfig,
isFrame,
currentDeviceType,
} = settingsStore;
@ -1324,9 +1328,10 @@ export default inject(
folderPath = navigationPath.filter((item) => !item.isRootRoom);
}
const isRoot = isFrame
? pathParts?.length === 1 || pathParts?.length === 2
: pathParts?.length === 1;
const isRoot =
isFrame && frameConfig?.id
? pathParts?.length === 1 || pathParts?.length === 2
: pathParts?.length === 1;
const haveLinksRight =
access === ShareAccessRights.RoomManager ||
@ -1456,6 +1461,8 @@ export default inject(
theme,
whiteLabelLogoUrls,
isFrame,
showTitle: frameConfig?.showTitle,
hideInfoPanel: isFrame && !frameConfig?.infoPanelVisible,
currentDeviceType,
setLeaveRoomDialogVisible,
inRoom,

View File

@ -293,11 +293,7 @@ const PureHome = (props) => {
<SectionWrapper {...sectionProps}>
{(!isErrorRoomNotAvailable || isAccountsPage || isSettingsPage) && (
<Section.SectionHeader>
{isFrame ? (
showTitle && <SectionHeaderContent />
) : (
<SectionHeaderContent />
)}
<SectionHeaderContent />
</Section.SectionHeader>
)}

View File

@ -320,6 +320,6 @@ export default inject(
}
)(
withLoading(
withTranslation(["Settings", "Common"])(observer(ArticleBodyContent))
withTranslation(["Settings", "Common", "JavascriptSdk"])(observer(ArticleBodyContent))
)
);

View File

@ -283,7 +283,7 @@ const AdditionalResources = (props) => {
);
};
export default inject(({ filesSettingsStore, common, currentQuotaStore }) => {
export default inject(({ settingsStore, common, currentQuotaStore }) => {
const { setIsLoadedAdditionalResources, isLoadedAdditionalResources } =
common;
@ -292,7 +292,7 @@ export default inject(({ filesSettingsStore, common, currentQuotaStore }) => {
additionalResourcesData,
additionalResourcesIsDefault,
} = filesSettingsStore;
} = settingsStore;
const { isBrandingAndCustomizationAvailable } = currentQuotaStore;

View File

@ -66,6 +66,14 @@ const CompanyInfoSettings = (props) => {
const navigate = useNavigate();
const location = useLocation();
const defaultCompanySettingsData = {
address: companyInfoSettingsData.address,
companyName: companyInfoSettingsData.companyName,
email: companyInfoSettingsData.email,
phone: companyInfoSettingsData.phone,
site: companyInfoSettingsData.site,
};
const defaultCompanySettingsError = {
hasErrorAddress: false,
hasErrorCompanyName: false,
@ -74,9 +82,11 @@ const CompanyInfoSettings = (props) => {
hasErrorSite: false,
};
const [companySettings, setCompanySettings] = useState({});
const [companySettings, setCompanySettings] = useState(
defaultCompanySettingsData,
);
const [companySettingsError, setCompanySettingsError] = useState(
defaultCompanySettingsError
defaultCompanySettingsError,
);
const [showReminder, setShowReminder] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@ -114,18 +124,11 @@ const CompanyInfoSettings = (props) => {
setIsLoadedCompanyInfoSettingsData(true);
}, [companyInfoSettingsData, tReady]);
const getSettings = () => {
const getSettings = async () => {
await getCompanyInfoSettings();
const companySettings = getFromSessionStorage("companySettings");
const defaultData = {
address: companyInfoSettingsData?.address,
companyName: companyInfoSettingsData?.companyName,
email: companyInfoSettingsData?.email,
phone: companyInfoSettingsData?.phone,
site: companyInfoSettingsData?.site,
};
saveToSessionStorage("defaultCompanySettings", defaultData);
saveToSessionStorage("defaultCompanySettings", defaultCompanySettingsData);
if (companySettings) {
setCompanySettings({
@ -136,7 +139,7 @@ const CompanyInfoSettings = (props) => {
site: companySettings?.site,
});
} else {
setCompanySettings(defaultData);
setCompanySettings(defaultCompanySettingsData);
}
};
@ -146,7 +149,7 @@ const CompanyInfoSettings = (props) => {
useEffect(() => {
const defaultCompanySettings = getFromSessionStorage(
"defaultCompanySettings"
"defaultCompanySettings",
);
const newSettings = {
@ -182,7 +185,7 @@ const CompanyInfoSettings = (props) => {
const validateEmpty = (value, type) => {
const hasError = value.trim() === "";
const phoneRegex = /^[\d\(\)\-+]+$/;
const phoneRegex = /^[\d\(\)\-\s+]+$/;
const hasErrorPhone = !phoneRegex.test(value);
if (type === "companyName") {
@ -318,6 +321,13 @@ const CompanyInfoSettings = (props) => {
setShowModal(false);
};
const isDisabled =
hasErrorAddress ||
hasErrorCompanyName ||
hasErrorEmail ||
hasErrorPhone ||
hasErrorSite;
if (!isLoadedCompanyInfoSettingsData) return <LoaderCompanyInfoSettings />;
return (
@ -327,7 +337,7 @@ const CompanyInfoSettings = (props) => {
onClose={onCloseModal}
buildVersionInfo={buildVersionInfo}
personal={personal}
previewData={companySettings}
previewData={defaultCompanySettingsData}
/>
<StyledComponent isSettingPaid={isSettingPaid}>
@ -444,6 +454,7 @@ const CompanyInfoSettings = (props) => {
cancelButtonLabel={t("Common:Restore")}
reminderText={t("YouHaveUnsavedChanges")}
displaySettings={true}
saveButtonDisabled={isDisabled}
hasScroll={true}
hideBorder={true}
showReminder={(isSettingPaid && showReminder) || isLoading}
@ -488,6 +499,6 @@ export default inject(({ settingsStore, common, currentQuotaStore }) => {
};
})(
withLoading(
withTranslation(["Settings", "Common"])(observer(CompanyInfoSettings))
)
withTranslation(["Settings", "Common"])(observer(CompanyInfoSettings)),
),
);

View File

@ -148,7 +148,7 @@ const WhiteLabel = (props) => {
const onRestoreDefault = async () => {
try {
await restoreWhiteLabelSettings(true);
await restoreWhiteLabelSettings();
await onResetCompanyName();
toastr.success(t("Settings:SuccessfullySaveSettingsMessage"));
} catch (error) {

View File

@ -16,7 +16,7 @@ import MobileView from "./Branding/MobileView";
import { UnavailableStyles } from "../../utils/commonSettingsStyles";
import { resetSessionStorage } from "../../utils";
import { useIsMobileView } from "../../utils/useIsMobileView";
import { DeviceType } from "@docspace/shared/enums";
const StyledComponent = styled.div`
max-width: 700px;
@ -60,8 +60,9 @@ const Branding = ({
isLoadedCompanyInfoSettingsData,
isSettingPaid,
standalone,
currentDeviceType
}) => {
const isMobileView = useIsMobileView();
const isMobileView = currentDeviceType === DeviceType.mobile;
useEffect(() => {
setDocumentTitle(t("Branding"));
@ -104,11 +105,12 @@ const Branding = ({
export default inject(({ settingsStore, currentQuotaStore, common }) => {
const { isBrandingAndCustomizationAvailable } = currentQuotaStore;
const { isLoadedCompanyInfoSettingsData } = common;
const { standalone } = settingsStore;
const { standalone, currentDeviceType } = settingsStore;
return {
isLoadedCompanyInfoSettingsData,
isSettingPaid: isBrandingAndCustomizationAvailable,
standalone,
currentDeviceType
};
})(withLoading(withTranslation(["Settings", "Common"])(observer(Branding))));

View File

@ -23,6 +23,7 @@ const SubmenuCommon = (props) => {
isLoadedSubmenu,
getWhiteLabelLogoUrls,
currentDeviceType,
isMobileView,
} = props;
const navigate = useNavigate();
@ -41,7 +42,10 @@ const SubmenuCommon = (props) => {
}, [tReady, isLoadedSubmenu]);
const load = async () => {
await loadBaseInfo();
const currentTab = getCurrentTab();
await loadBaseInfo(
!isMobileView ? (currentTab === 0 ? "general" : "branding") : ""
);
};
const data = [
@ -111,13 +115,14 @@ export default inject(({ settingsStore, common }) => {
const isMobileView = currentDeviceType === DeviceType.mobile;
return {
loadBaseInfo: async () => {
await initSettings(!isMobileView ? "general" : "");
loadBaseInfo: async (page) => {
await initSettings(page);
},
isLoaded,
setIsLoadedSubmenu,
isLoadedSubmenu,
getWhiteLabelLogoUrls,
currentDeviceType,
isMobileView,
};
})(withLoading(withTranslation("Settings")(observer(SubmenuCommon))));

View File

@ -49,7 +49,7 @@ const PortalDeletion = (props) => {
try {
await sendDeletePortalEmail();
toastr.success(
t("PortalDeletionEmailSended", { ownerEmail: owner.email })
t("PortalDeletionEmailSended", { ownerEmail: owner.email }),
);
} catch (error) {
toastr.error(error);
@ -62,7 +62,7 @@ const PortalDeletion = (props) => {
};
const notActivatedEmail =
owner.activationStatus === EmployeeActivationStatus.NotActivated;
owner?.activationStatus === EmployeeActivationStatus.NotActivated;
return (
<MainContainer>
@ -115,5 +115,5 @@ export default inject(({ settingsStore, userStore }) => {
sendActivationLink,
};
})(
withTranslation(["Settings", "MainBar", "People", "Common"])(PortalDeletion)
withTranslation(["Settings", "MainBar", "People", "Common"])(PortalDeletion),
);

View File

@ -1,29 +1,32 @@
import { useState, useEffect } from "react";
import React from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import styled, { css } from "styled-components";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import FilesSelectorInput from "SRC_DIR/components/FilesSelectorInput";
import { mobile, tablet } from "@docspace/shared/utils";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { useNavigate } from "react-router-dom";
import { RoomsType } from "@docspace/shared/constants";
import { inject, observer } from "mobx-react";
import { mobile, tablet } from "@docspace/shared/utils/device";
import { isMobile } from "react-device-detect";
import { HelpButton } from "@docspace/shared/components/help-button";
import { Box } from "@docspace/shared/components/box";
import { Link } from "@docspace/shared/components/link";
import { Text } from "@docspace/shared/components/text";
import GetCodeDialog from "./sub-components/GetCodeDialog";
import CSP from "./sub-components/csp";
import { Button } from "@docspace/shared/components/button";
import PresetTile from "./sub-components/PresetTile";
const showPreviewThreshold = 720;
import SimpleRoomImg from "PUBLIC_DIR/images/sdk-presets_simple-room.react.svg?url";
import ManagerImg from "PUBLIC_DIR/images/sdk-presets_manager.react.svg?url";
import RoomSelectorImg from "PUBLIC_DIR/images/sdk-presets_room-selector.react.svg?url";
import FileSelectorImg from "PUBLIC_DIR/images/sdk-presets_file-selector.react.svg?url";
import EditorImg from "PUBLIC_DIR/images/sdk-presets_editor.react.svg?url";
import ViewerImg from "PUBLIC_DIR/images/sdk-presets_viewer.react.svg?url";
import SimpleRoomImgDark from "PUBLIC_DIR/images/sdk-presets_simple-room_dark.react.svg?url";
import ManagerImgDark from "PUBLIC_DIR/images/sdk-presets_manager_dark.react.svg?url";
import RoomSelectorImgDark from "PUBLIC_DIR/images/sdk-presets_room-selector_dark.react.svg?url";
import FileSelectorImgDark from "PUBLIC_DIR/images/sdk-presets_file-selector_dark.react.svg?url";
import EditorImgDark from "PUBLIC_DIR/images/sdk-presets_editor_dark.react.svg?url";
import ViewerImgDark from "PUBLIC_DIR/images/sdk-presets_viewer_dark.react.svg?url";
const SDKContainer = styled(Box)`
@media ${tablet} {
@ -36,35 +39,9 @@ const SDKContainer = styled(Box)`
`}
`;
const Controls = styled(Box)`
max-width: 350px;
min-width: 350px;
width: 100%;
display: flex;
flex-direction: column;
gap: 16px;
@media ${tablet} {
min-width: 0;
}
${isMobile &&
css`
min-width: 0;
`}
.label {
min-width: fit-content;
}
.checkbox {
max-width: fit-content;
}
`;
const CategoryHeader = styled.div`
margin-top: 40px;
margin-bottom: 24px;
margin-bottom: 16px;
font-size: ${(props) => props.theme.getCorrectFontSize("16px")};
font-style: normal;
font-weight: 700;
@ -80,448 +57,81 @@ const CategoryHeader = styled.div`
`}
`;
const CategorySubHeader = styled.div`
margin-top: 8px;
margin-bottom: 8px;
font-size: ${(props) => props.theme.getCorrectFontSize("15px")};
font-style: normal;
font-weight: 600;
line-height: 16px;
@media ${tablet} {
&:not(&.copy-window-code) {
margin-bottom: 0;
}
}
${isMobile &&
css`
&:not(&.copy-window-code) {
margin-bottom: 0;
}
`}
@media ${mobile} {
&:first-of-type {
margin-top: 0;
}
}
`;
const CategoryDescription = styled(Box)`
margin-top: 5px;
margin-top: 2px;
max-width: 700px;
.sdk-description {
display: inline;
line-height: 20px;
color: ${(props) => props.theme.client.settings.common.descriptionColor};
}
`;
const ControlsGroup = styled(Box)`
display: flex;
flex-direction: column;
gap: 8px;
@media ${tablet} {
gap: 4px;
}
${isMobile &&
css`
gap: 4px;
`}
`;
const LabelGroup = styled(Box)`
display: inline-flex;
align-items: center;
gap: 4px;
`;
const InterfaceElements = styled(Box)`
display: flex;
flex-direction: column;
const PresetsContainer = styled.div`
display: grid;
grid-template-columns: repeat(2, minmax(min(200px, 100%), 1fr));
gap: 16px;
margin-top: 24px;
`;
const Frame = styled(Box)`
max-width: fit-content;
margin-top: 16px;
position: relative;
border-radius: 6px;
border: 1px solid #d0d5da;
width: ${(props) => (props.width ? props.width : "100%")};
height: calc(${(props) => (props.height ? props.height : "400px")} + 2px);
@media ${tablet} {
margin-top: 4px;
}
${(props) =>
props.targetId &&
`
#${props.targetId} {
border-radius: 6px;
}
`}
${isMobile &&
css`
margin-top: 4px;
`}
`;
const Container = styled(Box)`
width: 100%;
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
gap: 16px;
@media ${tablet} {
flex-direction: column;
}
${isMobile &&
css`
flex-direction: column;
`}
`;
const RowContainer = styled(Box)`
flex-direction: row;
display: flex;
gap: 8px;
${(props) =>
props.combo &&
`
height: 32px;
align-items: center;
`}
`;
const ColumnContainer = styled(Box)`
flex-direction: column;
display: flex;
gap: 8px;
`;
const Preview = styled(Box)`
width: 100%;
margin-top: 24px;
min-width: 660px;
flex-direction: row;
@media ${tablet} {
margin-top: 0;
min-width: 0;
}
${isMobile &&
css`
margin-top: 0;
min-width: 0;
`}
`;
const GetCodeButtonWrapper = styled.div`
padding-block: 30px;
position: sticky;
bottom: 0;
margin-top: 32px;
background-color: ${({ theme }) => theme.backgroundColor};
@media ${mobile} {
position: fixed;
padding-inline: 16px;
inset-inline: 0;
}
`;
const FilesSelectorInputWrapper = styled.div`
& > div {
margin: 0;
display: flex;
flex-direction: column;
}
`;
const PortalIntegration = (props) => {
const {
t,
setDocumentTitle,
currentColorScheme,
sdkLink,
fetchExternalLinks,
} = props;
const { t, setDocumentTitle, currentColorScheme, sdkLink, theme } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const navigate = useNavigate();
const dataSortBy = [
{ key: "DateAndTime", label: t("Common:LastModifiedDate"), default: true },
{ key: "AZ", label: t("Common:Title") },
{ key: "Type", label: t("Common:Type") },
{ key: "Size", label: t("Common:Size") },
{ key: "DateAndTimeCreation", label: t("Files:ByCreation") },
{ key: "Author", label: t("Files:ByAuthor") },
];
const navigateToSimpleRoom = () => navigate("room");
const navigateToManager = () => navigate("manager");
const navigateToRoomSelector = () => navigate("room-selector");
const navigateToFileSelector = () => navigate("file-selector");
const navigateToEditor = () => navigate("editor");
const navigateToViewer = () => navigate("viewer");
const dataSortOrder = [
{ key: "descending", label: t("Descending"), default: true },
{ key: "ascending", label: t("Ascending") },
];
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const [sortBy, setSortBy] = useState(dataSortBy[0]);
const [sortOrder, setSortOrder] = useState(dataSortOrder[0]);
const [widthDimension, setWidthDimension] = useState(dataDimensions[0]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[1]);
const [width, setWidth] = useState("100");
const [height, setHeight] = useState("600");
const [withSubfolders, setWithSubfolders] = useState(false);
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(
window.innerWidth > showPreviewThreshold
);
const [sharedLinks, setSharedLinks] = useState(null);
const [config, setConfig] = useState({
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
showHeader: true,
showTitle: true,
showMenu: true,
showFilter: true,
init: true,
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () =>
window.DocSpace.SDK.initFrame(config)
);
}, 500);
useEffect(() => {
loadFrame();
return () => destroyFrame();
});
const onChangeTab = () => {
loadFrame();
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFolderId = async (id, publicInPath) => {
let newConfig = { id, requestToken: null, rootPath: "/rooms/shared/" };
if (!!publicInPath) {
const links = await fetchExternalLinks(publicInPath.id);
if (links.length > 1) {
const linksOptions = links.map((link) => {
const { id, title, requestToken } = link.sharedTo;
return {
key: id,
label: title,
requestToken: requestToken,
};
});
setSharedLinks(linksOptions);
}
newConfig.requestToken = links[0].sharedTo?.requestToken;
newConfig.rootPath = "/rooms/share";
} else {
setSharedLinks(null);
}
setConfig((config) => {
return { ...config, ...newConfig };
});
};
const onChangeSharedLink = (link) => {
setConfig((config) => {
return { ...config, requestToken: link.requestToken };
});
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value };
});
};
const onChangeWithSubfolders = (e) => {
setConfig((config) => {
return { ...config, withSubfolders: !withSubfolders };
});
setWithSubfolders(!withSubfolders);
};
const onChangeSortBy = (item) => {
setConfig((config) => {
return { ...config, sortby: item.key };
});
setSortBy(item);
};
const onChangeSortOrder = (item) => {
setConfig((config) => {
return { ...config, sortorder: item.key };
});
setSortOrder(item);
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const onChangeShowHeader = (e) => {
setConfig((config) => {
return { ...config, showHeader: !config.showHeader };
});
};
const onChangeShowTitle = () => {
setConfig((config) => {
return { ...config, showTitle: !config.showTitle };
});
};
const onChangeShowMenu = (e) => {
setConfig((config) => {
return { ...config, showMenu: !config.showMenu };
});
};
const onChangeShowFilter = (e) => {
setConfig((config) => {
return { ...config, showFilter: !config.showFilter };
});
};
const onChangeCount = (e) => {
setConfig((config) => {
return { ...config, count: e.target.value };
});
};
const onChangePage = (e) => {
setConfig((config) => {
return { ...config, page: e.target.value };
});
};
const onChangeSearch = (e) => {
setConfig((config) => {
return { ...config, search: e.target.value };
});
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview)
setShowPreview(isEnoughWidthForPreview);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
<Box id={frameId}></Box>
</Frame>
);
const code = (
<>
<CategorySubHeader className="copy-window-code">
{t("CopyWindowCode")}
</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea="153px" />
</>
);
const dataTabs = [
const presetsData = [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
title: t("Common:Room"),
description: t("SimpleRoomDescription"),
image: theme.isBase ? SimpleRoomImg : SimpleRoomImgDark,
handleOnClick: navigateToSimpleRoom,
},
{
key: "code",
title: t("Code"),
content: code,
title: t("Manager"),
description: t("ManagerDescription"),
image: theme.isBase ? ManagerImg : ManagerImgDark,
handleOnClick: navigateToManager,
},
{
title: t("Editor"),
description: t("EditorDescription"),
image: theme.isBase ? EditorImg : EditorImgDark,
handleOnClick: navigateToEditor,
},
{
title: t("Viewer"),
description: t("ViewerDescription"),
image: theme.isBase ? ViewerImg : ViewerImgDark,
handleOnClick: navigateToViewer,
},
{
title: t("RoomSelector"),
description: t("RoomSelectorDescription"),
image: theme.isBase ? RoomSelectorImg : RoomSelectorImgDark,
handleOnClick: navigateToRoomSelector,
},
{
title: t("FileSelector"),
description: t("FileSelectorDescription"),
image: theme.isBase ? FileSelectorImg : FileSelectorImgDark,
handleOnClick: navigateToFileSelector,
},
];
@ -531,7 +141,7 @@ const PortalIntegration = (props) => {
<Text className="sdk-description">{t("SDKDescription")}</Text>
<Link
color={currentColorScheme?.main?.accent}
fontSize="12px"
fontSize="13px"
fontWeight="400"
onClick={() => window.open(sdkLink, "_blank")}
>
@ -540,233 +150,21 @@ const PortalIntegration = (props) => {
<CSP t={t} />
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer onSelect={onChangeTab} elements={dataTabs} />
</Preview>
)}
<Controls>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={2}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={3}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={4}
/>
</ControlsGroup>
<InterfaceElements>
<Label className="label">{t("InterfaceElements")}</Label>
<Checkbox
className="checkbox"
label={t("Menu")}
onChange={onChangeShowMenu}
isChecked={config.showMenu}
/>
<Checkbox
className="checkbox"
label={t("Header")}
onChange={onChangeShowHeader}
isChecked={config.showHeader}
/>
<Checkbox
className="checkbox"
label={t("Filter")}
onChange={onChangeShowFilter}
isChecked={config.showFilter}
/>
<RowContainer>
<Checkbox
label={t("Title")}
onChange={onChangeShowTitle}
isChecked={config.showTitle}
/>
<Text color="gray">{`(${t("MobileOnly")})`}</Text>
</RowContainer>
</InterfaceElements>
<CategorySubHeader>{t("DataDisplay")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("RoomOrFolder")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={
<Text fontSize="12px">{t("RoomOrFolderDescription")}</Text>
}
/>
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput onSelectFolder={onChangeFolderId} isSelect />
</FilesSelectorInputWrapper>
</ControlsGroup>
{sharedLinks && (
<ControlsGroup>
<LabelGroup>
<Label
className="label"
text={t("SharingPanel:ExternalLink")}
/>
<HelpButton
offsetRight={0}
size={12}
tooltipContent={
<Text fontSize="12px">
{t("CreateEditRoomDialog:PublicRoomDescription")}
</Text>
}
/>
</LabelGroup>
<ComboBox
scaled={true}
onSelect={onChangeSharedLink}
options={sharedLinks}
selectedOption={sharedLinks[0]}
displaySelectedOption
directionY="bottom"
/>
</ControlsGroup>
)}
<CategorySubHeader>{t("AdvancedDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("SearchTerm")} />
<ColumnContainer>
<TextInput
scale={true}
onChange={onChangeSearch}
placeholder={t("Common:Search")}
value={config.search}
tabIndex={5}
/>
<Checkbox
className="checkbox"
label={t("Files:WithSubfolders")}
onChange={onChangeWithSubfolders}
isChecked={withSubfolders}
/>
</ColumnContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("Common:SortBy")} />
<ComboBox
onSelect={onChangeSortBy}
options={dataSortBy}
scaled={true}
selectedOption={sortBy}
displaySelectedOption
directionY="top"
/>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("SortOrder")} />
<ComboBox
onSelect={onChangeSortOrder}
options={dataSortOrder}
scaled={true}
selectedOption={sortOrder}
displaySelectedOption
directionY="top"
/>
</ControlsGroup>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("ItemsCount")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={
<Text fontSize="12px">{t("ItemsCountDescription")}</Text>
}
/>
</LabelGroup>
<TextInput
scale={true}
onChange={onChangeCount}
placeholder={t("EnterCount")}
value={config.count}
tabIndex={6}
/>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("Page")} />
<TextInput
scale={true}
onChange={onChangePage}
placeholder={t("EnterPage")}
value={config.page}
isDisabled={!config.count}
tabIndex={7}
/>
</ControlsGroup>
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
<Text lineHeight="20px" color={theme.sdkPresets.secondaryColor}>
{t("InitializeSDK")}
</Text>
<PresetsContainer>
{presetsData.map((data) => (
<PresetTile
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
key={data.title}
title={data.title}
description={data.description}
image={data.image}
handleOnClick={data.handleOnClick}
/>
</>
)}
))}
</PresetsContainer>
</SDKContainer>
);
};
@ -774,14 +172,12 @@ const PortalIntegration = (props) => {
export default inject(({ settingsStore, authStore, publicRoomStore }) => {
const { setDocumentTitle } = authStore;
const { theme, currentColorScheme, sdkLink } = settingsStore;
const { fetchExternalLinks } = publicRoomStore;
return {
theme,
setDocumentTitle,
currentColorScheme,
sdkLink,
fetchExternalLinks,
};
})(
withTranslation([
@ -791,5 +187,5 @@ export default inject(({ settingsStore, authStore, publicRoomStore }) => {
"CreateEditRoomDialog",
"SharingPanel",
"Common",
])(observer(PortalIntegration))
])(observer(PortalIntegration)),
);

View File

@ -0,0 +1,350 @@
import { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import FilesSelectorInput from "SRC_DIR/components/FilesSelectorInput";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { inject, observer } from "mobx-react";
import { FilesSelectorFilterTypes } from "@docspace/shared/enums";
import { isTablet, isMobile } from "@docspace/shared/utils/device";
import EmptyIframeContainer from "../sub-components/EmptyIframeContainer";
import GetCodeDialog from "../sub-components/GetCodeDialog";
import { Button } from "@docspace/shared/components/button";
const showPreviewThreshold = 720;
import {
SDKContainer,
Controls,
CategoryHeader,
CategorySubHeader,
CategoryDescription,
ControlsGroup,
LabelGroup,
ControlsSection,
Frame,
Container,
RowContainer,
Preview,
GetCodeButtonWrapper,
FilesSelectorInputWrapper,
CodeWrapper,
} from "./StyledPresets";
const Editor = (props) => {
const { t, setDocumentTitle } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const [widthDimension, setWidthDimension] = useState(dataDimensions[0]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[0]);
const [width, setWidth] = useState("100");
const [height, setHeight] = useState("100");
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(window.innerWidth > showPreviewThreshold);
const [config, setConfig] = useState({
mode: "editor",
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
init: false,
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () => window.DocSpace.SDK.initFrame(config));
}, 500);
useEffect(() => {
loadFrame();
return () => destroyFrame();
});
const onChangeTab = () => {
loadFrame();
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFileId = (file) => {
setConfig((config) => {
return { ...config, id: file.id, init: true };
});
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value };
});
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview) setShowPreview(isEnoughWidthForPreview);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
{config.id !== undefined ? (
<>
<Box id={frameId}></Box>
</>
) : (
<EmptyIframeContainer
text={t("SelectFile")}
width={width + widthDimension.label}
height={height + heightDimension.label}
/>
)}
</Frame>
);
const code = (
<CodeWrapper>
<CategorySubHeader className="copy-window-code">{t("CopyWindowCode")}</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
</CodeWrapper>
);
const dataTabs = [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
];
return (
<SDKContainer>
<CategoryDescription>
<Text className="sdk-description">{t("EditorPresetDescription")}</Text>
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer
isDisabled={config?.id === undefined}
onSelect={onChangeTab}
elements={dataTabs}
/>
</Preview>
)}
<Controls>
<ControlsSection>
<CategorySubHeader>{t("FileId")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("Common:SelectFile")} />
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput
onSelectFile={onChangeFileId}
filterParam={FilesSelectorFilterTypes.ALL}
isSelect
/>
</FilesSelectorInputWrapper>
</ControlsGroup>
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={2}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={3}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={4}
/>
</ControlsGroup>
</ControlsSection>
{/* <InterfaceElements>
<Label className="label">{t("InterfaceElements")}</Label>
<Checkbox
className="checkbox"
label={t("RightPanelCollapsed")}
onChange={() => {}}
isChecked={true}
/>
<Checkbox
className="checkbox"
label={t("TabPlugins")}
onChange={() => {}}
isChecked={true}
/>
<RowContainer>
<Checkbox label={t("Chat")} onChange={() => {}} isChecked={true} />
<Text color="gray">({t("InLeftPanel")})</Text>
</RowContainer>
<RowContainer>
<Checkbox label={t("FeedbackAndSupport")} onChange={() => {}} isChecked={true} />
<Text color="gray">({t("InLeftPanel")})</Text>
</RowContainer>
</InterfaceElements> */}
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
/>
</>
)}
</SDKContainer>
);
};
export default inject(({ authStore, settingsStore }) => {
const { setDocumentTitle } = authStore;
const { theme } = settingsStore;
return {
theme,
setDocumentTitle,
};
})(withTranslation(["JavascriptSdk", "Files", "EmbeddingPanel", "Common"])(observer(Editor)));

View File

@ -0,0 +1,693 @@
import { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import FilesSelectorInput from "SRC_DIR/components/FilesSelectorInput";
import { RadioButtonGroup } from "@docspace/shared/components/radio-button-group";
import { SelectedItem } from "@docspace/shared/components/selected-item";
import { ColorInput } from "@docspace/shared/components/color-input";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { inject, observer } from "mobx-react";
import { isTablet, isMobile } from "@docspace/shared/utils/device";
import { HelpButton } from "@docspace/shared/components/help-button";
import { Button } from "@docspace/shared/components/button";
import GetCodeDialog from "../sub-components/GetCodeDialog";
import { FilesSelectorFilterTypes } from "@docspace/shared/enums";
import { TooltipContent } from "../sub-components/TooltipContent";
import SubtitleUrl from "PUBLIC_DIR/images/sdk-presets_subtitle.react.svg?url";
import SearchUrl from "PUBLIC_DIR/images/sdk-presets_files-search.react.svg?url";
import { toastr } from "@docspace/shared/components/toast";
const showPreviewThreshold = 720;
import {
SDKContainer,
Controls,
CategoryHeader,
CategorySubHeader,
CategoryDescription,
ControlsGroup,
LabelGroup,
ControlsSection,
Frame,
Container,
RowContainer,
Preview,
GetCodeButtonWrapper,
FilesSelectorInputWrapper,
SelectedItemsContainer,
CodeWrapper,
} from "./StyledPresets";
const FileSelector = (props) => {
const { t, setDocumentTitle, fetchExternalLinks } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const elementDisplayOptions = [
{ value: "element", label: t("ElementItself") },
{
value: "button",
label: (
<RowContainer>
{t("Common:Button")}
<Text color="gray">{`(${t("ElementCalledAfterClicking")})`}</Text>
</RowContainer>
),
},
];
const fileTypeDisplay = [
{ value: FilesSelectorFilterTypes.ALL, label: t("AllTypes") },
{ value: "custom-types", label: t("SelectTypes") },
];
const [fileOptions, setFileOptions] = useState([
{ key: FilesSelectorFilterTypes.DOCX, label: FilesSelectorFilterTypes.DOCX },
{ key: FilesSelectorFilterTypes.IMG, label: FilesSelectorFilterTypes.IMG },
{ key: FilesSelectorFilterTypes.BackupOnly, label: FilesSelectorFilterTypes.BackupOnly },
{ key: FilesSelectorFilterTypes.DOCXF, label: FilesSelectorFilterTypes.DOCXF },
{ key: FilesSelectorFilterTypes.XLSX, label: FilesSelectorFilterTypes.XLSX },
]);
const [widthDimension, setWidthDimension] = useState(dataDimensions[1]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[0]);
const [width, setWidth] = useState("600");
const [height, setHeight] = useState("100");
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(window.innerWidth > showPreviewThreshold);
const [sharedLinks, setSharedLinks] = useState(null);
const [selectedElementType, setSelectedElementType] = useState(elementDisplayOptions[1].value);
const [typeDisplay, setTypeDisplay] = useState(fileTypeDisplay[0].value);
const [selectedType, setSelectedType] = useState(fileOptions[0]);
const [selectedFileTypes, setSelectedFileTypes] = useState([
{ key: "file-type-documents", label: t("Common:Documents") },
{ key: "file-type-folders", label: t("Translations:Folders") },
{ key: "file-type-spreadsheets", label: t("Translations:Spreadsheets") },
{ key: "file-type-archives", label: t("Files:Archives") },
{ key: "file-type-presentations", label: t("Translations:Presentations") },
{ key: "file-type-images", label: t("Filse:Images") },
{ key: "file-type-media", label: t("Files:Media") },
{ key: "file-type-forms-templates", label: t("Files:FormsTemplates") },
{ key: "file-type-forms", label: t("Files:Forms") },
]);
const [config, setConfig] = useState({
mode: "file-selector",
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
init: true,
showSelectorCancel: true,
showSelectorHeader: true,
withSearch: true,
acceptButtonLabel: t("Common:SelectAction"),
cancelButtonLabel: t("Common:CancelButton"),
// withBreadCrumbs: true,
withSubtitle: true,
filterParam: FilesSelectorFilterTypes.ALL,
isButtonMode: false,
buttonWithLogo: true,
events: {
onSelectCallback: (items) => {
toastr.success(items[0].label);
},
onCloseCallback: null,
onAppReady: null,
onAppError: (e) => console.log("onAppError", e),
onEditorCloseCallback: null,
onAuthSuccess: null,
onSignOut: null,
},
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () => window.DocSpace.SDK.initFrame(config));
}, 500);
useEffect(() => {
loadFrame();
return () => destroyFrame();
});
const toggleButtonMode = (e) => {
setSelectedElementType(e.target.value);
setConfig((config) => ({ ...config, isButtonMode: e.target.value === "button" }));
};
const onChangeTab = (tab) => {
if (tab.key === "preview" && selectedElementType === "button") {
setConfig((config) => ({ ...config, isButtonMode: true }));
} else if (tab.key === "selector-preview") {
setConfig((config) => ({ ...config, isButtonMode: false }));
} else if (tab.key === "code") {
setConfig((config) => ({ ...config, isButtonMode: selectedElementType === "button" }));
}
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFolderId = async (id, publicInPath) => {
let newConfig = { id, requestToken: null, rootPath: "/rooms/shared/" };
if (!!publicInPath) {
const links = await fetchExternalLinks(publicInPath.id);
if (links.length > 1) {
const linksOptions = links.map((link) => {
const { id, title, requestToken } = link.sharedTo;
return {
key: id,
label: title,
requestToken: requestToken,
};
});
setSharedLinks(linksOptions);
}
newConfig.requestToken = links[0].sharedTo?.requestToken;
newConfig.rootPath = "/rooms/share";
} else {
setSharedLinks(null);
}
setConfig((config) => {
return { ...config, ...newConfig };
});
};
const onChangeSharedLink = (link) => {
setConfig((config) => {
return { ...config, requestToken: link.requestToken };
});
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value };
});
};
const changeColumnsOption = (e) => {
setTypeDisplay(e.target.value);
setConfig((config) => {
return {
...config,
filterParam:
e.target.value === FilesSelectorFilterTypes.ALL
? FilesSelectorFilterTypes.ALL
: selectedType,
};
});
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onTypeSelect = (option) => {
// setFileOptions((prevFileOptions) => prevFileOptions.filter((file) => file.key !== option.key));
setSelectedType(option);
setConfig((config) => {
return { ...config, filterParam: option.key };
});
// if (!selectedFileTypes.find((type) => type.key === option.key)) {
// setSelectedFileTypes((prevFileTypes) => [...prevFileTypes, option]);
// }
};
const deleteSelectedType = (option) => {
setFileOptions((prevFileOptions) => [option, ...prevFileOptions]);
const filteredTypes = selectedFileTypes.filter((type) => type.key !== option.key);
setSelectedFileTypes(filteredTypes);
};
const toggleWithSearch = () => {
setConfig((config) => ({ ...config, withSearch: !config.withSearch }));
};
// const toggleBreadCrumbs = () => {
// setConfig((config) => ({ ...config, withBreadCrumbs: !config.withBreadCrumbs }));
// };
const toggleWithSubtitle = () => {
setConfig((config) => ({ ...config, withSubtitle: !config.withSubtitle }));
};
const onChangeAcceptLabel = (e) => {
setConfig((config) => {
return { ...config, acceptButtonLabel: e.target.value };
});
};
const onChangeCancelLabel = (e) => {
setConfig((config) => {
return { ...config, cancelButtonLabel: e.target.value };
});
};
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview) setShowPreview(isEnoughWidthForPreview);
};
const setButtonColor = (color) => {
setConfig((config) => ({ ...config, buttonColor: color }));
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
<Box id={frameId}></Box>
</Frame>
);
const code = (
<CodeWrapper width={width + widthDimension.label} height={height + heightDimension.label}>
<CategorySubHeader className="copy-window-code">{t("CopyWindowCode")}</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
</CodeWrapper>
);
const dataTabs =
selectedElementType === "element"
? [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
]
: [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "selector-preview",
title: t("SelectorPreview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
];
return (
<SDKContainer>
<CategoryDescription>
<Text className="sdk-description">{t("FileSelectorPresetDescription")}</Text>
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer onSelect={onChangeTab} elements={dataTabs} />
</Preview>
)}
<Controls>
<ControlsSection>
<CategorySubHeader>{t("MainElementParameter")}</CategorySubHeader>
<RadioButtonGroup
orientation="vertical"
options={elementDisplayOptions}
name="elementDisplayInput"
selected={selectedElementType}
onClick={toggleButtonMode}
spacing="8px"
/>
{config.isButtonMode && (
<>
<CategorySubHeader>{t("ButtonCustomization")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("ButtonColor")} />
<ColorInput scale handleChange={setButtonColor} defaultColor={"#5299E0"} />
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("ButtonText")} />
<TextInput
scale
onChange={(e) => {
setConfig((config) => ({ ...config, buttonText: e.target.value }));
}}
placeholder={t("SelectToDocSpace")}
value={config.buttonText}
tabIndex={3}
/>
<Checkbox
className="checkbox"
label={"Logo"}
onChange={() => {
setConfig((config) => ({
...config,
buttonWithLogo: !config.buttonWithLogo,
}));
}}
isChecked={config.buttonWithLogo}
/>
</ControlsGroup>
</>
)}
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={2}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={3}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={4}
/>
</ControlsGroup>
</ControlsSection>
<ControlsSection>
<Label className="label">{t("InterfaceElements")}</Label>
{/* <Checkbox
className="checkbox"
label={t("Common:Title")}
onChange={toggleBreadCrumbs}
isChecked={config.withBreadCrumbs}
/> */}
<LabelGroup>
<Checkbox
className="checkbox"
label={t("Subtitle")}
onChange={toggleWithSubtitle}
isChecked={config.withSubtitle}
/>
<HelpButton
place="right"
offsetRight={4}
size={12}
tooltipContent={
<TooltipContent
title={t("Subtitle")}
description={t("SubtitleDescription")}
img={SubtitleUrl}
/>
}
/>
</LabelGroup>
<LabelGroup>
<Checkbox
className="checkbox"
label={t("Common:Search")}
onChange={toggleWithSearch}
isChecked={config.withSearch}
/>
<HelpButton
place="right"
offsetRight={4}
size={12}
tooltipContent={
<TooltipContent
title={t("Common:Search")}
description={t("FilesSearchDescription")}
img={SearchUrl}
/>
}
/>
</LabelGroup>
<Label className="label" text={t("SelectButtonText")} />
<TextInput
scale={true}
onChange={onChangeAcceptLabel}
placeholder={t("Common:SelectAction")}
value={config.acceptButtonLabel}
tabIndex={4}
/>
<Label className="label" text={t("CancelButtonText")} />
<TextInput
scale={true}
onChange={onChangeCancelLabel}
placeholder={t("Common:CancelButton")}
value={config.cancelButtonLabel}
tabIndex={4}
/>
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("DataDisplay")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("RoomOrFolder")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={<Text fontSize="12px">{t("RoomOrFolderDescription")}</Text>}
/>
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput onSelectFolder={onChangeFolderId} isSelect />
</FilesSelectorInputWrapper>
</ControlsGroup>
{sharedLinks && (
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("SharingPanel:ExternalLink")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={
<Text fontSize="12px">{t("CreateEditRoomDialog:PublicRoomDescription")}</Text>
}
/>
</LabelGroup>
<ComboBox
scaled={true}
onSelect={onChangeSharedLink}
options={sharedLinks}
selectedOption={sharedLinks[0]}
displaySelectedOption
directionY="bottom"
/>
</ControlsGroup>
)}
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("AdvancedDisplay")}</CategorySubHeader>
<Label className="label" text={t("FileTypeDisplay")} />
<RadioButtonGroup
orientation="vertical"
options={fileTypeDisplay}
name="columnsDisplayOptions"
selected={typeDisplay}
onClick={changeColumnsOption}
spacing="8px"
/>
{typeDisplay === "custom-types" && (
<>
<ComboBox
onSelect={onTypeSelect}
options={
fileOptions || {
key: "Select",
label: t("Common:SelectAction"),
}
}
scaled={true}
directionY="top"
selectedOption={selectedType}
// selectedOption={{
// key: "Select",
// label: t("Common:SelectAction"),
// }}
/>
{/* <SelectedItemsContainer>
{selectedFileTypes.map((type) => (
<SelectedItem
key={type.key}
onClick={() => deleteSelectedType(type)}
label={type.label}
/>
))}
</SelectedItemsContainer> */}
</>
)}
</ControlsSection>
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
/>
</>
)}
</SDKContainer>
);
};
export default inject(({ authStore, settingsStore, publicRoomStore }) => {
const { setDocumentTitle } = authStore;
const { theme } = settingsStore;
const { fetchExternalLinks } = publicRoomStore;
return {
theme,
setDocumentTitle,
fetchExternalLinks,
};
})(
withTranslation([
"JavascriptSdk",
"Files",
"EmbeddingPanel",
"Common",
"Translations",
"SharingPanel",
])(observer(FileSelector)),
);

View File

@ -0,0 +1,483 @@
import { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import { RadioButtonGroup } from "@docspace/shared/components/radio-button-group";
import { ColorInput } from "@docspace/shared/components/color-input";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { inject, observer } from "mobx-react";
import { isTablet, isMobile } from "@docspace/shared/utils/device";
import GetCodeDialog from "../sub-components/GetCodeDialog";
import { Button } from "@docspace/shared/components/button";
import { RoomsType } from "@docspace/shared/enums";
import { toastr } from "@docspace/shared/components/toast";
const showPreviewThreshold = 720;
import {
SDKContainer,
Controls,
CategoryHeader,
CategorySubHeader,
CategoryDescription,
ControlsGroup,
ControlsSection,
Frame,
Container,
RowContainer,
Preview,
GetCodeButtonWrapper,
CodeWrapper,
} from "./StyledPresets";
const RoomSelector = (props) => {
const { t, setDocumentTitle } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const elementDisplayOptions = [
{ value: "element", label: t("ElementItself") },
{
value: "button",
label: (
<RowContainer>
{t("Common:Button")}
<Text color="gray">{`(${t("ElementCalledAfterClicking")})`}</Text>
</RowContainer>
),
},
];
const roomTypeOptions = [
{ key: "room-type-all", label: t("AllTypes"), roomType: undefined, default: true },
{
key: "room-type-collaboration",
label: t("CreateEditRoomDialog:CollaborationRoomTitle"),
roomType: RoomsType.EditingRoom,
},
{ key: "room-type-public", label: t("Files:PublicRoom"), roomType: RoomsType.PublicRoom },
{
key: "room-type-custom",
label: t("CreateEditRoomDialog:CustomRoomTitle"),
roomType: RoomsType.CustomRoom,
},
];
const [widthDimension, setWidthDimension] = useState(dataDimensions[1]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[0]);
const [width, setWidth] = useState("600");
const [height, setHeight] = useState("100");
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(window.innerWidth > showPreviewThreshold);
const [selectedElementType, setSelectedElementType] = useState(elementDisplayOptions[0].value);
const [roomType, setRoomType] = useState(roomTypeOptions[0]);
const debouncedOnSelect = debounce((items) => {
toastr.success(items[0].label);
}, 0);
const [config, setConfig] = useState({
mode: "room-selector",
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
init: true,
showSelectorCancel: true,
showSelectorHeader: true,
withSearch: true,
acceptButtonLabel: t("Common:SelectAction"),
cancelButtonLabel: t("Common:CancelButton"),
isButtonMode: false,
buttonWithLogo: true,
events: {
onSelectCallback: debouncedOnSelect,
onCloseCallback: null,
onAppReady: null,
onAppError: (e) => console.log("onAppError", e),
onEditorCloseCallback: null,
onAuthSuccess: null,
onSignOut: null,
},
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () => window.DocSpace.SDK.initFrame(config));
}, 500);
useEffect(() => {
loadFrame();
return destroyFrame;
});
const toggleButtonMode = (e) => {
setSelectedElementType(e.target.value);
setConfig((config) => ({ ...config, isButtonMode: e.target.value === "button" }));
};
const changeRoomType = (option) => {
setRoomType(option);
setConfig((config) => ({ ...config, roomType: option.roomType }));
};
const onChangeTab = (tab) => {
if (tab.key === "preview" && selectedElementType === "button") {
setConfig((config) => ({ ...config, isButtonMode: true }));
} else if (tab.key === "selector-preview") {
setConfig((config) => ({ ...config, isButtonMode: false }));
} else if (tab.key === "code") {
setConfig((config) => ({ ...config, isButtonMode: selectedElementType === "button" }));
}
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value };
});
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const toggleWithSearch = () => {
setConfig((config) => ({ ...config, withSearch: !config.withSearch }));
};
const onChangeAcceptLabel = (e) => {
setConfig((config) => {
return { ...config, acceptButtonLabel: e.target.value };
});
};
const onChangeCancelLabel = (e) => {
setConfig((config) => {
return { ...config, cancelButtonLabel: e.target.value };
});
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview) setShowPreview(isEnoughWidthForPreview);
};
const setButtonColor = (color) => {
setConfig((config) => ({ ...config, buttonColor: color }));
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
<Box id={frameId}></Box>
</Frame>
);
const code = (
<CodeWrapper width={width + widthDimension.label} height={height + heightDimension.label}>
<CategorySubHeader className="copy-window-code">{t("CopyWindowCode")}</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
</CodeWrapper>
);
const dataTabs =
selectedElementType === "element"
? [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
]
: [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "selector-preview",
title: t("SelectorPreview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
];
return (
<SDKContainer>
<CategoryDescription>
<Text className="sdk-description">{t("RoomSelectorPresetDescription")}</Text>
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer onSelect={onChangeTab} elements={dataTabs} />
</Preview>
)}
<Controls>
<ControlsSection>
<CategorySubHeader>{t("MainElementParameter")}</CategorySubHeader>
<RadioButtonGroup
orientation="vertical"
options={elementDisplayOptions}
name="elementDisplayInput"
selected={selectedElementType}
onClick={toggleButtonMode}
spacing="8px"
/>
{config.isButtonMode && (
<>
<CategorySubHeader>{t("ButtonCustomization")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("ButtonColor")} />
<ColorInput scale handleChange={setButtonColor} defaultColor={"#5299E0"} />
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("ButtonText")} />
<TextInput
scale
onChange={(e) => {
setConfig((config) => ({ ...config, buttonText: e.target.value }));
}}
placeholder={t("SelectToDocSpace")}
value={config.buttonText}
tabIndex={3}
/>
<Checkbox
className="checkbox"
label={"Logo"}
onChange={() => {
setConfig((config) => ({
...config,
buttonWithLogo: !config.buttonWithLogo,
}));
}}
isChecked={config.buttonWithLogo}
/>
</ControlsGroup>
</>
)}
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={4}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={5}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={6}
/>
</ControlsGroup>
</ControlsSection>
<ControlsSection>
<Label className="label">{t("InterfaceElements")}</Label>
<Checkbox
className="checkbox"
label={t("Common:Search")}
onChange={toggleWithSearch}
isChecked={config.withSearch}
/>
<Label className="label" text={t("SelectButtonText")} />
<TextInput
scale={true}
onChange={onChangeAcceptLabel}
placeholder={t("Common:SelectAction")}
value={config.acceptButtonLabel}
tabIndex={7}
/>
<Label className="label" text={t("CancelButtonText")} />
<TextInput
scale={true}
onChange={onChangeCancelLabel}
placeholder={t("Common:CancelButton")}
value={config.cancelButtonLabel}
tabIndex={8}
/>
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("AdvancedDisplay")}</CategorySubHeader>
<Label className="label" text={t("RoomTypeDisplay")} />
<ComboBox
onSelect={changeRoomType}
options={roomTypeOptions}
scaled={true}
selectedOption={roomType}
displaySelectedOption
directionY="top"
/>
</ControlsSection>
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
/>
</>
)}
</SDKContainer>
);
};
export default inject(({ authStore, settingsStore }) => {
const { setDocumentTitle } = authStore;
const { theme } = settingsStore;
return {
theme,
setDocumentTitle,
};
})(
withTranslation(["JavascriptSdk", "Files", "EmbeddingPanel", "Common", "CreateEditRoomDialog"])(
observer(RoomSelector),
),
);

View File

@ -0,0 +1,407 @@
import { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import FilesSelectorInput from "SRC_DIR/components/FilesSelectorInput";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { inject, observer } from "mobx-react";
import { isTablet, isMobile } from "@docspace/shared/utils/device";
import { HelpButton } from "@docspace/shared/components/help-button";
import GetCodeDialog from "../sub-components/GetCodeDialog";
import { Button } from "@docspace/shared/components/button";
import EmptyIframeContainer from "../sub-components/EmptyIframeContainer";
const showPreviewThreshold = 720;
import {
SDKContainer,
Controls,
CategoryHeader,
CategorySubHeader,
CategoryDescription,
ControlsGroup,
LabelGroup,
Frame,
Container,
RowContainer,
Preview,
GetCodeButtonWrapper,
FilesSelectorInputWrapper,
ControlsSection,
CodeWrapper,
} from "./StyledPresets";
const SimpleRoom = (props) => {
const { t, setDocumentTitle, fetchExternalLinks } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const [widthDimension, setWidthDimension] = useState(dataDimensions[0]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[1]);
const [width, setWidth] = useState("100");
const [height, setHeight] = useState(isTablet() ? "400" : isMobile() ? "206" : "600");
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(window.innerWidth > showPreviewThreshold);
const [sharedLinks, setSharedLinks] = useState(null);
const [config, setConfig] = useState({
mode: "manager",
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
showHeader: false,
showTitle: true,
showMenu: false,
showFilter: true,
disableActionButton: false,
infoPanelVisible: false,
init: false,
filter: {
count: 100,
page: 1,
sortorder: "descending",
sortby: "DateAndTime",
search: "",
withSubfolders: false,
},
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () => window.DocSpace.SDK.initFrame(config));
}, 500);
useEffect(() => {
loadFrame();
return () => destroyFrame();
});
const onChangeTab = () => {
loadFrame();
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFolderId = async (id, publicInPath) => {
let newConfig = { id, requestToken: null, rootPath: "/rooms/shared/" };
if (!!publicInPath) {
const links = await fetchExternalLinks(publicInPath.id);
if (links.length > 1) {
const linksOptions = links.map((link) => {
const { id, title, requestToken } = link.sharedTo;
return {
key: id,
label: title,
requestToken: requestToken,
};
});
setSharedLinks(linksOptions);
}
newConfig.requestToken = links[0].sharedTo?.requestToken;
newConfig.rootPath = "/rooms/share";
} else {
setSharedLinks(null);
}
setConfig((config) => {
return { ...config, ...newConfig, init: true };
});
};
const onChangeSharedLink = (link) => {
setConfig((config) => {
return { ...config, requestToken: link.requestToken };
});
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value };
});
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview) setShowPreview(isEnoughWidthForPreview);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
{config.id !== undefined ? (
<>
<Box id={frameId}></Box>
</>
) : (
<EmptyIframeContainer
text={t("SelectRoom")}
width={width + widthDimension.label}
height={height + heightDimension.label}
/>
)}
</Frame>
);
const code = (
<CodeWrapper>
<CategorySubHeader className="copy-window-code">{t("CopyWindowCode")}</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
</CodeWrapper>
);
const dataTabs = [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
];
return (
<SDKContainer>
<CategoryDescription>
<Text className="sdk-description">{t("SimpleRoomPresetDescription")}</Text>
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer
isDisabled={config?.id === undefined}
onSelect={onChangeTab}
elements={dataTabs}
/>
</Preview>
)}
<Controls>
<ControlsSection>
<CategorySubHeader>{t("DataDisplay")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("Common:Room")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={<Text fontSize="12px">{t("RoomOrFolderDescription")}</Text>}
/>
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput onSelectFolder={onChangeFolderId} isSelect isRoomsOnly />
</FilesSelectorInputWrapper>
</ControlsGroup>
{sharedLinks && (
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("SharingPanel:ExternalLink")} />
<HelpButton
offsetRight={0}
size={12}
tooltipContent={
<Text fontSize="12px">{t("CreateEditRoomDialog:PublicRoomDescription")}</Text>
}
/>
</LabelGroup>
<ComboBox
scaled={true}
onSelect={onChangeSharedLink}
options={sharedLinks}
selectedOption={sharedLinks[0]}
displaySelectedOption
directionY="bottom"
/>
</ControlsGroup>
)}
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={2}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={3}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={4}
/>
</ControlsGroup>
</ControlsSection>
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
/>
</>
)}
</SDKContainer>
);
};
export default inject(({ authStore, settingsStore, publicRoomStore }) => {
const { setDocumentTitle } = authStore;
const { theme } = settingsStore;
const { fetchExternalLinks } = publicRoomStore;
return {
theme,
setDocumentTitle,
fetchExternalLinks,
};
})(
withTranslation([
"JavascriptSdk",
"Files",
"EmbeddingPanel",
"Common",
"Files",
"Translations",
"SharingPanel",
])(observer(SimpleRoom)),
);

View File

@ -0,0 +1,264 @@
import styled, { css } from "styled-components";
import { isMobile, mobile, tablet } from "@docspace/shared/utils/device";
import { Box } from "@docspace/shared/components/box";
import Base from "@docspace/shared/themes/base";
export const SDKContainer = styled(Box)`
@media ${tablet} {
width: 100%;
}
${isMobile() &&
css`
width: 100%;
`}
.tabs_body {
height: calc(100lvh - 260px);
}
`;
export const Controls = styled(Box)`
max-width: 350px;
min-width: 350px;
width: 100%;
display: flex;
flex-direction: column;
gap: 32px;
margin-top: 16px;
@media ${tablet} {
min-width: 0;
}
${isMobile() &&
css`
min-width: 0;
`}
.label {
min-width: fit-content;
}
.checkbox {
max-width: fit-content;
}
`;
export const CategoryHeader = styled.div`
margin-top: 24px;
margin-bottom: 24px;
font-size: ${(props) => props.theme.getCorrectFontSize("16px")};
font-style: normal;
font-weight: 700;
line-height: 22px;
@media ${tablet} {
margin-top: 24px;
}
${isMobile() &&
css`
margin-top: 24px;
`}
`;
export const CategorySubHeader = styled.div`
font-size: ${(props) => props.theme.getCorrectFontSize("15px")};
font-style: normal;
font-weight: 600;
line-height: 16px;
@media ${tablet} {
&:not(&.copy-window-code) {
margin-bottom: 0;
}
}
${isMobile() &&
css`
&:not(&.copy-window-code) {
margin-bottom: 0;
}
`}
@media ${mobile} {
&:first-of-type {
margin-top: 0;
}
}
`;
export const CategoryDescription = styled(Box)`
max-width: 700px;
.sdk-description {
line-height: 20px;
color: ${(props) => props.theme.client.settings.common.descriptionColor};
}
`;
export const ControlsGroup = styled(Box)`
display: flex;
flex-direction: column;
gap: 4px;
.toggle {
position: relative;
}
@media ${tablet} {
gap: 4px;
}
${isMobile() &&
css`
gap: 4px;
`}
`;
export const CheckboxGroup = styled(Box)`
display: flex;
flex-direction: column;
gap: 10px;
`;
export const LabelGroup = styled(Box)`
display: inline-flex;
align-items: center;
gap: 4px;
`;
export const ControlsSection = styled(Box)`
display: flex;
flex-direction: column;
gap: 16px;
`;
export const Frame = styled(Box)`
margin-top: 16px;
position: relative;
border-radius: 6px;
border: 1px solid ${(props) => props.theme.sdkPresets.borderColor};
width: calc(${(props) => (props.width ? props.width : "100%")} + 2px);
height: calc(${(props) => (props.height ? props.height : "400px")} + 2px);
display: flex;
align-items: center;
justify-content: center;
@media ${tablet} {
margin-top: 4px;
}
${(props) =>
props.targetId &&
`
#${props.targetId} {
border-radius: 6px;
}
`}
${isMobile() &&
css`
margin-top: 4px;
`}
.frame-container {
height: 100% !important;
& > iframe {
height: 100% !important;
}
}
`;
Frame.defaultProps = { theme: Base };
export const Container = styled(Box)`
width: 100%;
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
gap: 48px;
@media ${tablet} {
flex-direction: column;
}
${isMobile() &&
css`
flex-direction: column;
`}
`;
export const RowContainer = styled(Box)`
flex-direction: row;
display: flex;
gap: 8px;
${(props) =>
props.combo &&
`
height: 32px;
align-items: center;
`}
`;
export const ColumnContainer = styled(Box)`
flex-direction: column;
display: flex;
gap: 8px;
`;
export const Preview = styled(Box)`
width: 100%;
min-width: 660px;
flex-direction: row;
@media ${tablet} {
margin-top: 0;
min-width: 0;
}
${isMobile() &&
css`
margin-top: 0;
min-width: 0;
`}
`;
export const GetCodeButtonWrapper = styled.div`
padding-block: 30px;
position: sticky;
bottom: 0;
margin-top: 32px;
background-color: ${({ theme }) => theme.backgroundColor};
@media ${mobile} {
position: fixed;
padding-inline: 16px;
inset-inline: 0;
}
`;
export const FilesSelectorInputWrapper = styled.div`
& > div {
margin: 0;
}
`;
export const SelectedItemsContainer = styled.div`
display: flex;
flex-wrap: wrap;
`;
export const CodeWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 16px;
width: calc(${(props) => (props.width ? props.width : "100%")} + 2px);
height: calc(${(props) => (props.height ? props.height : "400px")} + 2px);
`;

View File

@ -0,0 +1,395 @@
import { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import debounce from "lodash.debounce";
import { Box } from "@docspace/shared/components/box";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
import { Label } from "@docspace/shared/components/label";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
import { ComboBox } from "@docspace/shared/components/combobox";
import { TabsContainer } from "@docspace/shared/components/tabs-container";
import FilesSelectorInput from "SRC_DIR/components/FilesSelectorInput";
import { objectToGetParams, loadScript } from "@docspace/shared/utils/common";
import { inject, observer } from "mobx-react";
import { ImageEditor } from "@docspace/shared/components/image-editor";
import { FilesSelectorFilterTypes } from "@docspace/shared/enums";
import { isTablet, isMobile } from "@docspace/shared/utils/device";
import EmptyIframeContainer from "../sub-components/EmptyIframeContainer";
import GetCodeDialog from "../sub-components/GetCodeDialog";
import { Button } from "@docspace/shared/components/button";
const showPreviewThreshold = 720;
import {
SDKContainer,
Controls,
CategoryHeader,
CategorySubHeader,
CategoryDescription,
ControlsGroup,
LabelGroup,
ControlsSection,
Frame,
Container,
RowContainer,
ColumnContainer,
Preview,
GetCodeButtonWrapper,
FilesSelectorInputWrapper,
CodeWrapper,
} from "./StyledPresets";
const Viewer = (props) => {
const { t, setDocumentTitle } = props;
setDocumentTitle(t("JavascriptSdk"));
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
const dataDimensions = [
{ key: "percent", label: "%", default: true },
{ key: "pixel", label: "px" },
];
const [widthDimension, setWidthDimension] = useState(dataDimensions[0]);
const [heightDimension, setHeightDimension] = useState(dataDimensions[0]);
const [width, setWidth] = useState("100");
const [height, setHeight] = useState("100");
const [isGetCodeDialogOpened, setIsGetCodeDialogOpened] = useState(false);
const [showPreview, setShowPreview] = useState(window.innerWidth > showPreviewThreshold);
const [config, setConfig] = useState({
mode: "viewer",
width: `${width}${widthDimension.label}`,
height: `${height}${heightDimension.label}`,
frameId: "ds-frame",
init: false,
});
const params = objectToGetParams(config);
const frameId = config.frameId || "ds-frame";
const destroyFrame = () => {
window.DocSpace?.SDK?.frames[frameId]?.destroyFrame();
};
const loadFrame = debounce(() => {
const script = document.getElementById("integration");
if (script) {
script.remove();
}
const params = objectToGetParams(config);
loadScript(`${scriptUrl}${params}`, "integration", () => window.DocSpace.SDK.initFrame(config));
}, 500);
useEffect(() => {
loadFrame();
return () => destroyFrame();
});
const onChangeTab = () => {
loadFrame();
};
const onChangeWidth = (e) => {
setConfig((config) => {
return { ...config, width: `${e.target.value}${widthDimension.label}` };
});
setWidth(e.target.value);
};
const onChangeHeight = (e) => {
setConfig((config) => {
return { ...config, height: `${e.target.value}${heightDimension.label}` };
});
setHeight(e.target.value);
};
const onChangeFileId = (file) => {
setConfig((config) => {
return { ...config, id: file.id };
});
};
const onChangeFrameId = (e) => {
setConfig((config) => {
return { ...config, frameId: e.target.value, init: true };
});
};
const onChangeWidthDimension = (item) => {
setConfig((config) => {
return { ...config, width: `${width}${item.label}` };
});
setWidthDimension(item);
};
const onChangeHeightDimension = (item) => {
setConfig((config) => {
return { ...config, height: `${height}${item.label}` };
});
setHeightDimension(item);
};
const openGetCodeModal = () => setIsGetCodeDialogOpened(true);
const closeGetCodeModal = () => setIsGetCodeDialogOpened(false);
const onResize = () => {
const isEnoughWidthForPreview = window.innerWidth > showPreviewThreshold;
if (isEnoughWidthForPreview !== showPreview) setShowPreview(isEnoughWidthForPreview);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, [showPreview]);
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
const preview = (
<Frame
width={width + widthDimension.label}
height={height + heightDimension.label}
targetId={frameId}
>
{config.id !== undefined ? (
<>
<Box id={frameId}></Box>
</>
) : (
<EmptyIframeContainer
text={t("SelectFile")}
width={width + widthDimension.label}
height={height + heightDimension.label}
/>
)}
</Frame>
);
const code = (
<CodeWrapper>
<CategorySubHeader className="copy-window-code">{t("CopyWindowCode")}</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
</CodeWrapper>
);
const dataTabs = [
{
key: "preview",
title: t("Common:Preview"),
content: preview,
},
{
key: "code",
title: t("Code"),
content: code,
},
];
return (
<SDKContainer>
<CategoryDescription>
<Text className="sdk-description">{t("ViewerPresetDescription")}</Text>
</CategoryDescription>
<CategoryHeader>{t("CreateSampleHeader")}</CategoryHeader>
<Container>
{showPreview && (
<Preview>
<TabsContainer
isDisabled={config?.id === undefined}
onSelect={onChangeTab}
elements={dataTabs}
/>
</Preview>
)}
<Controls>
<ControlsSection>
<CategorySubHeader>{t("FileId")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("Common:SelectFile")} />
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput
onSelectFile={onChangeFileId}
filterParam={FilesSelectorFilterTypes.ALL}
isSelect
/>
</FilesSelectorInputWrapper>
</ControlsGroup>
</ControlsSection>
<ControlsSection>
<CategorySubHeader>{t("CustomizingDisplay")}</CategorySubHeader>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Width")} />
<RowContainer combo>
<TextInput
onChange={onChangeWidth}
placeholder={t("EnterWidth")}
value={width}
tabIndex={2}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeWidthDimension}
options={dataDimensions}
selectedOption={widthDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("EmbeddingPanel:Height")} />
<RowContainer combo>
<TextInput
onChange={onChangeHeight}
placeholder={t("EnterHeight")}
value={height}
tabIndex={3}
/>
<ComboBox
size="content"
scaled={false}
scaledOptions={true}
onSelect={onChangeHeightDimension}
options={dataDimensions}
selectedOption={heightDimension}
displaySelectedOption
directionY="bottom"
/>
</RowContainer>
</ControlsGroup>
<ControlsGroup>
<Label className="label" text={t("FrameId")} />
<TextInput
scale={true}
onChange={onChangeFrameId}
placeholder={t("EnterId")}
value={config.frameId}
tabIndex={4}
/>
</ControlsGroup>
</ControlsSection>
{/* <InterfaceElements>
<Label className="label">{t("InterfaceElements")}</Label>
<Checkbox
className="checkbox"
label={t("TabPlugins")}
onChange={() => {}}
isChecked={true}
/>
<RowContainer>
<Checkbox label={t("Chat")} onChange={() => {}} isChecked={true} />
<Text color="gray">({t("InLeftPanel")})</Text>
</RowContainer>
<RowContainer>
<Checkbox label={t("FeedbackAndSupport")} onChange={() => {}} isChecked={true} />
<Text color="gray">({t("InLeftPanel")})</Text>
</RowContainer>
</InterfaceElements>
<CategorySubHeader>{t("AddWatermarks")}</CategorySubHeader>
<ControlsGroup>
<LabelGroup>
<Label className="label" text={t("SelectImage")} />
</LabelGroup>
<FilesSelectorInputWrapper>
<FilesSelectorInput onSelectFolder={onChangeFileId} isSelect />
</FilesSelectorInputWrapper>
</ControlsGroup>
<Label className="label" text={t("Scale")} />
<ComboBox
onSelect={() => {}}
options={[
{ key: "1", label: "100%", default: true },
{ key: "2", label: "50%" },
{ key: "3", label: "25%" },
]}
scaled={true}
selectedOption={{ key: "1", label: "100%", default: true }}
displaySelectedOption
directionY="top"
/>
<Label className="label" text={t("Rotate")} />
<ComboBox
onSelect={() => {}}
options={[
{ key: "1", label: "45%", default: true },
{ key: "2", label: "75%" },
{ key: "3", label: "90%" },
{ key: "4", label: "180%" },
]}
scaled={true}
selectedOption={{ key: "1", label: "45%", default: true }}
displaySelectedOption
directionY="top"
/>
<Label className="label" text={t("CreateEditRoomDialog:Icon")} />
<ImageEditor
t={t}
className="wrapper-image-editor"
classNameWrapperImageCropper="avatar-editor"
image={{}}
setPreview={() => {}}
onChangeImage={() => {}}
/> */}
</Controls>
</Container>
{!showPreview && (
<>
<GetCodeButtonWrapper>
<Button
id="get-sdk-code-button"
primary
size="normal"
scale
label={t("GetCode")}
onClick={openGetCodeModal}
/>
</GetCodeButtonWrapper>
<GetCodeDialog
t={t}
visible={isGetCodeDialogOpened}
codeBlock={codeBlock}
onClose={closeGetCodeModal}
/>
</>
)}
</SDKContainer>
);
};
export default inject(({ authStore, settingsStore }) => {
const { setDocumentTitle } = authStore;
const { theme } = settingsStore;
return {
theme,
setDocumentTitle,
};
})(
withTranslation(["JavascriptSdk", "Files", "EmbeddingPanel", "Common", "CreateEditRoomDialog"])(
observer(Viewer),
),
);

View File

@ -0,0 +1,39 @@
import React from "react";
import styled from "styled-components";
import { RectangleSkeleton } from "@docspace/shared/skeletons/rectangle";
import { Base } from "@docspace/shared/themes";
const StyledContainer = styled.div`
width: ${(props) => props.width};
height: ${(props) => props.height};
display: flex;
justify-content: center;
align-items: center;
border: 1px solid ${(props) => props.theme.plugins.borderColor};
border-radius: 6px;
overflow: hidden;
.emptyIframeText {
position: absolute;
font-size: 44px;
font-weight: 700;
line-height: 59.92px;
color: ${(props) => props.theme.text.emailColor};
}
`;
StyledContainer.defaultProps = { theme: Base };
const EmptyIframeContainer = ({ text, width, height }) => {
return (
<StyledContainer width={width} height={height}>
<RectangleSkeleton width="100%" height="100%" borderRadius="6px" />
<span className="emptyIframeText">{text}</span>
</StyledContainer>
);
};
export default EmptyIframeContainer;

View File

@ -0,0 +1,82 @@
import React from "react";
import styled, { css } from "styled-components";
import { Base } from "@docspace/shared/themes";
import { Text } from "@docspace/shared/components/text";
import { Button } from "@docspace/shared/components/button";
import ArrowIcon from "PUBLIC_DIR/images/arrow-left.react.svg";
const TileContainer = styled.div`
box-sizing: border-box;
width: 100%;
max-width: 342px;
height: 354px;
padding: 12px 16px;
border-radius: 6px;
border: 1px solid ${(props) => props.theme.sdkPresets.borderColor};
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 12px;
.tileContent {
display: flex;
flex-direction: column;
gap: 12px;
}
.navigationButton {
border: none;
.button-content {
flex-direction: row-reverse;
}
.icon {
transform: scale(-1, 1);
}
:hover {
${() =>
css`
border: ${(props) => props.theme.button.border.baseHover};
box-sizing: ${(props) => props.theme.button.boxSizing};
`}
}
}
`;
TileContainer.defaultProps = { theme: Base };
const PresetTile = (props) => {
const { t, title, description, image, handleOnClick } = props;
return (
<TileContainer>
<div className="tileContent">
<Text fontSize="16px" lineHeight="22px" fontWeight={700}>
{title}
</Text>
<img src={image} alt={title} />
<Text lineHeight="20px">{description}</Text>
</div>
<Button
className="navigationButton"
label={t("SetUp")}
icon={<ArrowIcon />}
scale
isClicked
size="small"
onClick={handleOnClick}
/>
</TileContainer>
);
};
export default PresetTile;

View File

@ -0,0 +1,49 @@
import React from "react";
import { Text } from "@docspace/shared/components/text";
import styled from "styled-components";
import XImg from "PUBLIC_DIR/images/x.react.svg";
const Wrapper = styled.div`
box-sizing: border-box;
max-width: 216px;
width: 100%;
padding: 8px 4px 16px;
`;
const HeaderContainer = styled.header`
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
svg {
cursor: pointer;
path {
fill: #333;
}
}
`;
const ImgWrapper = styled.div`
margin-top: 16px;
`;
export const TooltipContent = ({ title, description, img }) => {
return (
<Wrapper>
<HeaderContainer>
<Text fontSize="16px" fontWeight={700} lineHeight="22px">
{title}
</Text>
{/* <XImg /> */}
</HeaderContainer>
<Text>{description}</Text>
<ImgWrapper>
<img src={img} alt={title} />
</ImgWrapper>
</Wrapper>
);
};

View File

@ -8,6 +8,7 @@ import { Text } from "@docspace/shared/components/text";
import { SelectorAddButton } from "@docspace/shared/components/selector-add-button";
import { SelectedItem } from "@docspace/shared/components/selected-item";
import { tablet } from "@docspace/shared/utils";
import Base from "@docspace/shared/themes/base";
const CategoryHeader = styled.div`
margin-top: 24px;
@ -23,6 +24,7 @@ const Container = styled.div`
&.description-holder {
display: block;
color: ${(props) => props.theme.sdkPresets.secondaryColor};
}
&.description-holder > div {
@ -41,6 +43,8 @@ const Container = styled.div`
}
`;
Container.defaultProps = { theme: Base };
const ChipsContainer = styled.div`
display: flex;
align-items: center;

View File

@ -129,7 +129,9 @@ const DeliveryDatePicker = ({
const SelectedDateTime = () => {
const formattedTime = isTimeEqual
? ""
: ` ${filters.deliveryFrom.format("HH:mm")} - ${moment(filters.deliveryTo)
: ` ${moment(filters.deliveryFrom).tz(window.timezone).format("HH:mm")} - ${moment(
filters.deliveryTo,
)
.tz(window.timezone)
.format("HH:mm")}`;
@ -210,6 +212,7 @@ const DeliveryDatePicker = ({
hasError={!isTimeValid}
tabIndex={1}
locale={i18n.language}
initialTime={filters.deliveryFrom}
/>
</span>
@ -219,10 +222,11 @@ const DeliveryDatePicker = ({
<TimePicker
classNameInput="before-time"
date={filters.deliveryTo}
setDate={setDeliveryTo}
onChange={setDeliveryTo}
hasError={!isTimeValid}
tabIndex={2}
locale={i18n.language}
initialTime={filters.deliveryTo}
/>
</TimePickerCell>
) : (

View File

@ -37,6 +37,7 @@ const StatusBadgeSelector = ({
label={label}
onClick={handleOnClick}
primary={isStatusSelected(statusCode)}
size="extraSmall"
/>
);
};
@ -77,7 +78,7 @@ const StatusPicker = ({ filters, setFilters }) => {
handleStatusClick={handleStatusClick}
key={code}
/>
)
),
);
return (

Some files were not shown because too many files have changed in this diff Show More