Web:Client:Plugins: update list of plugins at portal settings, change settings plugin dialog
This commit is contained in:
parent
3ceccbba29
commit
e26bcc6ee2
6
packages/client/public/locales/en/WebPlugins.json
Normal file
6
packages/client/public/locales/en/WebPlugins.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Description": "You can embed software modules to extend the functionality of DocSpace. Click download plugin and select a file in zip archive format.",
|
||||
"Plugins": "Plugins",
|
||||
"UploadPlugin": "Upload plugin",
|
||||
"Metadata": "Metadata"
|
||||
}
|
@ -1,26 +1,38 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
|
||||
import {
|
||||
PluginSettingsType,
|
||||
PluginComponents,
|
||||
} from "SRC_DIR/helpers/plugins/constants";
|
||||
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
|
||||
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
|
||||
|
||||
import Header from "./sub-components/Header";
|
||||
import Info from "./sub-components/Info";
|
||||
import Footer from "./sub-components/Footer";
|
||||
|
||||
const SettingsPluginDialog = ({
|
||||
plugin,
|
||||
|
||||
onLoad,
|
||||
customSettings,
|
||||
|
||||
settings,
|
||||
saveButton,
|
||||
|
||||
settingsPluginDialogVisible,
|
||||
|
||||
onClose,
|
||||
|
||||
...rest
|
||||
}) => {
|
||||
console.log(rest);
|
||||
|
||||
const { t } = useTranslation("WebPlugins", "Common");
|
||||
|
||||
const [customSettingsProps, setCustomSettingsProps] =
|
||||
React.useState(customSettings);
|
||||
React.useState(settings);
|
||||
|
||||
const [saveButtonProps, setSaveButtonProps] = React.useState(saveButton);
|
||||
|
||||
const [modalRequestRunning, setModalRequestRunning] = React.useState(false);
|
||||
|
||||
@ -28,7 +40,12 @@ const SettingsPluginDialog = ({
|
||||
if (!onLoad) return;
|
||||
const res = await onLoad();
|
||||
|
||||
setCustomSettingsProps(res.customSettings);
|
||||
setCustomSettingsProps(res.settings);
|
||||
if (res.saveButton)
|
||||
setSaveButtonProps({
|
||||
...res.saveButton,
|
||||
props: { ...res.saveButton, scale: true },
|
||||
});
|
||||
}, [onLoad]);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,11 +60,14 @@ const SettingsPluginDialog = ({
|
||||
return (
|
||||
<ModalDialog
|
||||
visible={settingsPluginDialogVisible}
|
||||
displayType="modal"
|
||||
displayType="aside"
|
||||
onClose={onCloseAction}
|
||||
autoMaxHeight
|
||||
withBodyScroll
|
||||
withFooterBorder
|
||||
>
|
||||
<ModalDialog.Header>{plugin?.name}</ModalDialog.Header>
|
||||
<ModalDialog.Header>
|
||||
<Header t={t} name={plugin?.name} />
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<WrappedComponent
|
||||
pluginId={plugin.id}
|
||||
@ -55,9 +75,22 @@ const SettingsPluginDialog = ({
|
||||
component: PluginComponents.box,
|
||||
props: customSettingsProps,
|
||||
}}
|
||||
saveButton={saveButton}
|
||||
setSaveButtonProps={setSaveButtonProps}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
/>
|
||||
<Info t={t} plugin={plugin} />
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Footer
|
||||
t={t}
|
||||
id={plugin?.id}
|
||||
saveButtonProps={saveButtonProps}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
onCloseAction={onCloseAction}
|
||||
modalRequestRunning={modalRequestRunning}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
@ -69,42 +102,22 @@ export default inject(({ pluginStore }) => {
|
||||
setSettingsPluginDialogVisible,
|
||||
currentSettingsDialogPlugin,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setIsAdminSettingsDialog,
|
||||
isAdminSettingsDialog,
|
||||
updateStatus,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
} = pluginStore;
|
||||
|
||||
const isUserDialog = !isAdminSettingsDialog;
|
||||
|
||||
const plugin = pluginList.find((p) => p.id === currentSettingsDialogPlugin);
|
||||
|
||||
const pluginSettings = isUserDialog
|
||||
? plugin?.getUserPluginSettings()
|
||||
: plugin?.getAdminPluginSettings();
|
||||
const pluginSettings = plugin?.getAdminPluginSettings();
|
||||
|
||||
const onClose = () => {
|
||||
setSettingsPluginDialogVisible(false);
|
||||
setCurrentSettingsDialogPlugin(null);
|
||||
setIsAdminSettingsDialog(false);
|
||||
};
|
||||
|
||||
if (pluginSettings.type === PluginSettingsType.settingsPage) {
|
||||
onClose();
|
||||
}
|
||||
|
||||
return {
|
||||
plugin,
|
||||
...pluginSettings,
|
||||
settingsPluginDialogVisible,
|
||||
currentSettingsDialogPlugin,
|
||||
|
||||
onClose,
|
||||
isUserDialog,
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updateStatus,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
};
|
||||
})(observer(SettingsPluginDialog));
|
||||
|
@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
import { PluginComponent } from "SRC_DIR/helpers/plugins/WrappedComponent";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: space-between;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
const Footer = ({
|
||||
t,
|
||||
id,
|
||||
saveButtonProps,
|
||||
modalRequestRunning,
|
||||
setModalRequestRunning,
|
||||
onCloseAction,
|
||||
}) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<PluginComponent
|
||||
component={{
|
||||
...saveButtonProps,
|
||||
props: {
|
||||
...saveButtonProps.props,
|
||||
scale: true,
|
||||
isSaveButton: true,
|
||||
primary: true,
|
||||
size: "normal",
|
||||
label: t("Common:SaveButton"),
|
||||
modalRequestRunning,
|
||||
setSettingsModalRequestRunning: setModalRequestRunning,
|
||||
onCloseAction,
|
||||
},
|
||||
}}
|
||||
pluginId={id}
|
||||
/>
|
||||
<Button
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
onClick={onCloseAction}
|
||||
label={t("Common:CancelButton")}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
@ -0,0 +1,25 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
div {
|
||||
color: #a3a9ae;
|
||||
|
||||
// margin-left: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = ({ t, name }) => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
{t("Common:Settings")}
|
||||
<div>({name})</div>
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
@ -0,0 +1,140 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import Link from "@docspace/components/link";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledSeparator = styled.div`
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
margin: 24px 0;
|
||||
|
||||
background-color: #eceef1;
|
||||
`;
|
||||
|
||||
const StyledInfo = styled.div`
|
||||
margin-top: 24px;
|
||||
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: max-content 1fr;
|
||||
|
||||
gap: 8px 24px;
|
||||
`;
|
||||
|
||||
const Info = ({ t, plugin }) => {
|
||||
console.log(plugin);
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSeparator />
|
||||
<Text fontSize={"14px"} fontWeight={600} lineHeight={"16px"} noSelect>
|
||||
{t("Metadata")}
|
||||
</Text>
|
||||
<StyledInfo>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Author
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{plugin?.author}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Version
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{plugin?.version}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Uploader
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{plugin?.createBy}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Upload date
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{plugin?.createOn}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Status
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
Not need enter settings
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Homepage
|
||||
</Text>
|
||||
<Link
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
type={"page"}
|
||||
href={plugin?.homePage}
|
||||
target={"_blank"}
|
||||
noSelect
|
||||
isHovered
|
||||
>
|
||||
{plugin?.homePage}
|
||||
</Link>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Description
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{plugin?.description}
|
||||
</Text>
|
||||
</StyledInfo>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Info;
|
@ -46,10 +46,14 @@ const ComponentPure = ({
|
||||
} = React.useContext(PropsContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!component.contextName || !contextProps[component.contextName]) return;
|
||||
if (
|
||||
!component.contextName ||
|
||||
(contextProps && !contextProps[component.contextName])
|
||||
)
|
||||
return;
|
||||
|
||||
setElementProps(contextProps[component.contextName]);
|
||||
}, [contextProps[component.contextName]]);
|
||||
contextProps && setElementProps(contextProps[component.contextName]);
|
||||
}, [contextProps && contextProps[component.contextName]]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setElementProps(component.props);
|
||||
@ -62,7 +66,7 @@ const ComponentPure = ({
|
||||
case PluginComponents.box: {
|
||||
const childrenComponents = elementProps?.children?.map(
|
||||
(item, index) => (
|
||||
<Component
|
||||
<PluginComponent
|
||||
key={`box-${index}-${item.component}`}
|
||||
component={item}
|
||||
pluginId={pluginId}
|
||||
@ -194,13 +198,24 @@ const ComponentPure = ({
|
||||
}
|
||||
|
||||
case PluginComponents.button: {
|
||||
const { withLoadingAfterClick, disableWhileRequestRunning, ...rest } =
|
||||
elementProps;
|
||||
const {
|
||||
withLoadingAfterClick,
|
||||
disableWhileRequestRunning,
|
||||
isSaveButton,
|
||||
modalRequestRunning,
|
||||
setSettingsModalRequestRunning,
|
||||
onCloseAction,
|
||||
...rest
|
||||
} = elementProps;
|
||||
|
||||
const onClickAction = async () => {
|
||||
if (withLoadingAfterClick) {
|
||||
setIsRequestRunning(true);
|
||||
setIsRequestRunning && setIsRequestRunning(true);
|
||||
setModalRequestRunning && setModalRequestRunning(true);
|
||||
if (isSaveButton) {
|
||||
setSettingsModalRequestRunning &&
|
||||
setSettingsModalRequestRunning(true);
|
||||
}
|
||||
}
|
||||
const message = await elementProps.onClick();
|
||||
|
||||
@ -224,16 +239,26 @@ const ComponentPure = ({
|
||||
updateFileItems
|
||||
);
|
||||
|
||||
setIsRequestRunning(false);
|
||||
setIsRequestRunning && setIsRequestRunning(false);
|
||||
|
||||
if (isSaveButton) {
|
||||
setSettingsModalRequestRunning &&
|
||||
setSettingsModalRequestRunning(false);
|
||||
onCloseAction && onCloseAction();
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = withLoadingAfterClick
|
||||
? isRequestRunning
|
||||
? isSaveButton
|
||||
? modalRequestRunning
|
||||
: isRequestRunning
|
||||
? isRequestRunning
|
||||
: rest.isLoading
|
||||
: rest.isLoading;
|
||||
const isDisabled = disableWhileRequestRunning
|
||||
? isRequestRunning
|
||||
? isSaveButton
|
||||
? modalRequestRunning
|
||||
: isRequestRunning
|
||||
? isRequestRunning
|
||||
: rest.isDisabled
|
||||
: rest.isDisabled;
|
||||
@ -295,7 +320,7 @@ const ComponentPure = ({
|
||||
return element;
|
||||
};
|
||||
|
||||
const Component = inject(({ pluginStore }) => {
|
||||
export const PluginComponent = inject(({ pluginStore }) => {
|
||||
const {
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
@ -326,16 +351,29 @@ const Component = inject(({ pluginStore }) => {
|
||||
};
|
||||
})(observer(ComponentPure));
|
||||
|
||||
const WrappedComponent = ({ component, pluginId, setModalRequestRunning }) => {
|
||||
const WrappedComponent = ({
|
||||
pluginId,
|
||||
|
||||
component,
|
||||
|
||||
saveButton,
|
||||
setSaveButtonProps,
|
||||
|
||||
setModalRequestRunning,
|
||||
}) => {
|
||||
const [contextProps, setContextProps] = React.useState({});
|
||||
|
||||
const [isRequestRunning, setIsRequestRunning] = React.useState(false);
|
||||
|
||||
const updatePropsContext = (name, props) => {
|
||||
const newProps = { ...contextProps };
|
||||
newProps[name] = props;
|
||||
if (saveButton && name === saveButton.contextName) {
|
||||
setSaveButtonProps && setSaveButtonProps((val) => ({ ...val, props }));
|
||||
} else {
|
||||
const newProps = { ...contextProps };
|
||||
newProps[name] = props;
|
||||
|
||||
setContextProps(newProps);
|
||||
setContextProps(newProps);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -348,7 +386,7 @@ const WrappedComponent = ({ component, pluginId, setModalRequestRunning }) => {
|
||||
setModalRequestRunning,
|
||||
}}
|
||||
>
|
||||
<Component component={component} pluginId={pluginId} />
|
||||
<PluginComponent component={component} pluginId={pluginId} />
|
||||
</PropsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -5,8 +5,6 @@ import PluginHeader from "./PluginHeader";
|
||||
import PluginInfo from "./PluginInfo";
|
||||
import PluginSettings from "./PluginSettings";
|
||||
|
||||
import { PluginSettingsType } from "SRC_DIR/helpers/plugins/constants";
|
||||
|
||||
import { PluginScopes } from "../constants";
|
||||
|
||||
const StyledPlugin = styled.div`
|
||||
@ -57,21 +55,10 @@ const Plugin = ({
|
||||
|
||||
isLast,
|
||||
}) => {
|
||||
const withSettings = scopes.includes(PluginScopes.Settings);
|
||||
|
||||
const pluginSettings = isUserSettings
|
||||
? userPluginSettings
|
||||
: adminPluginSettings;
|
||||
|
||||
const showPluginSettingsPage =
|
||||
withSettings &&
|
||||
pluginSettings &&
|
||||
(pluginSettings.type === PluginSettingsType.both ||
|
||||
pluginSettings.type === PluginSettingsType.settingsPage);
|
||||
|
||||
const showModalPluginSettings =
|
||||
withSettings && pluginSettings?.type && !showPluginSettingsPage;
|
||||
|
||||
return (
|
||||
<StyledPlugin>
|
||||
<PluginHeader
|
||||
|
@ -20,13 +20,6 @@ export const PluginScopes = Object.freeze({
|
||||
export const PluginStatus = Object.freeze({
|
||||
active: "active",
|
||||
hide: "hide",
|
||||
pending: "pending",
|
||||
});
|
||||
|
||||
export const PluginSettingsType = Object.freeze({
|
||||
modal: "modal",
|
||||
settingsPage: "settings-page",
|
||||
both: "both",
|
||||
});
|
||||
|
||||
export const PluginActions = Object.freeze({
|
||||
@ -37,8 +30,8 @@ export const PluginActions = Object.freeze({
|
||||
|
||||
showToast: "show-toast",
|
||||
|
||||
showSettingsModal: "show-settings-modal",
|
||||
closeSettingsModal: "close-settings-modal",
|
||||
// showSettingsModal: "show-settings-modal",
|
||||
// closeSettingsModal: "close-settings-modal",
|
||||
|
||||
showCreateDialogModal: "show-create-dialog-modal",
|
||||
|
||||
|
@ -38,7 +38,7 @@ export const messageActions = (
|
||||
case PluginActions.updateContext:
|
||||
if (message.contextProps) {
|
||||
message.contextProps.forEach((prop) => {
|
||||
updatePropsContext(prop.name, prop.props);
|
||||
updatePropsContext && updatePropsContext(prop.name, prop.props);
|
||||
});
|
||||
}
|
||||
break;
|
||||
@ -102,39 +102,40 @@ export const messageActions = (
|
||||
|
||||
case PluginActions.showModal:
|
||||
if (message.modalDialogProps) {
|
||||
setPluginDialogVisible(true);
|
||||
setPluginDialogProps(message.modalDialogProps);
|
||||
setPluginDialogVisible && setPluginDialogVisible(true);
|
||||
setPluginDialogProps &&
|
||||
setPluginDialogProps(message.modalDialogProps);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PluginActions.closeModal:
|
||||
setPluginDialogVisible(false);
|
||||
setPluginDialogProps(null);
|
||||
setPluginDialogVisible && setPluginDialogVisible(false);
|
||||
setPluginDialogProps && setPluginDialogProps(null);
|
||||
break;
|
||||
|
||||
case PluginActions.updateContextMenuItems:
|
||||
updateContextMenuItems(pluginId);
|
||||
updateContextMenuItems && updateContextMenuItems(pluginId);
|
||||
|
||||
break;
|
||||
case PluginActions.updateInfoPanelItems:
|
||||
updateInfoPanelItems(pluginId);
|
||||
updateInfoPanelItems && updateInfoPanelItems(pluginId);
|
||||
|
||||
break;
|
||||
case PluginActions.updateMainButtonItems:
|
||||
updateMainButtonItems(pluginId);
|
||||
updateMainButtonItems && updateMainButtonItems(pluginId);
|
||||
|
||||
break;
|
||||
case PluginActions.updateProfileMenuItems:
|
||||
updateProfileMenuItems(pluginId);
|
||||
updateProfileMenuItems && updateProfileMenuItems(pluginId);
|
||||
|
||||
break;
|
||||
case PluginActions.updateEventListenerItems:
|
||||
updateEventListenerItems(pluginId);
|
||||
updateEventListenerItems && updateEventListenerItems(pluginId);
|
||||
|
||||
break;
|
||||
case PluginActions.updateFileItems:
|
||||
updateFileItems(pluginId);
|
||||
updateFileItems && updateFileItems(pluginId);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -28,7 +28,6 @@ const useFiles = ({
|
||||
|
||||
isAccountsPage,
|
||||
isSettingsPage,
|
||||
isPluginsSettingsPage,
|
||||
|
||||
location,
|
||||
|
||||
@ -86,7 +85,7 @@ const useFiles = ({
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isAccountsPage || isSettingsPage || isPluginsSettingsPage) return;
|
||||
if (isAccountsPage || isSettingsPage) return;
|
||||
setIsLoading(true);
|
||||
|
||||
if (!window.location.href.includes("#preview")) {
|
||||
@ -272,13 +271,7 @@ const useFiles = ({
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, [
|
||||
location.pathname,
|
||||
location.search,
|
||||
isAccountsPage,
|
||||
isSettingsPage,
|
||||
isPluginsSettingsPage,
|
||||
]);
|
||||
}, [location.pathname, location.search, isAccountsPage, isSettingsPage]);
|
||||
|
||||
return { onDrop };
|
||||
};
|
||||
|
@ -0,0 +1,13 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
export { StyledContainer };
|
@ -1,104 +1,56 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Plugin from "SRC_DIR/helpers/plugins/components/Plugin";
|
||||
import EmptyScreen from "SRC_DIR/helpers/plugins/components/EmptyScreen";
|
||||
import Button from "@docspace/components/button";
|
||||
import { Events } from "@docspace/common/constants";
|
||||
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
|
||||
|
||||
const StyledPluginsSettings = styled.div`
|
||||
width: 100%;
|
||||
import Header from "./sub-components/header";
|
||||
import UploadButton from "./sub-components/button";
|
||||
import PluginItem from "./sub-components/plugin";
|
||||
|
||||
.add-button {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledPluginList = styled.div`
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
`;
|
||||
|
||||
export { StyledPluginsSettings, StyledPluginList };
|
||||
import { StyledContainer } from "./StyledPlugins";
|
||||
|
||||
const PluginPage = ({
|
||||
withDelete,
|
||||
withUpload,
|
||||
|
||||
pluginList,
|
||||
changePluginStatus,
|
||||
|
||||
openSettingsDialog,
|
||||
uninstallPlugin,
|
||||
updatePluginStatus,
|
||||
|
||||
changePluginStatus,
|
||||
|
||||
addPlugin,
|
||||
uninstallPlugin,
|
||||
|
||||
currentColorScheme,
|
||||
}) => {
|
||||
const { t } = useTranslation(["FilesSettings"]);
|
||||
const { t } = useTranslation(["WebPlugins", "Common", "FilesSettings"]);
|
||||
|
||||
const pluginInputRef = React.useRef(null);
|
||||
|
||||
const onAddAction = () => {
|
||||
pluginInputRef.current.click();
|
||||
};
|
||||
|
||||
const onInputClick = (e) => {
|
||||
e.target.value = null;
|
||||
};
|
||||
|
||||
const onFileChange = (e) => {
|
||||
let formData = new FormData();
|
||||
|
||||
formData.append("file", e.target.files[0]);
|
||||
|
||||
addPlugin(formData);
|
||||
};
|
||||
React.useEffect(() => {
|
||||
setDocumentTitle(t("Plugins"));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<StyledPluginsSettings>
|
||||
{pluginList.length > 0 ? (
|
||||
<>
|
||||
<Button
|
||||
className={"add-button"}
|
||||
label={"UploadPlugin"}
|
||||
primary
|
||||
size={"normal"}
|
||||
onClick={onAddAction}
|
||||
/>
|
||||
<StyledPluginList>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<Plugin
|
||||
key={`plugin-${plugin.name}-${plugin.version}`}
|
||||
{...plugin}
|
||||
changePluginStatus={changePluginStatus}
|
||||
withDelete={withDelete}
|
||||
openSettingsDialog={openSettingsDialog}
|
||||
isLast={index === pluginList.length - 1}
|
||||
uninstallPlugin={uninstallPlugin}
|
||||
updatePluginStatus={updatePluginStatus}
|
||||
/>
|
||||
))}
|
||||
</StyledPluginList>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreen t={t} onAddAction={onAddAction} />
|
||||
)}
|
||||
<input
|
||||
id="customPluginInput"
|
||||
className="custom-file-input"
|
||||
type="file"
|
||||
accept=".zip"
|
||||
onChange={onFileChange}
|
||||
onClick={onInputClick}
|
||||
ref={pluginInputRef}
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
</StyledPluginsSettings>
|
||||
<StyledContainer>
|
||||
<Header t={t} currentColorScheme={currentColorScheme} />
|
||||
{withUpload && <UploadButton t={t} addPlugin={addPlugin} />}
|
||||
{pluginList.map((plugin) => (
|
||||
<PluginItem
|
||||
key={`plugin-${plugin.name}-${plugin.version}`}
|
||||
withDelete={withDelete}
|
||||
openSettingsDialog={openSettingsDialog}
|
||||
uninstallPlugin={uninstallPlugin}
|
||||
changePluginStatus={changePluginStatus}
|
||||
{...plugin}
|
||||
/>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, pluginStore }) => {
|
||||
const { enablePlugins, pluginOptions } = auth.settingsStore;
|
||||
const { pluginOptions, currentColorScheme } = auth.settingsStore;
|
||||
|
||||
const withUpload = pluginOptions.includes("upload");
|
||||
const withDelete = pluginOptions.includes("delete");
|
||||
@ -108,27 +60,30 @@ export default inject(({ auth, pluginStore }) => {
|
||||
changePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setIsAdminSettingsDialog,
|
||||
|
||||
uninstallPlugin,
|
||||
updatePluginStatus,
|
||||
|
||||
addPlugin,
|
||||
} = pluginStore;
|
||||
|
||||
const openSettingsDialog = (pluginId) => {
|
||||
setSettingsPluginDialogVisible(true);
|
||||
setCurrentSettingsDialogPlugin(pluginId);
|
||||
setIsAdminSettingsDialog(true);
|
||||
};
|
||||
|
||||
return {
|
||||
enablePlugins,
|
||||
withUpload,
|
||||
withDelete,
|
||||
|
||||
pluginList,
|
||||
|
||||
changePluginStatus,
|
||||
|
||||
openSettingsDialog,
|
||||
|
||||
uninstallPlugin,
|
||||
updatePluginStatus,
|
||||
addPlugin,
|
||||
|
||||
currentColorScheme,
|
||||
};
|
||||
})(observer(PluginPage));
|
||||
|
@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
const StyledUploadButton = styled.div`
|
||||
width: auto;
|
||||
`;
|
||||
|
||||
const UploadButton = ({ t, addPlugin }) => {
|
||||
const pluginInputRef = React.useRef(null);
|
||||
|
||||
const onAddAction = () => {
|
||||
pluginInputRef.current.click();
|
||||
};
|
||||
|
||||
const onInputClick = (e) => {
|
||||
e.target.value = null;
|
||||
};
|
||||
|
||||
const onFileChange = (e) => {
|
||||
let formData = new FormData();
|
||||
|
||||
formData.append("file", e.target.files[0]);
|
||||
|
||||
addPlugin(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledUploadButton>
|
||||
<Button
|
||||
className={"add-button"}
|
||||
label={t("UploadPlugin")}
|
||||
primary
|
||||
size={"normal"}
|
||||
scale={false}
|
||||
onClick={onAddAction}
|
||||
/>
|
||||
<input
|
||||
id="customPluginInput"
|
||||
className="custom-file-input"
|
||||
type="file"
|
||||
accept=".zip"
|
||||
onChange={onFileChange}
|
||||
onClick={onInputClick}
|
||||
ref={pluginInputRef}
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
</StyledUploadButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default UploadButton;
|
@ -0,0 +1,63 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import Heading from "@docspace/components/heading";
|
||||
import HelpButton from "@docspace/components/help-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import Link from "@docspace/components/link";
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.plugin-list-header {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
lien-height: 22px;
|
||||
|
||||
margin: 0 4px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = ({ t, currentColorScheme }) => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
<div className="header-container">
|
||||
<Heading className={"plugin-list-header"}>{t("Plugins")}</Heading>
|
||||
<HelpButton
|
||||
offsetBottom={0}
|
||||
offsetLeft={0}
|
||||
offsetRight={0}
|
||||
offsetTop={0}
|
||||
tooltipContent={<>TODO: Plugin help button</>}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Text>
|
||||
{t("Description")}{" "}
|
||||
<Link
|
||||
color={currentColorScheme?.main?.accent}
|
||||
type={"page"}
|
||||
target={"_blank"}
|
||||
href={"/"}
|
||||
>
|
||||
{t("Common:LearnMore")}
|
||||
</Link>
|
||||
</Text>
|
||||
</div>
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
@ -0,0 +1,153 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Heading from "@docspace/components/heading";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
import Badge from "@docspace/components/badge";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import PluginSettingsIconUrl from "PUBLIC_DIR/images/plugin.settings.react.svg?url";
|
||||
import PluginDefaultLogoUrl from "PUBLIC_DIR/images/plugin.default-logo.png";
|
||||
|
||||
import { getPluginUrl } from "SRC_DIR/helpers/plugins/utils";
|
||||
import { PluginScopes } from "SRC_DIR/helpers/plugins/constants";
|
||||
|
||||
const StyledPluginItem = styled.div`
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
|
||||
height: auto;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: 64px 1fr;
|
||||
|
||||
gap: 20px;
|
||||
|
||||
border: 1px solid #d0d5da;
|
||||
border-radius: 12px;
|
||||
|
||||
padding: 24px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
.plugin-logo {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.plugin-info {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledPluginHeader = styled.div`
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.plugin-name {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.plugin-controls {
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.plugin-toggle-button {
|
||||
position: relative;
|
||||
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const PluginItem = ({
|
||||
id,
|
||||
|
||||
name,
|
||||
version,
|
||||
description,
|
||||
|
||||
enabled,
|
||||
changePluginStatus,
|
||||
|
||||
scopes,
|
||||
openSettingsDialog,
|
||||
|
||||
image,
|
||||
url,
|
||||
|
||||
...rest
|
||||
}) => {
|
||||
const imgSrc = image ? getPluginUrl(url, `/assets/${image}`) : null;
|
||||
|
||||
const withSettings = scopes.includes(PluginScopes.Settings);
|
||||
|
||||
const onChangeStatus = () => {
|
||||
changePluginStatus && changePluginStatus(id, !enabled);
|
||||
};
|
||||
|
||||
const onOpenSettingsDialog = () => {
|
||||
openSettingsDialog && openSettingsDialog(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledPluginItem>
|
||||
<img
|
||||
className="plugin-logo"
|
||||
src={imgSrc || PluginDefaultLogoUrl}
|
||||
alt={"Plugin logo"}
|
||||
/>
|
||||
<div className="plugin-info">
|
||||
<StyledPluginHeader>
|
||||
<Heading className={"plugin-name"}>{name}</Heading>
|
||||
<div className="plugin-controls">
|
||||
{withSettings && (
|
||||
<IconButton
|
||||
iconName={PluginSettingsIconUrl}
|
||||
size={16}
|
||||
onClick={onOpenSettingsDialog}
|
||||
/>
|
||||
)}
|
||||
<ToggleButton
|
||||
className="plugin-toggle-button"
|
||||
onChange={onChangeStatus}
|
||||
isChecked={enabled}
|
||||
/>
|
||||
</div>
|
||||
</StyledPluginHeader>
|
||||
|
||||
<Badge
|
||||
label={version}
|
||||
fontSize={"12px"}
|
||||
fontWeight={700}
|
||||
noHover={true}
|
||||
backgroundColor={"#22C386"}
|
||||
/>
|
||||
{imgSrc && description && (
|
||||
<Text fontWeight={400} lineHeight={"20px"}>
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</StyledPluginItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginItem;
|
@ -45,6 +45,7 @@ const DeveloperToolsWrapper = (props) => {
|
||||
"JavascriptSdk",
|
||||
"Webhooks",
|
||||
"Settings",
|
||||
"WebPlugins",
|
||||
]);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
@ -85,7 +86,7 @@ const DeveloperToolsWrapper = (props) => {
|
||||
if (enablePlugins) {
|
||||
data.push({
|
||||
id: "plugins",
|
||||
name: "Plugins",
|
||||
name: t("WebPlugins:Plugins"),
|
||||
content: <PluginPage />,
|
||||
});
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ class PluginStore {
|
||||
|
||||
settingsPluginDialogVisible = false;
|
||||
currentSettingsDialogPlugin = null;
|
||||
isAdminSettingsDialog = false;
|
||||
|
||||
pluginDialogVisible = false;
|
||||
pluginDialogProps = null;
|
||||
@ -71,10 +70,6 @@ class PluginStore {
|
||||
this.settingsPluginDialogVisible = value;
|
||||
};
|
||||
|
||||
setIsAdminSettingsDialog = (value) => {
|
||||
this.isAdminSettingsDialog = value;
|
||||
};
|
||||
|
||||
setPluginDialogVisible = (value) => {
|
||||
this.pluginDialogVisible = value;
|
||||
};
|
||||
@ -230,13 +225,13 @@ class PluginStore {
|
||||
};
|
||||
|
||||
changePluginStatus = async (id, status) => {
|
||||
if (status === "true") {
|
||||
if (status) {
|
||||
this.activatePlugin(id);
|
||||
} else {
|
||||
this.deactivatePlugin(id);
|
||||
}
|
||||
|
||||
const plugin = await api.plugins.activatePlugin(id, status === "true");
|
||||
const plugin = await api.plugins.activatePlugin(id, status);
|
||||
|
||||
return plugin;
|
||||
};
|
||||
|
BIN
public/images/plugin.default-logo.png
Normal file
BIN
public/images/plugin.default-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 933 B |
10
public/images/plugin.settings.react.svg
Normal file
10
public/images/plugin.settings.react.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_5_16946)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.98173 1.76932C3.1458 1.637 3.37667 1.63302 3.55181 1.75028L5.06682 2.76461C5.47482 2.53553 5.91232 2.35277 6.3722 2.22344L6.72621 0.435008C6.76716 0.228156 6.93333 0.0676944 7.14301 0.0453639C7.42457 0.0153787 7.71049 0 8 0C8.28951 0 8.57543 0.0153788 8.85699 0.0453639C9.06667 0.0676944 9.23284 0.228155 9.27379 0.435008L9.6278 2.22344C10.0877 2.35277 10.5252 2.53553 10.9332 2.76461L12.4482 1.75028C12.6233 1.63302 12.8542 1.637 13.0183 1.76932C13.4643 2.12903 13.871 2.53567 14.2307 2.98173C14.363 3.1458 14.367 3.37667 14.2497 3.55181L13.2354 5.06682C13.4645 5.47482 13.6472 5.91232 13.7766 6.3722L15.565 6.72621C15.7718 6.76716 15.9323 6.93333 15.9546 7.14301C15.9846 7.42457 16 7.71049 16 8C16 8.28951 15.9846 8.57543 15.9546 8.85699C15.9323 9.06667 15.7718 9.23284 15.565 9.27379L13.7766 9.6278C13.6472 10.0877 13.4645 10.5252 13.2354 10.9332L14.2497 12.4482C14.367 12.6233 14.363 12.8542 14.2307 13.0183C13.871 13.4643 13.4643 13.871 13.0183 14.2307C12.8542 14.363 12.6233 14.367 12.4482 14.2497L10.9332 13.2354C10.5252 13.4645 10.0877 13.6472 9.6278 13.7766L9.27379 15.565C9.23284 15.7718 9.06667 15.9323 8.85699 15.9546C8.57543 15.9846 8.28951 16 8 16C7.71049 16 7.42457 15.9846 7.14301 15.9546C6.93333 15.9323 6.76716 15.7718 6.72621 15.565L6.3722 13.7766C5.91232 13.6472 5.47482 13.4645 5.06682 13.2354L3.55181 14.2497C3.37667 14.367 3.1458 14.363 2.98173 14.2307C2.53567 13.871 2.12903 13.4643 1.76932 13.0183C1.637 12.8542 1.63302 12.6233 1.75028 12.4482L2.76461 10.9332C2.53553 10.5252 2.35277 10.0877 2.22344 9.6278L0.435008 9.27379C0.228156 9.23284 0.0676944 9.06667 0.0453639 8.85699C0.0153788 8.57543 0 8.28951 0 8C0 7.71049 0.0153788 7.42457 0.0453639 7.14301C0.0676944 6.93333 0.228155 6.76716 0.435008 6.72621L2.22344 6.3722C2.35277 5.91232 2.53553 5.47482 2.76461 5.06682L1.75028 3.55181C1.63302 3.37667 1.637 3.1458 1.76932 2.98173C2.12903 2.53567 2.53567 2.12903 2.98173 1.76932ZM8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z" fill="#A3A9AE"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5_16946">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in New Issue
Block a user