diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/OAuth.types.ts b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/OAuth.types.ts index e1f640dcb8..3967c65ba9 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/OAuth.types.ts +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/OAuth.types.ts @@ -8,5 +8,5 @@ export interface OAuthProps { viewAs: ViewAsType; clientList: ClientProps[]; isEmptyClientList: boolean; - fetchClients: (page: number) => Promise; + fetchClients: () => Promise; } diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/index.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/index.tsx index ea8bfb3162..b38195c53f 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/index.tsx @@ -24,8 +24,8 @@ const OAuth = ({ const [isLoading, setIsLoading] = React.useState(true); const startLoadingRef = React.useRef(null); - const getClientList = React.useCallback(async (page = 0) => { - await fetchClients(page); + const getClientList = React.useCallback(async () => { + await fetchClients(); if (startLoadingRef.current) { const currentDate = new Date(); diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Header.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Header.tsx index d66f945eee..dd10613ec7 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Header.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Header.tsx @@ -1,18 +1,9 @@ -import React from "react"; import { useTranslation } from "react-i18next"; -import { inject, observer } from "mobx-react"; //@ts-ignore import TableHeader from "@docspace/components/table-container/TableHeader"; -const TABLE_VERSION = "1"; -const TABLE_COLUMNS = `oauthConfigColumns_ver-${TABLE_VERSION}`; - -interface HeaderProps { - sectionWidth: number; - tableRef: HTMLDivElement; - columnStorageName: string; -} +import { HeaderProps } from "./TableView.types"; const Header = (props: HeaderProps) => { const { sectionWidth, tableRef, columnStorageName } = props; diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Row.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Row.tsx index 64803a870f..c6dfdf7a9c 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Row.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/Row.tsx @@ -1,118 +1,31 @@ -import React from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import styled, { css } from "styled-components"; - -//@ts-ignore -import TableRow from "@docspace/components/table-container/TableRow"; //@ts-ignore import TableCell from "@docspace/components/table-container/TableCell"; import Text from "@docspace/components/text"; import ToggleButton from "@docspace/components/toggle-button"; -import { Base } from "@docspace/components/themes"; - -import SettingsIcon from "PUBLIC_DIR/images/catalog.settings.react.svg?url"; -import DeleteIcon from "PUBLIC_DIR/images/delete.react.svg?url"; - -import { ClientProps } from "@docspace/common/utils/oauth/interfaces"; import NameCell from "./columns/name"; -const StyledWrapper = styled.div` - display: contents; -`; - -const StyledTableRow = styled(TableRow)` - .table-container_cell { - text-overflow: ellipsis; - - padding-right: 8px; - } - - .mr-8 { - margin-right: 8px; - } - - .textOverflow { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .toggleButton { - display: contents; - - input { - position: relative; - - margin-left: -8px; - } - } - - .table-container_row-loader { - margin-left: 8px; - margin-right: 16px; - } - - :hover { - .table-container_cell { - cursor: pointer; - background: ${(props) => - `${props.theme.filesSection.tableView.row.backgroundActive} !important`}; - - margin-top: -1px; - - border-top: ${(props) => - `1px solid ${props.theme.filesSection.tableView.row.borderColor}`}; - } - - .table-container_file-name-cell { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: -24px; - padding-right: 24px; - ` - : css` - margin-left: -24px; - padding-left: 24px; - `} - } - .table-container_row-context-menu-wrapper { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: -20px; - padding-left: 18px; - ` - : css` - margin-right: -20px; - padding-right: 18px; - `} - } - } -`; - -StyledTableRow.defaultProps = { theme: Base }; - -interface RowProps { - item: ClientProps; - isChecked: boolean; - inProgress: boolean; - setSelection?: (clientId: string) => void; - changeClientStatus?: (clientId: string, status: boolean) => Promise; -} +import { StyledRowWrapper, StyledTableRow } from "./TableView.styled"; +import { RowProps } from "./TableView.types"; const Row = (props: RowProps) => { - const { item, changeClientStatus, isChecked, inProgress, setSelection } = - props; + const { + item, + changeClientStatus, + isChecked, + inProgress, + getContextMenuItems, + setSelection, + } = props; const navigate = useNavigate(); const { t } = useTranslation(["Webhooks", "Common"]); const editClient = () => { - navigate(window.location.pathname + `/${item.clientId}`); + navigate(`${item.clientId}`); }; const handleToggleEnabled = async () => { @@ -120,45 +33,31 @@ const Row = (props: RowProps) => { await changeClientStatus(item.clientId, !item.enabled); }; - const onDeleteOpen = () => {}; - const handleRowClick = (e: any) => { if ( e.target.closest(".checkbox") || e.target.closest(".table-container_row-checkbox") || - e.target.closest(".type-combobox") || - e.target.closest(".table-container_row-context-menu-wrapper") || - e.target.closest(".toggleButton") || e.detail === 0 ) { return; } + if ( + e.target.closest(".type-combobox") || + e.target.closest(".table-container_row-context-menu-wrapper") || + e.target.closest(".toggleButton") + ) { + return setSelection && setSelection(""); + } + editClient(); }; - const contextOptions = [ - { - key: "settings", - label: t("Common:Settings"), - icon: SettingsIcon, - onClick: editClient, - }, - { - key: "Separator dropdownItem", - isSeparator: true, - }, - { - key: "delete", - label: t("Common:Delete"), - icon: DeleteIcon, - onClick: onDeleteOpen, - }, - ]; + const contextOptions = getContextMenuItems && getContextMenuItems(t, item); return ( <> - + { /> - + ); }; diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.styled.ts b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.styled.ts new file mode 100644 index 0000000000..b9d53df1b0 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.styled.ts @@ -0,0 +1,98 @@ +import styled, { css } from "styled-components"; + +//@ts-ignore +import TableContainer from "@docspace/components/table-container/TableContainer"; +//@ts-ignore +import TableRow from "@docspace/components/table-container/TableRow"; +import { Base } from "@docspace/components/themes"; + +export const TableWrapper = styled(TableContainer)` + margin-top: 0px; + + .header-container-text { + font-size: 12px; + } + + .table-container_header { + position: absolute; + } +`; + +const StyledRowWrapper = styled.div` + display: contents; +`; + +const StyledTableRow = styled(TableRow)` + .table-container_cell { + text-overflow: ellipsis; + + padding-right: 8px; + } + + .mr-8 { + margin-right: 8px; + } + + .textOverflow { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .toggleButton { + display: contents; + + input { + position: relative; + + margin-left: -8px; + } + } + + .table-container_row-loader { + margin-left: 8px; + margin-right: 16px; + } + + :hover { + .table-container_cell { + cursor: pointer; + background: ${(props) => + `${props.theme.filesSection.tableView.row.backgroundActive} !important`}; + + margin-top: -1px; + + border-top: ${(props) => + `1px solid ${props.theme.filesSection.tableView.row.borderColor}`}; + } + + .table-container_file-name-cell { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: -24px; + padding-right: 24px; + ` + : css` + margin-left: -24px; + padding-left: 24px; + `} + } + .table-container_row-context-menu-wrapper { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: -20px; + padding-left: 18px; + ` + : css` + margin-right: -20px; + padding-right: 18px; + `} + } + } +`; + +StyledTableRow.defaultProps = { theme: Base }; + +export { StyledRowWrapper, StyledTableRow }; diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.types.ts b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.types.ts new file mode 100644 index 0000000000..9fea246cb4 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/TableView.types.ts @@ -0,0 +1,46 @@ +import { ClientProps } from "@docspace/common/utils/oauth/interfaces"; + +//@ts-ignore +import { ViewAsType } from "SRC_DIR/store/OAuthStore"; + +export interface TableViewProps { + items: ClientProps[]; + sectionWidth: number; + viewAs?: ViewAsType; + setViewAs?: (value: ViewAsType) => void; + userId?: string; + selection?: string[]; + setSelection?: (clientId: string) => void; + getContextMenuItems?: ( + t: any, + item: ClientProps + ) => { + [key: string]: any | string | boolean | ((clientId: string) => void); + }[]; + bufferSelection?: ClientProps | null; + activeClients?: string[]; + hasNextPage?: boolean; + totalElements?: number; + fetchNextClients?: (startIndex: number) => Promise; + changeClientStatus?: (clientId: string, status: boolean) => Promise; +} + +export interface HeaderProps { + sectionWidth: number; + tableRef: HTMLDivElement; + columnStorageName: string; +} + +export interface RowProps { + item: ClientProps; + isChecked: boolean; + inProgress: boolean; + getContextMenuItems?: ( + t: any, + item: ClientProps + ) => { + [key: string]: any | string | boolean | ((clientId: string) => void); + }[]; + setSelection?: (clientId: string) => void; + changeClientStatus?: (clientId: string, status: boolean) => Promise; +} diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/columns/name.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/columns/name.tsx index a678abc0b3..3dd219f7bf 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/columns/name.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/columns/name.tsx @@ -24,8 +24,8 @@ const StyledImage = styled.img` interface NameCellProps { name: string; - icon?: string; clientId: string; + icon?: string; inProgress?: boolean; isChecked?: boolean; setSelection?: (clientId: string) => void; @@ -39,7 +39,7 @@ const NameCell = ({ isChecked, setSelection, }: NameCellProps) => { - const onChange = (e: React.ChangeEvent) => { + const onChange = () => { setSelection && setSelection(clientId); }; diff --git a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/index.tsx b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/index.tsx index 0c81652cd9..57ded5a611 100644 --- a/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/developer-tools/OAuth/sub-components/List/TableView/index.tsx @@ -1,66 +1,35 @@ import React from "react"; import { inject, observer } from "mobx-react"; import { isMobile } from "react-device-detect"; -import styled from "styled-components"; -import { ClientProps } from "@docspace/common/utils/oauth/interfaces"; - -//@ts-ignore -import TableContainer from "@docspace/components/table-container/TableContainer"; //@ts-ignore import TableBody from "@docspace/components/table-container/TableBody"; //@ts-ignore -import { OAuthStoreProps, ViewAsType } from "SRC_DIR/store/OAuthStore"; +import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore"; import Row from "./Row"; import Header from "./Header"; -const TableWrapper = styled(TableContainer)` - margin-top: 0px; - - .header-container-text { - font-size: 12px; - } - - .table-container_header { - position: absolute; - } -`; +import { TableViewProps } from "./TableView.types"; +import { TableWrapper } from "./TableView.styled"; const TABLE_VERSION = "1"; const COLUMNS_SIZE = `oauthConfigColumnsSize_ver-${TABLE_VERSION}`; -interface TableViewProps { - items: ClientProps[]; - sectionWidth: number; - viewAs?: ViewAsType; - setViewAs?: (value: ViewAsType) => void; - userId?: string; - selection?: string[]; - setSelection?: (clientId: string) => void; - - bufferSelection?: ClientProps | null; - currentClient?: ClientProps | null; - setBufferSelection?: (clientId: string) => void; - changeClientStatus?: (clientId: string, status: boolean) => Promise; -} - const TableView = ({ items, - sectionWidth, - viewAs, setViewAs, - selection, - bufferSelection, - + activeClients, setSelection, - + getContextMenuItems, changeClientStatus, - userId, + hasNextPage, + totalElements, + fetchNextClients, }: TableViewProps) => { const tableRef = React.useRef(null); @@ -73,6 +42,29 @@ const TableView = ({ } }, [sectionWidth, viewAs, setViewAs]); + const clickOutside = React.useCallback( + (e: any) => { + if ( + e.target.closest(".checkbox") || + e.target.closest(".table-container_row-checkbox") || + e.detail === 0 + ) { + return; + } + + setSelection && setSelection(""); + }, + [setSelection] + ); + + React.useEffect(() => { + window.addEventListener("click", clickOutside); + + return () => { + window.removeEventListener("click", clickOutside); + }; + }, [clickOutside, setSelection]); + const columnStorageName = `${COLUMNS_SIZE}=${userId}`; return ( @@ -88,18 +80,24 @@ const TableView = ({ useReactWindow columnStorageName={columnStorageName} filesLength={items.length} - fetchMoreFiles={() => console.log("call")} - hasMoreFiles={false} - itemCount={items.length} + fetchMoreFiles={({ + startIndex, + }: { + startIndex: number; + stopIndex: number; + }) => fetchNextClients && fetchNextClients(startIndex)} + hasMoreFiles={hasNextPage} + itemCount={totalElements} > - {items.map((item, index) => ( + {items.map((item) => ( ))} @@ -115,26 +113,29 @@ export default inject( viewAs, setViewAs, selection, - bufferSelection, - setSelection, setBufferSelection, - changeClientStatus, - - currentClient, + getContextMenuItems, + activeClients, + hasNextPage, + totalElements, + fetchNextClients, } = oauthStore; + return { viewAs, setViewAs, userId, changeClientStatus, selection, - bufferSelection, - setSelection, setBufferSelection, - currentClient, + activeClients, + getContextMenuItems, + hasNextPage, + totalElements, + fetchNextClients, }; } )(observer(TableView)); diff --git a/packages/client/src/store/OAuthStore.ts b/packages/client/src/store/OAuthStore.ts index f329704e41..0e77e09411 100644 --- a/packages/client/src/store/OAuthStore.ts +++ b/packages/client/src/store/OAuthStore.ts @@ -1,4 +1,4 @@ -import { makeAutoObservable } from "mobx"; +import { makeAutoObservable, runInAction } from "mobx"; //@ts-ignore import { getPortal } from "@docspace/common/api/portal"; @@ -21,19 +21,37 @@ import { Scope, } from "@docspace/common/utils/oauth/interfaces"; +import SettingsIcon from "PUBLIC_DIR/images/catalog.settings.react.svg?url"; +import DeleteIcon from "PUBLIC_DIR/images/delete.react.svg?url"; +import EnableReactSvgUrl from "PUBLIC_DIR/images/enable.react.svg?url"; +import RemoveReactSvgUrl from "PUBLIC_DIR/images/remove.react.svg?url"; + const PAGE_LIMIT = 20; export type ViewAsType = "table" | "row"; export interface OAuthStoreProps { viewAs: ViewAsType; + setViewAs: (value: "table" | "row") => void; deleteDialogVisible: boolean; setDeleteDialogVisible: (value: boolean) => void; + editClient: (clientId: string) => void; + clients: ClientProps[]; + fetchClient: (clientId: string) => Promise; + fetchClients: () => Promise; + fetchNextClients: (startIndex: number) => Promise; + saveClient: (client: ClientProps) => Promise; + updateClient: (clientId: string, client: ClientProps) => Promise; + changeClientStatus: (clientId: string, status: boolean) => Promise; + regenerateSecret: (clientId: string) => Promise; + deleteClient: (clientId: string) => Promise; + currentPage: number; totalPages: number; + totalElements: number; selection: string[]; setSelection: (clientId: string) => void; @@ -44,21 +62,20 @@ export interface OAuthStoreProps { tenant: number; fetchTenant: () => Promise; + activeClients: string[]; + setActiveClient: (clientId: string) => void; + scopes: Scope[]; - - setViewAs: (value: "table" | "row") => void; - - fetchClient: (clientId: string) => Promise; - fetchClients: (page: number) => Promise; - saveClient: (client: ClientProps) => Promise; - updateClient: (clientId: string, client: ClientProps) => Promise; - changeClientStatus: (clientId: string, status: boolean) => Promise; - regenerateSecret: (clientId: string) => Promise; - deleteClient: (clientId: string) => Promise; - fetchScope: (name: string) => Promise; fetchScopes: () => Promise; + getContextMenuItems: ( + t: any, + item: ClientProps + ) => { + [key: string]: any | string | boolean | ((clientId: string) => void); + }[]; + clientList: ClientProps[]; isEmptyClientList: boolean; hasNextPage: boolean; @@ -70,6 +87,7 @@ class OAuthStore implements OAuthStoreProps { currentPage: number = 0; totalPages: number = 0; + totalElements: number = 0; deleteDialogVisible: boolean = false; @@ -81,6 +99,8 @@ class OAuthStore implements OAuthStoreProps { clients: ClientProps[] = []; + activeClients: string[] = []; + scopes: Scope[] = []; constructor() { @@ -96,10 +116,14 @@ class OAuthStore implements OAuthStoreProps { }; setSelection = (clientId: string) => { - if (this.selection.includes(clientId)) { - this.selection = this.selection.filter((s) => s !== clientId); + if (!clientId) { + this.selection = []; } else { - this.selection.push(clientId); + if (this.selection.includes(clientId)) { + this.selection = this.selection.filter((s) => s !== clientId); + } else { + this.selection.push(clientId); + } } }; @@ -111,6 +135,25 @@ class OAuthStore implements OAuthStoreProps { } }; + setActiveClient = (clientId: string) => { + if (!clientId) { + this.activeClients = []; + } else { + if (this.activeClients.includes(clientId)) { + this.activeClients = this.activeClients.filter((s) => s !== clientId); + } else { + this.activeClients.push(clientId); + } + } + }; + + editClient = (clientId: string) => { + //@ts-ignore + window?.DocSpace?.navigate( + `/portal-settings/developer-tools/oauth/${clientId}` + ); + }; + fetchTenant = async () => { if (this.tenant > -1) return this.tenant; @@ -130,27 +173,52 @@ class OAuthStore implements OAuthStoreProps { } }; - fetchClients = async (page: number) => { + fetchClients = async () => { try { + runInAction(() => { + this.currentPage = 1; + }); + const clientList: ClientListProps = await getClientList(0, PAGE_LIMIT); - this.totalPages = clientList.totalPages; - this.currentPage = page; - this.clients = clientList.content; + runInAction(() => { + this.totalPages = clientList.totalPages; + + this.totalElements = clientList.totalElements; + this.clients = clientList.content; + }); } catch (e) { console.log(e); } }; + fetchNextClients = async (startIndex: number) => { + const page = startIndex / PAGE_LIMIT; + runInAction(() => { + this.currentPage = page + 1; + }); + const clientList: ClientListProps = await getClientList(page, PAGE_LIMIT); + + runInAction(() => { + this.totalPages = clientList.totalPages; + + this.totalElements = clientList.totalElements; + this.clients = [...this.clients, ...clientList.content]; + }); + }; + //TODO: OAuth, add tenant and other params saveClient = async (client: ClientProps) => { try { client.tenant = 1; client.authenticationMethod = "zxc"; client.termsUrl = "zxc"; + const newClient = await addClient(client); - this.clients.push(newClient); + runInAction(() => { + this.clients.push(newClient); + }); } catch (e) { console.log(e); } @@ -163,7 +231,9 @@ class OAuthStore implements OAuthStoreProps { const idx = this.clients.findIndex((c) => c.clientId === clientId); if (idx > -1) { - this.clients[idx] = newClient; + runInAction(() => { + this.clients[idx] = newClient; + }); } } catch (e) { console.log(e); @@ -177,7 +247,9 @@ class OAuthStore implements OAuthStoreProps { const idx = this.clients.findIndex((c) => c.clientId === clientId); if (idx > -1) { - this.clients[idx] = { ...this.clients[idx], enabled: status }; + runInAction(() => { + this.clients[idx] = { ...this.clients[idx], enabled: status }; + }); } } catch (e) { console.log(e); @@ -222,6 +294,109 @@ class OAuthStore implements OAuthStoreProps { } }; + getContextMenuItems = (t: any, item: ClientProps) => { + const { clientId } = item; + + const isGroupContext = this.selection.length; + + const onDelete = () => { + if (!isGroupContext) { + this.setBufferSelection(clientId); + } + + this.setDeleteDialogVisible(true); + }; + + const onEnable = async (status: boolean) => { + if (isGroupContext) { + try { + const actions: Promise[] = []; + + this.selection.forEach((s) => { + this.setActiveClient(s); + actions.push(this.changeClientStatus(s, status)); + }); + + await Promise.all(actions); + + runInAction(() => { + this.activeClients = []; + this.selection = []; + }); + + //TODO OAuth, show toast + } catch (e) {} + } else { + this.setActiveClient(clientId); + + await this.changeClientStatus(clientId, status); + + //TODO OAuth, show toast + } + }; + + const settingsOption = { + key: "settings", + icon: SettingsIcon, + label: t("Common:Settings"), + onClick: () => this.editClient(clientId), + }; + + const enableOption = { + key: "enable", + icon: EnableReactSvgUrl, + label: t("Common:Enable"), + onClick: () => onEnable(true), + }; + + const disableOption = { + key: "disable", + icon: RemoveReactSvgUrl, + label: t("Common:Disable"), + onClick: () => onEnable(false), + }; + + const contextOptions = [ + { + key: "Separator dropdownItem", + isSeparator: true, + }, + { + key: "delete", + label: t("Common:Delete"), + icon: DeleteIcon, + onClick: () => onDelete(), + }, + ]; + + if (isGroupContext) { + let enabled = false; + + this.selection.forEach((s) => { + enabled = + enabled || + this.clientList.find((client) => client.clientId === s)?.enabled || + false; + }); + + if (enabled) { + contextOptions.unshift(disableOption); + } else { + contextOptions.unshift(enableOption); + } + } else { + if (item.enabled) { + contextOptions.unshift(disableOption); + } else { + contextOptions.unshift(enableOption); + } + + contextOptions.unshift(settingsOption); + } + + return contextOptions; + }; + get clientList() { return this.clients; } diff --git a/public/locales/en/Common.json b/public/locales/en/Common.json index e14ceaa88d..d85d62bca9 100644 --- a/public/locales/en/Common.json +++ b/public/locales/en/Common.json @@ -92,6 +92,7 @@ "Delete": "Delete", "DescriptionOfTheEveryoneRole": "The form is available for filling out by all participants of this room.", "DescriptionOfTheRoleQueue": "Each form filled out by users from the first role will go in turn to the next users listed below.", + "Disable": "Disable", "Disconnect": "Disconnect", "DocSpaceAdmin": "DocSpace admin", "DocSpaceOwner": "DocSpace owner",