mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 08:55:44 +01:00
use reflow to migrate from flow to typescript
This commit is contained in:
@@ -1,18 +1,16 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import PluginActionModal from "./PluginActionModal";
|
||||
import type { PendingPlugins } from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import React from 'react';
|
||||
import PluginActionModal from './PluginActionModal';
|
||||
import { PendingPlugins } from '@scm-manager/ui-types';
|
||||
import { apiClient } from '@scm-manager/ui-components';
|
||||
import { translate } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
refresh: () => void,
|
||||
pendingPlugins: PendingPlugins,
|
||||
onClose: () => void;
|
||||
refresh: () => void;
|
||||
pendingPlugins: PendingPlugins;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
class CancelPendingActionModal extends React.Component<Props> {
|
||||
@@ -21,8 +19,8 @@ class CancelPendingActionModal extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<PluginActionModal
|
||||
description={t("plugins.modal.cancelPending")}
|
||||
label={t("plugins.cancelPending")}
|
||||
description={t('plugins.modal.cancelPending')}
|
||||
label={t('plugins.cancelPending')}
|
||||
onClose={onClose}
|
||||
pendingPlugins={pendingPlugins}
|
||||
execute={this.cancelPending}
|
||||
@@ -39,4 +37,4 @@ class CancelPendingActionModal extends React.Component<Props> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("admin")(CancelPendingActionModal);
|
||||
export default translate('admin')(CancelPendingActionModal);
|
||||
@@ -1,38 +1,37 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Button } from "@scm-manager/ui-components";
|
||||
import type { PendingPlugins } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import ExecutePendingModal from "./ExecutePendingModal";
|
||||
import React from 'react';
|
||||
import { Button } from '@scm-manager/ui-components';
|
||||
import { PendingPlugins } from '@scm-manager/ui-types';
|
||||
import { translate } from 'react-i18next';
|
||||
import ExecutePendingModal from './ExecutePendingModal';
|
||||
|
||||
type Props = {
|
||||
pendingPlugins: PendingPlugins,
|
||||
pendingPlugins: PendingPlugins;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
showModal: boolean
|
||||
showModal: boolean;
|
||||
};
|
||||
|
||||
class ExecutePendingAction extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showModal: false
|
||||
showModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
openModal = () => {
|
||||
this.setState({
|
||||
showModal: true
|
||||
showModal: true,
|
||||
});
|
||||
};
|
||||
|
||||
closeModal = () => {
|
||||
this.setState({
|
||||
showModal: false
|
||||
showModal: false,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -57,7 +56,7 @@ class ExecutePendingAction extends React.Component<Props, State> {
|
||||
{this.renderModal()}
|
||||
<Button
|
||||
color="primary"
|
||||
label={t("plugins.executePending")}
|
||||
label={t('plugins.executePending')}
|
||||
action={this.openModal}
|
||||
/>
|
||||
</>
|
||||
@@ -65,4 +64,4 @@ class ExecutePendingAction extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(ExecutePendingAction);
|
||||
export default translate('admin')(ExecutePendingAction);
|
||||
@@ -1,45 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import PluginActionModal from "./PluginActionModal";
|
||||
import type { PendingPlugins } from "@scm-manager/ui-types";
|
||||
import waitForRestart from "./waitForRestart";
|
||||
import { apiClient, Notification } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
pendingPlugins: PendingPlugins,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class ExecutePendingActionModal extends React.Component<Props> {
|
||||
render() {
|
||||
const { onClose, pendingPlugins, t } = this.props;
|
||||
|
||||
return (
|
||||
<PluginActionModal
|
||||
description={t("plugins.modal.executePending")}
|
||||
label={t("plugins.modal.executeAndRestart")}
|
||||
onClose={onClose}
|
||||
pendingPlugins={pendingPlugins}
|
||||
execute={this.executeAndRestart}
|
||||
>
|
||||
<Notification type="warning">
|
||||
{t("plugins.modal.restartNotification")}
|
||||
</Notification>
|
||||
</PluginActionModal>
|
||||
);
|
||||
}
|
||||
|
||||
executeAndRestart = () => {
|
||||
const { pendingPlugins } = this.props;
|
||||
return apiClient
|
||||
.post(pendingPlugins._links.execute.href)
|
||||
.then(waitForRestart);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("admin")(ExecutePendingActionModal);
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import PluginActionModal from './PluginActionModal';
|
||||
import { PendingPlugins } from '@scm-manager/ui-types';
|
||||
import waitForRestart from './waitForRestart';
|
||||
import { apiClient, Notification } from '@scm-manager/ui-components';
|
||||
import { translate } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
pendingPlugins: PendingPlugins;
|
||||
|
||||
// context props
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
class ExecutePendingActionModal extends React.Component<Props> {
|
||||
render() {
|
||||
const { onClose, pendingPlugins, t } = this.props;
|
||||
|
||||
return (
|
||||
<PluginActionModal
|
||||
description={t('plugins.modal.executePending')}
|
||||
label={t('plugins.modal.executeAndRestart')}
|
||||
onClose={onClose}
|
||||
pendingPlugins={pendingPlugins}
|
||||
execute={this.executeAndRestart}
|
||||
>
|
||||
<Notification type="warning">
|
||||
{t('plugins.modal.restartNotification')}
|
||||
</Notification>
|
||||
</PluginActionModal>
|
||||
);
|
||||
}
|
||||
|
||||
executeAndRestart = () => {
|
||||
const { pendingPlugins } = this.props;
|
||||
return apiClient
|
||||
.post(pendingPlugins._links.execute.href)
|
||||
.then(waitForRestart);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate('admin')(ExecutePendingActionModal);
|
||||
@@ -1,30 +1,29 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
apiClient,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
ErrorNotification,
|
||||
Modal,
|
||||
Notification
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { PendingPlugins } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import waitForRestart from "./waitForRestart";
|
||||
import SuccessNotification from "./SuccessNotification";
|
||||
Notification,
|
||||
} from '@scm-manager/ui-components';
|
||||
import { PendingPlugins } from '@scm-manager/ui-types';
|
||||
import { translate } from 'react-i18next';
|
||||
import waitForRestart from './waitForRestart';
|
||||
import SuccessNotification from './SuccessNotification';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
pendingPlugins: PendingPlugins,
|
||||
onClose: () => void;
|
||||
pendingPlugins: PendingPlugins;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean,
|
||||
success: boolean,
|
||||
error?: Error
|
||||
loading: boolean;
|
||||
success: boolean;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
class ExecutePendingModal extends React.Component<Props, State> {
|
||||
@@ -32,7 +31,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
success: false
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +45,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
} else {
|
||||
return (
|
||||
<Notification type="warning">
|
||||
{t("plugins.modal.restartNotification")}
|
||||
{t('plugins.modal.restartNotification')}
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
@@ -55,7 +54,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
executeAndRestart = () => {
|
||||
const { pendingPlugins } = this.props;
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
apiClient
|
||||
@@ -65,14 +64,14 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
this.setState({
|
||||
success: true,
|
||||
loading: false,
|
||||
error: undefined
|
||||
error: undefined,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
success: false,
|
||||
loading: false,
|
||||
error: error
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -81,17 +80,16 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
const { pendingPlugins, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
{pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.new.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.installQueue")}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.new.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
{pendingPlugins._embedded && pendingPlugins._embedded.new.length > 0 && (
|
||||
<>
|
||||
<strong>{t('plugins.modal.installQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.new.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -103,7 +101,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
{pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.update.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<strong>{t('plugins.modal.updateQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.update.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
@@ -122,7 +120,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
{pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.uninstall.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.uninstallQueue")}</strong>
|
||||
<strong>{t('plugins.modal.uninstallQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.uninstall.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
@@ -140,7 +138,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
<>
|
||||
<div className="media">
|
||||
<div className="content">
|
||||
<p>{t("plugins.modal.executePending")}</p>
|
||||
<p>{t('plugins.modal.executePending')}</p>
|
||||
{this.renderInstallQueue()}
|
||||
{this.renderUpdateQueue()}
|
||||
{this.renderUninstallQueue()}
|
||||
@@ -158,12 +156,12 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
color="warning"
|
||||
label={t("plugins.modal.executeAndRestart")}
|
||||
label={t('plugins.modal.executeAndRestart')}
|
||||
loading={loading}
|
||||
action={this.executeAndRestart}
|
||||
disabled={error || success}
|
||||
/>
|
||||
<Button label={t("plugins.modal.abort")} action={onClose} />
|
||||
<Button label={t('plugins.modal.abort')} action={onClose} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
@@ -172,7 +170,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
const { onClose, t } = this.props;
|
||||
return (
|
||||
<Modal
|
||||
title={t("plugins.modal.executeAndRestart")}
|
||||
title={t('plugins.modal.executeAndRestart')}
|
||||
closeFunction={onClose}
|
||||
body={this.renderBody()}
|
||||
footer={this.renderFooter()}
|
||||
@@ -182,4 +180,4 @@ class ExecutePendingModal extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(ExecutePendingModal);
|
||||
export default translate('admin')(ExecutePendingModal);
|
||||
@@ -1,35 +1,34 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
ErrorNotification,
|
||||
Modal
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import SuccessNotification from "./SuccessNotification";
|
||||
Modal,
|
||||
} from '@scm-manager/ui-components';
|
||||
import { PendingPlugins, PluginCollection } from '@scm-manager/ui-types';
|
||||
import { translate } from 'react-i18next';
|
||||
import SuccessNotification from './SuccessNotification';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
actionType: string,
|
||||
pendingPlugins?: PendingPlugins,
|
||||
installedPlugins?: PluginCollection,
|
||||
refresh: () => void,
|
||||
execute: () => Promise<any>,
|
||||
description: string,
|
||||
label: string,
|
||||
onClose: () => void;
|
||||
actionType: string;
|
||||
pendingPlugins?: PendingPlugins;
|
||||
installedPlugins?: PluginCollection;
|
||||
refresh: () => void;
|
||||
execute: () => Promise<any>;
|
||||
description: string;
|
||||
label: string;
|
||||
|
||||
children?: React.Node,
|
||||
children?: React.Node;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean,
|
||||
success: boolean,
|
||||
error?: Error
|
||||
loading: boolean;
|
||||
success: boolean;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
class PluginActionModal extends React.Component<Props, State> {
|
||||
@@ -37,7 +36,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
success: false
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +54,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
|
||||
executeAction = () => {
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
|
||||
this.props
|
||||
@@ -63,14 +62,14 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
.then(() => {
|
||||
this.setState({
|
||||
success: true,
|
||||
loading: false
|
||||
loading: false,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
success: false,
|
||||
loading: false,
|
||||
error: error
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -94,7 +93,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
installedPlugins._embedded &&
|
||||
installedPlugins._embedded.plugins && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<strong>{t('plugins.modal.updateQueue')}</strong>
|
||||
<ul>
|
||||
{installedPlugins._embedded.plugins
|
||||
.filter(plugin => plugin._links && plugin._links.update)
|
||||
@@ -116,7 +115,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.new.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.installQueue")}</strong>
|
||||
<strong>{t('plugins.modal.installQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.new.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
@@ -136,7 +135,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.update.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.updateQueue")}</strong>
|
||||
<strong>{t('plugins.modal.updateQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.update.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
@@ -156,7 +155,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
pendingPlugins._embedded &&
|
||||
pendingPlugins._embedded.uninstall.length > 0 && (
|
||||
<>
|
||||
<strong>{t("plugins.modal.uninstallQueue")}</strong>
|
||||
<strong>{t('plugins.modal.uninstallQueue')}</strong>
|
||||
<ul>
|
||||
{pendingPlugins._embedded.uninstall.map(plugin => (
|
||||
<li key={plugin.name}>{plugin.name}</li>
|
||||
@@ -194,7 +193,7 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
action={this.executeAction}
|
||||
disabled={error || success}
|
||||
/>
|
||||
<Button label={t("plugins.modal.abort")} action={onClose} />
|
||||
<Button label={t('plugins.modal.abort')} action={onClose} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
@@ -213,4 +212,4 @@ class PluginActionModal extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(PluginActionModal);
|
||||
export default translate('admin')(PluginActionModal);
|
||||
@@ -1,22 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
import type {Plugin} from "@scm-manager/ui-types";
|
||||
import {Image} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin
|
||||
};
|
||||
|
||||
export default class PluginAvatar extends React.Component<Props> {
|
||||
render() {
|
||||
const { plugin } = this.props;
|
||||
return (
|
||||
<p className="image is-64x64">
|
||||
<ExtensionPoint name="plugins.plugin-avatar" props={{ plugin }}>
|
||||
<Image src={plugin.avatarUrl ? plugin.avatarUrl : "/images/blib.jpg"} alt="Logo" />
|
||||
</ExtensionPoint>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { ExtensionPoint } from '@scm-manager/ui-extensions';
|
||||
import { Plugin } from '@scm-manager/ui-types';
|
||||
import { Image } from '@scm-manager/ui-components';
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin;
|
||||
};
|
||||
|
||||
export default class PluginAvatar extends React.Component<Props> {
|
||||
render() {
|
||||
const { plugin } = this.props;
|
||||
return (
|
||||
<p className="image is-64x64">
|
||||
<ExtensionPoint
|
||||
name="plugins.plugin-avatar"
|
||||
props={{
|
||||
plugin,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={plugin.avatarUrl ? plugin.avatarUrl : '/images/blib.jpg'}
|
||||
alt="Logo"
|
||||
/>
|
||||
</ExtensionPoint>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
children?: React.Node;
|
||||
};
|
||||
|
||||
const ActionWrapper = styled.div`
|
||||
@@ -1,31 +1,30 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import type { Plugin } from "@scm-manager/ui-types";
|
||||
import { CardColumn, Icon } from "@scm-manager/ui-components";
|
||||
import PluginAvatar from "./PluginAvatar";
|
||||
import PluginModal from "./PluginModal";
|
||||
import React from 'react';
|
||||
import { translate } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
import { Plugin } from '@scm-manager/ui-types';
|
||||
import { CardColumn, Icon } from '@scm-manager/ui-components';
|
||||
import PluginAvatar from './PluginAvatar';
|
||||
import PluginModal from './PluginModal';
|
||||
|
||||
export const PluginAction = {
|
||||
INSTALL: "install",
|
||||
UPDATE: "update",
|
||||
UNINSTALL: "uninstall"
|
||||
INSTALL: 'install',
|
||||
UPDATE: 'update',
|
||||
UNINSTALL: 'uninstall',
|
||||
};
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin,
|
||||
refresh: () => void,
|
||||
plugin: Plugin;
|
||||
refresh: () => void;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
showInstallModal: boolean,
|
||||
showUpdateModal: boolean,
|
||||
showUninstallModal: boolean
|
||||
showInstallModal: boolean;
|
||||
showUpdateModal: boolean;
|
||||
showUninstallModal: boolean;
|
||||
};
|
||||
|
||||
const ActionbarWrapper = styled.div`
|
||||
@@ -54,7 +53,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
this.state = {
|
||||
showInstallModal: false,
|
||||
showUpdateModal: false,
|
||||
showUninstallModal: false
|
||||
showUninstallModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,7 +63,9 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
|
||||
toggleModal = (showModal: string) => {
|
||||
const oldValue = this.state[showModal];
|
||||
this.setState({ [showModal]: !oldValue });
|
||||
this.setState({
|
||||
[showModal]: !oldValue,
|
||||
});
|
||||
};
|
||||
|
||||
createFooterRight = (plugin: Plugin) => {
|
||||
@@ -95,10 +96,10 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
{this.isInstallable() && (
|
||||
<IconWrapper
|
||||
className="level-item"
|
||||
onClick={() => this.toggleModal("showInstallModal")}
|
||||
onClick={() => this.toggleModal('showInstallModal')}
|
||||
>
|
||||
<Icon
|
||||
title={t("plugins.modal.install")}
|
||||
title={t('plugins.modal.install')}
|
||||
name="download"
|
||||
color="info"
|
||||
/>
|
||||
@@ -107,10 +108,10 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
{this.isUninstallable() && (
|
||||
<IconWrapper
|
||||
className="level-item"
|
||||
onClick={() => this.toggleModal("showUninstallModal")}
|
||||
onClick={() => this.toggleModal('showUninstallModal')}
|
||||
>
|
||||
<Icon
|
||||
title={t("plugins.modal.uninstall")}
|
||||
title={t('plugins.modal.uninstall')}
|
||||
name="trash"
|
||||
color="info"
|
||||
/>
|
||||
@@ -119,10 +120,10 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
{this.isUpdatable() && (
|
||||
<IconWrapper
|
||||
className="level-item"
|
||||
onClick={() => this.toggleModal("showUpdateModal")}
|
||||
onClick={() => this.toggleModal('showUpdateModal')}
|
||||
>
|
||||
<Icon
|
||||
title={t("plugins.modal.update")}
|
||||
title={t('plugins.modal.update')}
|
||||
name="sync-alt"
|
||||
color="info"
|
||||
/>
|
||||
@@ -140,7 +141,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
plugin={plugin}
|
||||
pluginAction={PluginAction.INSTALL}
|
||||
refresh={refresh}
|
||||
onClose={() => this.toggleModal("showInstallModal")}
|
||||
onClose={() => this.toggleModal('showInstallModal')}
|
||||
/>
|
||||
);
|
||||
} else if (this.state.showUpdateModal && this.isUpdatable()) {
|
||||
@@ -149,7 +150,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
plugin={plugin}
|
||||
pluginAction={PluginAction.UPDATE}
|
||||
refresh={refresh}
|
||||
onClose={() => this.toggleModal("showUpdateModal")}
|
||||
onClose={() => this.toggleModal('showUpdateModal')}
|
||||
/>
|
||||
);
|
||||
} else if (this.state.showUninstallModal && this.isUninstallable()) {
|
||||
@@ -158,7 +159,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
plugin={plugin}
|
||||
pluginAction={PluginAction.UNINSTALL}
|
||||
refresh={refresh}
|
||||
onClose={() => this.toggleModal("showUninstallModal")}
|
||||
onClose={() => this.toggleModal('showUninstallModal')}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -172,7 +173,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
<Icon
|
||||
className="fa-spin fa-lg"
|
||||
name="spinner"
|
||||
color={plugin.markedForUninstall ? "danger" : "info"}
|
||||
color={plugin.markedForUninstall ? 'danger' : 'info'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -190,7 +191,7 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
<CardColumn
|
||||
action={
|
||||
this.isInstallable()
|
||||
? () => this.toggleModal("showInstallModal")
|
||||
? () => this.toggleModal('showInstallModal')
|
||||
: null
|
||||
}
|
||||
avatar={avatar}
|
||||
@@ -209,4 +210,4 @@ class PluginEntry extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(PluginEntry);
|
||||
export default translate('admin')(PluginEntry);
|
||||
@@ -1,22 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||
import type { PluginGroup } from "@scm-manager/ui-types";
|
||||
import PluginEntry from "./PluginEntry";
|
||||
|
||||
type Props = {
|
||||
group: PluginGroup,
|
||||
refresh: () => void
|
||||
};
|
||||
|
||||
class PluginGroupEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { group, refresh } = this.props;
|
||||
const entries = group.plugins.map(plugin => {
|
||||
return <PluginEntry plugin={plugin} key={plugin.name} refresh={refresh} />;
|
||||
});
|
||||
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginGroupEntry;
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { CardColumnGroup } from '@scm-manager/ui-components';
|
||||
import { PluginGroup } from '@scm-manager/ui-types';
|
||||
import PluginEntry from './PluginEntry';
|
||||
|
||||
type Props = {
|
||||
group: PluginGroup;
|
||||
refresh: () => void;
|
||||
};
|
||||
|
||||
class PluginGroupEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { group, refresh } = this.props;
|
||||
const entries = group.plugins.map(plugin => {
|
||||
return (
|
||||
<PluginEntry plugin={plugin} key={plugin.name} refresh={refresh} />
|
||||
);
|
||||
});
|
||||
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginGroupEntry;
|
||||
@@ -1,27 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Plugin } from "@scm-manager/ui-types";
|
||||
import PluginGroupEntry from "../components/PluginGroupEntry";
|
||||
import groupByCategory from "./groupByCategory";
|
||||
|
||||
type Props = {
|
||||
plugins: Plugin[],
|
||||
refresh: () => void
|
||||
};
|
||||
|
||||
class PluginList extends React.Component<Props> {
|
||||
render() {
|
||||
const { plugins, refresh } = this.props;
|
||||
|
||||
const groups = groupByCategory(plugins);
|
||||
return (
|
||||
<div className="content is-plugin-page">
|
||||
{groups.map(group => {
|
||||
return <PluginGroupEntry group={group} key={group.name} refresh={refresh} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginList;
|
||||
32
scm-ui/ui-webapp/src/admin/plugins/components/PluginList.tsx
Normal file
32
scm-ui/ui-webapp/src/admin/plugins/components/PluginList.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Plugin } from '@scm-manager/ui-types';
|
||||
import PluginGroupEntry from '../components/PluginGroupEntry';
|
||||
import groupByCategory from './groupByCategory';
|
||||
|
||||
type Props = {
|
||||
plugins: Plugin[];
|
||||
refresh: () => void;
|
||||
};
|
||||
|
||||
class PluginList extends React.Component<Props> {
|
||||
render() {
|
||||
const { plugins, refresh } = this.props;
|
||||
|
||||
const groups = groupByCategory(plugins);
|
||||
return (
|
||||
<div className="content is-plugin-page">
|
||||
{groups.map(group => {
|
||||
return (
|
||||
<PluginGroupEntry
|
||||
group={group}
|
||||
key={group.name}
|
||||
refresh={refresh}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginList;
|
||||
@@ -1,9 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import type { Plugin } from "@scm-manager/ui-types";
|
||||
import React from 'react';
|
||||
import { translate } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
import { Plugin } from '@scm-manager/ui-types';
|
||||
import {
|
||||
apiClient,
|
||||
Button,
|
||||
@@ -11,33 +10,33 @@ import {
|
||||
Checkbox,
|
||||
ErrorNotification,
|
||||
Modal,
|
||||
Notification
|
||||
} from "@scm-manager/ui-components";
|
||||
import waitForRestart from "./waitForRestart";
|
||||
import SuccessNotification from "./SuccessNotification";
|
||||
import { PluginAction } from "./PluginEntry";
|
||||
Notification,
|
||||
} from '@scm-manager/ui-components';
|
||||
import waitForRestart from './waitForRestart';
|
||||
import SuccessNotification from './SuccessNotification';
|
||||
import { PluginAction } from './PluginEntry';
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin,
|
||||
pluginAction: string,
|
||||
refresh: () => void,
|
||||
onClose: () => void,
|
||||
plugin: Plugin;
|
||||
pluginAction: string;
|
||||
refresh: () => void;
|
||||
onClose: () => void;
|
||||
|
||||
// context props
|
||||
t: (key: string, params?: Object) => string
|
||||
t: (key: string, params?: object) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
success: boolean,
|
||||
restart: boolean,
|
||||
loading: boolean,
|
||||
error?: Error
|
||||
success: boolean;
|
||||
restart: boolean;
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
const ListParent = styled.div`
|
||||
margin-right: 0;
|
||||
min-width: ${props =>
|
||||
props.pluginAction === PluginAction.INSTALL ? "5.5em" : "10em"};
|
||||
props.pluginAction === PluginAction.INSTALL ? '5.5em' : '10em'};
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
@@ -51,7 +50,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
this.state = {
|
||||
loading: false,
|
||||
restart: false,
|
||||
success: false
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,7 +60,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
|
||||
const newState = {
|
||||
loading: false,
|
||||
error: undefined
|
||||
error: undefined,
|
||||
};
|
||||
|
||||
if (restart) {
|
||||
@@ -69,14 +68,14 @@ class PluginModal extends React.Component<Props, State> {
|
||||
.then(() => {
|
||||
this.setState({
|
||||
...newState,
|
||||
success: true
|
||||
success: true,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
success: false,
|
||||
error
|
||||
error,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@@ -91,7 +90,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
const { plugin, pluginAction } = this.props;
|
||||
const { restart } = this.state;
|
||||
|
||||
let pluginActionLink = "";
|
||||
let pluginActionLink = '';
|
||||
|
||||
if (pluginAction === PluginAction.INSTALL) {
|
||||
pluginActionLink = plugin._links.install.href;
|
||||
@@ -100,12 +99,12 @@ class PluginModal extends React.Component<Props, State> {
|
||||
} else if (pluginAction === PluginAction.UNINSTALL) {
|
||||
pluginActionLink = plugin._links.uninstall.href;
|
||||
}
|
||||
return pluginActionLink + "?restart=" + restart.toString();
|
||||
return pluginActionLink + '?restart=' + restart.toString();
|
||||
};
|
||||
|
||||
handlePluginAction = (e: Event) => {
|
||||
this.setState({
|
||||
loading: true
|
||||
loading: true,
|
||||
});
|
||||
e.preventDefault();
|
||||
apiClient
|
||||
@@ -115,7 +114,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
this.setState({
|
||||
loading: false,
|
||||
success: false,
|
||||
error: error
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -124,10 +123,10 @@ class PluginModal extends React.Component<Props, State> {
|
||||
const { pluginAction, onClose, t } = this.props;
|
||||
const { loading, error, restart, success } = this.state;
|
||||
|
||||
let color = pluginAction === PluginAction.UNINSTALL ? "warning" : "primary";
|
||||
let color = pluginAction === PluginAction.UNINSTALL ? 'warning' : 'primary';
|
||||
let label = `plugins.modal.${pluginAction}`;
|
||||
if (restart) {
|
||||
color = "warning";
|
||||
color = 'warning';
|
||||
label = `plugins.modal.${pluginAction}AndRestart`;
|
||||
}
|
||||
return (
|
||||
@@ -139,7 +138,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
loading={loading}
|
||||
disabled={!!error || success}
|
||||
/>
|
||||
<Button label={t("plugins.modal.abort")} action={onClose} />
|
||||
<Button label={t('plugins.modal.abort')} action={onClose} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
@@ -152,7 +151,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
dependencies = (
|
||||
<div className="media">
|
||||
<Notification type="warning">
|
||||
<strong>{t("plugins.modal.dependencyNotification")}</strong>
|
||||
<strong>{t('plugins.modal.dependencyNotification')}</strong>
|
||||
<ul>
|
||||
{plugin.dependencies.map((dependency, index) => {
|
||||
return <li key={index}>{dependency}</li>;
|
||||
@@ -184,7 +183,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
return (
|
||||
<div className="media">
|
||||
<Notification type="warning">
|
||||
{t("plugins.modal.restartNotification")}
|
||||
{t('plugins.modal.restartNotification')}
|
||||
</Notification>
|
||||
</div>
|
||||
);
|
||||
@@ -194,7 +193,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
|
||||
handleRestartChange = (value: boolean) => {
|
||||
this.setState({
|
||||
restart: value
|
||||
restart: value,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -213,25 +212,25 @@ class PluginModal extends React.Component<Props, State> {
|
||||
<div className="media-content">
|
||||
<div className="field is-horizontal">
|
||||
<ListParent
|
||||
className={classNames("field-label", "is-inline-flex")}
|
||||
className={classNames('field-label', 'is-inline-flex')}
|
||||
pluginAction={pluginAction}
|
||||
>
|
||||
{t("plugins.modal.author")}:
|
||||
{t('plugins.modal.author')}:
|
||||
</ListParent>
|
||||
<ListChild className={classNames("field-body", "is-inline-flex")}>
|
||||
<ListChild className={classNames('field-body', 'is-inline-flex')}>
|
||||
{plugin.author}
|
||||
</ListChild>
|
||||
</div>
|
||||
{pluginAction === PluginAction.INSTALL && (
|
||||
<div className="field is-horizontal">
|
||||
<ListParent
|
||||
className={classNames("field-label", "is-inline-flex")}
|
||||
className={classNames('field-label', 'is-inline-flex')}
|
||||
pluginAction={pluginAction}
|
||||
>
|
||||
{t("plugins.modal.version")}:
|
||||
{t('plugins.modal.version')}:
|
||||
</ListParent>
|
||||
<ListChild
|
||||
className={classNames("field-body", "is-inline-flex")}
|
||||
className={classNames('field-body', 'is-inline-flex')}
|
||||
>
|
||||
{plugin.version}
|
||||
</ListChild>
|
||||
@@ -241,12 +240,12 @@ class PluginModal extends React.Component<Props, State> {
|
||||
pluginAction === PluginAction.UNINSTALL) && (
|
||||
<div className="field is-horizontal">
|
||||
<ListParent
|
||||
className={classNames("field-label", "is-inline-flex")}
|
||||
className={classNames('field-label', 'is-inline-flex')}
|
||||
>
|
||||
{t("plugins.modal.currentVersion")}:
|
||||
{t('plugins.modal.currentVersion')}:
|
||||
</ListParent>
|
||||
<ListChild
|
||||
className={classNames("field-body", "is-inline-flex")}
|
||||
className={classNames('field-body', 'is-inline-flex')}
|
||||
>
|
||||
{plugin.version}
|
||||
</ListChild>
|
||||
@@ -255,12 +254,12 @@ class PluginModal extends React.Component<Props, State> {
|
||||
{pluginAction === PluginAction.UPDATE && (
|
||||
<div className="field is-horizontal">
|
||||
<ListParent
|
||||
className={classNames("field-label", "is-inline-flex")}
|
||||
className={classNames('field-label', 'is-inline-flex')}
|
||||
>
|
||||
{t("plugins.modal.newVersion")}:
|
||||
{t('plugins.modal.newVersion')}:
|
||||
</ListParent>
|
||||
<ListChild
|
||||
className={classNames("field-body", "is-inline-flex")}
|
||||
className={classNames('field-body', 'is-inline-flex')}
|
||||
>
|
||||
{plugin.newVersion}
|
||||
</ListChild>
|
||||
@@ -273,7 +272,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
<div className="media-content">
|
||||
<Checkbox
|
||||
checked={restart}
|
||||
label={t("plugins.modal.restart")}
|
||||
label={t('plugins.modal.restart')}
|
||||
onChange={this.handleRestartChange}
|
||||
disabled={false}
|
||||
/>
|
||||
@@ -286,7 +285,7 @@ class PluginModal extends React.Component<Props, State> {
|
||||
return (
|
||||
<Modal
|
||||
title={t(`plugins.modal.title.${pluginAction}`, {
|
||||
name: plugin.displayName ? plugin.displayName : plugin.name
|
||||
name: plugin.displayName ? plugin.displayName : plugin.name,
|
||||
})}
|
||||
closeFunction={() => onClose()}
|
||||
body={body}
|
||||
@@ -297,4 +296,4 @@ class PluginModal extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(PluginModal);
|
||||
export default translate('admin')(PluginModal);
|
||||
@@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
children?: React.Node;
|
||||
};
|
||||
|
||||
const ChildWrapper = styled.div`
|
||||
@@ -18,10 +17,10 @@ export default class PluginTopActions extends React.Component<Props> {
|
||||
return (
|
||||
<ChildWrapper
|
||||
className={classNames(
|
||||
"column",
|
||||
"is-flex",
|
||||
"is-one-fifths",
|
||||
"is-mobile-action-spacing"
|
||||
'column',
|
||||
'is-flex',
|
||||
'is-one-fifths',
|
||||
'is-mobile-action-spacing',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
@@ -1,25 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Notification } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
// context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class InstallSuccessNotification extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Notification type="success">
|
||||
{t("plugins.modal.successNotification")}{" "}
|
||||
<a onClick={e => window.location.reload(true)}>
|
||||
{t("plugins.modal.reload")}
|
||||
</a>
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("admin")(InstallSuccessNotification);
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { translate } from 'react-i18next';
|
||||
import { Notification } from '@scm-manager/ui-components';
|
||||
|
||||
type Props = {
|
||||
// context props
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
class InstallSuccessNotification extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Notification type="success">
|
||||
{t('plugins.modal.successNotification')}{' '}
|
||||
<a onClick={e => window.location.reload(true)}>
|
||||
{t('plugins.modal.reload')}
|
||||
</a>
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate('admin')(InstallSuccessNotification);
|
||||
@@ -1,18 +1,16 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import PluginActionModal from "./PluginActionModal";
|
||||
import type { PluginCollection } from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import React from 'react';
|
||||
import PluginActionModal from './PluginActionModal';
|
||||
import { PluginCollection } from '@scm-manager/ui-types';
|
||||
import { apiClient } from '@scm-manager/ui-components';
|
||||
import { translate } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
refresh: () => void,
|
||||
installedPlugins: PluginCollection,
|
||||
onClose: () => void;
|
||||
refresh: () => void;
|
||||
installedPlugins: PluginCollection;
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
t: (p: string) => string;
|
||||
};
|
||||
|
||||
class UpdateAllActionModal extends React.Component<Props> {
|
||||
@@ -21,8 +19,8 @@ class UpdateAllActionModal extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<PluginActionModal
|
||||
description={t("plugins.modal.updateAll")}
|
||||
label={t("plugins.updateAll")}
|
||||
description={t('plugins.modal.updateAll')}
|
||||
label={t('plugins.updateAll')}
|
||||
onClose={onClose}
|
||||
installedPlugins={installedPlugins}
|
||||
execute={this.updateAll}
|
||||
@@ -39,4 +37,4 @@ class UpdateAllActionModal extends React.Component<Props> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("admin")(UpdateAllActionModal);
|
||||
export default translate('admin')(UpdateAllActionModal);
|
||||
@@ -1,9 +1,6 @@
|
||||
// @flow
|
||||
import type { Plugin, PluginGroup } from "@scm-manager/ui-types";
|
||||
import { Plugin, PluginGroup } from '@scm-manager/ui-types';
|
||||
|
||||
export default function groupByCategory(
|
||||
plugins: Plugin[]
|
||||
): PluginGroup[] {
|
||||
export default function groupByCategory(plugins: Plugin[]): PluginGroup[] {
|
||||
let groups = {};
|
||||
for (let plugin of plugins) {
|
||||
const groupName = plugin.category;
|
||||
@@ -12,7 +9,7 @@ export default function groupByCategory(
|
||||
if (!group) {
|
||||
group = {
|
||||
name: groupName,
|
||||
plugins: []
|
||||
plugins: [],
|
||||
};
|
||||
groups[groupName] = group;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
// @flow
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { apiClient } from '@scm-manager/ui-components';
|
||||
|
||||
const waitForRestart = () => {
|
||||
const endTime = Number(new Date()) + 10000;
|
||||
@@ -12,13 +11,13 @@ const waitForRestart = () => {
|
||||
setTimeout(executor, 1000, resolve, reject);
|
||||
} else {
|
||||
apiClient
|
||||
.get("")
|
||||
.get('')
|
||||
.then(resolve)
|
||||
.catch(() => {
|
||||
if (Number(new Date()) < endTime) {
|
||||
setTimeout(executor, 500, resolve, reject);
|
||||
} else {
|
||||
reject(new Error("timeout reached"));
|
||||
reject(new Error('timeout reached'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import { compose } from "redux";
|
||||
import type { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { translate } from 'react-i18next';
|
||||
import { compose } from 'redux';
|
||||
import { PendingPlugins, PluginCollection } from '@scm-manager/ui-types';
|
||||
import {
|
||||
ButtonGroup,
|
||||
ErrorNotification,
|
||||
@@ -11,60 +10,60 @@ import {
|
||||
Notification,
|
||||
Subtitle,
|
||||
Title,
|
||||
Button
|
||||
} from "@scm-manager/ui-components";
|
||||
Button,
|
||||
} from '@scm-manager/ui-components';
|
||||
import {
|
||||
fetchPendingPlugins,
|
||||
fetchPluginsByLink,
|
||||
getFetchPluginsFailure,
|
||||
getPendingPlugins,
|
||||
getPluginCollection,
|
||||
isFetchPluginsPending
|
||||
} from "../modules/plugins";
|
||||
import PluginsList from "../components/PluginList";
|
||||
isFetchPluginsPending,
|
||||
} from '../modules/plugins';
|
||||
import PluginsList from '../components/PluginList';
|
||||
import {
|
||||
getAvailablePluginsLink,
|
||||
getInstalledPluginsLink,
|
||||
getPendingPluginsLink
|
||||
} from "../../../modules/indexResource";
|
||||
import PluginTopActions from "../components/PluginTopActions";
|
||||
import PluginBottomActions from "../components/PluginBottomActions";
|
||||
import ExecutePendingActionModal from "../components/ExecutePendingActionModal";
|
||||
import CancelPendingActionModal from "../components/CancelPendingActionModal";
|
||||
import UpdateAllActionModal from "../components/UpdateAllActionModal";
|
||||
getPendingPluginsLink,
|
||||
} from '../../../modules/indexResource';
|
||||
import PluginTopActions from '../components/PluginTopActions';
|
||||
import PluginBottomActions from '../components/PluginBottomActions';
|
||||
import ExecutePendingActionModal from '../components/ExecutePendingActionModal';
|
||||
import CancelPendingActionModal from '../components/CancelPendingActionModal';
|
||||
import UpdateAllActionModal from '../components/UpdateAllActionModal';
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
collection: PluginCollection,
|
||||
baseUrl: string,
|
||||
installed: boolean,
|
||||
availablePluginsLink: string,
|
||||
installedPluginsLink: string,
|
||||
pendingPluginsLink: string,
|
||||
pendingPlugins: PendingPlugins,
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
collection: PluginCollection;
|
||||
baseUrl: string;
|
||||
installed: boolean;
|
||||
availablePluginsLink: string;
|
||||
installedPluginsLink: string;
|
||||
pendingPluginsLink: string;
|
||||
pendingPlugins: PendingPlugins;
|
||||
|
||||
// context objects
|
||||
t: (key: string, params?: Object) => string,
|
||||
t: (key: string, params?: object) => string;
|
||||
|
||||
// dispatched functions
|
||||
fetchPluginsByLink: (link: string) => void,
|
||||
fetchPendingPlugins: (link: string) => void
|
||||
fetchPluginsByLink: (link: string) => void;
|
||||
fetchPendingPlugins: (link: string) => void;
|
||||
};
|
||||
|
||||
type State = {
|
||||
showPendingModal: boolean,
|
||||
showUpdateAllModal: boolean,
|
||||
showCancelModal: boolean
|
||||
showPendingModal: boolean;
|
||||
showUpdateAllModal: boolean;
|
||||
showCancelModal: boolean;
|
||||
};
|
||||
|
||||
class PluginsOverview extends React.Component<Props, State> {
|
||||
constructor(props: Props, context: *) {
|
||||
super(props, context);
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showPendingModal: false,
|
||||
showUpdateAllModal: false,
|
||||
showCancelModal: false
|
||||
showCancelModal: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,7 +85,7 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
availablePluginsLink,
|
||||
installedPluginsLink,
|
||||
pendingPluginsLink,
|
||||
fetchPendingPlugins
|
||||
fetchPendingPlugins,
|
||||
} = this.props;
|
||||
fetchPluginsByLink(installed ? installedPluginsLink : availablePluginsLink);
|
||||
if (pendingPluginsLink) {
|
||||
@@ -99,12 +98,12 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
return (
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<Title title={t("plugins.title")} />
|
||||
<Title title={t('plugins.title')} />
|
||||
<Subtitle
|
||||
subtitle={
|
||||
installed
|
||||
? t("plugins.installedSubtitle")
|
||||
: t("plugins.availableSubtitle")
|
||||
? t('plugins.installedSubtitle')
|
||||
: t('plugins.availableSubtitle')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -133,11 +132,15 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
<Button
|
||||
color="primary"
|
||||
reducedMobile={true}
|
||||
key={"executePending"}
|
||||
icon={"arrow-circle-right"}
|
||||
label={t("plugins.executePending")}
|
||||
action={() => this.setState({ showPendingModal: true })}
|
||||
/>
|
||||
key={'executePending'}
|
||||
icon={'arrow-circle-right'}
|
||||
label={t('plugins.executePending')}
|
||||
action={() =>
|
||||
this.setState({
|
||||
showPendingModal: true,
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,11 +153,15 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
<Button
|
||||
color="primary"
|
||||
reducedMobile={true}
|
||||
key={"cancelPending"}
|
||||
icon={"times"}
|
||||
label={t("plugins.cancelPending")}
|
||||
action={() => this.setState({ showCancelModal: true })}
|
||||
/>
|
||||
key={'cancelPending'}
|
||||
icon={'times'}
|
||||
label={t('plugins.cancelPending')}
|
||||
action={() =>
|
||||
this.setState({
|
||||
showCancelModal: true,
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -163,11 +170,15 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
<Button
|
||||
color="primary"
|
||||
reducedMobile={true}
|
||||
key={"updateAll"}
|
||||
icon={"sync-alt"}
|
||||
key={'updateAll'}
|
||||
icon={'sync-alt'}
|
||||
label={this.computeUpdateAllSize()}
|
||||
action={() => this.setState({ showUpdateAllModal: true })}
|
||||
/>
|
||||
action={() =>
|
||||
this.setState({
|
||||
showUpdateAllModal: true,
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -180,10 +191,10 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
computeUpdateAllSize = () => {
|
||||
const { collection, t } = this.props;
|
||||
const outdatedPlugins = collection._embedded.plugins.filter(
|
||||
p => p._links.update
|
||||
p => p._links.update,
|
||||
).length;
|
||||
return t("plugins.outdatedPlugins", {
|
||||
count: outdatedPlugins
|
||||
return t('plugins.outdatedPlugins', {
|
||||
count: outdatedPlugins,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -215,13 +226,17 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
const {
|
||||
showPendingModal,
|
||||
showCancelModal,
|
||||
showUpdateAllModal
|
||||
showUpdateAllModal,
|
||||
} = this.state;
|
||||
|
||||
if (showPendingModal) {
|
||||
return (
|
||||
<ExecutePendingActionModal
|
||||
onClose={() => this.setState({ showPendingModal: false })}
|
||||
onClose={() =>
|
||||
this.setState({
|
||||
showPendingModal: false,
|
||||
})
|
||||
}
|
||||
pendingPlugins={pendingPlugins}
|
||||
/>
|
||||
);
|
||||
@@ -229,7 +244,11 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
if (showCancelModal) {
|
||||
return (
|
||||
<CancelPendingActionModal
|
||||
onClose={() => this.setState({ showCancelModal: false })}
|
||||
onClose={() =>
|
||||
this.setState({
|
||||
showCancelModal: false,
|
||||
})
|
||||
}
|
||||
refresh={this.fetchPlugins}
|
||||
pendingPlugins={pendingPlugins}
|
||||
/>
|
||||
@@ -238,7 +257,11 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
if (showUpdateAllModal) {
|
||||
return (
|
||||
<UpdateAllActionModal
|
||||
onClose={() => this.setState({ showUpdateAllModal: false })}
|
||||
onClose={() =>
|
||||
this.setState({
|
||||
showUpdateAllModal: false,
|
||||
})
|
||||
}
|
||||
refresh={this.fetchPlugins}
|
||||
installedPlugins={collection}
|
||||
/>
|
||||
@@ -257,7 +280,7 @@ class PluginsOverview extends React.Component<Props, State> {
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <Notification type="info">{t("plugins.noPlugins")}</Notification>;
|
||||
return <Notification type="info">{t('plugins.noPlugins')}</Notification>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +300,7 @@ const mapStateToProps = state => {
|
||||
availablePluginsLink,
|
||||
installedPluginsLink,
|
||||
pendingPluginsLink,
|
||||
pendingPlugins
|
||||
pendingPlugins,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -288,14 +311,14 @@ const mapDispatchToProps = dispatch => {
|
||||
},
|
||||
fetchPendingPlugins: (link: string) => {
|
||||
dispatch(fetchPendingPlugins(link));
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default compose(
|
||||
translate("admin"),
|
||||
translate('admin'),
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)
|
||||
mapDispatchToProps,
|
||||
),
|
||||
)(PluginsOverview);
|
||||
@@ -1,353 +0,0 @@
|
||||
// @flow
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
import reducer, {
|
||||
FETCH_PLUGINS,
|
||||
FETCH_PLUGINS_PENDING,
|
||||
FETCH_PLUGINS_SUCCESS,
|
||||
FETCH_PLUGINS_FAILURE,
|
||||
FETCH_PLUGIN,
|
||||
FETCH_PLUGIN_PENDING,
|
||||
FETCH_PLUGIN_SUCCESS,
|
||||
FETCH_PLUGIN_FAILURE,
|
||||
fetchPluginsByLink,
|
||||
fetchPluginsSuccess,
|
||||
getPluginCollection,
|
||||
isFetchPluginsPending,
|
||||
getFetchPluginsFailure,
|
||||
fetchPluginByLink,
|
||||
fetchPluginByName,
|
||||
fetchPluginSuccess,
|
||||
getPlugin,
|
||||
isFetchPluginPending,
|
||||
getFetchPluginFailure
|
||||
} from "./plugins";
|
||||
import type { Plugin, PluginCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const groupManagerPlugin: Plugin = {
|
||||
name: "scm-groupmanager-plugin",
|
||||
bundles: ["/scm/groupmanager-plugin.bundle.js"],
|
||||
type: "Administration",
|
||||
version: "2.0.0-SNAPSHOT",
|
||||
author: "Sebastian Sdorra",
|
||||
description: "Notify a remote webserver whenever a plugin is pushed to.",
|
||||
_links: {
|
||||
self: {
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scriptPlugin: Plugin = {
|
||||
name: "scm-script-plugin",
|
||||
bundles: ["/scm/script-plugin.bundle.js"],
|
||||
type: "Miscellaneous",
|
||||
version: "2.0.0-SNAPSHOT",
|
||||
author: "Sebastian Sdorra",
|
||||
description: "Script support for scm-manager.",
|
||||
_links: {
|
||||
self: {
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-script-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const branchwpPlugin: Plugin = {
|
||||
name: "scm-branchwp-plugin",
|
||||
bundles: ["/scm/branchwp-plugin.bundle.js"],
|
||||
type: "Miscellaneous",
|
||||
version: "2.0.0-SNAPSHOT",
|
||||
author: "Sebastian Sdorra",
|
||||
description: "This plugin adds branch write protection for plugins.",
|
||||
_links: {
|
||||
self: {
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pluginCollectionWithNames: PluginCollection = {
|
||||
_links: {
|
||||
self: {
|
||||
href: "http://localhost:8081/api/v2/ui/plugins"
|
||||
}
|
||||
},
|
||||
_embedded: {
|
||||
plugins: [groupManagerPlugin.name, scriptPlugin.name, branchwpPlugin.name]
|
||||
}
|
||||
};
|
||||
|
||||
const pluginCollection: PluginCollection = {
|
||||
_links: {
|
||||
self: {
|
||||
href: "http://localhost:8081/api/v2/ui/plugins"
|
||||
}
|
||||
},
|
||||
_embedded: {
|
||||
plugins: [groupManagerPlugin, scriptPlugin, branchwpPlugin]
|
||||
}
|
||||
};
|
||||
|
||||
describe("plugins fetch", () => {
|
||||
const URL = "ui/plugins";
|
||||
const PLUGINS_URL = "/api/v2/ui/plugins";
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it("should successfully fetch plugins from link", () => {
|
||||
fetchMock.getOnce(PLUGINS_URL, pluginCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_PLUGINS_PENDING },
|
||||
{
|
||||
type: FETCH_PLUGINS_SUCCESS,
|
||||
payload: pluginCollection
|
||||
}
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginsByLink(URL)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_PLUGINS_FAILURE if request fails", () => {
|
||||
fetchMock.getOnce(PLUGINS_URL, {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginsByLink(URL)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGINS_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGINS_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should successfully fetch scm-groupmanager-plugin by name", () => {
|
||||
fetchMock.getOnce(
|
||||
PLUGINS_URL + "/scm-groupmanager-plugin",
|
||||
groupManagerPlugin
|
||||
);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_PLUGIN_PENDING,
|
||||
payload: {
|
||||
name: "scm-groupmanager-plugin"
|
||||
},
|
||||
itemId: "scm-groupmanager-plugin"
|
||||
},
|
||||
{
|
||||
type: FETCH_PLUGIN_SUCCESS,
|
||||
payload: groupManagerPlugin,
|
||||
itemId: "scm-groupmanager-plugin"
|
||||
}
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchPluginByName(URL, "scm-groupmanager-plugin"))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_PLUGIN_FAILURE, if the request for scm-groupmanager-plugin by name fails", () => {
|
||||
fetchMock.getOnce(PLUGINS_URL + "/scm-groupmanager-plugin", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchPluginByName(URL, "scm-groupmanager-plugin"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
|
||||
expect(actions[1].payload.name).toBe("scm-groupmanager-plugin");
|
||||
expect(actions[1].payload.error).toBeDefined();
|
||||
expect(actions[1].itemId).toBe("scm-groupmanager-plugin");
|
||||
});
|
||||
});
|
||||
|
||||
it("should successfully fetch scm-groupmanager-plugin", () => {
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin",
|
||||
groupManagerPlugin
|
||||
);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_PLUGIN_PENDING,
|
||||
payload: {
|
||||
name: "scm-groupmanager-plugin"
|
||||
},
|
||||
itemId: "scm-groupmanager-plugin"
|
||||
},
|
||||
{
|
||||
type: FETCH_PLUGIN_SUCCESS,
|
||||
payload: groupManagerPlugin,
|
||||
itemId: "scm-groupmanager-plugin"
|
||||
}
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginByLink(groupManagerPlugin)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_PLUGIN_FAILURE, it the request for scm-groupmanager-plugin fails", () => {
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin",
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginByLink(groupManagerPlugin)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
|
||||
expect(actions[1].payload.name).toBe("scm-groupmanager-plugin");
|
||||
expect(actions[1].payload.error).toBeDefined();
|
||||
expect(actions[1].itemId).toBe("scm-groupmanager-plugin");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("plugins reducer", () => {
|
||||
it("should return empty object, if state and action is undefined", () => {
|
||||
expect(reducer()).toEqual({});
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is undefined", () => {
|
||||
const state = { x: true };
|
||||
expect(reducer(state)).toBe(state);
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is unknown to the reducer", () => {
|
||||
const state = { x: true };
|
||||
expect(reducer(state, { type: "EL_SPECIALE" })).toBe(state);
|
||||
});
|
||||
|
||||
it("should store the plugins by it's type and name on FETCH_PLUGINS_SUCCESS", () => {
|
||||
const newState = reducer({}, fetchPluginsSuccess(pluginCollection));
|
||||
expect(newState.list._embedded.plugins).toEqual([
|
||||
"scm-groupmanager-plugin",
|
||||
"scm-script-plugin",
|
||||
"scm-branchwp-plugin"
|
||||
]);
|
||||
expect(newState.byNames["scm-groupmanager-plugin"]).toBe(
|
||||
groupManagerPlugin
|
||||
);
|
||||
expect(newState.byNames["scm-script-plugin"]).toBe(scriptPlugin);
|
||||
expect(newState.byNames["scm-branchwp-plugin"]).toBe(branchwpPlugin);
|
||||
});
|
||||
|
||||
it("should store the plugin at byNames", () => {
|
||||
const newState = reducer({}, fetchPluginSuccess(groupManagerPlugin));
|
||||
expect(newState.byNames["scm-groupmanager-plugin"]).toBe(
|
||||
groupManagerPlugin
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("plugins selectors", () => {
|
||||
const error = new Error("something went wrong");
|
||||
|
||||
it("should return the plugins collection", () => {
|
||||
const state = {
|
||||
plugins: {
|
||||
list: pluginCollectionWithNames,
|
||||
byNames: {
|
||||
"scm-groupmanager-plugin": groupManagerPlugin,
|
||||
"scm-script-plugin": scriptPlugin,
|
||||
"scm-branchwp-plugin": branchwpPlugin
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const collection = getPluginCollection(state);
|
||||
expect(collection).toEqual(pluginCollection);
|
||||
});
|
||||
|
||||
it("should return true, when fetch plugins is pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_PLUGINS]: true
|
||||
}
|
||||
};
|
||||
expect(isFetchPluginsPending(state)).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false, when fetch plugins is not pending", () => {
|
||||
expect(isFetchPluginsPending({})).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return error when fetch plugins did fail", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_PLUGINS]: error
|
||||
}
|
||||
};
|
||||
expect(getFetchPluginsFailure(state)).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when fetch plugins did not fail", () => {
|
||||
expect(getFetchPluginsFailure({})).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should return the plugin collection", () => {
|
||||
const state = {
|
||||
plugins: {
|
||||
byNames: {
|
||||
"scm-groupmanager-plugin": groupManagerPlugin
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const plugin = getPlugin(state, "scm-groupmanager-plugin");
|
||||
expect(plugin).toEqual(groupManagerPlugin);
|
||||
});
|
||||
|
||||
it("should return true, when fetch plugin is pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: true
|
||||
}
|
||||
};
|
||||
expect(isFetchPluginPending(state, "scm-groupmanager-plugin")).toEqual(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("should return false, when fetch plugin is not pending", () => {
|
||||
expect(isFetchPluginPending({}, "scm-groupmanager-plugin")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return error when fetch plugin did fail", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: error
|
||||
}
|
||||
};
|
||||
expect(getFetchPluginFailure(state, "scm-groupmanager-plugin")).toEqual(
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it("should return undefined when fetch plugin did not fail", () => {
|
||||
expect(getFetchPluginFailure({}, "scm-groupmanager-plugin")).toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
362
scm-ui/ui-webapp/src/admin/plugins/modules/plugins.test.ts
Normal file
362
scm-ui/ui-webapp/src/admin/plugins/modules/plugins.test.ts
Normal file
@@ -0,0 +1,362 @@
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import reducer, {
|
||||
FETCH_PLUGINS,
|
||||
FETCH_PLUGINS_PENDING,
|
||||
FETCH_PLUGINS_SUCCESS,
|
||||
FETCH_PLUGINS_FAILURE,
|
||||
FETCH_PLUGIN,
|
||||
FETCH_PLUGIN_PENDING,
|
||||
FETCH_PLUGIN_SUCCESS,
|
||||
FETCH_PLUGIN_FAILURE,
|
||||
fetchPluginsByLink,
|
||||
fetchPluginsSuccess,
|
||||
getPluginCollection,
|
||||
isFetchPluginsPending,
|
||||
getFetchPluginsFailure,
|
||||
fetchPluginByLink,
|
||||
fetchPluginByName,
|
||||
fetchPluginSuccess,
|
||||
getPlugin,
|
||||
isFetchPluginPending,
|
||||
getFetchPluginFailure,
|
||||
} from './plugins';
|
||||
import { Plugin, PluginCollection } from '@scm-manager/ui-types';
|
||||
|
||||
const groupManagerPlugin: Plugin = {
|
||||
name: 'scm-groupmanager-plugin',
|
||||
bundles: ['/scm/groupmanager-plugin.bundle.js'],
|
||||
type: 'Administration',
|
||||
version: '2.0.0-SNAPSHOT',
|
||||
author: 'Sebastian Sdorra',
|
||||
description: 'Notify a remote webserver whenever a plugin is pushed to.',
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const scriptPlugin: Plugin = {
|
||||
name: 'scm-script-plugin',
|
||||
bundles: ['/scm/script-plugin.bundle.js'],
|
||||
type: 'Miscellaneous',
|
||||
version: '2.0.0-SNAPSHOT',
|
||||
author: 'Sebastian Sdorra',
|
||||
description: 'Script support for scm-manager.',
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost:8081/api/v2/ui/plugins/scm-script-plugin',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const branchwpPlugin: Plugin = {
|
||||
name: 'scm-branchwp-plugin',
|
||||
bundles: ['/scm/branchwp-plugin.bundle.js'],
|
||||
type: 'Miscellaneous',
|
||||
version: '2.0.0-SNAPSHOT',
|
||||
author: 'Sebastian Sdorra',
|
||||
description: 'This plugin adds branch write protection for plugins.',
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const pluginCollectionWithNames: PluginCollection = {
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost:8081/api/v2/ui/plugins',
|
||||
},
|
||||
},
|
||||
_embedded: {
|
||||
plugins: [groupManagerPlugin.name, scriptPlugin.name, branchwpPlugin.name],
|
||||
},
|
||||
};
|
||||
|
||||
const pluginCollection: PluginCollection = {
|
||||
_links: {
|
||||
self: {
|
||||
href: 'http://localhost:8081/api/v2/ui/plugins',
|
||||
},
|
||||
},
|
||||
_embedded: {
|
||||
plugins: [groupManagerPlugin, scriptPlugin, branchwpPlugin],
|
||||
},
|
||||
};
|
||||
|
||||
describe('plugins fetch', () => {
|
||||
const URL = 'ui/plugins';
|
||||
const PLUGINS_URL = '/api/v2/ui/plugins';
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it('should successfully fetch plugins from link', () => {
|
||||
fetchMock.getOnce(PLUGINS_URL, pluginCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_PLUGINS_PENDING,
|
||||
},
|
||||
{
|
||||
type: FETCH_PLUGINS_SUCCESS,
|
||||
payload: pluginCollection,
|
||||
},
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginsByLink(URL)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch FETCH_PLUGINS_FAILURE if request fails', () => {
|
||||
fetchMock.getOnce(PLUGINS_URL, {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginsByLink(URL)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGINS_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGINS_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully fetch scm-groupmanager-plugin by name', () => {
|
||||
fetchMock.getOnce(
|
||||
PLUGINS_URL + '/scm-groupmanager-plugin',
|
||||
groupManagerPlugin,
|
||||
);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_PLUGIN_PENDING,
|
||||
payload: {
|
||||
name: 'scm-groupmanager-plugin',
|
||||
},
|
||||
itemId: 'scm-groupmanager-plugin',
|
||||
},
|
||||
{
|
||||
type: FETCH_PLUGIN_SUCCESS,
|
||||
payload: groupManagerPlugin,
|
||||
itemId: 'scm-groupmanager-plugin',
|
||||
},
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchPluginByName(URL, 'scm-groupmanager-plugin'))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch FETCH_PLUGIN_FAILURE, if the request for scm-groupmanager-plugin by name fails', () => {
|
||||
fetchMock.getOnce(PLUGINS_URL + '/scm-groupmanager-plugin', {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchPluginByName(URL, 'scm-groupmanager-plugin'))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
|
||||
expect(actions[1].payload.name).toBe('scm-groupmanager-plugin');
|
||||
expect(actions[1].payload.error).toBeDefined();
|
||||
expect(actions[1].itemId).toBe('scm-groupmanager-plugin');
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully fetch scm-groupmanager-plugin', () => {
|
||||
fetchMock.getOnce(
|
||||
'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
|
||||
groupManagerPlugin,
|
||||
);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_PLUGIN_PENDING,
|
||||
payload: {
|
||||
name: 'scm-groupmanager-plugin',
|
||||
},
|
||||
itemId: 'scm-groupmanager-plugin',
|
||||
},
|
||||
{
|
||||
type: FETCH_PLUGIN_SUCCESS,
|
||||
payload: groupManagerPlugin,
|
||||
itemId: 'scm-groupmanager-plugin',
|
||||
},
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginByLink(groupManagerPlugin)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch FETCH_PLUGIN_FAILURE, it the request for scm-groupmanager-plugin fails', () => {
|
||||
fetchMock.getOnce(
|
||||
'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
|
||||
{
|
||||
status: 500,
|
||||
},
|
||||
);
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchPluginByLink(groupManagerPlugin)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
|
||||
expect(actions[1].payload.name).toBe('scm-groupmanager-plugin');
|
||||
expect(actions[1].payload.error).toBeDefined();
|
||||
expect(actions[1].itemId).toBe('scm-groupmanager-plugin');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('plugins reducer', () => {
|
||||
it('should return empty object, if state and action is undefined', () => {
|
||||
expect(reducer()).toEqual({});
|
||||
});
|
||||
|
||||
it('should return the same state, if the action is undefined', () => {
|
||||
const state = {
|
||||
x: true,
|
||||
};
|
||||
expect(reducer(state)).toBe(state);
|
||||
});
|
||||
|
||||
it('should return the same state, if the action is unknown to the reducer', () => {
|
||||
const state = {
|
||||
x: true,
|
||||
};
|
||||
expect(
|
||||
reducer(state, {
|
||||
type: 'EL_SPECIALE',
|
||||
}),
|
||||
).toBe(state);
|
||||
});
|
||||
|
||||
it("should store the plugins by it's type and name on FETCH_PLUGINS_SUCCESS", () => {
|
||||
const newState = reducer({}, fetchPluginsSuccess(pluginCollection));
|
||||
expect(newState.list._embedded.plugins).toEqual([
|
||||
'scm-groupmanager-plugin',
|
||||
'scm-script-plugin',
|
||||
'scm-branchwp-plugin',
|
||||
]);
|
||||
expect(newState.byNames['scm-groupmanager-plugin']).toBe(
|
||||
groupManagerPlugin,
|
||||
);
|
||||
expect(newState.byNames['scm-script-plugin']).toBe(scriptPlugin);
|
||||
expect(newState.byNames['scm-branchwp-plugin']).toBe(branchwpPlugin);
|
||||
});
|
||||
|
||||
it('should store the plugin at byNames', () => {
|
||||
const newState = reducer({}, fetchPluginSuccess(groupManagerPlugin));
|
||||
expect(newState.byNames['scm-groupmanager-plugin']).toBe(
|
||||
groupManagerPlugin,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('plugins selectors', () => {
|
||||
const error = new Error('something went wrong');
|
||||
|
||||
it('should return the plugins collection', () => {
|
||||
const state = {
|
||||
plugins: {
|
||||
list: pluginCollectionWithNames,
|
||||
byNames: {
|
||||
'scm-groupmanager-plugin': groupManagerPlugin,
|
||||
'scm-script-plugin': scriptPlugin,
|
||||
'scm-branchwp-plugin': branchwpPlugin,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const collection = getPluginCollection(state);
|
||||
expect(collection).toEqual(pluginCollection);
|
||||
});
|
||||
|
||||
it('should return true, when fetch plugins is pending', () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_PLUGINS]: true,
|
||||
},
|
||||
};
|
||||
expect(isFetchPluginsPending(state)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false, when fetch plugins is not pending', () => {
|
||||
expect(isFetchPluginsPending({})).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return error when fetch plugins did fail', () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_PLUGINS]: error,
|
||||
},
|
||||
};
|
||||
expect(getFetchPluginsFailure(state)).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return undefined when fetch plugins did not fail', () => {
|
||||
expect(getFetchPluginsFailure({})).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return the plugin collection', () => {
|
||||
const state = {
|
||||
plugins: {
|
||||
byNames: {
|
||||
'scm-groupmanager-plugin': groupManagerPlugin,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const plugin = getPlugin(state, 'scm-groupmanager-plugin');
|
||||
expect(plugin).toEqual(groupManagerPlugin);
|
||||
});
|
||||
|
||||
it('should return true, when fetch plugin is pending', () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_PLUGIN + '/scm-groupmanager-plugin']: true,
|
||||
},
|
||||
};
|
||||
expect(isFetchPluginPending(state, 'scm-groupmanager-plugin')).toEqual(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false, when fetch plugin is not pending', () => {
|
||||
expect(isFetchPluginPending({}, 'scm-groupmanager-plugin')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return error when fetch plugin did fail', () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_PLUGIN + '/scm-groupmanager-plugin']: error,
|
||||
},
|
||||
};
|
||||
expect(getFetchPluginFailure(state, 'scm-groupmanager-plugin')).toEqual(
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return undefined when fetch plugin did not fail', () => {
|
||||
expect(getFetchPluginFailure({}, 'scm-groupmanager-plugin')).toBe(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,30 +1,23 @@
|
||||
// @flow
|
||||
import * as types from "../../../modules/types";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
import type { Action, Plugin, PluginCollection } from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from '../../../modules/types';
|
||||
import { isPending } from '../../../modules/pending';
|
||||
import { getFailure } from '../../../modules/failure';
|
||||
import { Action, Plugin, PluginCollection } from '@scm-manager/ui-types';
|
||||
import { apiClient } from '@scm-manager/ui-components';
|
||||
|
||||
export const FETCH_PLUGINS = "scm/plugins/FETCH_PLUGINS";
|
||||
export const FETCH_PLUGINS = 'scm/plugins/FETCH_PLUGINS';
|
||||
export const FETCH_PLUGINS_PENDING = `${FETCH_PLUGINS}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_PLUGINS_SUCCESS = `${FETCH_PLUGINS}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_PLUGINS_FAILURE = `${FETCH_PLUGINS}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export const FETCH_PLUGIN = "scm/plugins/FETCH_PLUGIN";
|
||||
export const FETCH_PLUGIN = 'scm/plugins/FETCH_PLUGIN';
|
||||
export const FETCH_PLUGIN_PENDING = `${FETCH_PLUGIN}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_PLUGIN_SUCCESS = `${FETCH_PLUGIN}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_PLUGIN_FAILURE = `${FETCH_PLUGIN}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export const FETCH_PENDING_PLUGINS = "scm/plugins/FETCH_PENDING_PLUGINS";
|
||||
export const FETCH_PENDING_PLUGINS_PENDING = `${FETCH_PENDING_PLUGINS}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PENDING_PLUGINS_SUCCESS = `${FETCH_PENDING_PLUGINS}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PENDING_PLUGINS_FAILURE = `${FETCH_PENDING_PLUGINS}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PENDING_PLUGINS = 'scm/plugins/FETCH_PENDING_PLUGINS';
|
||||
export const FETCH_PENDING_PLUGINS_PENDING = `${FETCH_PENDING_PLUGINS}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_PENDING_PLUGINS_SUCCESS = `${FETCH_PENDING_PLUGINS}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_PENDING_PLUGINS_FAILURE = `${FETCH_PENDING_PLUGINS}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
// fetch plugins
|
||||
export function fetchPluginsByLink(link: string) {
|
||||
@@ -44,21 +37,21 @@ export function fetchPluginsByLink(link: string) {
|
||||
|
||||
export function fetchPluginsPending(): Action {
|
||||
return {
|
||||
type: FETCH_PLUGINS_PENDING
|
||||
type: FETCH_PLUGINS_PENDING,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPluginsSuccess(plugins: PluginCollection): Action {
|
||||
return {
|
||||
type: FETCH_PLUGINS_SUCCESS,
|
||||
payload: plugins
|
||||
payload: plugins,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPluginsFailure(err: Error): Action {
|
||||
return {
|
||||
type: FETCH_PLUGINS_FAILURE,
|
||||
payload: err
|
||||
payload: err,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,7 +61,7 @@ export function fetchPluginByLink(plugin: Plugin) {
|
||||
}
|
||||
|
||||
export function fetchPluginByName(link: string, name: string) {
|
||||
const pluginUrl = link.endsWith("/") ? link : link + "/";
|
||||
const pluginUrl = link.endsWith('/') ? link : link + '/';
|
||||
return fetchPlugin(pluginUrl + name, name);
|
||||
}
|
||||
|
||||
@@ -91,9 +84,9 @@ export function fetchPluginPending(name: string): Action {
|
||||
return {
|
||||
type: FETCH_PLUGIN_PENDING,
|
||||
payload: {
|
||||
name
|
||||
name,
|
||||
},
|
||||
itemId: name
|
||||
itemId: name,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +94,7 @@ export function fetchPluginSuccess(plugin: Plugin): Action {
|
||||
return {
|
||||
type: FETCH_PLUGIN_SUCCESS,
|
||||
payload: plugin,
|
||||
itemId: plugin.name
|
||||
itemId: plugin.name,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,9 +103,9 @@ export function fetchPluginFailure(name: string, error: Error): Action {
|
||||
type: FETCH_PLUGIN_FAILURE,
|
||||
payload: {
|
||||
name,
|
||||
error
|
||||
error,
|
||||
},
|
||||
itemId: name
|
||||
itemId: name,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -134,26 +127,26 @@ export function fetchPendingPlugins(link: string) {
|
||||
|
||||
export function fetchPendingPluginsPending(): Action {
|
||||
return {
|
||||
type: FETCH_PENDING_PLUGINS_PENDING
|
||||
type: FETCH_PENDING_PLUGINS_PENDING,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPendingPluginsSuccess(PendingPlugins: {}): Action {
|
||||
return {
|
||||
type: FETCH_PENDING_PLUGINS_SUCCESS,
|
||||
payload: PendingPlugins
|
||||
payload: PendingPlugins,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPendingPluginsFailure(err: Error): Action {
|
||||
return {
|
||||
type: FETCH_PENDING_PLUGINS_FAILURE,
|
||||
payload: err
|
||||
payload: err,
|
||||
};
|
||||
}
|
||||
|
||||
// reducer
|
||||
function normalizeByName(state: Object, pluginCollection: PluginCollection) {
|
||||
function normalizeByName(state: object, pluginCollection: PluginCollection) {
|
||||
const names = [];
|
||||
const byNames = {};
|
||||
for (const plugin of pluginCollection._embedded.plugins) {
|
||||
@@ -165,27 +158,29 @@ function normalizeByName(state: Object, pluginCollection: PluginCollection) {
|
||||
list: {
|
||||
...pluginCollection,
|
||||
_embedded: {
|
||||
plugins: names
|
||||
}
|
||||
plugins: names,
|
||||
},
|
||||
},
|
||||
byNames: byNames
|
||||
byNames: byNames,
|
||||
};
|
||||
}
|
||||
|
||||
const reducerByNames = (state: Object, plugin: Plugin) => {
|
||||
const reducerByNames = (state: object, plugin: Plugin) => {
|
||||
return {
|
||||
...state,
|
||||
byNames: {
|
||||
...state.byNames,
|
||||
[plugin.name]: plugin
|
||||
}
|
||||
[plugin.name]: plugin,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function reducer(
|
||||
state: Object = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): Object {
|
||||
state: object = {},
|
||||
action: Action = {
|
||||
type: 'UNKNOWN',
|
||||
},
|
||||
): object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
@@ -196,14 +191,17 @@ export default function reducer(
|
||||
case FETCH_PLUGIN_SUCCESS:
|
||||
return reducerByNames(state, action.payload);
|
||||
case FETCH_PENDING_PLUGINS_SUCCESS:
|
||||
return { ...state, pending: action.payload };
|
||||
return {
|
||||
...state,
|
||||
pending: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// selectors
|
||||
export function getPluginCollection(state: Object) {
|
||||
export function getPluginCollection(state: object) {
|
||||
if (state.plugins && state.plugins.list && state.plugins.byNames) {
|
||||
const plugins = [];
|
||||
for (let pluginName of state.plugins.list._embedded.plugins) {
|
||||
@@ -212,44 +210,44 @@ export function getPluginCollection(state: Object) {
|
||||
return {
|
||||
...state.plugins.list,
|
||||
_embedded: {
|
||||
plugins
|
||||
}
|
||||
plugins,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchPluginsPending(state: Object) {
|
||||
export function isFetchPluginsPending(state: object) {
|
||||
return isPending(state, FETCH_PLUGINS);
|
||||
}
|
||||
|
||||
export function getFetchPluginsFailure(state: Object) {
|
||||
export function getFetchPluginsFailure(state: object) {
|
||||
return getFailure(state, FETCH_PLUGINS);
|
||||
}
|
||||
|
||||
export function getPlugin(state: Object, name: string) {
|
||||
export function getPlugin(state: object, name: string) {
|
||||
if (state.plugins && state.plugins.byNames) {
|
||||
return state.plugins.byNames[name];
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchPluginPending(state: Object, name: string) {
|
||||
export function isFetchPluginPending(state: object, name: string) {
|
||||
return isPending(state, FETCH_PLUGIN, name);
|
||||
}
|
||||
|
||||
export function getFetchPluginFailure(state: Object, name: string) {
|
||||
export function getFetchPluginFailure(state: object, name: string) {
|
||||
return getFailure(state, FETCH_PLUGIN, name);
|
||||
}
|
||||
|
||||
export function getPendingPlugins(state: Object) {
|
||||
export function getPendingPlugins(state: object) {
|
||||
if (state.plugins && state.plugins.pending) {
|
||||
return state.plugins.pending;
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchPendingPluginsPending(state: Object) {
|
||||
export function isFetchPendingPluginsPending(state: object) {
|
||||
return isPending(state, FETCH_PENDING_PLUGINS);
|
||||
}
|
||||
|
||||
export function getFetchPendingPluginsFailure(state: Object) {
|
||||
export function getFetchPendingPluginsFailure(state: object) {
|
||||
return getFailure(state, FETCH_PENDING_PLUGINS);
|
||||
}
|
||||
Reference in New Issue
Block a user