diff --git a/packages/client/src/components/FilesPanels/index.js b/packages/client/src/components/FilesPanels/index.js index 572bfed105..322965fd2e 100644 --- a/packages/client/src/components/FilesPanels/index.js +++ b/packages/client/src/components/FilesPanels/index.js @@ -65,6 +65,7 @@ import { PluginDialog, DeletePluginDialog, ShareFolderDialog, + CreateRoomTemplateDialog, } from "../dialogs"; import ConvertPasswordDialog from "../dialogs/ConvertPasswordDialog"; import ArchiveDialog from "../dialogs/ArchiveDialog"; @@ -127,6 +128,7 @@ const Panels = (props) => { changeRoomOwnerIsVisible, deletePluginDialogVisible, shareFolderDialogVisible, + createRoomTemplateDialogVisible, } = props; const [createPDFFormFile, setCreatePDFFormFile] = useState({ @@ -312,6 +314,9 @@ const Panels = (props) => { {...createPDFFormFile} /> ), + createRoomTemplateDialogVisible && ( + + ), ]; }; @@ -370,6 +375,7 @@ export default inject( leaveRoomDialogVisible, changeRoomOwnerIsVisible, shareFolderDialogVisible, + createRoomTemplateDialogVisible, } = dialogsStore; const { preparationPortalDialogVisible } = backup; @@ -435,6 +441,7 @@ export default inject( changeRoomOwnerIsVisible, deletePluginDialogVisible, shareFolderDialogVisible, + createRoomTemplateDialogVisible, }; }, )(observer(Panels)); diff --git a/packages/client/src/components/FilesSelector/FilesSelector.types.ts b/packages/client/src/components/FilesSelector/FilesSelector.types.ts index 10c23a564b..f01589543e 100644 --- a/packages/client/src/components/FilesSelector/FilesSelector.types.ts +++ b/packages/client/src/components/FilesSelector/FilesSelector.types.ts @@ -54,6 +54,7 @@ export type FilesSelectorProps = { isMove?: boolean; isCopy?: boolean; isRestore: boolean; + isTemplate: boolean; isRestoreAll?: boolean; isSelect?: boolean; isFormRoom?: boolean; diff --git a/packages/client/src/components/FilesSelector/index.tsx b/packages/client/src/components/FilesSelector/index.tsx index f5e5ad7c1e..96761ce57d 100644 --- a/packages/client/src/components/FilesSelector/index.tsx +++ b/packages/client/src/components/FilesSelector/index.tsx @@ -75,6 +75,7 @@ const FilesSelectorWrapper = ({ isMove, isCopy, isRestore, + isTemplate, isRestoreAll, isSelect, isFormRoom, @@ -277,6 +278,7 @@ const FilesSelectorWrapper = ({ isRestore, isFormRoom, isThirdParty, + isTemplate, isSelectFolder, ); diff --git a/packages/client/src/components/FilesSelector/utils.ts b/packages/client/src/components/FilesSelector/utils.ts index cad2195967..628801f2e7 100644 --- a/packages/client/src/components/FilesSelector/utils.ts +++ b/packages/client/src/components/FilesSelector/utils.ts @@ -44,6 +44,7 @@ export const getHeaderLabel = ( isFormRoom?: boolean, isThirdParty?: boolean, isSelectFolder?: boolean, + isTemplate?: boolean, ) => { if (isRestore) return t("Common:RestoreTo"); if (isSelectFolder) return t("Common:SelectFolder"); @@ -53,6 +54,9 @@ export const getHeaderLabel = ( if (isSelect) { return filterParam ? t("Common:SelectFile") : t("Common:SelectAction"); } + if (isTemplate) { + return t("Files:FromTemplate"); + } if (isFormRoom) { return t("Common:SelectFromDocSpace"); diff --git a/packages/client/src/components/GlobalEvents/CreateRoomTemplateEvent.js b/packages/client/src/components/GlobalEvents/CreateRoomTemplateEvent.js new file mode 100644 index 0000000000..ac398ae6a9 --- /dev/null +++ b/packages/client/src/components/GlobalEvents/CreateRoomTemplateEvent.js @@ -0,0 +1,63 @@ +// (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, useCallback } from "react"; +import { inject, observer } from "mobx-react"; +import { useTranslation } from "react-i18next"; + +import { toastr } from "@docspace/shared/components/toast"; + +import { CreateRoomTemplateDialog } from "../dialogs"; +let timerId = null; +const CreateRoomTemplateEvent = (props) => { + const { visible, onClose, item, fetchTags } = props; + + const [fetchedTags, setFetchedTags] = useState([]); + + const fetchTagsAction = useCallback(async () => { + const tags = await fetchTags(); + setFetchedTags(tags); + }, []); + + useEffect(() => { + fetchTagsAction(); + }, [fetchTagsAction]); + + return ( + + ); +}; + +export default inject(({ tagsStore }) => { + const { fetchTags } = tagsStore; + return { fetchTags }; +})(observer(CreateRoomTemplateEvent)); diff --git a/packages/client/src/components/GlobalEvents/index.js b/packages/client/src/components/GlobalEvents/index.js index 37f1c59e04..afc93f2de3 100644 --- a/packages/client/src/components/GlobalEvents/index.js +++ b/packages/client/src/components/GlobalEvents/index.js @@ -40,6 +40,7 @@ import EditGroupEvent from "./GroupEvents/EditGroupEvent"; import ChangeUserTypeEvent from "./ChangeUserTypeEvent"; import CreatePluginFile from "./CreatePluginFileEvent"; import ChangeQuotaEvent from "./ChangeQuotaEvent"; +import CreateRoomTemplateEvent from "./CreateRoomTemplateEvent"; const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { const [createDialogProps, setCreateDialogProps] = useState({ visible: false, @@ -96,6 +97,11 @@ const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { props: null, onClose: null, }); + const [createRoomTemplateDialog, setCreateRoomTemplateDialog] = useState({ + visible: false, + props: null, + onClose: null, + }); const eventHandlersList = useRef([]); @@ -250,6 +256,24 @@ const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { }, }); }, []); + + const onCreateRoomTemplate = (e) => { + console.log("onCreateRoomTemplate", e); + + const visible = e.item ? true : false; + + setCreateRoomTemplateDialog({ + visible: visible, + item: e.item, + onClose: () => { + setCreateRoomTemplateDialog({ + visible: false, + item: null, + }); + }, + }); + }; + useEffect(() => { window.addEventListener(Events.CREATE, onCreate); window.addEventListener(Events.RENAME, onRename); @@ -259,6 +283,7 @@ const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { window.addEventListener(Events.GROUP_CREATE, onCreateGroup); window.addEventListener(Events.GROUP_EDIT, onEditGroup); window.addEventListener(Events.CHANGE_QUOTA, onChangeQuota); + window.addEventListener(Events.CREATE_ROOM_TEMPLATE, onCreateRoomTemplate); if (enablePlugins) { window.addEventListener( Events.CREATE_PLUGIN_FILE, @@ -286,6 +311,10 @@ const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { window.removeEventListener(Events.CHANGE_USER_TYPE, onChangeUserType); window.removeEventListener(Events.GROUP_CREATE, onCreateGroup); window.removeEventListener(Events.GROUP_EDIT, onEditGroup); + window.addEventListener( + Events.CREATE_ROOM_TEMPLATE, + onCreateRoomTemplate, + ); if (enablePlugins) { window.removeEventListener( @@ -346,6 +375,12 @@ const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => { {...createPluginFileDialog} /> ), + createRoomTemplateDialog.visible && ( + + ), changeQuotaDialog.visible && ( ), diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js index 0c234d66b6..ba034da21b 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js @@ -42,6 +42,8 @@ export const getRoomTypeTitleTranslation = (roomType = 1, t) => { return t("Files:PublicRoom"); case RoomsType.FormRoom: return t("CreateEditRoomDialog:FormFilingRoomTitle"); + case RoomsType.TemplateRoom: + return t("Files:FromTemplate"); } }; @@ -61,6 +63,8 @@ export const getRoomTypeDescriptionTranslation = (roomType = 1, t) => { return t("CreateEditRoomDialog:PublicRoomDescription"); case RoomsType.FormRoom: return t("CreateEditRoomDialog:FormFilingRoomDescription"); + case RoomsType.TemplateRoom: + return t("CreateEditRoomDialog:FromTemplateDescription"); } }; diff --git a/packages/client/src/components/dialogs/CreateRoomTemplate/index.js b/packages/client/src/components/dialogs/CreateRoomTemplate/index.js new file mode 100644 index 0000000000..99d3c556e8 --- /dev/null +++ b/packages/client/src/components/dialogs/CreateRoomTemplate/index.js @@ -0,0 +1,261 @@ +// (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 } from "react"; +import { inject, observer } from "mobx-react"; +import styled, { css } from "styled-components"; +import { useTranslation } from "react-i18next"; + +import { Button } from "@docspace/shared/components/button"; +import { ModalDialog } from "@docspace/shared/components/modal-dialog"; +import { Text } from "@docspace/shared/components/text"; +import { Checkbox } from "@docspace/shared/components/checkbox"; + +import RoomType from "../CreateEditRoomDialog/sub-components/RoomType"; +import InputParam from "../CreateEditRoomDialog/sub-components/Params/InputParam"; +import TagInput from "../CreateEditRoomDialog/sub-components/TagInput"; +import TagHandler from "../CreateEditRoomDialog/handlers/TagHandler"; +import { ImageEditor } from "@docspace/shared/components/image-editor"; +import ChangeRoomOwner from "../CreateEditRoomDialog/sub-components/ChangeRoomOwner"; +import PreviewTile from "@docspace/shared/components/image-editor/PreviewTile"; +import { getRoomTypeTitleTranslation } from "../CreateEditRoomDialog/data"; + +const StyledModalDialog = styled(ModalDialog)` + .create-room-template_body { + display: flex; + flex-direction: column; + width: 100%; + gap: 22px; + } + + .icon-editor_text { + margin-bottom: 6px; + } + + .icon-editor { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: start; + gap: 16px; + } + + .modal-footer { + display: block; + } +`; + +const StyledButtonContainer = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + + gap: 8px; + + margin-top: 16px; +`; + +const CreateRoomTemplate = (props) => { + const { visible, onClose, item, fetchedTags, folderFormValidation } = props; + const { roomType, title, logo, createdBy } = item; + console.log("item", item); + + const startTags = Object.values(item.tags); + const startObjTags = startTags.map((tag, i) => ({ id: i, name: tag })); + const [tags, setTags] = useState(startObjTags); + + const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]); + + const [roomName, setRoomName] = useState(title); + const [isValidTitle, setIsValidTitle] = useState(true); + const [isWrongTitle, setIsWrongTitle] = useState(false); + const [icon, setIcon] = useState({ + uploadedFile: logo.original, + tmpFile: "", + x: 0.5, + y: 0.5, + zoom: 1, + }); + const [previewIcon, setPreviewIcon] = useState(null); + + const [openCreatedIsChecked, setOpenCreatedIsChecked] = useState(true); + const [isScrollLocked, setIsScrollLocked] = useState(false); + + const onChangeName = (e) => { + setIsValidTitle(true); + if (e.target.value.match(folderFormValidation)) { + setIsWrongTitle(true); + } else { + setIsWrongTitle(false); + } + setRoomName(e.target.value); + }; + + const onKeyUp = (e) => { + if (isWrongTitle) return; + if (e.keyCode === 13) onCreateRoomTemplate(); + }; + + const onCreateRoomTemplate = () => { + if (!roomName.trim()) { + setIsValidTitle(false); + return; + } + + // onSave(roomParams); + + console.log("onCreateRoomTemplate"); + // onClose(); + }; + + const setRoomTags = (newTags) => { + setTags(newTags); + }; + + const onChangeImage = (icon) => { + setIcon(icon); + }; + + const onChangeOpenCreated = () => { + setOpenCreatedIsChecked(!openCreatedIsChecked); + }; + + const tagHandler = new TagHandler(tags, setRoomTags, fetchedTags); + + return ( + + + + {t("Files:SaveAsTemplate")} + + + + +
+ + + + + + + console.log("Access settings")} + /> + +
+ + {t("Icon")} + + + } + /> +
+
+
+ + + + + +