Skip to content

Commit

Permalink
feat(*): project update (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
qiuxiaolian authored and caicloud-bot committed Jan 28, 2019
1 parent bc0cd0c commit f722aa4
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 84 deletions.
3 changes: 3 additions & 0 deletions web/src/api/index.js
Expand Up @@ -31,6 +31,9 @@ const fetchApi = {
createProject(data) {
return http.post('/projects', data);
},
updateProject(data, name) {
return http.put(`/projects/${name}`, data);
},
removeProject(name) {
return http.delete(`/projects/${name}`);
},
Expand Down
1 change: 0 additions & 1 deletion web/src/components/project/component/List.js
@@ -1,4 +1,3 @@
import React from 'react';
import { Table, Button, Modal } from 'antd';
import { inject, observer } from 'mobx-react';
import PropTypes from 'prop-types';
Expand Down
18 changes: 15 additions & 3 deletions web/src/components/project/component/ProjectDetail.js
@@ -1,4 +1,3 @@
import React from 'react';
import { Tabs, Button, Spin } from 'antd';
import { inject, observer } from 'mobx-react';
import Detail from '@/components/public/detail';
Expand All @@ -14,6 +13,7 @@ class ProjectDetail extends React.Component {
static propTypes = {
match: PropTypes.object,
project: PropTypes.object,
history: PropTypes.object,
};
constructor(props) {
super(props);
Expand All @@ -25,7 +25,13 @@ class ProjectDetail extends React.Component {
this.props.project.getProject(projectId);
}
render() {
const { project } = this.props;
const {
project,
history,
match: {
params: { projectId },
},
} = this.props;
const loading = project.detailLoading;
if (loading) {
return <Spin />;
Expand All @@ -35,7 +41,13 @@ class ProjectDetail extends React.Component {
<Detail
actions={
<DetailAction>
<Button>{intl.get('operation.update')}</Button>
<Button
onClick={() => {
history.push(`/project/${projectId}/update`);
}}
>
{intl.get('operation.update')}
</Button>
</DetailAction>
}
>
Expand Down
@@ -1,4 +1,3 @@
import React from 'react';
import { Field } from 'formik';
import PropTypes from 'prop-types';
import { Form, Input, Button } from 'antd';
Expand All @@ -10,7 +9,7 @@ const { TextArea } = Input;
const InputField = MakeField(Input);
const TextareaField = MakeField(TextArea);

const FormContent = ({ history, handleSubmit, setFieldValue }) => {
const FormContent = ({ history, handleSubmit, setFieldValue, update }) => {
return (
<Form layout={'horizontal'} onSubmit={handleSubmit}>
<Field
Expand All @@ -27,14 +26,15 @@ const FormContent = ({ history, handleSubmit, setFieldValue }) => {
/>
<Field
label={intl.get('project.externalSystem')}
name="spec.services"
name="spec.integrations"
required
component={IntegrationSelect}
/>
<Field
label={intl.get('allocation.quotaConfig')}
name="spec.quota"
component={Quota}
update={update}
onChange={value => {
setFieldValue('spec.quota', value);
}}
Expand All @@ -59,6 +59,7 @@ FormContent.propTypes = {
history: PropTypes.object,
handleSubmit: PropTypes.func,
setFieldValue: PropTypes.func,
update: PropTypes.bool,
};

export default FormContent;
103 changes: 82 additions & 21 deletions web/src/components/project/component/addProject/FormWrap.js
@@ -1,23 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Formik } from 'formik';
import { inject, observer } from 'mobx-react';
import { toJS } from 'mobx';
import { Spin } from 'antd';
import FormContent from './FormContent';
import { inject, observer } from 'mobx-react';

@inject('project')
@observer
class AddProject extends React.Component {
constructor(props) {
super(props);
const {
match: { params },
} = props;
const update = !!_.get(params, 'projectId');
this.state = {
update,
};
if (update) {
props.project.getProject(params.projectId);
}
}

// submit form data
submit = values => {
const { project, history } = this.props;
const data = { ...values };
data.spec.services = _.map(values.spec.services, n => {
const {
project,
history,
match: { params },
} = this.props;
const { update } = this.state;
const data = _.omit(values, 'metadata.description');
data.spec.integrations = _.map(values.spec.integrations, n => {
const resources = n.split('/');
return { type: resources[0], name: resources[1] };
});
project.createProject(data, () => {
history.replace(`/project`);
});
data.metadata.labels = {
'cyclone.io/description': values.metadata.description,
};
if (update) {
project.updateProject(data, params.projectId, () => {
history.replace(`/project`);
});
} else {
project.createProject(data, () => {
history.replace(`/project`);
});
}
};

validate = values => {
Expand All @@ -29,30 +58,61 @@ class AddProject extends React.Component {
return errors;
};

render() {
const { history } = this.props;
const initValue = {
getInitialValues = () => {
const { update } = this.state;
let defaultValue = {
metadata: { name: '', description: '' },
spec: {
services: [],
integrations: [],
quota: {
limits: {
cpu: '',
memory: '',
},
requests: {
cpu: '',
memory: '',
},
'limits.cpu': '',
'limits.memory': '',
'requests.cpu': '',
'requests.memory': '',
},
},
};
if (update) {
const proejctInfo = toJS(this.props.project.projectDetail);
const values = _.pick(proejctInfo, [
'metadata.name',
'spec.integrations',
'spec.quota',
]);
const description = _.get(proejctInfo, [
'metadata',
'labels',
'cyclone.io/description',
]);
values.spec.integrations = _.map(
_.get(values, 'spec.integrations', []),
n => `${n.type}/${n.name}`
);
values.metadata.description = description;
defaultValue = _.merge(defaultValue, values);
}

return defaultValue;
};

render() {
const { history } = this.props;
if (this.props.project.detailLoading) {
return <Spin />;
}
const initValue = this.getInitialValues();
return (
<Formik
initialValues={initValue}
validate={this.validate}
onSubmit={this.submit}
render={props => <FormContent {...props} history={history} />}
render={props => (
<FormContent
{...props}
history={history}
update={this.state.update}
/>
)}
/>
);
}
Expand All @@ -63,6 +123,7 @@ AddProject.propTypes = {
setFieldValue: PropTypes.func,
project: PropTypes.object,
history: PropTypes.object,
match: PropTypes.object,
};

export default AddProject;
@@ -1,4 +1,3 @@
import React from 'react';
import { Select, Row, Col } from 'antd';
import { Form, Button } from 'antd';
import PropTypes from 'prop-types';
Expand Down
5 changes: 4 additions & 1 deletion web/src/components/project/index.js
Expand Up @@ -3,13 +3,16 @@ import ProjectDetail from './component/ProjectDetail';
import CreateProject from './component/addProject';
import { Route, Switch } from 'react-router-dom';
import PropTypes from 'prop-types';
import React from 'react';

const ProjectRoutes = ({ match }) => {
return (
<Switch>
<Route path="/project" exact component={Project} />
<Route path={`${match.path}/add`} component={CreateProject} />
<Route
path={`${match.path}/:projectId/update`}
component={CreateProject}
/>
<Route path={`${match.path}/:projectId`} component={ProjectDetail} />
</Switch>
);
Expand Down
57 changes: 40 additions & 17 deletions web/src/components/public/inputWithUnit/InputWithUnit.js
Expand Up @@ -6,6 +6,7 @@ import classnames from 'classnames';
const FormItem = Form.Item;
const Option = Select.Option;

const memoryUnit = ['Mi', 'Gi', 'Ti'];
class InputAddon extends React.Component {
static propTypes = {
defaultAddon: PropTypes.string,
Expand All @@ -18,16 +19,36 @@ class InputAddon extends React.Component {
label: PropTypes.string,
className: PropTypes.string,
onBlur: PropTypes.func,
type: PropTypes.oneOf(['cpu', 'memory']),
};

static defaultProps = {
addonAfter: [
{ name: 'MiB', value: 'Mi' },
{ name: 'GiB', value: 'Gi' },
{ name: 'TiB', value: 'Ti' },
],
defaultAddon: 'Mi',
};

constructor(props) {
super(props);
this.state = {
byteFieldNum: '',
byteUnit: props.defaultAddon === undefined ? 'Mi' : props.defaultAddon,
byteFieldNum: props.field.value ? parseFloat(props.field.value) : '',
byteUnit: props.type === 'cpu' ? '' : this.getUnit(props.field.value),
};
}

getUnit = value => {
let _unit = this.props.defaultAddon;
const num = parseFloat(value);
const unit = _.replace(value, num, '');
if (_.includes(memoryUnit, unit)) {
_unit = unit;
}
return _unit;
};

handleAddon = value => {
const { byteFieldNum } = this.state;

Expand Down Expand Up @@ -64,26 +85,28 @@ class InputAddon extends React.Component {
field,
className,
form: { errors, touched },
type,
} = this.props;
const { byteFieldNum, byteUnit } = this.state;
const cls = classnames('u-input-unit', { [className]: !!className });
const name = field.name;
const hasError = _.get(touched, name) && _.get(errors, name);
const addonComp = _.isArray(addonAfter) ? (
<Select
defaultValue={byteUnit}
style={{ width: 80 }}
onChange={this.handleAddon}
>
{addonAfter.map(o => (
<Option value={o.value} key={o.value}>
{o.name}
</Option>
))}
</Select>
) : (
addonAfter
);
const addonComp =
type === 'memory' ? (
<Select
defaultValue={byteUnit}
style={{ width: 80 }}
onChange={this.handleAddon}
>
{addonAfter.map(o => (
<Option value={o.value} key={o.value}>
{o.name}
</Option>
))}
</Select>
) : (
'Core'
);

const _attr = { name: field.name };
_attr.onChange = this.inputChange;
Expand Down
Empty file.

0 comments on commit f722aa4

Please sign in to comment.