diff --git a/scm-ui-components/packages/ui-components/src/config/Configuration.js b/scm-ui-components/packages/ui-components/src/config/Configuration.js new file mode 100644 index 0000000000..07b68f39a6 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/config/Configuration.js @@ -0,0 +1,162 @@ +//@flow +import React from "react"; +import { translate } from "react-i18next"; +import type { Links } from "@scm-manager/ui-types"; +import { + apiClient, + SubmitButton, + Loading, + ErrorNotification +} from "../"; + +type RenderProps = { + readOnly: boolean, + initialConfiguration: ConfigurationType, + onConfigurationChange: (ConfigurationType, boolean) => void +}; + +type Props = { + link: string, + render: (props: RenderProps) => any, // ??? + + // context props + t: (string) => string +}; + +type ConfigurationType = { + _links: Links +} & Object; + +type State = { + error?: Error, + fetching: boolean, + modifying: boolean, + contentType?: string, + + configuration?: ConfigurationType, + modifiedConfiguration?: ConfigurationType, + valid: boolean +}; + +/** + * GlobalConfiguration uses the render prop pattern to encapsulate the logic for + * synchronizing the configuration with the backend. + */ +class Configuration extends React.Component { + + constructor(props: Props) { + super(props); + this.state = { + fetching: true, + modifying: false, + valid: false + }; + } + + componentDidMount() { + const { link } = this.props; + + apiClient.get(link) + .then(this.captureContentType) + .then(response => response.json()) + .then(this.loadConfig) + .catch(this.handleError); + } + + captureContentType = (response: Response) => { + const contentType = response.headers.get("Content-Type"); + this.setState({ + contentType + }); + return response; + }; + + getContentType = (): string => { + const { contentType } = this.state; + return contentType ? contentType : "application/json"; + }; + + handleError = (error: Error) => { + this.setState({ + error, + fetching: false, + modifying: false + }); + }; + + loadConfig = (configuration: ConfigurationType) => { + this.setState({ + configuration, + fetching: false, + error: undefined + }); + }; + + getModificationUrl = (): ?string => { + const { configuration } = this.state; + if (configuration) { + const links = configuration._links; + if (links && links.update) { + return links.update.href; + } + } + }; + + isReadOnly = (): boolean => { + const modificationUrl = this.getModificationUrl(); + return !modificationUrl; + }; + + configurationChanged = (configuration: ConfigurationType, valid: boolean) => { + this.setState({ + modifiedConfiguration: configuration, + valid + }); + }; + + modifyConfiguration = (event: Event) => { + event.preventDefault(); + + this.setState({ modifying: true }); + + const {modifiedConfiguration} = this.state; + + apiClient.put(this.getModificationUrl(), modifiedConfiguration, this.getContentType()) + .then(() => this.setState({ modifying: false })) + .catch(this.handleError); + }; + + render() { + const { t } = this.props; + const { fetching, error, configuration, modifying, valid } = this.state; + + if (error) { + return ; + } else if (fetching || !configuration) { + return ; + } else { + const readOnly = this.isReadOnly(); + + const renderProps: RenderProps = { + readOnly, + initialConfiguration: configuration, + onConfigurationChange: this.configurationChanged + }; + + return ( +
+ { this.props.render(renderProps) } +
+ + + ); + } + } + +} + +export default translate("config")(Configuration); diff --git a/scm-ui-components/packages/ui-components/src/config/index.js b/scm-ui-components/packages/ui-components/src/config/index.js index 9596e9cda5..fba48c8054 100644 --- a/scm-ui-components/packages/ui-components/src/config/index.js +++ b/scm-ui-components/packages/ui-components/src/config/index.js @@ -1,3 +1,4 @@ // @flow export { default as ConfigurationBinder } from "./ConfigurationBinder"; -export { default as GlobalConfiguration } from "./GlobalConfiguration"; +export { default as Configuration } from "./Configuration"; +export { default as RepositoryConfigurationBinder } from "./RepositoryConfigurationBinder";