@@ -202,9 +213,32 @@ const AttributeMapping = (props) => {
onChange={onChangeValue}
value={userQuotaLimit}
scale
- isDisabled={!isLdapEnabled || isUIDisabled}
+ isDisabled={
+ !isDefaultUsersQuotaSet || !isLdapEnabled || isUIDisabled
+ }
tabIndex={11}
/>
+ {!isDefaultUsersQuotaSet && (
+
+ ,
+ ]}
+ />
+
+ )}
@@ -250,7 +284,7 @@ const AttributeMapping = (props) => {
);
};
-export default inject(({ ldapStore }) => {
+export default inject(({ ldapStore, currentQuotaStore }) => {
const {
setMail,
setFirstName,
@@ -274,6 +308,8 @@ export default inject(({ ldapStore }) => {
userType,
} = requiredSettings;
+ const { isDefaultUsersQuotaSet } = currentQuotaStore;
+
return {
setFirstName,
setSecondName,
@@ -292,5 +328,7 @@ export default inject(({ ldapStore }) => {
errors,
isLdapEnabled,
isUIDisabled,
+
+ isDefaultUsersQuotaSet,
};
})(observer(AttributeMapping));
diff --git a/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/AuthenticationContainer.js b/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/AuthenticationContainer.js
index 7466f19375..180b7ff02b 100644
--- a/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/AuthenticationContainer.js
+++ b/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/AuthenticationContainer.js
@@ -32,6 +32,7 @@ import { HelpButton } from "@docspace/shared/components/help-button";
import { FieldContainer } from "@docspace/shared/components/field-container";
import { ToggleButton } from "@docspace/shared/components/toggle-button";
import LdapFieldComponent from "./LdapFieldComponent";
+import { InputSize, InputType } from "@docspace/shared/components/text-input";
const LOGIN = "login",
PASSWORD = "password";
@@ -109,6 +110,7 @@ const AuthenticationContainer = ({
isRequired
>
diff --git a/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/LdapFieldComponent.js b/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/LdapFieldComponent.js
index 7e09c9a9a6..ccb0ff1dc1 100644
--- a/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/LdapFieldComponent.js
+++ b/packages/client/src/pages/PortalSettings/categories/integration/LDAP/sub-components/LdapFieldComponent.js
@@ -28,6 +28,7 @@ import React from "react";
import { inject, observer } from "mobx-react";
import { TextInput } from "@docspace/shared/components/text-input";
import { Textarea } from "@docspace/shared/components/textarea";
+import { PasswordInput } from "@docspace/shared/components/password-input";
const LdapFieldComponent = (props) => {
const {
@@ -37,6 +38,7 @@ const LdapFieldComponent = (props) => {
setErrorField,
name,
onChange,
+ isPassword,
...prop
} = props;
@@ -68,6 +70,17 @@ const LdapFieldComponent = (props) => {
if (isTextArea)
return
;
+ if (isPassword)
+ return (
+
+ );
+
return (
{
resetForm,
confirmationResetModal,
isSubmitLoading,
- hasErrors,
- hasChanges,
isLoadingXml,
- enableSso,
isSSOAvailable,
closeResetModal,
confirmReset,
+ isDisabledSaveButton,
} = props;
return (
@@ -62,9 +60,7 @@ const SubmitResetButtons = (props) => {
displaySettings={true}
hasScroll={true}
isSaving={isSubmitLoading}
- saveButtonDisabled={
- !enableSso || hasErrors || !hasChanges || isLoadingXml
- }
+ saveButtonDisabled={isDisabledSaveButton}
disableRestoreToDefault={
isSubmitLoading || isLoadingXml || !isSSOAvailable
}
@@ -90,12 +86,10 @@ export default inject(({ ssoStore, currentQuotaStore }) => {
resetForm,
confirmationResetModal,
isSubmitLoading,
- hasErrors,
- hasChanges,
isLoadingXml,
- enableSso,
closeResetModal,
confirmReset,
+ isDisabledSaveButton,
} = ssoStore;
const { isSSOAvailable } = currentQuotaStore;
@@ -106,12 +100,10 @@ export default inject(({ ssoStore, currentQuotaStore }) => {
resetForm,
confirmationResetModal,
isSubmitLoading,
- hasErrors,
- hasChanges,
isLoadingXml,
- enableSso,
isSSOAvailable,
closeResetModal,
confirmReset,
+ isDisabledSaveButton,
};
})(observer(SubmitResetButtons));
diff --git a/packages/client/src/pages/PortalSettings/categories/integration/SingleSignOn/styled-containers/StyledSsoPageContainer.js b/packages/client/src/pages/PortalSettings/categories/integration/SingleSignOn/styled-containers/StyledSsoPageContainer.js
index 4b8a01996c..2e6b3b76d6 100644
--- a/packages/client/src/pages/PortalSettings/categories/integration/SingleSignOn/styled-containers/StyledSsoPageContainer.js
+++ b/packages/client/src/pages/PortalSettings/categories/integration/SingleSignOn/styled-containers/StyledSsoPageContainer.js
@@ -51,16 +51,6 @@ const StyledSsoPage = styled.div`
margin-bottom: 4px;
}
- .field-label {
- display: flex;
- align-items: center;
- height: auto;
- font-weight: 600;
- line-height: 20px;
- overflow: visible;
- white-space: normal;
- }
-
.xml-input {
.field-label-icon {
margin-bottom: 8px;
diff --git a/packages/client/src/store/ContextOptionsStore.js b/packages/client/src/store/ContextOptionsStore.js
index 45381b7aa9..6964870b22 100644
--- a/packages/client/src/store/ContextOptionsStore.js
+++ b/packages/client/src/store/ContextOptionsStore.js
@@ -139,6 +139,7 @@ class ContextOptionsStore {
pluginStore;
infoPanelStore;
currentTariffStatusStore;
+ currentQuotaStore;
userStore;
clientLoadingStore;
@@ -160,6 +161,7 @@ class ContextOptionsStore {
pluginStore,
infoPanelStore,
currentTariffStatusStore,
+ currentQuotaStore,
userStore,
clientLoadingStore,
) {
@@ -179,6 +181,7 @@ class ContextOptionsStore {
this.pluginStore = pluginStore;
this.infoPanelStore = infoPanelStore;
this.currentTariffStatusStore = currentTariffStatusStore;
+ this.currentQuotaStore = currentQuotaStore;
this.userStore = userStore;
this.clientLoadingStore = clientLoadingStore;
}
@@ -556,16 +559,12 @@ class ContextOptionsStore {
this.dialogsStore.setDownloadDialogVisible(true);
};
- onClickCreateRoom = (item) => {
- this.filesActionsStore.setProcessCreatingRoomFromData(true);
- const event = new Event(Events.ROOM_CREATE);
- if (item && item.isFolder) {
- event.title = item.title;
- }
- window.dispatchEvent(event);
- };
-
onDuplicate = (item) => {
+ if (this.currentQuotaStore.isWarningRoomsDialog) {
+ this.dialogsStore.setQuotaWarningDialogVisible(true);
+ return;
+ }
+
this.filesActionsStore.duplicateAction(item);
};
@@ -862,7 +861,7 @@ class ContextOptionsStore {
const { isGracePeriod } = this.currentTariffStatusStore;
if (isGracePeriod) {
- this.dialogsStore.setInviteUsersWarningDialogVisible(true);
+ this.dialogsStore.setQuotaWarningDialogVisible(true);
} else {
this.dialogsStore.setInvitePanelOptions({
visible: true,
@@ -883,15 +882,15 @@ class ContextOptionsStore {
onClickArchive = (e) => {
const data = (e.currentTarget && e.currentTarget.dataset) || e;
const { action } = data;
- const { isGracePeriod } = this.currentTariffStatusStore;
+ const { isWarningRoomsDialog } = this.currentQuotaStore;
const {
setArchiveDialogVisible,
setRestoreRoomDialogVisible,
- setInviteUsersWarningDialogVisible,
+ setQuotaWarningDialogVisible,
} = this.dialogsStore;
- if (action === "unarchive" && isGracePeriod) {
- setInviteUsersWarningDialogVisible(true);
+ if (action === "unarchive" && isWarningRoomsDialog) {
+ setQuotaWarningDialogVisible(true);
return;
}
@@ -1105,7 +1104,7 @@ class ContextOptionsStore {
onRestoreAllArchiveAction = () => {
const { activeFiles, activeFolders } = this.filesStore;
const {
- setInviteUsersWarningDialogVisible,
+ setQuotaWarningDialogVisible,
setRestoreAllArchive,
setRestoreRoomDialogVisible,
} = this.dialogsStore;
@@ -1114,8 +1113,8 @@ class ContextOptionsStore {
if (isExistActiveItems) return;
- if (this.currentTariffStatusStore.isGracePeriod) {
- setInviteUsersWarningDialogVisible(true);
+ if (this.currentQuotaStore.isWarningRoomsDialog) {
+ setQuotaWarningDialogVisible(true);
return;
}
@@ -1667,7 +1666,7 @@ class ContextOptionsStore {
key: "create-room",
label: t("Files:CreateRoom"),
icon: CatalogRoomsReactSvgUrl,
- onClick: () => this.onClickCreateRoom(item),
+ onClick: () => this.onCreateRoom(item, true),
disabled: !item.security?.CreateRoomFrom,
},
{
@@ -1980,7 +1979,7 @@ class ContextOptionsStore {
key: "create-room",
label: t("Files:CreateRoom"),
icon: CatalogRoomsReactSvgUrl,
- onClick: this.onClickCreateRoom,
+ onClick: () => this.onCreateRoom(null, true),
disabled: !selection.security?.CreateRoomFrom,
},
{
@@ -2077,13 +2076,13 @@ class ContextOptionsStore {
};
onInvite = (e) => {
- const { setInviteUsersWarningDialogVisible, setInvitePanelOptions } =
+ const { setQuotaWarningDialogVisible, setInvitePanelOptions } =
this.dialogsStore;
const type = e.item["data-type"];
- if (this.currentTariffStatusStore.isGracePeriod) {
- setInviteUsersWarningDialogVisible(true);
+ if (this.currentQuotaStore.showWarningDialog(type)) {
+ setQuotaWarningDialogVisible(true);
return;
}
@@ -2108,13 +2107,22 @@ class ContextOptionsStore {
window.dispatchEvent(event);
};
- onCreateRoom = () => {
- if (this.currentTariffStatusStore.isGracePeriod) {
- this.dialogsStore.setInviteUsersWarningDialogVisible(true);
+ onCreateRoom = (item, fromItem) => {
+ if (this.currentQuotaStore.isWarningRoomsDialog) {
+ this.dialogsStore.setQuotaWarningDialogVisible(true);
return;
}
+ if (fromItem) {
+ this.filesActionsStore.setProcessCreatingRoomFromData(true);
+ }
+
const event = new Event(Events.ROOM_CREATE);
+
+ if (item && item.isFolder) {
+ event.title = item.title;
+ }
+
window.dispatchEvent(event);
};
diff --git a/packages/client/src/store/CreateEditRoomStore.js b/packages/client/src/store/CreateEditRoomStore.js
index 49b0bd9bb5..b9afd866e0 100644
--- a/packages/client/src/store/CreateEditRoomStore.js
+++ b/packages/client/src/store/CreateEditRoomStore.js
@@ -46,6 +46,7 @@ class CreateEditRoomStore {
settingsStore = null;
infoPanelStore = null;
currentQuotaStore = null;
+ dialogsStore = null;
constructor(
filesStore,
@@ -57,6 +58,7 @@ class CreateEditRoomStore {
infoPanelStore,
currentQuotaStore,
clientLoadingStore,
+ dialogsStore,
) {
makeAutoObservable(this);
@@ -69,6 +71,7 @@ class CreateEditRoomStore {
this.infoPanelStore = infoPanelStore;
this.currentQuotaStore = currentQuotaStore;
this.clientLoadingStore = clientLoadingStore;
+ this.dialogsStore = dialogsStore;
}
setRoomParams = (roomParams) => {
@@ -87,8 +90,8 @@ class CreateEditRoomStore {
this.onClose = onClose;
};
- setRoomIsCreated = (onClose) => {
- this.onClose = onClose;
+ setIsRoomCreatedByCurrentUser = (value) => {
+ this.isRoomCreatedByCurrentUser = value;
};
onCreateRoom = async (withConfirm = false, t) => {
@@ -152,6 +155,8 @@ class CreateEditRoomStore {
? await createRoomInThirdpary(storageFolderId, createRoomData)
: await createRoom(createRoomData);
+ this.dialogsStore.setIsNewRoomByCurrentUser(true);
+
room.isLogoLoading = true;
const actions = [];
diff --git a/packages/client/src/store/DialogsStore.js b/packages/client/src/store/DialogsStore.js
index 59bd7784f8..6d0ee66d28 100644
--- a/packages/client/src/store/DialogsStore.js
+++ b/packages/client/src/store/DialogsStore.js
@@ -26,6 +26,7 @@
import { getNewFiles } from "@docspace/shared/api/files";
import {
+ EmployeeType,
FilesSelectorFilterTypes,
FilterType,
ShareAccessRights,
@@ -58,7 +59,7 @@ class DialogsStore {
selectFileDialogVisible = false;
selectFileFormRoomDialogVisible = false;
convertPasswordDialogVisible = false;
- inviteUsersWarningDialogVisible = false;
+ inviteQuotaWarningDialogVisible = false;
changeQuotaDialogVisible = false;
unsavedChangesDialogVisible = false;
moveToPublicRoomVisible = false;
@@ -129,6 +130,8 @@ class DialogsStore {
};
warningQuotaDialogVisible = false;
+ invitePaidUsersCount = 0;
+ isNewQuotaItemsByCurrentUser = false;
constructor(
authStore,
@@ -435,15 +438,48 @@ class DialogsStore {
this.inviteItems = inviteItems;
};
+ setInvitePaidUsersCount = (modifier = 1) => {
+ this.invitePaidUsersCount = this.invitePaidUsersCount + modifier;
+ if (this.invitePaidUsersCount === -1) this.invitePaidUsersCount = 0;
+ };
+
+ isPaidUserAccess = (selectedAccess) => {
+ return (
+ selectedAccess === EmployeeType.Admin ||
+ selectedAccess === EmployeeType.Collaborator ||
+ selectedAccess === EmployeeType.User
+ );
+ };
+
changeInviteItem = async (item) =>
runInAction(() => {
const index = this.inviteItems.findIndex((iItem) => iItem.id === item.id);
+ const isPrevAccessPaid = this.isPaidUserAccess(
+ this.inviteItems[index].access,
+ );
+ const isCurrAccessPaid = this.isPaidUserAccess(item.access);
+
+ let modifier = 0;
+
+ if (isPrevAccessPaid && !isCurrAccessPaid) modifier = -1;
+ if (!isPrevAccessPaid && isCurrAccessPaid) modifier = 1;
+
+ this.setInvitePaidUsersCount(modifier);
+
this.inviteItems[index] = { ...this.inviteItems[index], ...item };
});
- setInviteUsersWarningDialogVisible = (inviteUsersWarningDialogVisible) => {
- this.inviteUsersWarningDialogVisible = inviteUsersWarningDialogVisible;
+ setQuotaWarningDialogVisible = (inviteQuotaWarningDialogVisible) => {
+ this.inviteQuotaWarningDialogVisible = inviteQuotaWarningDialogVisible;
+ };
+
+ setIsNewRoomByCurrentUser = (value) => {
+ this.isNewRoomByCurrentUser = value;
+ };
+
+ setIsNewUserByCurrentUser = (value) => {
+ this.isNewUserByCurrentUser = value;
};
setCreateRoomDialogVisible = (createRoomDialogVisible) => {
diff --git a/packages/client/src/store/FilesActionsStore.js b/packages/client/src/store/FilesActionsStore.js
index 1349a9ec62..5e0df634bf 100644
--- a/packages/client/src/store/FilesActionsStore.js
+++ b/packages/client/src/store/FilesActionsStore.js
@@ -229,17 +229,21 @@ class FilesActionStore {
let level = { result };
try {
folders.forEach((folder) => {
- folder.path
- .split("/")
- .filter((name) => name !== "")
- .reduce((r, name, i, a) => {
- if (!r[name]) {
- r[name] = { result: [] };
- r.result.push({ name, children: r[name].result });
- }
+ const folderPath = folder.path.split("/").filter((name) => name !== "");
- return r[name];
- }, level);
+ folderPath.reduce((r, name, i, a) => {
+ if (!r[name]) {
+ r[name] = { result: [] };
+ r.result.push({
+ name,
+ children: r[name].result,
+ isFile: folderPath.length - 1 === i && !folder.isEmptyDirectory,
+ file: folder,
+ });
+ }
+
+ return r[name];
+ }, level);
});
} catch (e) {
console.error("convertToTree", e);
@@ -247,52 +251,71 @@ class FilesActionStore {
return result;
};
- createFolderTree = async (treeList, parentFolderId) => {
+ createFolderTree = async (treeList, parentFolderId, filesList) => {
if (!treeList || !treeList.length) return;
for (let i = 0; i < treeList.length; i++) {
const treeNode = treeList[i];
+ const isFile = treeList[i].isFile;
// console.log(
// `createFolderTree parent id = ${parentFolderId} name '${treeNode.name}': `,
// treeNode.children
// );
+ if (isFile) {
+ treeList[i].file.parentFolderId = parentFolderId;
+ filesList.push(treeList[i].file);
+ continue;
+ }
+
const folder = await createFolder(parentFolderId, treeNode.name);
const parentId = folder.id;
if (treeNode.children.length == 0) continue;
- await this.createFolderTree(treeNode.children, parentId);
+ await this.createFolderTree(treeNode.children, parentId, filesList);
}
+
+ return treeList;
};
- uploadEmptyFolders = async (emptyFolders, folderId) => {
- //console.log("uploadEmptyFolders", emptyFolders, folderId);
+ createFoldersTree = async (files, folderId) => {
+ //console.log("createFoldersTree", files, folderId);
- const { secondaryProgressDataStore } = this.uploadDataStore;
- const { setSecondaryProgressBarData, clearSecondaryProgressData } =
- secondaryProgressDataStore;
+ const { primaryProgressDataStore } = this.uploadDataStore;
+
+ const { setPrimaryProgressBarData, clearPrimaryProgressData } =
+ primaryProgressDataStore;
const operationId = uniqueid("operation_");
const toFolderId = folderId ? folderId : this.selectedFolderStore.id;
- setSecondaryProgressBarData({
- icon: "file",
+ const pbData = {
+ icon: "upload",
visible: true,
percent: 0,
label: "",
alert: false,
- operationId,
- });
+ };
- const tree = this.convertToTree(emptyFolders);
- await this.createFolderTree(tree, toFolderId);
+ setPrimaryProgressBarData({ ...pbData, disableUploadPanelOpen: true });
+
+ const tree = this.convertToTree(files);
+
+ const filesList = [];
+ await this.createFolderTree(tree, toFolderId, filesList);
this.updateCurrentFolder(null, [folderId], null, operationId);
- setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
+ if (!filesList.length) {
+ setTimeout(() => clearPrimaryProgressData(), TIMEOUT);
+ } else {
+ setPrimaryProgressBarData({ ...pbData, disableUploadPanelOpen: false });
+ }
+
+ return filesList;
};
updateFilesAfterDelete = (operationId) => {
@@ -1796,13 +1819,14 @@ class FilesActionStore {
archiveRooms = (action) => {
const {
setArchiveDialogVisible,
- setInviteUsersWarningDialogVisible,
+ setQuotaWarningDialogVisible,
setRestoreRoomDialogVisible,
} = this.dialogsStore;
- const { isGracePeriod } = this.currentTariffStatusStore;
- if (action === "unarchive" && isGracePeriod) {
- setInviteUsersWarningDialogVisible(true);
+ const { isWarningRoomsDialog } = this.currentQuotaStore;
+
+ if (action === "unarchive" && isWarningRoomsDialog) {
+ setQuotaWarningDialogVisible(true);
return;
}
diff --git a/packages/client/src/store/GroupsStore.ts b/packages/client/src/store/GroupsStore.ts
index 28a14c28b2..ca184a7265 100644
--- a/packages/client/src/store/GroupsStore.ts
+++ b/packages/client/src/store/GroupsStore.ts
@@ -152,6 +152,24 @@ class GroupsStore {
return false;
}
+ get insideGroupIsFiltered() {
+ return (
+ this.insideGroupFilter.activationStatus ||
+ this.insideGroupFilter.employeeStatus ||
+ this.insideGroupFilter.payments ||
+ this.insideGroupFilter.search ||
+ this.insideGroupFilter.role ||
+ this.insideGroupFilter.accountLoginType
+ );
+ }
+
+ get isCurrentGroupEmpty() {
+ return (
+ !this.insideGroupIsFiltered &&
+ this.peopleStore.usersStore.peopleList.length === 0
+ );
+ }
+
// Inside Group Filter
setInsideGroupFilter = (filter) => {
@@ -282,6 +300,7 @@ class GroupsStore {
filter,
updateFilter = false,
withFilterLocalStorage = false,
+ updateCurrentGroup = false,
) => {
this.setInsideGroupLoading(true);
@@ -309,7 +328,7 @@ class GroupsStore {
requests.push(api.people.getUserList(filterData));
- if (groupId !== this.currentGroup?.id) {
+ if (updateCurrentGroup || groupId !== this.currentGroup?.id) {
requests.push(groupsApi.getGroupById(groupId));
}
@@ -329,6 +348,18 @@ class GroupsStore {
return Promise.resolve(filteredMembersRes.items);
};
+ refreshInsideGroup = async () => {
+ if (!this.currentGroup) return;
+
+ await this.fetchGroup(
+ this.currentGroup.id,
+ this.insideGroupFilter,
+ true,
+ false,
+ true,
+ );
+ };
+
get hasMoreInsideGroupUsers() {
return (
this.peopleStore.usersStore.users.length < this.insideGroupFilter.total
@@ -662,10 +693,12 @@ class GroupsStore {
}
if (getIsInsideGroup() && this.currentGroup?.id === groupId) {
+ const filter = this.insideGroupFilter.clone();
+
this.setCurrentGroup(res);
- const members = await api.people.getUserList(
- this.insideGroupFilter.clone(),
- );
+ const members = await api.people.getUserList(filter);
+ filter.total = members.total;
+ this.setInsideGroupFilter(filter);
this.peopleStore.usersStore.setUsers(members.items);
this.setInsideGroupTempTitle(res.name);
}
diff --git a/packages/client/src/store/HotkeyStore.js b/packages/client/src/store/HotkeyStore.js
index 46ed343cdb..559ed6d98d 100644
--- a/packages/client/src/store/HotkeyStore.js
+++ b/packages/client/src/store/HotkeyStore.js
@@ -683,9 +683,8 @@ class HotkeyStore {
};
uploadClipboardFiles = async (t, event) => {
- const { uploadEmptyFolders } = this.filesActionsStore;
+ const { createFoldersTree } = this.filesActionsStore;
const { startUpload } = this.uploadDataStore;
- const currentFolderId = this.selectedFolderStore.id;
if (this.filesStore.hotkeysClipboard.length) {
return this.moveFilesFromClipboard(t);
@@ -693,16 +692,9 @@ class HotkeyStore {
const files = await getFilesFromEvent(event);
- const emptyFolders = files.filter((f) => f.isEmptyDirectory);
-
- if (emptyFolders.length > 0) {
- uploadEmptyFolders(emptyFolders, currentFolderId).then(() => {
- const onlyFiles = files.filter((f) => !f.isEmptyDirectory);
- if (onlyFiles.length > 0) startUpload(onlyFiles, currentFolderId, t);
- });
- } else {
- startUpload(files, currentFolderId, t);
- }
+ createFoldersTree(files, uploadToFolder).then((f) => {
+ if (f.length > 0) startUpload(f, null, t);
+ });
};
get countTilesInRow() {
diff --git a/packages/client/src/store/MediaViewerDataStore.js b/packages/client/src/store/MediaViewerDataStore.js
index 9127e55e8c..610a18ce31 100644
--- a/packages/client/src/store/MediaViewerDataStore.js
+++ b/packages/client/src/store/MediaViewerDataStore.js
@@ -47,6 +47,8 @@ class MediaViewerDataStore {
publicRoomStore;
+ autoPlay = true;
+
id = null;
visible = false;
previewFile = null;
@@ -60,9 +62,14 @@ class MediaViewerDataStore {
this.publicRoomStore = publicRoomStore;
}
+ setAutoPlay = (value) => {
+ this.autoPlay = value;
+ };
+
setMediaViewerData = (mediaData) => {
this.id = mediaData.id;
this.visible = mediaData.visible;
+ this.setAutoPlay(true);
if (!mediaData.visible) this.setCurrentItem(null);
};
@@ -109,6 +116,7 @@ class MediaViewerDataStore {
)
return;
+ this.setAutoPlay(false);
this.previewFile = file;
this.id = file.id;
this.visible = visible;
@@ -167,6 +175,9 @@ class MediaViewerDataStore {
if (postionIndex === 0) {
return;
}
+
+ this.setAutoPlay(false);
+
const currentFileId = this.playlist[postionIndex].fileId;
const targetFile = files.find((item) => item.id === currentFileId);
@@ -187,6 +198,8 @@ class MediaViewerDataStore {
return;
}
+ this.setAutoPlay(false);
+
const currentFileId = this.playlist[currentPlaylistPos].fileId;
const targetFile = files.find((item) => item.id === currentFileId);
diff --git a/packages/client/src/store/PrimaryProgressDataStore.js b/packages/client/src/store/PrimaryProgressDataStore.js
index 3230bc1132..345724c93e 100644
--- a/packages/client/src/store/PrimaryProgressDataStore.js
+++ b/packages/client/src/store/PrimaryProgressDataStore.js
@@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
-import { makeObservable, action, observable } from "mobx";
+import { makeAutoObservable } from "mobx";
class PrimaryProgressDataStore {
percent = 0;
@@ -34,21 +34,10 @@ class PrimaryProgressDataStore {
alert = false;
loadingFile = null;
errors = 0;
+ disableUploadPanelOpen = false;
constructor() {
- makeObservable(this, {
- percent: observable,
- label: observable,
- visible: observable,
- icon: observable,
- alert: observable,
- loadingFile: observable,
- errors: observable,
-
- setPrimaryProgressBarData: action,
- clearPrimaryProgressData: action,
- setPrimaryProgressBarErrors: action,
- });
+ makeAutoObservable(this);
}
setPrimaryProgressBarData = (primaryProgressData) => {
@@ -66,6 +55,7 @@ class PrimaryProgressDataStore {
icon: "",
alert: false,
errors: 0,
+ disableUploadPanelOpen: false,
});
};
diff --git a/packages/client/src/store/SsoFormStore.js b/packages/client/src/store/SsoFormStore.js
index 3ba3500ef1..0d31489d6e 100644
--- a/packages/client/src/store/SsoFormStore.js
+++ b/packages/client/src/store/SsoFormStore.js
@@ -946,6 +946,33 @@ class SsoFormStore {
);
}
+ get isDisabledSaveButton() {
+ return (
+ !this.enableSso ||
+ this.hasErrors ||
+ !this.hasChanges ||
+ this.isLoadingXml ||
+ this.isRequiredFieldsEmpty
+ );
+ }
+
+ get isRequiredFieldsEmpty() {
+ return (
+ this.entityId.trim().length === 0 ||
+ (this.ssoBinding === BINDING_POST &&
+ this.ssoUrlPost.trim().length === 0) ||
+ (this.sloBinding === BINDING_POST &&
+ this.sloUrlPost.trim().length === 0) ||
+ (this.ssoBinding === BINDING_REDIRECT &&
+ this.ssoUrlRedirect.trim().length === 0) ||
+ (this.sloBinding === BINDING_REDIRECT &&
+ this.sloUrlRedirect.trim().length === 0) ||
+ this.firstName.trim().length === 0 ||
+ this.lastName.trim().length === 0 ||
+ this.email.trim().length === 0
+ );
+ }
+
scrollToField = () => {
for (let key in this) {
if (key.includes("HasError") && this[key] !== false) {
diff --git a/packages/client/src/store/UploadDataStore.js b/packages/client/src/store/UploadDataStore.js
index fb1331c616..305ce7c1ed 100644
--- a/packages/client/src/store/UploadDataStore.js
+++ b/packages/client/src/store/UploadDataStore.js
@@ -25,9 +25,11 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { makeAutoObservable, runInAction } from "mobx";
+import { Trans } from "react-i18next";
import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import uniqueid from "lodash/uniqueId";
import sumBy from "lodash/sumBy";
+import uniqBy from "lodash/uniqBy";
import { ConflictResolveType } from "@docspace/shared/enums";
import {
getFileInfo,
@@ -57,6 +59,8 @@ import {
getCategoryTypeByFolderType,
getCategoryUrl,
} from "SRC_DIR/helpers/utils";
+import { Link } from "@docspace/shared/components/link";
+import { globalColors } from "@docspace/shared/themes";
class UploadDataStore {
settingsStore;
@@ -636,7 +640,7 @@ class UploadDataStore {
if (this.uploaded || (this.isParallel && allFilesIsUploaded)) {
this.setConversionPercent(100);
- this.finishUploadFiles();
+ this.finishUploadFiles(t);
} else {
runInAction(() => {
this.converted = true;
@@ -773,7 +777,8 @@ class UploadDataStore {
file: file,
uniqueId: uniqueid("download_row-key_"),
fileId: null,
- toFolderId,
+ // toFolderId,
+ toFolderId: file.parentFolderId,
action: "upload",
error: file.size ? null : t("Files:EmptyFile"),
fileInfo: null,
@@ -1267,7 +1272,7 @@ class UploadDataStore {
let files = this.files;
if (files.length === 0 || this.filesSize === 0) {
- return this.finishUploadFiles();
+ return this.finishUploadFiles(t);
}
const progressData = {
@@ -1314,7 +1319,7 @@ class UploadDataStore {
}
if (!this.filesToConversion.length) {
- this.finishUploadFiles();
+ this.finishUploadFiles(t);
} else {
runInAction(() => {
this.uploaded = true;
@@ -1376,7 +1381,7 @@ class UploadDataStore {
toFolderId,
fileName,
fileSize,
- relativePath,
+ "", // relativePath,
file.encrypted,
file.lastModifiedDate,
createNewIfExist,
@@ -1516,7 +1521,7 @@ class UploadDataStore {
if (allFilesIsUploaded) {
if (!this.filesToConversion.length) {
- this.finishUploadFiles();
+ this.finishUploadFiles(t);
} else {
runInAction(() => {
this.uploaded = true;
@@ -1546,7 +1551,7 @@ class UploadDataStore {
});
};
- finishUploadFiles = () => {
+ finishUploadFiles = (t) => {
const { fetchFiles, filter } = this.filesStore;
const { withPaging } = this.settingsStore;
@@ -1558,7 +1563,7 @@ class UploadDataStore {
this.asyncUploadObj = {};
for (let item of this.tempFiles) {
- const { uploadFiles, folderId, t } = item;
+ const { uploadFiles, folderId } = item;
this.startUpload(uploadFiles, folderId, t);
}
this.tempFiles = [];
@@ -1566,12 +1571,36 @@ class UploadDataStore {
return;
}
- const totalErrorsCount = sumBy(this.files, (f) => {
- f.error && toastr.error(f.error);
- return f.error ? 1 : 0;
- });
+ const filesWithErrors = this.files.filter((f) => f.error);
+
+ const totalErrorsCount = filesWithErrors.length;
if (totalErrorsCount > 0) {
+ const uniqErrors = uniqBy(filesWithErrors, "error");
+ uniqErrors.forEach((f) =>
+ f.error.indexOf("password") > -1
+ ? toastr.warning(
+ {
+ toastr.clear();
+ this.setUploadPanelVisible(true);
+ }}
+ />,
+ ]}
+ />,
+ null,
+ 60000,
+ true,
+ )
+ : toastr.error(f.error),
+ );
+
this.primaryProgressDataStore.setPrimaryProgressBarShowError(true); // for empty file
this.primaryProgressDataStore.setPrimaryProgressBarErrors(
totalErrorsCount,
diff --git a/packages/client/src/store/UsersStore.js b/packages/client/src/store/UsersStore.js
index 6d5bf98f91..fec925f401 100644
--- a/packages/client/src/store/UsersStore.js
+++ b/packages/client/src/store/UsersStore.js
@@ -149,9 +149,14 @@ class UsersStore {
return Promise.resolve(result);
};
- removeUser = async (userId, filter) => {
+ removeUser = async (userId, filter, isInsideGroup) => {
+ const { refreshInsideGroup } = this.peopleStore.groupsStore;
+
await api.people.deleteUsers(userId);
- await this.getUsersList(filter, true);
+
+ isInsideGroup
+ ? await refreshInsideGroup()
+ : await this.getUsersList(filter, true);
};
get needResetUserSelection() {
diff --git a/packages/client/src/store/index.js b/packages/client/src/store/index.js
index b011fd0096..68561efb8c 100644
--- a/packages/client/src/store/index.js
+++ b/packages/client/src/store/index.js
@@ -239,6 +239,7 @@ const contextOptionsStore = new ContextOptionsStore(
pluginStore,
infoPanelStore,
currentTariffStatusStore,
+ currentQuotaStore,
userStore,
clientLoadingStore,
);
@@ -291,6 +292,7 @@ const createEditRoomStore = new CreateEditRoomStore(
infoPanelStore,
currentQuotaStore,
clientLoadingStore,
+ dialogsStore,
);
const webhooksStore = new WebhooksStore(settingsStore);
diff --git a/packages/shared/api/portal/types.ts b/packages/shared/api/portal/types.ts
index 2c9369dd44..172c813186 100644
--- a/packages/shared/api/portal/types.ts
+++ b/packages/shared/api/portal/types.ts
@@ -110,15 +110,6 @@ export type TTariff = {
quotas: TQuotas[];
};
-export type TTenantExtraRes = {
- customMode: boolean;
- opensource: boolean;
- enterprise: boolean;
- notPaid: boolean;
- licenseAccept: Date;
- enableTariffPage: boolean;
-};
-
export type TRestoreProgress = {
progress: number;
error?: TError;
diff --git a/packages/shared/components/access-right-select/AccessRightSelect.tsx b/packages/shared/components/access-right-select/AccessRightSelect.tsx
index 3034fe0195..7c1b0a94f7 100644
--- a/packages/shared/components/access-right-select/AccessRightSelect.tsx
+++ b/packages/shared/components/access-right-select/AccessRightSelect.tsx
@@ -29,6 +29,7 @@ import React, { useState, useEffect, useCallback } from "react";
import { DropDownItem } from "../drop-down-item";
import { Badge } from "../badge";
import { TOption } from "../combobox";
+import { toastr } from "../toast";
import {
StyledItemTitle,
@@ -47,6 +48,9 @@ export const AccessRightSelectPure = ({
selectedOption,
className,
type,
+ isSelectionDisabled,
+ selectionErrorText,
+ availableAccess,
...props
}: AccessRightSelectProps) => {
const [currentItem, setCurrentItem] = useState(selectedOption);
@@ -58,6 +62,20 @@ export const AccessRightSelectPure = ({
const onSelectCurrentItem = useCallback(
(option: TOption) => {
if (option) {
+ if (isSelectionDisabled) {
+ let isError =
+ option.access && option.access !== selectedOption.access;
+
+ if (availableAccess && option.access) {
+ isError = availableAccess.every((item) => item !== option.access);
+ }
+
+ if (isError) {
+ toastr.error(selectionErrorText);
+ return;
+ }
+ }
+
setCurrentItem(option);
onSelect?.(option);
}
diff --git a/packages/shared/components/access-right-select/AccessRightSelect.types.ts b/packages/shared/components/access-right-select/AccessRightSelect.types.ts
index c3299c0ee4..420d10b34b 100644
--- a/packages/shared/components/access-right-select/AccessRightSelect.types.ts
+++ b/packages/shared/components/access-right-select/AccessRightSelect.types.ts
@@ -52,4 +52,7 @@ type PropsFromCombobox = Pick<
export type AccessRightSelectProps = PropsFromCombobox & {
/** List of access options */
accessOptions: ComboboxProps["options"];
+ isSelectionDisabled?: boolean;
+ selectionErrorText?: React.ReactNode;
+ availableAccess?: number[];
};
diff --git a/packages/shared/components/media-viewer/MediaViewer.tsx b/packages/shared/components/media-viewer/MediaViewer.tsx
index 149811225c..15123122a3 100644
--- a/packages/shared/components/media-viewer/MediaViewer.tsx
+++ b/packages/shared/components/media-viewer/MediaViewer.tsx
@@ -64,6 +64,7 @@ const MediaViewer = (props: MediaViewerProps): JSX.Element | undefined => {
pluginContextMenuItems,
currentDeviceType,
isPublicFile = false,
+ autoPlay = false,
t,
getIcon,
@@ -453,6 +454,7 @@ const MediaViewer = (props: MediaViewerProps): JSX.Element | undefined => {
playlistPos={playlistPos}
isPublicFile={isPublicFile}
isPreviewFile={isPreviewFile}
+ autoPlay={autoPlay}
currentDeviceType={currentDeviceType}
onClose={onClose}
onPrevClick={prevMedia}
diff --git a/packages/shared/components/media-viewer/MediaViewer.types.ts b/packages/shared/components/media-viewer/MediaViewer.types.ts
index c7cc7880d2..d5583d9c81 100644
--- a/packages/shared/components/media-viewer/MediaViewer.types.ts
+++ b/packages/shared/components/media-viewer/MediaViewer.types.ts
@@ -91,6 +91,9 @@ export interface MediaViewerProps {
playlistPos: number;
/** Indicates if the current file is a preview. */
isPreviewFile?: boolean;
+
+ autoPlay?: boolean;
+
isPublicFile?: boolean;
/** List of playlists. */
playlist: PlaylistType[];
diff --git a/packages/shared/components/media-viewer/sub-components/Viewer/Viewer.props.ts b/packages/shared/components/media-viewer/sub-components/Viewer/Viewer.props.ts
index fb793bd68c..e923b7aa68 100644
--- a/packages/shared/components/media-viewer/sub-components/Viewer/Viewer.props.ts
+++ b/packages/shared/components/media-viewer/sub-components/Viewer/Viewer.props.ts
@@ -38,7 +38,7 @@ interface ViewerProps {
visible: boolean;
isImage: boolean;
isPdf: boolean;
-
+ autoPlay: boolean;
playlist: PlaylistType[];
audioIcon: string;
diff --git a/packages/shared/components/media-viewer/sub-components/Viewer/index.tsx b/packages/shared/components/media-viewer/sub-components/Viewer/index.tsx
index 39f7918417..56bcf2faa0 100644
--- a/packages/shared/components/media-viewer/sub-components/Viewer/index.tsx
+++ b/packages/shared/components/media-viewer/sub-components/Viewer/index.tsx
@@ -59,6 +59,7 @@ export const Viewer = (props: ViewerProps) => {
fileUrl,
toolbar,
playlist,
+ autoPlay,
audioIcon,
errorTitle,
targetFile,
@@ -277,6 +278,7 @@ export const Viewer = (props: ViewerProps) => {
props.theme.errorContainer.background};
+ cursor: default;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 0 auto 8px 0;
+ padding: 100px 20px 20px 20px;
+ border: 0;
+ box-sizing: border-box;
+
+ .operation-logo {
+ margin-top: 115px;
+ margin-bottom: 50px;
+ }
+
+ .operation-description {
+ margin-top: 16px;
+ color: ${(props) =>
+ props.theme.preparationPortalProgress.descriptionTextColor};
+ }
+`;
+
+export { StyledOperationContainer };
diff --git a/packages/shared/components/operation-container/OperationContainer.types.ts b/packages/shared/components/operation-container/OperationContainer.types.ts
new file mode 100644
index 0000000000..47e1dba80a
--- /dev/null
+++ b/packages/shared/components/operation-container/OperationContainer.types.ts
@@ -0,0 +1,31 @@
+// (c) Copyright Ascensio System SIA 2009-2024
+//
+// This program is a free software product.
+// You can redistribute it and/or modify it under the terms
+// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
+// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
+// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
+// any third-party rights.
+//
+// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
+// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
+//
+// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
+//
+// The interactive user interfaces in modified source and object code versions of the Program must
+// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
+//
+// Pursuant to Section 7(b) of the License you must retain the original Product logo when
+// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
+// trademark law for use of our trademarks.
+//
+// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
+// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
+// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
+
+export interface IOperationContainer {
+ url?: string;
+ title: string;
+ description: string;
+}
diff --git a/packages/shared/components/operation-container/index.tsx b/packages/shared/components/operation-container/index.tsx
new file mode 100644
index 0000000000..276231f652
--- /dev/null
+++ b/packages/shared/components/operation-container/index.tsx
@@ -0,0 +1,56 @@
+// (c) Copyright Ascensio System SIA 2009-2024
+//
+// This program is a free software product.
+// You can redistribute it and/or modify it under the terms
+// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
+// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
+// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
+// any third-party rights.
+//
+// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
+// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
+//
+// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
+//
+// The interactive user interfaces in modified source and object code versions of the Program must
+// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
+//
+// Pursuant to Section 7(b) of the License you must retain the original Product logo when
+// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
+// trademark law for use of our trademarks.
+//
+// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
+// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
+// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
+import React, { useEffect } from "react";
+
+import DownloadingReactSvg from "PUBLIC_DIR/images/downloading.react.svg";
+
+import { StyledOperationContainer } from "./OperationContainer.styled";
+import { IOperationContainer } from "./OperationContainer.types";
+import { Text } from "../text";
+import PortalLogo from "../portal-logo/PortalLogo";
+
+const OperationContainer = (props: IOperationContainer) => {
+ const { url, title, description } = props;
+
+ useEffect(() => {
+ if (url) window.location.replace(url);
+ }, [url]);
+
+ return (
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+ );
+};
+
+export default OperationContainer;
diff --git a/packages/shared/components/section/Section.types.ts b/packages/shared/components/section/Section.types.ts
index 061b602129..90ad52348f 100644
--- a/packages/shared/components/section/Section.types.ts
+++ b/packages/shared/components/section/Section.types.ts
@@ -145,3 +145,7 @@ export interface SectionProps {
isDesktop?: boolean;
getContextModel?: () => ContextMenuModel[];
}
+
+export interface SectionContextMenuProps {
+ getContextModel: () => ContextMenuModel[];
+}
diff --git a/packages/shared/components/section/sub-components/SectionBody.tsx b/packages/shared/components/section/sub-components/SectionBody.tsx
index 914a2e92c6..eba713e65f 100644
--- a/packages/shared/components/section/sub-components/SectionBody.tsx
+++ b/packages/shared/components/section/sub-components/SectionBody.tsx
@@ -29,14 +29,13 @@ import { useLocation } from "react-router-dom";
// import { inject, observer } from "mobx-react";
-import { ContextMenu } from "@docspace/shared/components/context-menu";
-
import {
StyledDropZoneBody,
StyledSpacer,
StyledSectionBody,
} from "../Section.styled";
import { SectionBodyProps } from "../Section.types";
+import SectionContextMenu from "./SectionContextMenu";
const SectionBody = React.memo(
({
@@ -53,49 +52,9 @@ const SectionBody = React.memo(
getContextModel,
}: SectionBodyProps) => {
const focusRef = React.useRef(null);
- const cmRef = React.useRef void;
- hide: (e: React.MouseEvent | MouseEvent) => void;
- toggle: (e: React.MouseEvent | MouseEvent) => boolean;
- getVisible: () => boolean;
- }>(null);
+
const location = useLocation();
- const [isOpen, setIsOpen] = React.useState(false);
-
- const onContextMenu = React.useCallback(
- (e: MouseEvent | React.MouseEvent) => {
- const bodyElem = document.getElementsByClassName(
- "section-body",
- )[0] as HTMLDivElement;
-
- const target = e.target as Node;
-
- if (
- !getContextModel ||
- !getContextModel() ||
- !bodyElem ||
- !bodyElem.contains(target)
- )
- return;
-
- e.stopPropagation();
- e.preventDefault();
-
- // if (cmRef.current) cmRef.current.toggle(e);
- if (cmRef.current) {
- if (!isOpen) cmRef?.current?.show(e);
- else cmRef?.current?.hide(e);
- setIsOpen(!isOpen);
- }
- },
- [getContextModel, isOpen],
- );
-
- const onHide = () => {
- setIsOpen(false);
- };
-
const focusSectionBody = React.useCallback(() => {
if (focusRef.current) focusRef.current.focus({ preventScroll: true });
}, []);
@@ -108,14 +67,6 @@ const SectionBody = React.memo(
[focusSectionBody],
);
- React.useEffect(() => {
- document.addEventListener("contextmenu", onContextMenu);
-
- return () => {
- document.removeEventListener("contextmenu", onContextMenu);
- };
- }, [onContextMenu]);
-
React.useEffect(() => {
if (!autoFocus) return;
@@ -145,16 +96,6 @@ const SectionBody = React.memo(
}
: {};
- const contextBlock = (
-
- );
-
return uploadFiles ? (
)}
- {contextBlock}
+
) : (
{children}
)}
- {contextBlock}
+