Adds basic internationalization support
This patch adds an I18NProvider component to implement react-intl's translation mechanisms and replaces text strings in the deployment_plan components with formatted messages. The patch also contains scripts to convert extracted messages to `.pot` files (which can be used with zanata.org) and back. See more details in the README changes. More components need to be updated once this patch has landed. Change-Id: Ida9fc65c65bedf377341220be1d7225d1ae58b2c Implements: blueprint tripleo-ui-i18n-support-for-js
This commit is contained in:
parent
9f2244f848
commit
57c98c657d
8
.babelrc
8
.babelrc
|
@ -1,3 +1,9 @@
|
||||||
{
|
{
|
||||||
"presets": ["es2015", "stage-0", "react"]
|
"presets": ["es2015", "stage-0", "react"],
|
||||||
|
"plugins": [
|
||||||
|
["react-intl", {
|
||||||
|
"messagesDir": "./i18n/extracted-messages/",
|
||||||
|
"enforceDescriptions": false
|
||||||
|
}]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,3 +9,5 @@ tripleo-ui-*.tar.gz
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
app.conf
|
app.conf
|
||||||
|
messages.pot
|
||||||
|
i18n/extracted-messages*
|
||||||
|
|
44
README.md
44
README.md
|
@ -122,3 +122,47 @@ src/js/components/environment_configuration
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
Use JSDoc docstrings in code to provide source for autogenerated documentation (http://usejsdoc.org/).
|
Use JSDoc docstrings in code to provide source for autogenerated documentation (http://usejsdoc.org/).
|
||||||
|
|
||||||
|
|
||||||
|
# Translation
|
||||||
|
|
||||||
|
tripleo-ui uses the react-intl package for translation.
|
||||||
|
|
||||||
|
|
||||||
|
## Adding translateable strings
|
||||||
|
|
||||||
|
Strings are prepared for translation using react-intl's `defineMessages` API. Check out `./src/js/components/deployment_plan/` for examples.
|
||||||
|
|
||||||
|
|
||||||
|
## Extracting messages from components
|
||||||
|
|
||||||
|
Messages are extracted during the build process (`npm run build`) and stored in the `./i18n/extracted-messages/` folder. These files can be converted into a single `.pot` file with this command:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run json2pot
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting file (`./messages.pot`) can be uploaded to http://zanata.org to create/update the translation.
|
||||||
|
|
||||||
|
|
||||||
|
## Using translated `.po` files
|
||||||
|
|
||||||
|
The translated language file (`messages.po`) then needs to be converted into one JSON file per language (Japanese in this example):
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run po2json -- messages.po -o ./i18n/locales/ja.json
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Adding a new language
|
||||||
|
|
||||||
|
The languages are defined in the ./src/js/components/i18n/I18NProvider component. To add another language, import the relevant locale data from the react-intl packages, as well as the JSON containing the translation and add the new language to the MESSAGES constant:
|
||||||
|
|
||||||
|
```
|
||||||
|
import ja from 'react-intl/locale-data/ja';
|
||||||
|
import jaMessages from '../../../../i18n/locales/ja.json';
|
||||||
|
|
||||||
|
const MESSAGES = {
|
||||||
|
ja: jaMessages.messages
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"messages":{"ConfigurePlanStep.editConfigurationLink":"","DeleteStackButton.deleteDeployment":"","DeploymentFailure.deleteDeployment":"","DeploymentSuccess.deleteDeployment":"","DeleteStackButton.deleteConfirmationQuestion":"","DeploymentConfiguration.deploymentConfiguration":"","DeploymentConfiguration.overallSettings":"","DeploymentConfiguration.parameters":"パラメーター","DeploymentConfigurationSummary.loadingCurrentConfiguration":"","DeploymentFailure.requestingDeletion":"","DeploymentProgress.requestingDeletion":"","DeploymentSuccess.requestingDeletion":"","DeploymentPlan.hardwareStepHeader":"","DeploymentPlan.configureRolesStepHeader":"","DeploymentPlan.deploymentConfigurationStepHeader":"","DeploymentPlan.deployStepHeader":"展開する","DeploymentProgress.cancelDeployment":"","DeploymentProgress.deploymentInProgress":"","DeploymentProgress.viewInformation":"","DeployStep.validateAndDeploy":"","HardwareStep.registerNodes":"","NodesAssignment.assignUnassignNodes":"","NodesAssignment.done":"","NoPlans.noPlansAvailable":"","NoPlans.noPlansAvailableMessage":"","NoPlans.createNewPlan":"","PlansDropdown.manageDeployments":"","PlansDropdown.selectDeployment":"","RoleCard.nodesAssigned":"","RoleCard.assignNodes":"","Roles.loadingDeploymentRoles":"","RolesStep.loadingNodes":""}}
|
|
@ -71,7 +71,9 @@
|
||||||
"lint": "eslint --max-warnings 0 src",
|
"lint": "eslint --max-warnings 0 src",
|
||||||
"start": "webpack-dev-server --progress",
|
"start": "webpack-dev-server --progress",
|
||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"test:watch": "karma start"
|
"test:watch": "karma start",
|
||||||
|
"json2pot": "rip json2pot ./i18n/extracted-messages/**/*.json -o ./messages.pot",
|
||||||
|
"po2json": "rip po2json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
import React from 'react';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import DeploymentConfigurationSummary from './DeploymentConfigurationSummary';
|
import DeploymentConfigurationSummary from './DeploymentConfigurationSummary';
|
||||||
|
|
||||||
export const ConfigurePlanStep = (props) => {
|
const messages = defineMessages({
|
||||||
|
editConfigurationLink: {
|
||||||
|
id: 'ConfigurePlanStep.editConfigurationLink',
|
||||||
|
defaultMessage: 'Edit Configurations'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConfigurePlanStep = (props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<DeploymentConfigurationSummary { ...props } />
|
<DeploymentConfigurationSummary { ...props } />
|
||||||
|
|
||||||
<Link to="/deployment-plan/configuration">
|
<Link to="/deployment-plan/configuration">
|
||||||
Edit Configuration
|
<FormattedMessage { ...messages.editConfigurationLink} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -21,3 +29,5 @@ ConfigurePlanStep.propTypes = {
|
||||||
planName: React.PropTypes.string.isRequired,
|
planName: React.PropTypes.string.isRequired,
|
||||||
summary: React.PropTypes.string.isRequired
|
summary: React.PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default ConfigurePlanStep;
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ConfirmationModal from '../ui/ConfirmationModal';
|
import ConfirmationModal from '../ui/ConfirmationModal';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
|
|
||||||
export default class DeleteStackButton extends React.Component {
|
const messages = defineMessages({
|
||||||
|
deleteDeployment: {
|
||||||
|
id: 'DeleteStackButton.deleteDeployment',
|
||||||
|
defaultMessage: 'Delete Deployment'
|
||||||
|
},
|
||||||
|
deleteConfirmationQuestion: {
|
||||||
|
id: 'DeleteStackButton.deleteConfirmationQuestion',
|
||||||
|
defaultMessage: 'Are you sure you want to delete the stack?'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class DeleteStackButton extends React.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -18,6 +30,8 @@ export default class DeleteStackButton extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { formatMessage } = this.props.intl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button onClick={() => this.setState({ showDeleteModal: true })}
|
<button onClick={() => this.setState({ showDeleteModal: true })}
|
||||||
|
@ -33,8 +47,8 @@ export default class DeleteStackButton extends React.Component {
|
||||||
</Loader>
|
</Loader>
|
||||||
</button>
|
</button>
|
||||||
<ConfirmationModal show={this.state.showDeleteModal}
|
<ConfirmationModal show={this.state.showDeleteModal}
|
||||||
title="Delete Deployment"
|
title={formatMessage(messages.deleteDeployment)}
|
||||||
question="Are you sure you want to delete the stack?"
|
question={formatMessage(messages.deleteConfirmationQuestion)}
|
||||||
iconClass="pficon pficon-delete"
|
iconClass="pficon pficon-delete"
|
||||||
confirmActionName="delete"
|
confirmActionName="delete"
|
||||||
onConfirm={this.confirmDelete.bind(this)}
|
onConfirm={this.confirmDelete.bind(this)}
|
||||||
|
@ -49,6 +63,7 @@ DeleteStackButton.propTypes = {
|
||||||
content: React.PropTypes.string.isRequired,
|
content: React.PropTypes.string.isRequired,
|
||||||
deleteStack: React.PropTypes.func.isRequired,
|
deleteStack: React.PropTypes.func.isRequired,
|
||||||
disabled: React.PropTypes.bool.isRequired,
|
disabled: React.PropTypes.bool.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
loaded: React.PropTypes.bool.isRequired,
|
loaded: React.PropTypes.bool.isRequired,
|
||||||
loaderContent: React.PropTypes.string.isRequired,
|
loaderContent: React.PropTypes.string.isRequired,
|
||||||
stack: ImmutablePropTypes.record.isRequired
|
stack: ImmutablePropTypes.record.isRequired
|
||||||
|
@ -56,3 +71,5 @@ DeleteStackButton.propTypes = {
|
||||||
DeleteStackButton.defaultProps = {
|
DeleteStackButton.defaultProps = {
|
||||||
buttonIconClass: 'pficon pficon-delete'
|
buttonIconClass: 'pficon pficon-delete'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(DeleteStackButton);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import DeploymentSuccess from './DeploymentSuccess';
|
import DeploymentSuccess from './DeploymentSuccess';
|
||||||
import DeploymentFailure from './DeploymentFailure';
|
import DeploymentFailure from './DeploymentFailure';
|
||||||
|
@ -8,11 +9,18 @@ import Link from '../ui/Link';
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import { stackStates } from '../../constants/StacksConstants';
|
import { stackStates } from '../../constants/StacksConstants';
|
||||||
|
|
||||||
export const DeployStep = ({ currentPlan, currentStack, currentStackResources,
|
const messages = defineMessages({
|
||||||
currentStackResourcesLoaded, currentStackDeploymentProgress,
|
validateAndDeploy: {
|
||||||
deleteStack, deployPlan, fetchStackResource, fetchStackEnvironment,
|
id: 'DeployStep.validateAndDeploy',
|
||||||
isRequestingStackDelete, runPostDeploymentValidations,
|
defaultMessage: 'Validate and Deploy'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DeployStep = ({ currentPlan, currentStack, currentStackResources, currentStackResourcesLoaded,
|
||||||
|
currentStackDeploymentProgress, deleteStack, deployPlan, fetchStackResource,
|
||||||
|
fetchStackEnvironment, isRequestingStackDelete, runPostDeploymentValidations,
|
||||||
stacksLoaded }) => {
|
stacksLoaded }) => {
|
||||||
|
|
||||||
if (!currentStack || currentStack.stack_status === stackStates.DELETE_COMPLETE) {
|
if (!currentStack || currentStack.stack_status === stackStates.DELETE_COMPLETE) {
|
||||||
return (
|
return (
|
||||||
<Loader loaded={stacksLoaded}>
|
<Loader loaded={stacksLoaded}>
|
||||||
|
@ -23,7 +31,8 @@ export const DeployStep = ({ currentPlan, currentStack, currentStackResources,
|
||||||
content="Requesting a deploy..."
|
content="Requesting a deploy..."
|
||||||
component="span"
|
component="span"
|
||||||
inline>
|
inline>
|
||||||
<span className="fa fa-cloud-upload"/> Validate and Deploy
|
<span className="fa fa-cloud-upload"/> <FormattedMessage
|
||||||
|
{...messages.validateAndDeploy}/>
|
||||||
</Loader>
|
</Loader>
|
||||||
</Link>
|
</Link>
|
||||||
</Loader>
|
</Loader>
|
||||||
|
@ -69,3 +78,5 @@ DeployStep.propTypes = {
|
||||||
runPostDeploymentValidations: React.PropTypes.func.isRequired,
|
runPostDeploymentValidations: React.PropTypes.func.isRequired,
|
||||||
stacksLoaded: React.PropTypes.bool.isRequired
|
stacksLoaded: React.PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default DeployStep;
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
import React from 'react';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import NavTab from '../ui/NavTab';
|
import NavTab from '../ui/NavTab';
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
deploymentConfiguration: {
|
||||||
|
id: 'DeploymentConfiguration.deploymentConfiguration',
|
||||||
|
defaultMessage: 'Deployment Configuration'
|
||||||
|
},
|
||||||
|
overallSettings: {
|
||||||
|
id: 'DeploymentConfiguration.overallSettings',
|
||||||
|
defaultMessage: 'Overall Settings'
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
id: 'DeploymentConfiguration.parameters',
|
||||||
|
defaultMessage: 'Parameters'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default class DeploymentConfiguration extends React.Component {
|
export default class DeploymentConfiguration extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -14,12 +30,18 @@ export default class DeploymentConfiguration extends React.Component {
|
||||||
className="close">
|
className="close">
|
||||||
<span aria-hidden="true" className="pficon pficon-close"/>
|
<span aria-hidden="true" className="pficon pficon-close"/>
|
||||||
</Link>
|
</Link>
|
||||||
<h4 className="modal-title">Deployment Configuration</h4>
|
<h4 className="modal-title">
|
||||||
|
<FormattedMessage {...messages.deploymentConfiguration}/>
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="nav nav-tabs">
|
<ul className="nav nav-tabs">
|
||||||
<NavTab to="/deployment-plan/configuration/environment">Overall Settings</NavTab>
|
<NavTab to="/deployment-plan/configuration/environment">
|
||||||
<NavTab to="/deployment-plan/configuration/parameters">Parameters</NavTab>
|
<FormattedMessage {...messages.overallSettings}/>
|
||||||
|
</NavTab>
|
||||||
|
<NavTab to="/deployment-plan/configuration/parameters">
|
||||||
|
<FormattedMessage {...messages.parameters}/>
|
||||||
|
</NavTab>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{React.cloneElement(this.props.children,
|
{React.cloneElement(this.props.children,
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
|
|
||||||
export default class DeploymentConfigurationSummary extends React.Component {
|
const messages = defineMessages({
|
||||||
|
loadingCurrentConfiguration: {
|
||||||
|
id: 'DeploymentConfigurationSummary.loadingCurrentConfiguration',
|
||||||
|
defaultMessage: 'Loading current Deployment Configuration...'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class DeploymentConfigurationSummary extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if(this.props.planName) {
|
if(this.props.planName) {
|
||||||
this.props.fetchEnvironmentConfiguration(this.props.planName);
|
this.props.fetchEnvironmentConfiguration(this.props.planName);
|
||||||
|
@ -18,7 +26,7 @@ export default class DeploymentConfigurationSummary extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Loader loaded={this.props.loaded}
|
<Loader loaded={this.props.loaded}
|
||||||
content="Loading current Deployment Configuration..."
|
content={this.props.intl.formatMessage(messages.loadingCurrentConfiguration)}
|
||||||
component="span"
|
component="span"
|
||||||
inline>
|
inline>
|
||||||
<span>{this.props.summary}</span>
|
<span>{this.props.summary}</span>
|
||||||
|
@ -28,8 +36,11 @@ export default class DeploymentConfigurationSummary extends React.Component {
|
||||||
}
|
}
|
||||||
DeploymentConfigurationSummary.propTypes = {
|
DeploymentConfigurationSummary.propTypes = {
|
||||||
fetchEnvironmentConfiguration: React.PropTypes.func.isRequired,
|
fetchEnvironmentConfiguration: React.PropTypes.func.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isFetching: React.PropTypes.bool.isRequired,
|
isFetching: React.PropTypes.bool.isRequired,
|
||||||
loaded: React.PropTypes.bool.isRequired,
|
loaded: React.PropTypes.bool.isRequired,
|
||||||
planName: React.PropTypes.string,
|
planName: React.PropTypes.string,
|
||||||
summary: React.PropTypes.string.isRequired
|
summary: React.PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(DeploymentConfigurationSummary);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
@ -6,8 +7,21 @@ import DeleteStackButton from './DeleteStackButton';
|
||||||
import InlineNotification from '../ui/InlineNotification';
|
import InlineNotification from '../ui/InlineNotification';
|
||||||
import { deploymentStatusMessages } from '../../constants/StacksConstants';
|
import { deploymentStatusMessages } from '../../constants/StacksConstants';
|
||||||
|
|
||||||
export default class DeploymentFailure extends React.Component {
|
const messages = defineMessages({
|
||||||
|
deleteDeployment: {
|
||||||
|
id: 'DeploymentFailure.deleteDeployment',
|
||||||
|
defaultMessage: 'Delete Deployment'
|
||||||
|
},
|
||||||
|
requestingDeletion: {
|
||||||
|
id: 'DeploymentFailure.requestingDeletion',
|
||||||
|
defaultMessage: 'Requesting Deletion of Deployment'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class DeploymentFailure extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
const { formatMessage } = this.props.intl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<InlineNotification type="error"
|
<InlineNotification type="error"
|
||||||
|
@ -17,11 +31,11 @@ export default class DeploymentFailure extends React.Component {
|
||||||
More details</Link>
|
More details</Link>
|
||||||
</p>
|
</p>
|
||||||
</InlineNotification>
|
</InlineNotification>
|
||||||
<DeleteStackButton content="Delete Deployment"
|
<DeleteStackButton content={formatMessage(messages.deleteDeployment)}
|
||||||
deleteStack={this.props.deleteStack}
|
deleteStack={this.props.deleteStack}
|
||||||
disabled={this.props.isRequestingStackDelete}
|
disabled={this.props.isRequestingStackDelete}
|
||||||
loaded={!this.props.isRequestingStackDelete}
|
loaded={!this.props.isRequestingStackDelete}
|
||||||
loaderContent="Requesting Deletion of Deployment"
|
loaderContent={formatMessage(messages.requestingDeletion)}
|
||||||
stack={this.props.stack}/>
|
stack={this.props.stack}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -30,6 +44,9 @@ export default class DeploymentFailure extends React.Component {
|
||||||
|
|
||||||
DeploymentFailure.propTypes = {
|
DeploymentFailure.propTypes = {
|
||||||
deleteStack: React.PropTypes.func.isRequired,
|
deleteStack: React.PropTypes.func.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isRequestingStackDelete: React.PropTypes.bool,
|
isRequestingStackDelete: React.PropTypes.bool,
|
||||||
stack: ImmutablePropTypes.record.isRequired
|
stack: ImmutablePropTypes.record.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(DeploymentFailure);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -9,22 +10,41 @@ import { getCurrentStack,
|
||||||
import { getAvailableNodes, getUnassignedAvailableNodes } from '../../selectors/nodes';
|
import { getAvailableNodes, getUnassignedAvailableNodes } from '../../selectors/nodes';
|
||||||
import { getEnvironmentConfigurationSummary } from '../../selectors/environmentConfiguration';
|
import { getEnvironmentConfigurationSummary } from '../../selectors/environmentConfiguration';
|
||||||
import { getCurrentPlan } from '../../selectors/plans';
|
import { getCurrentPlan } from '../../selectors/plans';
|
||||||
import { ConfigurePlanStep } from './ConfigurePlanStep';
|
import ConfigurePlanStep from './ConfigurePlanStep';
|
||||||
import CurrentPlanActions from '../../actions/CurrentPlanActions';
|
import CurrentPlanActions from '../../actions/CurrentPlanActions';
|
||||||
import { DeploymentPlanStep } from './DeploymentPlanStep';
|
import { DeploymentPlanStep } from './DeploymentPlanStep';
|
||||||
import { DeployStep } from './DeployStep';
|
import DeployStep from './DeployStep';
|
||||||
import EnvironmentConfigurationActions from '../../actions/EnvironmentConfigurationActions';
|
import EnvironmentConfigurationActions from '../../actions/EnvironmentConfigurationActions';
|
||||||
import { HardwareStep } from './HardwareStep';
|
import HardwareStep from './HardwareStep';
|
||||||
import PlansDropdown from './PlansDropdown';
|
import PlansDropdown from './PlansDropdown';
|
||||||
import NodesActions from '../../actions/NodesActions';
|
import NodesActions from '../../actions/NodesActions';
|
||||||
import NoPlans from './NoPlans';
|
import NoPlans from './NoPlans';
|
||||||
import NotificationActions from '../../actions/NotificationActions';
|
import NotificationActions from '../../actions/NotificationActions';
|
||||||
import PlanActions from '../../actions/PlansActions';
|
import PlanActions from '../../actions/PlansActions';
|
||||||
import StacksActions from '../../actions/StacksActions';
|
import StacksActions from '../../actions/StacksActions';
|
||||||
import { RolesStep } from './RolesStep';
|
import RolesStep from './RolesStep';
|
||||||
import RolesActions from '../../actions/RolesActions';
|
import RolesActions from '../../actions/RolesActions';
|
||||||
import ValidationsActions from '../../actions/ValidationsActions';
|
import ValidationsActions from '../../actions/ValidationsActions';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
hardwareStepHeader: {
|
||||||
|
id: 'DeploymentPlan.hardwareStepHeader',
|
||||||
|
defaultMessage: 'Prepare Hardware'
|
||||||
|
},
|
||||||
|
configureRolesStepHeader: {
|
||||||
|
id: 'DeploymentPlan.configureRolesStepHeader',
|
||||||
|
defaultMessage: 'Configure Roles and Assign Nodes'
|
||||||
|
},
|
||||||
|
deploymentConfigurationStepHeader: {
|
||||||
|
id: 'DeploymentPlan.deploymentConfigurationStepHeader',
|
||||||
|
defaultMessage: 'Specify Deployment Configuration'
|
||||||
|
},
|
||||||
|
deployStepHeader: {
|
||||||
|
id: 'DeploymentPlan.deployStepHeader',
|
||||||
|
defaultMessage: 'Deploy'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class DeploymentPlan extends React.Component {
|
class DeploymentPlan extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchStacks();
|
this.props.fetchStacks();
|
||||||
|
@ -52,6 +72,7 @@ class DeploymentPlan extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { formatMessage } = this.props.intl;
|
||||||
let children;
|
let children;
|
||||||
const currentPlanName = this.props.hasPlans ? this.props.currentPlan.name : undefined;
|
const currentPlanName = this.props.hasPlans ? this.props.currentPlan.name : undefined;
|
||||||
|
|
||||||
|
@ -75,11 +96,11 @@ class DeploymentPlan extends React.Component {
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<ol className="deployment-step-list">
|
<ol className="deployment-step-list">
|
||||||
<DeploymentPlanStep title="Prepare Hardware"
|
<DeploymentPlanStep title={formatMessage(messages.hardwareStepHeader)}
|
||||||
disabled={this.props.currentStackDeploymentInProgress}>
|
disabled={this.props.currentStackDeploymentInProgress}>
|
||||||
<HardwareStep />
|
<HardwareStep />
|
||||||
</DeploymentPlanStep>
|
</DeploymentPlanStep>
|
||||||
<DeploymentPlanStep title="Specify Deployment Configuration"
|
<DeploymentPlanStep title={formatMessage(messages.deploymentConfigurationStepHeader)}
|
||||||
disabled={this.props.currentStackDeploymentInProgress}>
|
disabled={this.props.currentStackDeploymentInProgress}>
|
||||||
<ConfigurePlanStep
|
<ConfigurePlanStep
|
||||||
fetchEnvironmentConfiguration={this.props.fetchEnvironmentConfiguration}
|
fetchEnvironmentConfiguration={this.props.fetchEnvironmentConfiguration}
|
||||||
|
@ -88,7 +109,7 @@ class DeploymentPlan extends React.Component {
|
||||||
isFetching={this.props.isFetchingEnvironmentConfiguration}
|
isFetching={this.props.isFetchingEnvironmentConfiguration}
|
||||||
loaded={this.props.environmentConfigurationLoaded}/>
|
loaded={this.props.environmentConfigurationLoaded}/>
|
||||||
</DeploymentPlanStep>
|
</DeploymentPlanStep>
|
||||||
<DeploymentPlanStep title="Configure Roles and Assign Nodes"
|
<DeploymentPlanStep title={formatMessage(messages.configureRolesStepHeader)}
|
||||||
disabled={this.props.currentStackDeploymentInProgress}>
|
disabled={this.props.currentStackDeploymentInProgress}>
|
||||||
<RolesStep availableNodes={this.props.availableNodes}
|
<RolesStep availableNodes={this.props.availableNodes}
|
||||||
fetchNodes={this.props.fetchNodes}
|
fetchNodes={this.props.fetchNodes}
|
||||||
|
@ -99,7 +120,7 @@ class DeploymentPlan extends React.Component {
|
||||||
rolesLoaded={this.props.rolesLoaded}
|
rolesLoaded={this.props.rolesLoaded}
|
||||||
unassignedAvailableNodes={this.props.unassignedAvailableNodes}/>
|
unassignedAvailableNodes={this.props.unassignedAvailableNodes}/>
|
||||||
</DeploymentPlanStep>
|
</DeploymentPlanStep>
|
||||||
<DeploymentPlanStep title="Deploy">
|
<DeploymentPlanStep title={formatMessage(messages.deployStepHeader)}>
|
||||||
<DeployStep
|
<DeployStep
|
||||||
currentPlan={this.props.currentPlan}
|
currentPlan={this.props.currentPlan}
|
||||||
currentStack={this.props.currentStack}
|
currentStack={this.props.currentStack}
|
||||||
|
@ -151,6 +172,7 @@ DeploymentPlan.propTypes = {
|
||||||
fetchStacks: React.PropTypes.func,
|
fetchStacks: React.PropTypes.func,
|
||||||
hasPlans: React.PropTypes.bool,
|
hasPlans: React.PropTypes.bool,
|
||||||
inactivePlans: ImmutablePropTypes.map,
|
inactivePlans: ImmutablePropTypes.map,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isFetchingEnvironmentConfiguration: React.PropTypes.bool,
|
isFetchingEnvironmentConfiguration: React.PropTypes.bool,
|
||||||
isFetchingNodes: React.PropTypes.bool,
|
isFetchingNodes: React.PropTypes.bool,
|
||||||
isFetchingRoles: React.PropTypes.bool,
|
isFetchingRoles: React.PropTypes.bool,
|
||||||
|
@ -213,4 +235,4 @@ function mapDispatchToProps(dispatch) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DeploymentPlan);
|
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(DeploymentPlan));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React from 'react';
|
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import { deploymentStatusMessages as statusMessages,
|
import { deploymentStatusMessages as statusMessages,
|
||||||
stackStates } from '../../constants/StacksConstants';
|
stackStates } from '../../constants/StacksConstants';
|
||||||
|
@ -8,7 +9,26 @@ import DeleteStackButton from './DeleteStackButton';
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import ProgressBar from '../ui/ProgressBar';
|
import ProgressBar from '../ui/ProgressBar';
|
||||||
|
|
||||||
export default class DeploymentProgress extends React.Component {
|
const messages = defineMessages({
|
||||||
|
cancelDeployment: {
|
||||||
|
id: 'DeploymentProgress.cancelDeployment',
|
||||||
|
defaultMessage: 'Cancel Deployment'
|
||||||
|
},
|
||||||
|
requestingDeletion: {
|
||||||
|
id: 'DeploymentProgress.requestingDeletion',
|
||||||
|
defaultMessage: 'Requesting Deletion of Deployment'
|
||||||
|
},
|
||||||
|
deploymentInProgress: {
|
||||||
|
id: 'DeploymentProgress.deploymentInProgress',
|
||||||
|
defaultMessage: 'Deployment is currently in progress.'
|
||||||
|
},
|
||||||
|
viewInformation: {
|
||||||
|
id: 'DeploymentProgress.viewInformation',
|
||||||
|
defaultMessage: 'View detailed information'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class DeploymentProgress extends React.Component {
|
||||||
renderProgressBar() {
|
renderProgressBar() {
|
||||||
return (
|
return (
|
||||||
this.props.stack.stack_status === stackStates.CREATE_IN_PROGRESS ? (
|
this.props.stack.stack_status === stackStates.CREATE_IN_PROGRESS ? (
|
||||||
|
@ -20,24 +40,27 @@ export default class DeploymentProgress extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { formatMessage } = this.props.intl;
|
||||||
|
|
||||||
const statusMessage = (
|
const statusMessage = (
|
||||||
<strong>{statusMessages[this.props.stack.stack_status]}</strong>
|
<strong>{statusMessages[this.props.stack.stack_status]}</strong>
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteButton = this.props.stack.stack_status !== stackStates.DELETE_IN_PROGRESS
|
const deleteButton = this.props.stack.stack_status !== stackStates.DELETE_IN_PROGRESS
|
||||||
? (<DeleteStackButton content="Cancel Deployment"
|
? (<DeleteStackButton content={formatMessage(messages.cancelDeployment)}
|
||||||
buttonIconClass="fa fa-ban"
|
buttonIconClass="fa fa-ban"
|
||||||
deleteStack={this.props.deleteStack}
|
deleteStack={this.props.deleteStack}
|
||||||
disabled={this.props.isRequestingStackDelete}
|
disabled={this.props.isRequestingStackDelete}
|
||||||
loaded={!this.props.isRequestingStackDelete}
|
loaded={!this.props.isRequestingStackDelete}
|
||||||
loaderContent="Requesting Deletion of Deployment"
|
loaderContent={formatMessage(messages.requestingDeletion)}
|
||||||
stack={this.props.stack}/>) : null;
|
stack={this.props.stack}/>) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Deployment is currently in progress. <Link to="/deployment-plan/deployment-detail">
|
<span><FormattedMessage {...messages.deploymentInProgress}/> </span>
|
||||||
View detailed information
|
<Link to="/deployment-plan/deployment-detail">
|
||||||
|
<FormattedMessage {...messages.viewInformation}/>
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
<div className="progress-description">
|
<div className="progress-description">
|
||||||
|
@ -53,6 +76,9 @@ export default class DeploymentProgress extends React.Component {
|
||||||
DeploymentProgress.propTypes = {
|
DeploymentProgress.propTypes = {
|
||||||
deleteStack: React.PropTypes.func.isRequired,
|
deleteStack: React.PropTypes.func.isRequired,
|
||||||
deploymentProgress: React.PropTypes.number.isRequired,
|
deploymentProgress: React.PropTypes.number.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isRequestingStackDelete: React.PropTypes.bool.isRequired,
|
isRequestingStackDelete: React.PropTypes.bool.isRequired,
|
||||||
stack: ImmutablePropTypes.record.isRequired
|
stack: ImmutablePropTypes.record.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(DeploymentProgress);
|
||||||
|
|
|
@ -1,12 +1,24 @@
|
||||||
import React from 'react';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import DeleteStackButton from './DeleteStackButton';
|
import DeleteStackButton from './DeleteStackButton';
|
||||||
import { deploymentStatusMessages } from '../../constants/StacksConstants';
|
import { deploymentStatusMessages } from '../../constants/StacksConstants';
|
||||||
import InlineNotification from '../ui/InlineNotification';
|
import InlineNotification from '../ui/InlineNotification';
|
||||||
import OvercloudInfo from '../deployment/OvercloudInfo';
|
import OvercloudInfo from '../deployment/OvercloudInfo';
|
||||||
|
|
||||||
export default class DeploymentSuccess extends React.Component {
|
const messages = defineMessages({
|
||||||
|
deleteDeployment: {
|
||||||
|
id: 'DeploymentSuccess.deleteDeployment',
|
||||||
|
defaultMessage: 'Delete Deployment'
|
||||||
|
},
|
||||||
|
requestingDeletion: {
|
||||||
|
id: 'DeploymentSuccess.requestingDeletion',
|
||||||
|
defaultMessage: 'Requesting Deletion of Deployment'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class DeploymentSuccess extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchStackResource(this.props.stack, 'PublicVirtualIP');
|
this.props.fetchStackResource(this.props.stack, 'PublicVirtualIP');
|
||||||
this.props.fetchStackEnvironment(this.props.stack);
|
this.props.fetchStackEnvironment(this.props.stack);
|
||||||
|
@ -14,6 +26,8 @@ export default class DeploymentSuccess extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { formatMessage } = this.props.intl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<InlineNotification type="success"
|
<InlineNotification type="success"
|
||||||
|
@ -23,11 +37,11 @@ export default class DeploymentSuccess extends React.Component {
|
||||||
<OvercloudInfo stackResourcesLoaded={this.props.stackResourcesLoaded}
|
<OvercloudInfo stackResourcesLoaded={this.props.stackResourcesLoaded}
|
||||||
stack={this.props.stack}
|
stack={this.props.stack}
|
||||||
stackResources={this.props.stackResources}/>
|
stackResources={this.props.stackResources}/>
|
||||||
<DeleteStackButton content="Delete Deployment"
|
<DeleteStackButton content={formatMessage(messages.deleteDeployment)}
|
||||||
deleteStack={this.props.deleteStack}
|
deleteStack={this.props.deleteStack}
|
||||||
disabled={this.props.isRequestingStackDelete}
|
disabled={this.props.isRequestingStackDelete}
|
||||||
loaded={!this.props.isRequestingStackDelete}
|
loaded={!this.props.isRequestingStackDelete}
|
||||||
loaderContent="Requesting Deletion of Deployment"
|
loaderContent={formatMessage(messages.requestingDeletion)}
|
||||||
stack={this.props.stack}/>
|
stack={this.props.stack}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -38,9 +52,12 @@ DeploymentSuccess.propTypes = {
|
||||||
deleteStack: React.PropTypes.func.isRequired,
|
deleteStack: React.PropTypes.func.isRequired,
|
||||||
fetchStackEnvironment: React.PropTypes.func.isRequired,
|
fetchStackEnvironment: React.PropTypes.func.isRequired,
|
||||||
fetchStackResource: React.PropTypes.func.isRequired,
|
fetchStackResource: React.PropTypes.func.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isRequestingStackDelete: React.PropTypes.bool,
|
isRequestingStackDelete: React.PropTypes.bool,
|
||||||
runPostDeploymentValidations: React.PropTypes.func.isRequired,
|
runPostDeploymentValidations: React.PropTypes.func.isRequired,
|
||||||
stack: ImmutablePropTypes.record.isRequired,
|
stack: ImmutablePropTypes.record.isRequired,
|
||||||
stackResources: ImmutablePropTypes.map.isRequired,
|
stackResources: ImmutablePropTypes.map.isRequired,
|
||||||
stackResourcesLoaded: React.PropTypes.bool.isRequired
|
stackResourcesLoaded: React.PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(DeploymentSuccess);
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
import React from 'react';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export const HardwareStep = () => {
|
const messages = defineMessages({
|
||||||
|
registerNodes: {
|
||||||
|
id: 'HardwareStep.registerNodes',
|
||||||
|
defaultMessage: 'Register Nodes'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const HardwareStep = () => {
|
||||||
return (
|
return (
|
||||||
<Link className="btn btn-default" to="/nodes/registered/register">
|
<Link className="btn btn-default" to="/nodes/registered/register">
|
||||||
<span className="fa fa-plus"/> Register Nodes
|
<span className="fa fa-plus"/> <FormattedMessage {...messages.registerNodes}/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default HardwareStep;
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
noPlansAvailable: {
|
||||||
|
id: 'NoPlans.noPlansAvailable',
|
||||||
|
defaultMessage: 'No Deployment Plans Available'
|
||||||
|
},
|
||||||
|
noPlansAvailableMessage: {
|
||||||
|
id: 'NoPlans.noPlansAvailableMessage',
|
||||||
|
defaultMessage: 'There are no Deployment Plans available. Please create one first.'
|
||||||
|
},
|
||||||
|
createNewPlan: {
|
||||||
|
id: 'NoPlans.createNewPlan',
|
||||||
|
defaultMessage: 'Create New Plan'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default class NoPlans extends React.Component {
|
export default class NoPlans extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -8,13 +24,13 @@ export default class NoPlans extends React.Component {
|
||||||
<div className="blank-slate-pf-icon">
|
<div className="blank-slate-pf-icon">
|
||||||
<span className="fa fa-ban"></span>
|
<span className="fa fa-ban"></span>
|
||||||
</div>
|
</div>
|
||||||
<h1>No Deployment Plans Available</h1>
|
<h1><FormattedMessage {...messages.noPlansAvailable}/></h1>
|
||||||
<p>There are no Deployment Plans available. Please create one first.</p>
|
<p><FormattedMessage {...messages.noPlansAvailableMessage}/></p>
|
||||||
<div className="blank-slate-pf-main-action">
|
<div className="blank-slate-pf-main-action">
|
||||||
<Link to="/plans/new"
|
<Link to="/plans/new"
|
||||||
query={{tab: 'newPlan'}}
|
query={{tab: 'newPlan'}}
|
||||||
className="btn btn-lg btn-primary">
|
className="btn btn-lg btn-primary">
|
||||||
<span className="fa fa-plus"/> Create New Plan
|
<span className="fa fa-plus"/> <FormattedMessage {...messages.createNewPlan}/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import Formsy from 'formsy-react';
|
import Formsy from 'formsy-react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
@ -16,6 +17,17 @@ import Modal from '../ui/Modal';
|
||||||
import NodesActions from '../../actions/NodesActions';
|
import NodesActions from '../../actions/NodesActions';
|
||||||
import NodesTable from '../nodes/NodesTable';
|
import NodesTable from '../nodes/NodesTable';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
assignUnassignNodes: {
|
||||||
|
id: 'NodesAssignment.assignUnassignNodes',
|
||||||
|
defaultMessage: 'Assign/Unassign Selected Nodes'
|
||||||
|
},
|
||||||
|
done: {
|
||||||
|
id: 'NodesAssignment.done',
|
||||||
|
defaultMessage: 'Done'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class NodesAssignment extends React.Component {
|
class NodesAssignment extends React.Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -36,7 +48,7 @@ class NodesAssignment extends React.Component {
|
||||||
<button className="btn btn-primary"
|
<button className="btn btn-primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={this.props.nodesOperationInProgress}>
|
disabled={this.props.nodesOperationInProgress}>
|
||||||
Assign/Unassign Selected Nodes
|
<FormattedMessage {...messages.assignUnassignNodes}/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -98,7 +110,7 @@ class NodesAssignment extends React.Component {
|
||||||
|
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
<Link to="/deployment-plan" type="button" className="btn btn-default" >
|
<Link to="/deployment-plan" type="button" className="btn btn-default" >
|
||||||
Done
|
<FormattedMessage {...messages.done}/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</Formsy.Form>
|
</Formsy.Form>
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React from 'react';
|
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import DropdownItem from '../ui/dropdown/DropdownItem';
|
import DropdownItem from '../ui/dropdown/DropdownItem';
|
||||||
import DropdownButton from '../ui/dropdown/DropdownButton';
|
import DropdownButton from '../ui/dropdown/DropdownButton';
|
||||||
import Dropdown from '../ui/dropdown/Dropdown';
|
import Dropdown from '../ui/dropdown/Dropdown';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
manageDeployments: {
|
||||||
|
id: 'PlansDropdown.manageDeployments',
|
||||||
|
defaultMessage: 'Manage Deployments'
|
||||||
|
},
|
||||||
|
selectDeployment: {
|
||||||
|
id: 'PlansDropdown.selectDeployment',
|
||||||
|
defaultMessage: 'Select Deployment'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default class PlansDropdown extends React.Component {
|
export default class PlansDropdown extends React.Component {
|
||||||
renderRecentPlans() {
|
renderRecentPlans() {
|
||||||
return this.props.plans.toList().map(plan => {
|
return this.props.plans.toList().map(plan => {
|
||||||
|
@ -21,15 +33,21 @@ export default class PlansDropdown extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
if(this.props.plans.isEmpty()) {
|
if(this.props.plans.isEmpty()) {
|
||||||
return (
|
return (
|
||||||
<Link className="btn btn-link" to="/plans/list">Manage Deployments</Link>
|
<Link className="btn btn-link" to="/plans/list">
|
||||||
|
<FormattedMessage {...messages.manageDeployments}/>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownButton className="btn-link">Select Deployment</DropdownButton>
|
<DropdownButton className="btn-link">
|
||||||
|
<FormattedMessage {...messages.selectDeployment}/>
|
||||||
|
</DropdownButton>
|
||||||
{this.renderRecentPlans()}
|
{this.renderRecentPlans()}
|
||||||
<DropdownItem key="divider" divider/>
|
<DropdownItem key="divider" divider/>
|
||||||
<DropdownItem key="plansLink" to="/plans/list">Manage Deployments</DropdownItem>
|
<DropdownItem key="plansLink" to="/plans/list">
|
||||||
|
<FormattedMessage {...messages.manageDeployments}/>
|
||||||
|
</DropdownItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Link from '../ui/Link';
|
import Link from '../ui/Link';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
nodesAssigned: {
|
||||||
|
id: 'RoleCard.nodesAssigned',
|
||||||
|
defaultMessage: 'Nodes assigned'
|
||||||
|
},
|
||||||
|
assignNodes: {
|
||||||
|
id: 'RoleCard.assignNodes',
|
||||||
|
defaultMessage: 'Assign Nodes'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default class RoleCard extends React.Component {
|
export default class RoleCard extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const disabled = !this.props.availableNodesCount && !this.props.assignedNodesCount;
|
const disabled = !this.props.availableNodesCount && !this.props.assignedNodesCount;
|
||||||
|
@ -22,7 +35,9 @@ export default class RoleCard extends React.Component {
|
||||||
</span>
|
</span>
|
||||||
<span className="card-pf-utilization-card-details-description">
|
<span className="card-pf-utilization-card-details-description">
|
||||||
<span className="card-pf-utilization-card-details-line-1"> </span>
|
<span className="card-pf-utilization-card-details-line-1"> </span>
|
||||||
<span className="card-pf-utilization-card-details-line-2">Nodes assigned</span>
|
<span className="card-pf-utilization-card-details-line-2">
|
||||||
|
<FormattedMessage {...messages.nodesAssigned}/>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,7 +47,8 @@ export default class RoleCard extends React.Component {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
to={`/deployment-plan/${this.props.identifier}/assign-nodes`}
|
to={`/deployment-plan/${this.props.identifier}/assign-nodes`}
|
||||||
className="card-pf-link-with-icon">
|
className="card-pf-link-with-icon">
|
||||||
<span className="pficon pficon-add-circle-o" />Assign Nodes
|
<span className="pficon pficon-add-circle-o" />
|
||||||
|
<FormattedMessage {...messages.assignNodes}/>
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
import React from 'react';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import { getAssignedNodes } from '../../selectors/nodes';
|
import { getAssignedNodes } from '../../selectors/nodes';
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import RoleCard from './RoleCard';
|
import RoleCard from './RoleCard';
|
||||||
|
|
||||||
export default class Roles extends React.Component {
|
const messages = defineMessages({
|
||||||
|
loadingDeploymentRoles: {
|
||||||
|
id: 'Roles.loadingDeploymentRoles',
|
||||||
|
defaultMessage: 'Loading Deployment Roles...'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class Roles extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchRoles();
|
this.props.fetchRoles();
|
||||||
this.props.fetchNodes();
|
this.props.fetchNodes();
|
||||||
|
@ -45,7 +53,7 @@ export default class Roles extends React.Component {
|
||||||
<div className="panel panel-default roles-panel">
|
<div className="panel panel-default roles-panel">
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<Loader loaded={this.props.loaded}
|
<Loader loaded={this.props.loaded}
|
||||||
content="Loading Deployment Roles..."
|
content={this.props.intl.formatMessage(messages.loadingDeploymentRoles)}
|
||||||
height={40}>
|
height={40}>
|
||||||
<div className="row-cards-pf">
|
<div className="row-cards-pf">
|
||||||
{this.renderRoleCards()}
|
{this.renderRoleCards()}
|
||||||
|
@ -60,9 +68,12 @@ Roles.propTypes = {
|
||||||
availableNodes: ImmutablePropTypes.map,
|
availableNodes: ImmutablePropTypes.map,
|
||||||
fetchNodes: React.PropTypes.func.isRequired,
|
fetchNodes: React.PropTypes.func.isRequired,
|
||||||
fetchRoles: React.PropTypes.func.isRequired,
|
fetchRoles: React.PropTypes.func.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isFetchingNodes: React.PropTypes.bool,
|
isFetchingNodes: React.PropTypes.bool,
|
||||||
isFetchingRoles: React.PropTypes.bool,
|
isFetchingRoles: React.PropTypes.bool,
|
||||||
loaded: React.PropTypes.bool.isRequired,
|
loaded: React.PropTypes.bool.isRequired,
|
||||||
roles: React.PropTypes.array.isRequired,
|
roles: React.PropTypes.array.isRequired,
|
||||||
unassignedAvailableNodes: ImmutablePropTypes.map
|
unassignedAvailableNodes: ImmutablePropTypes.map
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(Roles);
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
import React from 'react';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import Roles from './Roles';
|
import Roles from './Roles';
|
||||||
|
|
||||||
export const RolesStep = ({ isFetchingNodes,
|
const messages = defineMessages({
|
||||||
|
loadingNodes: {
|
||||||
|
id: 'RolesStep.loadingNodes',
|
||||||
|
defaultMessage: 'Loading Nodes...'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const RolesStep = ({ isFetchingNodes,
|
||||||
availableNodes,
|
availableNodes,
|
||||||
unassignedAvailableNodes,
|
unassignedAvailableNodes,
|
||||||
roles,
|
roles,
|
||||||
fetchRoles,
|
fetchRoles,
|
||||||
fetchNodes,
|
fetchNodes,
|
||||||
|
intl,
|
||||||
isFetchingRoles,
|
isFetchingRoles,
|
||||||
rolesLoaded }) => {
|
rolesLoaded }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<Loader loaded={!isFetchingNodes}
|
<Loader loaded={!isFetchingNodes}
|
||||||
content="Loading Nodes..."
|
content={intl.formatMessage(messages.loadingNodes)}
|
||||||
component="span"
|
component="span"
|
||||||
inline>
|
inline>
|
||||||
<strong>{unassignedAvailableNodes.size}</strong> Nodes
|
<strong>{unassignedAvailableNodes.size}</strong> Nodes
|
||||||
|
@ -38,9 +47,12 @@ RolesStep.propTypes = {
|
||||||
availableNodes: ImmutablePropTypes.map.isRequired,
|
availableNodes: ImmutablePropTypes.map.isRequired,
|
||||||
fetchNodes: React.PropTypes.func.isRequired,
|
fetchNodes: React.PropTypes.func.isRequired,
|
||||||
fetchRoles: React.PropTypes.func.isRequired,
|
fetchRoles: React.PropTypes.func.isRequired,
|
||||||
|
intl: React.PropTypes.object,
|
||||||
isFetchingNodes: React.PropTypes.bool.isRequired,
|
isFetchingNodes: React.PropTypes.bool.isRequired,
|
||||||
isFetchingRoles: React.PropTypes.bool.isRequired,
|
isFetchingRoles: React.PropTypes.bool.isRequired,
|
||||||
roles: ImmutablePropTypes.map.isRequired,
|
roles: ImmutablePropTypes.map.isRequired,
|
||||||
rolesLoaded: React.PropTypes.bool.isRequired,
|
rolesLoaded: React.PropTypes.bool.isRequired,
|
||||||
unassignedAvailableNodes: ImmutablePropTypes.map.isRequired
|
unassignedAvailableNodes: ImmutablePropTypes.map.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default injectIntl(RolesStep);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { addLocaleData, IntlProvider } from 'react-intl';
|
||||||
|
import React from 'react';
|
||||||
|
import ja from 'react-intl/locale-data/ja';
|
||||||
|
|
||||||
|
import jaMessages from '../../../../i18n/locales/ja.json';
|
||||||
|
|
||||||
|
|
||||||
|
const MESSAGES = {
|
||||||
|
ja: jaMessages.messages
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class I18nProvider extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
addLocaleData([...ja]);
|
||||||
|
this.state = {
|
||||||
|
locale: 'en'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
const locale = localStorage.getItem('language') ||
|
||||||
|
(navigator.languages && navigator.languages[0]) ||
|
||||||
|
navigator.language || navigator.userLanguage;
|
||||||
|
// We only use the country part of the locale:
|
||||||
|
const language = locale.substr(0, 2);
|
||||||
|
if(MESSAGES[language]) {
|
||||||
|
this.setState({ locale: language });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<IntlProvider locale={this.state.locale} messages={MESSAGES[this.state.locale]}>
|
||||||
|
{this.props.children}
|
||||||
|
</IntlProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I18nProvider.propTypes = {
|
||||||
|
children: React.PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export default I18nProvider;
|
|
@ -3,7 +3,6 @@ import 'babel-polyfill';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
import { Router, Route, Redirect } from 'react-router';
|
import { Router, Route, Redirect } from 'react-router';
|
||||||
import { browserHistory } from 'react-router';
|
import { browserHistory } from 'react-router';
|
||||||
|
|
||||||
|
@ -18,6 +17,7 @@ import EditPlan from './components/plan/EditPlan';
|
||||||
import EnvironmentConfiguration from
|
import EnvironmentConfiguration from
|
||||||
'./components/environment_configuration/EnvironmentConfiguration.js';
|
'./components/environment_configuration/EnvironmentConfiguration.js';
|
||||||
import { getCurrentStackDeploymentInProgress } from './selectors/stacks';
|
import { getCurrentStackDeploymentInProgress } from './selectors/stacks';
|
||||||
|
import I18nProvider from './components/i18n/I18nProvider';
|
||||||
import initFormsy from './components/utils/Formsy';
|
import initFormsy from './components/utils/Formsy';
|
||||||
import ListPlans from './components/plan/ListPlans';
|
import ListPlans from './components/plan/ListPlans';
|
||||||
import Login from './components/Login';
|
import Login from './components/Login';
|
||||||
|
@ -41,6 +41,7 @@ import store from './store';
|
||||||
|
|
||||||
import '../less/base.less';
|
import '../less/base.less';
|
||||||
|
|
||||||
|
|
||||||
TempStorage.initialized.then(() => {
|
TempStorage.initialized.then(() => {
|
||||||
/**
|
/**
|
||||||
* @function checkAuth
|
* @function checkAuth
|
||||||
|
@ -134,9 +135,9 @@ TempStorage.initialized.then(() => {
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<IntlProvider locale="en">
|
<I18nProvider>
|
||||||
<Router history={browserHistory}>{routes}</Router>
|
<Router history={browserHistory}>{routes}</Router>
|
||||||
</IntlProvider>
|
</I18nProvider>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('react-app-index')
|
document.getElementById('react-app-index')
|
||||||
);
|
);
|
||||||
|
|
|
@ -85,6 +85,12 @@ module.exports = {
|
||||||
{
|
{
|
||||||
loader: __dirname + '/src/js/loaders/version.js',
|
loader: __dirname + '/src/js/loaders/version.js',
|
||||||
test: /src\/js\/index.js$/
|
test: /src\/js\/index.js$/
|
||||||
|
},
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
{
|
||||||
|
test: /\.json$/,
|
||||||
|
loader: 'json'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue