Web: Files: Templates: added TemplateAccessSettingsPanel
This commit is contained in:
parent
c8c7d29588
commit
8f64f6cc2d
@ -195,5 +195,11 @@
|
||||
"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"
|
||||
"YouLeftTheRoom": "You have left the room",
|
||||
"TemplateAvailable": "Template available to everyone",
|
||||
"TemplateAvailableDescription": "All DocSpace and Room admins will be able to create rooms using this template.",
|
||||
"AddUsersOrGroups": "Add Users or Groups",
|
||||
"AddUsersOrGroupsDescription": "The added administrators will be able to create rooms using this template.",
|
||||
"AddUsersOrGroupsInfo": "Only DocSpace and Room admins are shown her",
|
||||
"AddUsersOrGroupsInfoGroups": "Only DocSpace and Room admins from the selected groups will be able to create rooms using this template."
|
||||
}
|
||||
|
@ -100,6 +100,10 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
}
|
||||
};
|
||||
|
||||
onCreateRoom = () => {
|
||||
this.props.onCreateRoomFromTemplate(this.props.item);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLoading } = this.state;
|
||||
|
||||
@ -115,7 +119,6 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
isPersonalRoom,
|
||||
isArchiveFolder,
|
||||
isTemplatesFolder,
|
||||
onCreateRoomFromTemplate,
|
||||
} = this.props;
|
||||
|
||||
const quickButtonsComponent = (
|
||||
@ -136,7 +139,7 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
folderCategory={folderCategory}
|
||||
onCopyPrimaryLink={this.onCopyPrimaryLink}
|
||||
isArchiveFolder={isArchiveFolder}
|
||||
onCreateRoom={onCreateRoomFromTemplate}
|
||||
onCreateRoom={this.onCreateRoom}
|
||||
isTemplatesFolder={isTemplatesFolder}
|
||||
/>
|
||||
);
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
InvitePanel,
|
||||
EditLinkPanel,
|
||||
EmbeddingPanel,
|
||||
TemplateAccessSettingsPanel,
|
||||
} from "../panels";
|
||||
import {
|
||||
ConnectDialog,
|
||||
@ -131,6 +132,7 @@ const Panels = (props) => {
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
createRoomTemplateDialogVisible,
|
||||
templateAccessSettingsVisible,
|
||||
} = props;
|
||||
|
||||
const [createPDFFormFile, setCreatePDFFormFile] = useState({
|
||||
@ -320,6 +322,9 @@ const Panels = (props) => {
|
||||
createRoomTemplateDialogVisible && (
|
||||
<CreateRoomTemplateDialog key="create-room-template-dialog" />
|
||||
),
|
||||
templateAccessSettingsVisible && (
|
||||
<TemplateAccessSettingsPanel key="template-access-settings" />
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
@ -380,6 +385,7 @@ export default inject(
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
createRoomTemplateDialogVisible,
|
||||
templateAccessSettingsVisible,
|
||||
} = dialogsStore;
|
||||
|
||||
const { preparationPortalDialogVisible } = backup;
|
||||
@ -447,6 +453,7 @@ export default inject(
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
createRoomTemplateDialogVisible,
|
||||
templateAccessSettingsVisible,
|
||||
};
|
||||
},
|
||||
)(observer(Panels));
|
||||
|
@ -83,8 +83,15 @@ const StyledButtonContainer = styled.div`
|
||||
`;
|
||||
|
||||
const CreateRoomTemplate = (props) => {
|
||||
const { visible, onClose, item, fetchedTags, folderFormValidation, isEdit } =
|
||||
props;
|
||||
const {
|
||||
visible,
|
||||
onClose,
|
||||
item,
|
||||
fetchedTags,
|
||||
folderFormValidation,
|
||||
isEdit,
|
||||
setAccessSettingsIsVisible,
|
||||
} = props;
|
||||
const { roomType, title, logo, createdBy } = item;
|
||||
console.log("item", item);
|
||||
|
||||
@ -148,6 +155,10 @@ const CreateRoomTemplate = (props) => {
|
||||
setOpenCreatedIsChecked(!openCreatedIsChecked);
|
||||
};
|
||||
|
||||
const onOpenAccessSettings = () => {
|
||||
setAccessSettingsIsVisible(true);
|
||||
};
|
||||
|
||||
const tagHandler = new TagHandler(tags, setRoomTags, fetchedTags);
|
||||
|
||||
return (
|
||||
@ -198,7 +209,7 @@ const CreateRoomTemplate = (props) => {
|
||||
<ChangeRoomOwner
|
||||
roomOwner={createdBy}
|
||||
isTemplate
|
||||
onOwnerChange={() => console.log("Access settings")}
|
||||
onOwnerChange={onOpenAccessSettings}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@ -262,7 +273,10 @@ const CreateRoomTemplate = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ settingsStore }) => {
|
||||
export default inject(({ settingsStore, dialogsStore }) => {
|
||||
const { folderFormValidation } = settingsStore;
|
||||
return { folderFormValidation };
|
||||
const { setTemplateAccessSettingsVisible: setAccessSettingsIsVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return { folderFormValidation, setAccessSettingsIsVisible };
|
||||
})(observer(CreateRoomTemplate));
|
||||
|
@ -0,0 +1,553 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Heading } from "@docspace/shared/components/heading";
|
||||
import { TextInput } from "@docspace/shared/components/text-input";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { DropDown } from "@docspace/shared/components/drop-down";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { ToggleButton } from "@docspace/shared/components/toggle-button";
|
||||
import { mobile, commonIconsStyles } from "@docspace/shared/utils";
|
||||
import CheckIcon from "PUBLIC_DIR/images/check.edit.react.svg";
|
||||
import CrossIcon from "PUBLIC_DIR/images/cross.edit.react.svg";
|
||||
import CrossIconMobile from "PUBLIC_DIR/images/cross.react.svg";
|
||||
import DeleteIcon from "PUBLIC_DIR/images/mobile.actions.remove.react.svg";
|
||||
import { isMobile, desktop, commonInputStyles } from "@docspace/shared/utils";
|
||||
import Base from "@docspace/shared/themes/base";
|
||||
|
||||
const fillAvailableWidth = css`
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
width: -webkit-fill-available;
|
||||
width: fill-available;
|
||||
`;
|
||||
|
||||
const StyledInvitePanel = styled.div`
|
||||
@media ${mobile} {
|
||||
user-select: none;
|
||||
height: auto;
|
||||
width: auto;
|
||||
background: ${(props) => props.theme.infoPanel.blurColor};
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 309;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.invite_panel {
|
||||
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
|
||||
border-left: ${(props) =>
|
||||
`1px solid ${props.theme.infoPanel.borderColor}`};
|
||||
position: absolute;
|
||||
border: none;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: calc(100% - 64px);
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
.invite-panel-body {
|
||||
height: calc(100% - 55px - 73px);
|
||||
|
||||
.scroll-body {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-left: 0px !important;
|
||||
`
|
||||
: css`
|
||||
padding-right: 0px !important;
|
||||
`}
|
||||
|
||||
@media ${desktop} {
|
||||
width: 480px;
|
||||
min-width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
!props.addUsersPanelVisible &&
|
||||
isMobile() &&
|
||||
props.theme.interfaceDirection !== "rtl" &&
|
||||
css`
|
||||
.trackYVisible {
|
||||
.scroller {
|
||||
margin-right: -20px !important;
|
||||
}
|
||||
}
|
||||
`}
|
||||
}
|
||||
`;
|
||||
|
||||
const ScrollList = styled.div`
|
||||
width: 100%;
|
||||
height: ${(props) =>
|
||||
props.scrollAllPanelContent && props.isTotalListHeight
|
||||
? "auto"
|
||||
: props.offsetTop && `calc(100% - ${props.offsetTop}px)`};
|
||||
|
||||
${!isMobile() &&
|
||||
css`
|
||||
.row-item {
|
||||
width: 448px !important;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledBlock = styled.div`
|
||||
padding: ${(props) => (props.noPadding ? "0px" : "0 16px")};
|
||||
border-bottom: ${(props) => props.theme.filesPanels.sharing.borderBottom};
|
||||
`;
|
||||
|
||||
StyledBlock.defaultProps = { theme: Base };
|
||||
|
||||
const StyledInviteUserBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const StyledHeading = styled(Heading)`
|
||||
font-weight: 700;
|
||||
font-size: 21px;
|
||||
`;
|
||||
|
||||
const StyledSubHeader = styled(Heading)`
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin: 16px 0 8px 0;
|
||||
|
||||
${(props) =>
|
||||
props.inline &&
|
||||
css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
`};
|
||||
`;
|
||||
|
||||
const StyledDescription = styled(Text)`
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
margin-bottom: 16px;
|
||||
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
`;
|
||||
|
||||
StyledDescription.defaultProps = { theme: Base };
|
||||
|
||||
const StyledRow = styled.div`
|
||||
width: calc(100% - 32px) !important;
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
min-height: 41px;
|
||||
|
||||
margin-inline-start: 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: none;
|
||||
|
||||
a {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.invite-panel_access-selector {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: 0;
|
||||
|
||||
${({ hasWarning }) => hasWarning && `margin-inline-start: 0;`}
|
||||
}
|
||||
|
||||
.warning {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.combo-button-label {
|
||||
color: ${(props) => props.theme.text.disableColor};
|
||||
}
|
||||
.combo-buttons_expander-icon path {
|
||||
fill: ${(props) => props.theme.text.disableColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledInviteInput = styled.div`
|
||||
${fillAvailableWidth}
|
||||
|
||||
margin: 0px 16px;
|
||||
|
||||
.input-link {
|
||||
height: 32px;
|
||||
border: 0px;
|
||||
|
||||
> input {
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
border: 1px solid rgb(208, 213, 218);
|
||||
border-radius: 3px;
|
||||
|
||||
.copy-link-icon {
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: ${(props) => props.theme.inputBlock.hoverIconColor} !important;
|
||||
}
|
||||
}
|
||||
|
||||
svg path {
|
||||
fill: ${(props) => props.theme.inputBlock.iconColor} !important;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-results-button,
|
||||
input[type="search"]::-webkit-search-results-decoration {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.append {
|
||||
display: ${(props) => (props.isShowCross ? "flex" : "none")};
|
||||
align-items: center;
|
||||
padding-right: 8px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
${commonInputStyles}
|
||||
|
||||
:focus-within {
|
||||
border-color: ${(props) => props.theme.inputBlock.borderColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledAccessSelector = styled.div`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 16px;
|
||||
`
|
||||
: css`
|
||||
margin-right: 16px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledEditInput = styled(TextInput)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledInviteInputContainer = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.header_aside-panel {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropDown = styled(DropDown)`
|
||||
${(props) => props.width && `width: ${props.width}px`};
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
height: 48px;
|
||||
|
||||
.list-item_content {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const SearchItemText = styled(Text)`
|
||||
line-height: ${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl" ? `20px` : `16px`};
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: ${(props) =>
|
||||
props.primary ? "14px" : props.info ? "11px" : "12px"};
|
||||
font-weight: ${(props) => (props.primary || props.info ? "600" : "400")};
|
||||
|
||||
color: ${(props) =>
|
||||
(props.primary && !props.disabled) || props.info
|
||||
? props.theme.text.color
|
||||
: props.theme.text.emailColor};
|
||||
${(props) => props.info && `margin-inline-start: auto`}
|
||||
`;
|
||||
|
||||
SearchItemText.defaultProps = { theme: Base };
|
||||
|
||||
const StyledEditButton = styled(Button)`
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0px;
|
||||
`;
|
||||
|
||||
const iconStyles = css`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: ${(props) => props.theme.filesEditingWrapper.fill} !important;
|
||||
}
|
||||
:hover {
|
||||
fill: ${(props) => props.theme.filesEditingWrapper.hoverFill} !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCheckIcon = styled(CheckIcon)`
|
||||
${iconStyles}
|
||||
`;
|
||||
|
||||
StyledCheckIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCrossIcon = styled(CrossIcon)`
|
||||
${iconStyles}
|
||||
`;
|
||||
|
||||
StyledCrossIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledDeleteIcon = styled(DeleteIcon)`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: auto;
|
||||
`
|
||||
: css`
|
||||
margin-left: auto;
|
||||
`}
|
||||
|
||||
${iconStyles}
|
||||
`;
|
||||
|
||||
StyledDeleteIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledHelpButton = styled(HelpButton)`
|
||||
margin-inline-start: 8px;
|
||||
`;
|
||||
|
||||
const StyledButtons = styled(Box)`
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
background: ${(props) => props.theme.filesPanels.sharing.backgroundButtons};
|
||||
border-top: ${(props) => props.theme.filesPanels.sharing.borderTop};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
float: ${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl" ? `left` : `right`};
|
||||
`;
|
||||
|
||||
const ResetLink = styled(Link)`
|
||||
float: ${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl" ? `right` : `left`};
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 13px;
|
||||
color: ${(props) => props.theme.createEditRoomDialog.commonParam.textColor};
|
||||
font-style: normal;
|
||||
line-height: 15px;
|
||||
`;
|
||||
|
||||
StyledButtons.defaultProps = { theme: Base };
|
||||
|
||||
const StyledToggleButton = styled(ToggleButton)`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
left: 8px;
|
||||
`
|
||||
: css`
|
||||
right: 8px;
|
||||
`}
|
||||
margin-top: -4px;
|
||||
`;
|
||||
|
||||
const StyledControlContainer = styled.div`
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
position: absolute;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 450;
|
||||
|
||||
@media ${mobile} {
|
||||
display: flex;
|
||||
|
||||
top: -27px;
|
||||
right: 10px;
|
||||
left: unset;
|
||||
}
|
||||
`;
|
||||
const StyledInviteLanguage = styled.div`
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin-top: -12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
height: 28px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
.list-link {
|
||||
margin-left: 4px;
|
||||
color: ${(props) => props.theme.createEditRoomDialog.commonParam.textColor};
|
||||
}
|
||||
|
||||
.invitation-language {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
}
|
||||
.language-combo-box {
|
||||
.combo-button {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.combo-button_closed:not(:hover) .combo-button-label {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
}
|
||||
.combo-button_closed:not(:hover) .combo-buttons_arrow-icon {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.language-combo-box-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
`;
|
||||
const StyledCrossIconMobile = styled(CrossIconMobile)`
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
z-index: 455;
|
||||
path {
|
||||
fill: ${(props) => props.theme.catalog.control.fill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledCrossIcon.defaultProps = { theme: Base };
|
||||
export {
|
||||
StyledBlock,
|
||||
StyledHeading,
|
||||
StyledInvitePanel,
|
||||
StyledRow,
|
||||
StyledSubHeader,
|
||||
StyledInviteInput,
|
||||
StyledInviteInputContainer,
|
||||
StyledDropDown,
|
||||
SearchItemText,
|
||||
StyledEditInput,
|
||||
StyledEditButton,
|
||||
StyledCheckIcon,
|
||||
StyledCrossIcon,
|
||||
StyledHelpButton,
|
||||
StyledDeleteIcon,
|
||||
StyledButtons,
|
||||
StyledLink,
|
||||
ResetLink,
|
||||
ScrollList,
|
||||
StyledAccessSelector,
|
||||
StyledToggleButton,
|
||||
StyledDescription,
|
||||
StyledInviteLanguage,
|
||||
StyledControlContainer,
|
||||
StyledCrossIconMobile,
|
||||
StyledInviteUserBody,
|
||||
};
|
@ -0,0 +1,309 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState, useMemo, useRef } from "react";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
import { Backdrop } from "@docspace/shared/components/backdrop";
|
||||
import { Aside } from "@docspace/shared/components/aside";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { Portal } from "@docspace/shared/components/portal";
|
||||
import { isDesktop, isMobile, size } from "@docspace/shared/utils";
|
||||
|
||||
import {
|
||||
StyledBlock,
|
||||
StyledHeading,
|
||||
StyledInvitePanel,
|
||||
StyledButtons,
|
||||
StyledControlContainer,
|
||||
StyledCrossIconMobile,
|
||||
StyledSubHeader,
|
||||
StyledToggleButton,
|
||||
StyledDescription,
|
||||
} from "./StyledInvitePanel";
|
||||
|
||||
import ItemsList from "./sub-components/ItemsList";
|
||||
import InviteInput from "./sub-components/InviteInput";
|
||||
import { Scrollbar } from "@docspace/shared/components/scrollbar";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import ArrowPathReactSvgUrl from "PUBLIC_DIR/images/arrow.path.react.svg?url";
|
||||
|
||||
const TemplateAccessSettingsPanel = ({
|
||||
t,
|
||||
visible,
|
||||
setIsVisible,
|
||||
setInfoPanelIsMobileHidden,
|
||||
currentDeviceType,
|
||||
}) => {
|
||||
const [isAvailable, setIsAvailable] = useState(false);
|
||||
const [inviteItems, setInviteItems] = useState([]);
|
||||
|
||||
const [hasErrors, setHasErrors] = useState(false);
|
||||
|
||||
const [scrollAllPanelContent, setScrollAllPanelContent] = useState(false);
|
||||
const [addUsersPanelVisible, setAddUsersPanelVisible] = useState(false);
|
||||
const [isMobileView, setIsMobileView] = useState(isMobile());
|
||||
|
||||
const invitePanelBodyRef = useRef();
|
||||
|
||||
const zIndex = 311;
|
||||
|
||||
useEffect(() => {
|
||||
const hasErrors = inviteItems.some((item) => !!item.errors?.length);
|
||||
|
||||
setHasErrors(hasErrors);
|
||||
}, [inviteItems]);
|
||||
|
||||
useEffect(() => {
|
||||
onCheckHeight();
|
||||
window.addEventListener("resize", onCheckHeight);
|
||||
return () => {
|
||||
window.removeEventListener("resize", onCheckHeight);
|
||||
window.removeEventListener("mousedown", onMouseDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
isMobileView && window.addEventListener("mousedown", onMouseDown);
|
||||
}, [isMobileView]);
|
||||
|
||||
const onMouseDown = (e) => {
|
||||
if (e.target.id === "InvitePanelWrapper") onClose();
|
||||
};
|
||||
|
||||
const onCheckHeight = () => {
|
||||
setScrollAllPanelContent(!isDesktop());
|
||||
setIsMobileView(isMobile());
|
||||
};
|
||||
|
||||
const onAvailableChange = () => {
|
||||
setIsAvailable(!isAvailable);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setInfoPanelIsMobileHidden(false);
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
const onKeyPress = (e) =>
|
||||
(e.key === "Esc" || e.key === "Escape") && onClose();
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keyup", onKeyPress);
|
||||
return () => document.removeEventListener("keyup", onKeyPress);
|
||||
});
|
||||
|
||||
const roomType = 2; //TODO: Templates
|
||||
// const roomType = -1;
|
||||
const hasInvitedUsers = !!inviteItems.length;
|
||||
|
||||
const bodyInvitePanel = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<StyledBlock noPadding>
|
||||
<StyledSubHeader inline>
|
||||
{t("Files:TemplateAvailable")}
|
||||
|
||||
<StyledToggleButton
|
||||
className="invite-via-link"
|
||||
isChecked={isAvailable}
|
||||
onChange={onAvailableChange}
|
||||
/>
|
||||
</StyledSubHeader>
|
||||
<StyledDescription>
|
||||
{t("Files:TemplateAvailableDescription")}
|
||||
</StyledDescription>
|
||||
</StyledBlock>
|
||||
<InviteInput
|
||||
t={t}
|
||||
onClose={onClose}
|
||||
inviteItems={inviteItems}
|
||||
setInviteItems={setInviteItems}
|
||||
roomType={roomType}
|
||||
addUsersPanelVisible={addUsersPanelVisible}
|
||||
setAddUsersPanelVisible={setAddUsersPanelVisible}
|
||||
isMobileView={isMobileView}
|
||||
/>
|
||||
<StyledSubHeader>{t("Files:AccessToTemplate")}</StyledSubHeader>
|
||||
{hasInvitedUsers && (
|
||||
<ItemsList
|
||||
t={t}
|
||||
inviteItems={inviteItems}
|
||||
setInviteItems={setInviteItems}
|
||||
setHasErrors={setHasErrors}
|
||||
roomType={roomType}
|
||||
scrollAllPanelContent={scrollAllPanelContent}
|
||||
invitePanelBodyRef={invitePanelBodyRef}
|
||||
isMobileView={isMobileView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [
|
||||
t,
|
||||
roomType,
|
||||
onClose,
|
||||
setHasErrors,
|
||||
scrollAllPanelContent,
|
||||
hasInvitedUsers,
|
||||
invitePanelBodyRef,
|
||||
]);
|
||||
|
||||
const invitePanelNode = (
|
||||
<>
|
||||
<StyledBlock
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={17}
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
className="sharing_panel-arrow"
|
||||
onClick={() => console.log("onArrowClick")} //TODO: Templates
|
||||
/>
|
||||
<StyledHeading>{t("Files:AccessSettings")}</StyledHeading>
|
||||
</StyledBlock>
|
||||
{
|
||||
<>
|
||||
{scrollAllPanelContent ? (
|
||||
<div className="invite-panel-body" ref={invitePanelBodyRef}>
|
||||
<Scrollbar>{bodyInvitePanel}</Scrollbar>
|
||||
</div>
|
||||
) : (
|
||||
bodyInvitePanel
|
||||
)}
|
||||
|
||||
<StyledButtons>
|
||||
<Button
|
||||
className="send-invitation"
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
isDisabled={hasErrors || !hasInvitedUsers}
|
||||
primary
|
||||
onClick={() => console.log("onSave")} //TODO: Templates
|
||||
label={t("Common:SaveButton")}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
onClick={onClose}
|
||||
label={t("Common:CancelButton")}
|
||||
/>
|
||||
</StyledButtons>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
||||
const invitePanelComponent = (
|
||||
<StyledInvitePanel
|
||||
id="InvitePanelWrapper"
|
||||
hasInvitedUsers={hasInvitedUsers}
|
||||
scrollAllPanelContent={scrollAllPanelContent}
|
||||
addUsersPanelVisible={addUsersPanelVisible}
|
||||
>
|
||||
{isMobileView ? (
|
||||
<div className="invite_panel">
|
||||
<StyledControlContainer onClick={onClose}>
|
||||
<StyledCrossIconMobile />
|
||||
</StyledControlContainer>
|
||||
{invitePanelNode}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Backdrop
|
||||
onClick={onClose}
|
||||
visible={visible}
|
||||
isAside={true}
|
||||
zIndex={currentDeviceType === DeviceType.mobile ? 11 : zIndex}
|
||||
/>
|
||||
<Aside
|
||||
className="invite_panel"
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
withoutBodyScroll
|
||||
zIndex={zIndex}
|
||||
>
|
||||
{invitePanelNode}
|
||||
</Aside>
|
||||
</>
|
||||
)}
|
||||
</StyledInvitePanel>
|
||||
);
|
||||
|
||||
const renderPortalInvitePanel = () => {
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
return (
|
||||
<Portal
|
||||
element={invitePanelComponent}
|
||||
appendTo={rootElement}
|
||||
visible={visible}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return currentDeviceType === DeviceType.mobile
|
||||
? renderPortalInvitePanel()
|
||||
: invitePanelComponent;
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ settingsStore, peopleStore, dialogsStore, infoPanelStore }) => {
|
||||
const { theme, currentDeviceType } = settingsStore;
|
||||
|
||||
const { getUsersByQuery } = peopleStore.usersStore;
|
||||
const { setIsMobileHidden: setInfoPanelIsMobileHidden } = infoPanelStore;
|
||||
|
||||
const { templateAccessSettingsVisible, setTemplateAccessSettingsVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
getUsersByQuery,
|
||||
theme,
|
||||
visible: templateAccessSettingsVisible,
|
||||
setIsVisible: setTemplateAccessSettingsVisible,
|
||||
setInfoPanelIsMobileHidden,
|
||||
currentDeviceType,
|
||||
};
|
||||
},
|
||||
)(
|
||||
withTranslation([
|
||||
"InviteDialog",
|
||||
"SharingPanel",
|
||||
"Translations",
|
||||
"Common",
|
||||
"InfoPanel",
|
||||
"PeopleSelector",
|
||||
])(observer(TemplateAccessSettingsPanel)),
|
||||
);
|
@ -0,0 +1,371 @@
|
||||
// (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 debounce from "lodash.debounce";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { useMemo, useState, useCallback, useEffect, useRef } from "react";
|
||||
|
||||
import { Avatar } from "@docspace/shared/components/avatar";
|
||||
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 { Aside } from "@docspace/shared/components/aside";
|
||||
import { Backdrop } from "@docspace/shared/components/backdrop";
|
||||
import PeopleSelector from "@docspace/shared/selectors/People";
|
||||
import Filter from "@docspace/shared/api/people/filter";
|
||||
import { getMembersList } from "@docspace/shared/api/people";
|
||||
import {
|
||||
AccountsSearchArea,
|
||||
EmployeeType,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
} from "@docspace/shared/enums";
|
||||
import withCultureNames from "SRC_DIR/HOCs/withCultureNames";
|
||||
import { checkIfAccessPaid } from "SRC_DIR/helpers";
|
||||
|
||||
import AddUsersPanel from "../../AddUsersPanel";
|
||||
import { getTopFreeRole } from "../utils";
|
||||
|
||||
import {
|
||||
StyledSubHeader,
|
||||
StyledLink,
|
||||
StyledInviteInput,
|
||||
StyledInviteInputContainer,
|
||||
StyledDropDown,
|
||||
SearchItemText,
|
||||
StyledDescription,
|
||||
StyledCrossIcon,
|
||||
} from "../StyledInvitePanel";
|
||||
|
||||
const minSearchValue = 1;
|
||||
const PEOPLE_TAB_ID = "0";
|
||||
|
||||
const InviteInput = ({
|
||||
t,
|
||||
roomId = 254, //TODO: Templates
|
||||
onClose,
|
||||
roomType,
|
||||
inviteItems,
|
||||
setInviteItems,
|
||||
addUsersPanelVisible,
|
||||
setAddUsersPanelVisible,
|
||||
isMobileView,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [selectedTab, setSelectedTab] = useState("");
|
||||
const [usersList, setUsersList] = useState([]);
|
||||
const [isAddEmailPanelBlocked, setIsAddEmailPanelBlocked] = useState(true);
|
||||
const [dropDownWidth, setDropDownWidth] = useState(0);
|
||||
const searchRef = useRef();
|
||||
|
||||
console.log("roomId", roomId);
|
||||
|
||||
const isPublicRoomType = roomType === RoomsType.PublicRoom;
|
||||
|
||||
const dropDownMaxHeight = usersList.length > 5 ? { maxHeight: 240 } : {};
|
||||
const foundUsers = usersList.map((user) => getItemContent(user));
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const width = searchRef?.current?.offsetWidth ?? 0;
|
||||
if (width !== dropDownWidth) setDropDownWidth(width);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
const searchByQuery = async (value) => {
|
||||
console.log("searchByQuery");
|
||||
return;
|
||||
|
||||
const query = value.trim();
|
||||
|
||||
if (query.length >= minSearchValue) {
|
||||
const searchArea = isPublicRoomType
|
||||
? AccountsSearchArea.People
|
||||
: AccountsSearchArea.Any;
|
||||
const filter = Filter.getFilterWithOutDisabledUser();
|
||||
filter.role = [EmployeeType.Admin, EmployeeType.User];
|
||||
filter.search = query;
|
||||
|
||||
const users = await getMembersList(searchArea, roomId, filter);
|
||||
|
||||
setUsersList(users.items);
|
||||
|
||||
if (users.total) setIsAddEmailPanelBlocked(false);
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
setIsAddEmailPanelBlocked(true);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedSearch = useCallback(
|
||||
debounce((value) => searchByQuery(value), 300),
|
||||
[],
|
||||
);
|
||||
|
||||
const onChange = (e) => {
|
||||
const value = e.target.value;
|
||||
onChangeInput(value);
|
||||
};
|
||||
|
||||
const onChangeInput = (value) => {
|
||||
const clearValue = value.trim();
|
||||
|
||||
setInputValue(value);
|
||||
|
||||
if (clearValue.length < minSearchValue) {
|
||||
setUsersList([]);
|
||||
setIsAddEmailPanelBlocked(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomId !== -1) {
|
||||
debouncedSearch(clearValue);
|
||||
}
|
||||
|
||||
setIsAddEmailPanelBlocked(true);
|
||||
};
|
||||
|
||||
const removeExist = (items) => {
|
||||
const filtered = items.reduce((unique, o) => {
|
||||
!unique.some((obj) =>
|
||||
obj.isGroup ? obj.id === o.id : obj.email === o.email,
|
||||
) && unique.push(o);
|
||||
|
||||
return unique;
|
||||
}, []);
|
||||
|
||||
if (items.length > filtered.length) toastr.warning(t("UsersAlreadyAdded"));
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
const getItemContent = (item) => {
|
||||
const {
|
||||
avatar,
|
||||
displayName,
|
||||
name: groupName,
|
||||
email,
|
||||
id,
|
||||
shared,
|
||||
isGroup = false,
|
||||
} = item;
|
||||
|
||||
const addUser = () => {
|
||||
console.log("addUser");
|
||||
if (item.isOwner || item.isAdmin)
|
||||
item.access = ShareAccessRights.RoomManager;
|
||||
|
||||
// if (isGroup && checkIfAccessPaid(item.access)) {
|
||||
// const topFreeRole = getTopFreeRole(t, roomType);
|
||||
// item.access = topFreeRole.access;
|
||||
// item.warning = t("GroupMaxAvailableRoleWarning", {
|
||||
// role: topFreeRole.label,
|
||||
// });
|
||||
// }
|
||||
|
||||
const items = removeExist([item, ...inviteItems]);
|
||||
setInviteItems(items);
|
||||
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
setIsAddEmailPanelBlocked(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropDownItem
|
||||
key={id}
|
||||
onClick={addUser}
|
||||
height={48}
|
||||
heightTablet={48}
|
||||
className="list-item"
|
||||
>
|
||||
<Avatar
|
||||
size="min"
|
||||
role="user"
|
||||
source={avatar}
|
||||
userName={groupName}
|
||||
isGroup={isGroup}
|
||||
/>
|
||||
<div className="list-item_content">
|
||||
<SearchItemText primary disabled={shared}>
|
||||
{displayName || groupName}
|
||||
</SearchItemText>
|
||||
<SearchItemText>{email}</SearchItemText>
|
||||
</div>
|
||||
{shared && <SearchItemText info>{t("Common:Invited")}</SearchItemText>}
|
||||
</DropDownItem>
|
||||
);
|
||||
};
|
||||
|
||||
const addItems = (users) => {
|
||||
console.log("addItems", users);
|
||||
const topFreeRole = getTopFreeRole(t, roomType);
|
||||
users.forEach((u) => {
|
||||
if (u.isGroup && checkIfAccessPaid(u.access)) {
|
||||
u.access = topFreeRole.access;
|
||||
u.warning = t("GroupMaxAvailableRoleWarning", {
|
||||
role: topFreeRole.label,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const items = [...users, ...inviteItems];
|
||||
|
||||
const filtered = removeExist(items);
|
||||
|
||||
setInviteItems(filtered);
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
};
|
||||
|
||||
const getSelectedTab = (item) => setSelectedTab(item);
|
||||
|
||||
const openUsersPanel = () => {
|
||||
setInputValue("");
|
||||
setAddUsersPanelVisible(true);
|
||||
setIsAddEmailPanelBlocked(true);
|
||||
};
|
||||
|
||||
const closeUsersPanel = () => {
|
||||
setAddUsersPanelVisible(false);
|
||||
};
|
||||
|
||||
const onClearInput = () => onChangeInput("");
|
||||
|
||||
const invitedUsers = useMemo(
|
||||
() => inviteItems.map((item) => item.id),
|
||||
[inviteItems],
|
||||
);
|
||||
|
||||
const filter = new Filter();
|
||||
filter.role = [EmployeeType.Admin, EmployeeType.User]; // 1(EmployeeType.User) - RoomAdmin | 3(EmployeeType.Admin) - DocSpaceAdmin
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledSubHeader>
|
||||
{t("Files:AddUsersOrGroups")}
|
||||
|
||||
<StyledLink
|
||||
className="link-list"
|
||||
fontWeight="600"
|
||||
type="action"
|
||||
isHovered
|
||||
onClick={openUsersPanel}
|
||||
>
|
||||
{t("Translations:ChooseFromList")}
|
||||
</StyledLink>
|
||||
</StyledSubHeader>
|
||||
<StyledDescription>
|
||||
{t("Files:AddUsersOrGroupsDescription")}
|
||||
</StyledDescription>
|
||||
|
||||
<StyledInviteInputContainer>
|
||||
<StyledInviteInput ref={searchRef} isShowCross={!!inputValue}>
|
||||
<TextInput
|
||||
className="invite-input"
|
||||
scale
|
||||
onChange={onChange}
|
||||
placeholder={
|
||||
roomId === -1
|
||||
? t("InviteAccountSearchPlaceholder")
|
||||
: t("InviteRoomSearchPlaceholder")
|
||||
}
|
||||
value={inputValue}
|
||||
isAutoFocussed={true}
|
||||
type="search"
|
||||
withBorder={false}
|
||||
/>
|
||||
|
||||
<div className="append" onClick={onClearInput}>
|
||||
<StyledCrossIcon />
|
||||
</div>
|
||||
</StyledInviteInput>
|
||||
{isAddEmailPanelBlocked ? (
|
||||
<></>
|
||||
) : (
|
||||
<StyledDropDown
|
||||
width={dropDownWidth}
|
||||
isDefaultMode={false}
|
||||
open
|
||||
manualX="16px"
|
||||
showDisabledItems
|
||||
eventTypes="click"
|
||||
withBackdrop={false}
|
||||
zIndex={399}
|
||||
{...dropDownMaxHeight}
|
||||
>
|
||||
{foundUsers}
|
||||
</StyledDropDown>
|
||||
)}
|
||||
|
||||
{addUsersPanelVisible && (
|
||||
<AddUsersPanel
|
||||
onParentPanelClose={onClose}
|
||||
onClose={closeUsersPanel}
|
||||
visible={addUsersPanelVisible}
|
||||
tempDataItems={inviteItems}
|
||||
setDataItems={addItems}
|
||||
isMultiSelect
|
||||
withoutBackground={isMobileView}
|
||||
withBlur={!isMobileView}
|
||||
roomId={roomId} // fixed groups request // need template id for correct working
|
||||
withGroups={!isPublicRoomType}
|
||||
withInfo
|
||||
infoText={
|
||||
selectedTab === PEOPLE_TAB_ID
|
||||
? t("Files:AddUsersOrGroupsInfo")
|
||||
: t("Files:AddUsersOrGroupsInfoGroups")
|
||||
}
|
||||
withInfoBadge
|
||||
invitedUsers={invitedUsers}
|
||||
disableDisabledUsers
|
||||
filter={filter}
|
||||
setActiveTabId={getSelectedTab}
|
||||
isUsersList
|
||||
/>
|
||||
)}
|
||||
</StyledInviteInputContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ dialogsStore }) => {
|
||||
const { invitePanelOptions } = dialogsStore;
|
||||
|
||||
return {
|
||||
roomId: invitePanelOptions.roomId,
|
||||
};
|
||||
})(
|
||||
withCultureNames(
|
||||
withTranslation(["InviteDialog", "Common", "Translations"])(
|
||||
observer(InviteInput),
|
||||
),
|
||||
),
|
||||
);
|
@ -0,0 +1,267 @@
|
||||
// (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 InfoEditReactSvgUrl from "PUBLIC_DIR/images/info.edit.react.svg?url";
|
||||
import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url";
|
||||
import AlertSvgUrl from "PUBLIC_DIR/images/icons/12/alert.react.svg?url";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Avatar } from "@docspace/shared/components/avatar";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { parseAddresses } from "@docspace/shared/utils";
|
||||
import { getAccessOptions } from "../utils";
|
||||
import { getUserTypeLabel } from "@docspace/shared/utils/common";
|
||||
|
||||
import {
|
||||
StyledEditInput,
|
||||
StyledEditButton,
|
||||
StyledCheckIcon,
|
||||
StyledCrossIcon,
|
||||
StyledHelpButton,
|
||||
StyledDeleteIcon,
|
||||
StyledInviteUserBody,
|
||||
} from "../StyledInvitePanel";
|
||||
import { filterGroupRoleOptions, filterUserRoleOptions } from "SRC_DIR/helpers";
|
||||
|
||||
const Item = ({
|
||||
t,
|
||||
item,
|
||||
setInviteItems,
|
||||
inviteItems,
|
||||
changeInviteItem,
|
||||
setHasErrors,
|
||||
roomType,
|
||||
isOwner,
|
||||
standalone,
|
||||
}) => {
|
||||
const {
|
||||
avatar,
|
||||
displayName,
|
||||
email,
|
||||
id,
|
||||
errors,
|
||||
access,
|
||||
isGroup,
|
||||
name: groupName,
|
||||
warning,
|
||||
} = item;
|
||||
|
||||
const name = isGroup
|
||||
? groupName
|
||||
: !!avatar
|
||||
? displayName !== ""
|
||||
? displayName
|
||||
: email
|
||||
: email;
|
||||
const source = !!avatar ? avatar : isGroup ? "" : AtReactSvgUrl;
|
||||
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [inputValue, setInputValue] = useState(name);
|
||||
const [parseErrors, setParseErrors] = useState(errors);
|
||||
|
||||
console.log("roomType", roomType);
|
||||
|
||||
const accesses = getAccessOptions(
|
||||
t,
|
||||
roomType,
|
||||
true,
|
||||
true,
|
||||
isOwner,
|
||||
standalone,
|
||||
);
|
||||
|
||||
const filteredAccesses = item.isGroup
|
||||
? filterGroupRoleOptions(accesses)
|
||||
: filterUserRoleOptions(accesses, item, true);
|
||||
|
||||
console.log("filteredAccesses", filteredAccesses);
|
||||
|
||||
const defaultAccess = filteredAccesses.find(
|
||||
(option) => option.access === +access,
|
||||
);
|
||||
const getUserType = (item) => {
|
||||
if (item.isOwner) return "owner";
|
||||
if (item.isAdmin) return "admin";
|
||||
if (item.isRoomAdmin) return "manager";
|
||||
if (item.isCollaborator) return "collaborator";
|
||||
return "user";
|
||||
};
|
||||
|
||||
const type = getUserType(item);
|
||||
|
||||
const typeLabel = item?.isEmailInvite
|
||||
? getUserTypeLabel(defaultAccess.type, t)
|
||||
: (type === "user" && defaultAccess?.type !== type) ||
|
||||
(defaultAccess?.type === "manager" && type !== "admin")
|
||||
? getUserTypeLabel(defaultAccess.type, t)
|
||||
: getUserTypeLabel(type, t);
|
||||
|
||||
const errorsInList = () => {
|
||||
const hasErrors = inviteItems.some((item) => !!item.errors?.length);
|
||||
setHasErrors(hasErrors);
|
||||
};
|
||||
|
||||
const onEdit = (e) => {
|
||||
if (e.detail === 2) {
|
||||
setEdit(true);
|
||||
}
|
||||
};
|
||||
|
||||
const cancelEdit = (e) => {
|
||||
setInputValue(name);
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
const saveEdit = (e) => {
|
||||
const value = inputValue === "" ? name : inputValue;
|
||||
|
||||
setEdit(false);
|
||||
validateValue(value);
|
||||
};
|
||||
|
||||
const onKeyPress = (e) => {
|
||||
if (edit) {
|
||||
if (e.key === "Enter") {
|
||||
saveEdit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keyup", onKeyPress);
|
||||
return () => document.removeEventListener("keyup", onKeyPress);
|
||||
});
|
||||
|
||||
const validateValue = (value) => {
|
||||
const email = parseAddresses(value);
|
||||
const parseErrors = email[0].parseErrors;
|
||||
const errors = !!parseErrors.length ? parseErrors : [];
|
||||
|
||||
setParseErrors(errors);
|
||||
changeInviteItem({ id, email: value, errors }).then(() => errorsInList());
|
||||
};
|
||||
|
||||
const changeValue = (e) => {
|
||||
const value = e.target.value.trim();
|
||||
|
||||
setInputValue(value);
|
||||
};
|
||||
|
||||
const hasError = parseErrors && !!parseErrors.length;
|
||||
|
||||
const removeItem = () => {
|
||||
const newItems = inviteItems.filter((item) => item.id !== id);
|
||||
|
||||
setInviteItems(newItems);
|
||||
};
|
||||
|
||||
const selectItemAccess = (selected) => {
|
||||
if (selected.key === "remove") return removeItem();
|
||||
|
||||
changeInviteItem({ id, access: selected.access });
|
||||
};
|
||||
|
||||
const textProps = !!avatar || isGroup ? {} : { onClick: onEdit };
|
||||
|
||||
const displayBody = (
|
||||
<>
|
||||
<StyledInviteUserBody>
|
||||
<Text {...textProps} truncate noSelect>
|
||||
{inputValue}
|
||||
</Text>
|
||||
|
||||
{!isGroup && (
|
||||
<Text
|
||||
className="label"
|
||||
fontWeight={400}
|
||||
fontSize="12px"
|
||||
noSelect
|
||||
color="#A3A9AE"
|
||||
truncate
|
||||
>
|
||||
{`${typeLabel} | ${email}`}
|
||||
</Text>
|
||||
)}
|
||||
</StyledInviteUserBody>
|
||||
|
||||
{hasError ? (
|
||||
<>
|
||||
<StyledHelpButton
|
||||
iconName={InfoEditReactSvgUrl}
|
||||
displayType="auto"
|
||||
offsetRight={0}
|
||||
tooltipContent={t("EmailErrorMessage")}
|
||||
openOnClick={false}
|
||||
size={16}
|
||||
color="#F21C0E"
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
className="delete-icon"
|
||||
size="medium"
|
||||
onClick={removeItem}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{warning && (
|
||||
<div className="warning">
|
||||
<StyledHelpButton
|
||||
tooltipContent={warning}
|
||||
iconName={AlertSvgUrl}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const okIcon = <StyledCheckIcon size="scale" />;
|
||||
const cancelIcon = <StyledCrossIcon size="scale" />;
|
||||
|
||||
const editBody = (
|
||||
<>
|
||||
<StyledEditInput value={inputValue} onChange={changeValue} />
|
||||
<StyledEditButton icon={okIcon} onClick={saveEdit} />
|
||||
<StyledEditButton icon={cancelIcon} onClick={cancelEdit} />
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Avatar
|
||||
size="min"
|
||||
role={type}
|
||||
source={source}
|
||||
isGroup={isGroup}
|
||||
userName={groupName}
|
||||
/>
|
||||
{edit ? editBody : displayBody}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Item;
|
@ -0,0 +1,202 @@
|
||||
// (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, { useState, useEffect, useRef, memo, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { FixedSizeList as List } from "react-window";
|
||||
import { CustomScrollbarsVirtualList } from "@docspace/shared/components/scrollbar";
|
||||
import useResizeObserver from "use-resize-observer";
|
||||
import Item from "./Item";
|
||||
|
||||
import { StyledRow, ScrollList } from "../StyledInvitePanel";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
const FOOTER_HEIGHT = 73;
|
||||
const USER_ITEM_HEIGHT = 48;
|
||||
|
||||
const Row = memo(({ data, index, style }) => {
|
||||
const {
|
||||
inviteItems,
|
||||
setInviteItems,
|
||||
changeInviteItem,
|
||||
t,
|
||||
setHasErrors,
|
||||
roomType,
|
||||
isOwner,
|
||||
setIsOpenItemAccess,
|
||||
isMobileView,
|
||||
standalone,
|
||||
} = data;
|
||||
|
||||
if (inviteItems === undefined) return;
|
||||
|
||||
const item = inviteItems[index];
|
||||
|
||||
return (
|
||||
<StyledRow
|
||||
key={item.id}
|
||||
style={style}
|
||||
className="row-item"
|
||||
hasWarning={!!item.warning}
|
||||
>
|
||||
<Item
|
||||
t={t}
|
||||
item={item}
|
||||
setInviteItems={setInviteItems}
|
||||
changeInviteItem={changeInviteItem}
|
||||
inviteItems={inviteItems}
|
||||
setHasErrors={setHasErrors}
|
||||
roomType={roomType}
|
||||
isOwner={isOwner}
|
||||
setIsOpenItemAccess={setIsOpenItemAccess}
|
||||
isMobileView={isMobileView}
|
||||
standalone={standalone}
|
||||
/>
|
||||
</StyledRow>
|
||||
);
|
||||
});
|
||||
|
||||
const ItemsList = ({
|
||||
t,
|
||||
setInviteItems,
|
||||
inviteItems,
|
||||
changeInviteItem,
|
||||
setHasErrors,
|
||||
roomType,
|
||||
isOwner,
|
||||
externalLinksVisible,
|
||||
scrollAllPanelContent,
|
||||
invitePanelBodyRef,
|
||||
isMobileView,
|
||||
standalone,
|
||||
}) => {
|
||||
const [bodyHeight, setBodyHeight] = useState(0);
|
||||
const [offsetTop, setOffsetTop] = useState(0);
|
||||
const [isTotalListHeight, setIsTotalListHeight] = useState(false);
|
||||
const [isOpenItemAccess, setIsOpenItemAccess] = useState(false);
|
||||
const bodyRef = useRef();
|
||||
const { height } = useResizeObserver({ ref: bodyRef });
|
||||
const { interfaceDirection } = useTheme();
|
||||
|
||||
const onBodyResize = useCallback(() => {
|
||||
const scrollHeight = bodyRef?.current?.firstChild.scrollHeight;
|
||||
const heightList = height ? height : bodyRef.current.offsetHeight;
|
||||
const totalHeightItems = inviteItems.length * USER_ITEM_HEIGHT;
|
||||
const listAreaHeight = heightList;
|
||||
const heightBody = invitePanelBodyRef?.current?.clientHeight;
|
||||
const fullHeightList = heightBody - bodyRef.current.offsetTop;
|
||||
const heightWitchOpenItemAccess = Math.max(scrollHeight, fullHeightList);
|
||||
|
||||
const calculatedHeight = scrollAllPanelContent
|
||||
? Math.max(
|
||||
totalHeightItems,
|
||||
listAreaHeight,
|
||||
isOpenItemAccess ? heightWitchOpenItemAccess : 0,
|
||||
)
|
||||
: heightList - FOOTER_HEIGHT;
|
||||
|
||||
const finalHeight = scrollAllPanelContent
|
||||
? isOpenItemAccess
|
||||
? calculatedHeight
|
||||
: totalHeightItems
|
||||
: calculatedHeight;
|
||||
|
||||
setBodyHeight(finalHeight);
|
||||
setOffsetTop(bodyRef.current.offsetTop);
|
||||
|
||||
if (scrollAllPanelContent && totalHeightItems && listAreaHeight)
|
||||
setIsTotalListHeight(
|
||||
totalHeightItems >= listAreaHeight && totalHeightItems >= scrollHeight,
|
||||
);
|
||||
}, [
|
||||
height,
|
||||
bodyRef?.current?.offsetHeight,
|
||||
inviteItems.length,
|
||||
scrollAllPanelContent,
|
||||
isOpenItemAccess,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
onBodyResize();
|
||||
}, [
|
||||
bodyRef.current,
|
||||
externalLinksVisible,
|
||||
height,
|
||||
inviteItems.length,
|
||||
scrollAllPanelContent,
|
||||
isOpenItemAccess,
|
||||
]);
|
||||
|
||||
const overflowStyle = scrollAllPanelContent ? "hidden" : "scroll";
|
||||
|
||||
const willChangeStyle =
|
||||
isMobileView && isOpenItemAccess ? "auto" : "transform";
|
||||
|
||||
return (
|
||||
<ScrollList
|
||||
offsetTop={offsetTop}
|
||||
ref={bodyRef}
|
||||
scrollAllPanelContent={scrollAllPanelContent}
|
||||
isTotalListHeight={isTotalListHeight}
|
||||
>
|
||||
<List
|
||||
style={{ overflow: overflowStyle, willChange: willChangeStyle }}
|
||||
direction={interfaceDirection}
|
||||
height={bodyHeight}
|
||||
width="auto"
|
||||
itemCount={inviteItems.length}
|
||||
itemSize={USER_ITEM_HEIGHT}
|
||||
itemData={{
|
||||
inviteItems,
|
||||
setInviteItems,
|
||||
changeInviteItem,
|
||||
setHasErrors,
|
||||
roomType,
|
||||
isOwner,
|
||||
setIsOpenItemAccess,
|
||||
isMobileView,
|
||||
t,
|
||||
standalone,
|
||||
}}
|
||||
outerElementType={!scrollAllPanelContent && CustomScrollbarsVirtualList}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
</ScrollList>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore, dialogsStore, settingsStore }) => {
|
||||
const { changeInviteItem } = dialogsStore;
|
||||
const { isOwner } = userStore.user;
|
||||
const { standalone } = settingsStore;
|
||||
|
||||
return {
|
||||
changeInviteItem,
|
||||
isOwner,
|
||||
standalone,
|
||||
};
|
||||
})(observer(ItemsList));
|
@ -0,0 +1,215 @@
|
||||
// (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 {
|
||||
ShareAccessRights,
|
||||
RoomsType,
|
||||
EmployeeType,
|
||||
} from "@docspace/shared/enums";
|
||||
import { checkIfAccessPaid } from "SRC_DIR/helpers";
|
||||
|
||||
export const getAccessOptions = (
|
||||
t,
|
||||
roomType = RoomsType.CustomRoom,
|
||||
withRemove = false,
|
||||
withSeparator = false,
|
||||
isOwner = false,
|
||||
standalone = false,
|
||||
) => {
|
||||
let options = [];
|
||||
const accesses = {
|
||||
docSpaceAdmin: {
|
||||
key: "docSpaceAdmin",
|
||||
label: t("Common:DocSpaceAdmin"),
|
||||
description: t("Translations:RoleDocSpaceAdminDescription"),
|
||||
...(!standalone && { quota: t("Common:Paid") }),
|
||||
color: "#EDC409",
|
||||
access:
|
||||
roomType === -1 ? EmployeeType.Admin : ShareAccessRights.FullAccess,
|
||||
type: "admin",
|
||||
},
|
||||
roomAdmin: {
|
||||
key: "roomAdmin",
|
||||
label: t("Common:RoomAdmin"),
|
||||
description: t("Translations:RoleRoomAdminDescription"),
|
||||
...(!standalone && { quota: t("Common:Paid") }),
|
||||
color: "#EDC409",
|
||||
access:
|
||||
roomType === -1 ? EmployeeType.User : ShareAccessRights.RoomManager,
|
||||
type: "manager",
|
||||
},
|
||||
collaborator: {
|
||||
key: "collaborator",
|
||||
label: t("Common:PowerUser"),
|
||||
description: t("Translations:RolePowerUserDescription"),
|
||||
...(!standalone && { quota: t("Common:Paid") }),
|
||||
color: "#EDC409",
|
||||
access:
|
||||
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("Common:Editor"),
|
||||
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",
|
||||
},
|
||||
};
|
||||
|
||||
switch (roomType) {
|
||||
case RoomsType.FillingFormsRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.formFiller,
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case RoomsType.EditingRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.editor,
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case RoomsType.ReviewRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.reviewer,
|
||||
accesses.commentator,
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case RoomsType.ReadOnlyRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case RoomsType.CustomRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.editor,
|
||||
accesses.formFiller,
|
||||
accesses.reviewer,
|
||||
accesses.commentator,
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case RoomsType.PublicRoom:
|
||||
options = [accesses.roomAdmin, accesses.collaborator];
|
||||
break;
|
||||
|
||||
case RoomsType.FormRoom:
|
||||
options = [
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.viewer,
|
||||
accesses.formFiller,
|
||||
];
|
||||
break;
|
||||
case -1:
|
||||
if (isOwner) options.push(accesses.docSpaceAdmin);
|
||||
|
||||
options = [
|
||||
...options,
|
||||
accesses.roomAdmin,
|
||||
accesses.collaborator,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.user,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
const removeOption = [
|
||||
{
|
||||
key: "s2",
|
||||
isSeparator: true,
|
||||
},
|
||||
{
|
||||
key: "remove",
|
||||
label: t("Common:Remove"),
|
||||
},
|
||||
];
|
||||
|
||||
return withRemove ? [...options, ...removeOption] : options;
|
||||
};
|
||||
|
||||
export const getTopFreeRole = (t, roomType) => {
|
||||
const accesses = getAccessOptions(t, roomType);
|
||||
const freeAccesses = accesses.filter(
|
||||
(item) => !checkIfAccessPaid(item.access) && item.key !== "s1",
|
||||
);
|
||||
return freeAccesses[0];
|
||||
};
|
@ -33,6 +33,7 @@ import UploadPanel from "./UploadPanel";
|
||||
import HotkeyPanel from "./HotkeysPanel";
|
||||
import InvitePanel from "./InvitePanel";
|
||||
import EditLinkPanel from "./EditLinkPanel";
|
||||
import TemplateAccessSettingsPanel from "./TemplateAccessSettingsPanel";
|
||||
|
||||
export {
|
||||
AddUsersPanel,
|
||||
@ -44,4 +45,5 @@ export {
|
||||
HotkeyPanel,
|
||||
InvitePanel,
|
||||
EditLinkPanel,
|
||||
TemplateAccessSettingsPanel,
|
||||
};
|
||||
|
@ -682,6 +682,10 @@ class ContextOptionsStore {
|
||||
this.filesActionsStore.onCreateRoomFromTemplate({ ...item, isEdit: true });
|
||||
};
|
||||
|
||||
onOpenTemplateAccessOptions = () => {
|
||||
this.dialogsStore.setTemplateAccessSettingsVisible(true);
|
||||
};
|
||||
|
||||
// onLoadLinks = async (t, item) => {
|
||||
// const promise = new Promise(async (resolve, reject) => {
|
||||
// let linksArray = [];
|
||||
@ -1339,6 +1343,14 @@ class ContextOptionsStore {
|
||||
onClick: () => this.onEditRoomTemplate(item),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_access-settings",
|
||||
key: "access-settings",
|
||||
label: t("AccessSettings"),
|
||||
icon: PersonReactSvgUrl,
|
||||
onClick: () => this.onOpenTemplateAccessOptions(),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_invite-users-to-room",
|
||||
key: "invite-users-to-room",
|
||||
|
@ -116,6 +116,7 @@ class DialogsStore {
|
||||
shareFolderDialogVisible = false;
|
||||
cancelUploadDialogVisible = false;
|
||||
createRoomTemplateDialogVisible = false;
|
||||
templateAccessSettingsVisible = true;
|
||||
|
||||
selectFileFormRoomFilterParam = FilesSelectorFilterTypes.DOCX;
|
||||
|
||||
@ -523,6 +524,10 @@ class DialogsStore {
|
||||
setCreateRoomTemplateDialogVisible = (visible) => {
|
||||
this.createRoomTemplateDialogVisible = visible;
|
||||
};
|
||||
|
||||
setTemplateAccessSettingsVisible = (isVisible) => {
|
||||
this.templateAccessSettingsVisible = isVisible;
|
||||
};
|
||||
}
|
||||
|
||||
export default DialogsStore;
|
||||
|
@ -438,6 +438,11 @@ const StyledInfo = styled.div`
|
||||
.text {
|
||||
color: ${(props) => props.theme.selector.info.color};
|
||||
}
|
||||
|
||||
.selector-info-text-wrapper {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSelector.defaultProps = { theme: Base };
|
||||
|
@ -134,6 +134,7 @@ const Selector = ({
|
||||
|
||||
withInfo,
|
||||
infoText,
|
||||
withInfoBadge,
|
||||
}: SelectorProps) => {
|
||||
const [footerVisible, setFooterVisible] = React.useState<boolean>(false);
|
||||
const [isSearch, setIsSearch] = React.useState<boolean>(false);
|
||||
@ -530,6 +531,7 @@ const Selector = ({
|
||||
? {
|
||||
withInfo,
|
||||
infoText,
|
||||
withInfoBadge,
|
||||
}
|
||||
: {};
|
||||
|
||||
|
@ -295,8 +295,12 @@ export type TSelectorFooterCheckbox = TSelectorCheckbox & {
|
||||
};
|
||||
|
||||
export type TSelectorInfo =
|
||||
| { withInfo: true; infoText: string }
|
||||
| { withInfo?: undefined; infoText?: undefined };
|
||||
| { withInfo: true; infoText: string; withInfoBadge?: boolean }
|
||||
| {
|
||||
withInfo?: undefined;
|
||||
infoText?: undefined;
|
||||
withInfoBadge?: undefined;
|
||||
};
|
||||
|
||||
export type SelectorProps = TSelectorHeader &
|
||||
TSelectorInfo &
|
||||
@ -377,6 +381,7 @@ export type BodyProps = TSelectorBreadCrumbs &
|
||||
withFooterInput?: boolean;
|
||||
withFooterCheckbox?: boolean;
|
||||
descriptionText?: string;
|
||||
withInfoBadge?: boolean;
|
||||
};
|
||||
|
||||
export type FooterProps = TSelectorFooterSubmitButton &
|
||||
|
@ -101,6 +101,7 @@ const Body = ({
|
||||
|
||||
withInfo,
|
||||
infoText,
|
||||
withInfoBadge,
|
||||
}: BodyProps) => {
|
||||
const [bodyHeight, setBodyHeight] = React.useState(0);
|
||||
|
||||
@ -212,7 +213,11 @@ const Body = ({
|
||||
) : null}
|
||||
|
||||
{withInfo && !isLoading && (
|
||||
<Info withInfo={withInfo} infoText={infoText} />
|
||||
<Info
|
||||
withInfo={withInfo}
|
||||
infoText={infoText}
|
||||
withInfoBadge={withInfoBadge}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
|
@ -26,17 +26,27 @@
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { ReactSVG } from "react-svg";
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
|
||||
import { Text } from "../../text";
|
||||
|
||||
import { TSelectorInfo } from "../Selector.types";
|
||||
import { StyledInfo } from "../Selector.styled";
|
||||
|
||||
export const Info = ({ infoText }: TSelectorInfo) => {
|
||||
export const Info = ({ infoText, withInfoBadge }: TSelectorInfo) => {
|
||||
return (
|
||||
<StyledInfo id="selector-info-text">
|
||||
<Text fontSize="12px" fontWeight={400} lineHeight="16px" className="text">
|
||||
{infoText}
|
||||
</Text>
|
||||
<div className="selector-info-text-wrapper">
|
||||
{withInfoBadge && <ReactSVG src={InfoIcon} />}
|
||||
<Text
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
lineHeight="16px"
|
||||
className="text"
|
||||
>
|
||||
{infoText}
|
||||
</Text>
|
||||
</div>
|
||||
</StyledInfo>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user