Merge branch 'application-mgt-new' into 'application-mgt-new'

Publisher Send Login request

See merge request tcdlpds/carbon-device-mgt!2
feature/appm-store/pbac
Dharmakeerthi Lasantha 6 years ago
commit 71c792739c

@ -12,6 +12,7 @@
"dependencies": { "dependencies": {
"acorn": "^6.1.1", "acorn": "^6.1.1",
"antd": "^3.15.0", "antd": "^3.15.0",
"axios": "^0.18.0",
"react": "^16.8.4", "react": "^16.8.4",
"react-dom": "^16.8.4", "react-dom": "^16.8.4",
"react-highlight-words": "^0.16.0", "react-highlight-words": "^0.16.0",
@ -49,6 +50,7 @@
"react": "^15.6.2", "react": "^15.6.2",
"react-dom": "^15.6.2", "react-dom": "^15.6.2",
"react-intl": "^2.4.0", "react-intl": "^2.4.0",
"react-redux": "^7.0.2",
"redux": "^4.0.1", "redux": "^4.0.1",
"sass-loader": "^6.0.7", "sass-loader": "^6.0.7",
"style-loader": "^0.18.2", "style-loader": "^0.18.2",

@ -31,3 +31,7 @@
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.ant-upload.ant-upload-drag {
height: 170px;
}

@ -7,7 +7,8 @@ import Dashboard from "./pages/dashboard/Dashboard";
import Apps from "./pages/dashboard/apps/Apps"; import Apps from "./pages/dashboard/apps/Apps";
import AddNewApp from "./pages/dashboard/add-new-app/AddNewApp"; import AddNewApp from "./pages/dashboard/add-new-app/AddNewApp";
import './index.css'; import './index.css';
import store from "./js/store/index";
import {Provider} from "react-redux";
const routes = [ const routes = [
@ -29,11 +30,14 @@ const routes = [
} }
] ]
} }
] ];
ReactDOM.render( <App routes={routes}/>, document.getElementById('root')); ReactDOM.render(
<Provider store={store}>
<App routes={routes}/>
</Provider>,
document.getElementById('root'));
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. // unregister() to register() below. Note this comes with some pitfalls.

@ -0,0 +1,9 @@
const initialState = {
};
function rootReducer(state = initialState, action) {
return state;
}
export default rootReducer;

@ -0,0 +1,4 @@
import { createStore } from "redux";
import rootReducer from "../reducers/index";
const store = createStore(rootReducer);
export default store;

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox,} from 'antd'; import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox,} from 'antd';
import styles from './Login.less'; import styles from './Login.less';
import axios from 'axios';
const {Title} = Typography; const {Title} = Typography;
class Login extends React.Component { class Login extends React.Component {
@ -38,26 +40,36 @@ class NormalLoginForm extends React.Component {
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
let data = "username="+values.username+"&password="+values.password+"&platform=publisher";
axios.post('https://localhost:9443/api/application-mgt-handler/v1.0/login', data
).then(res => {
console.log(res);
console.log(res.data);
})
} }
}); });
} };
render() { render() {
const { getFieldDecorator } = this.props.form; const {getFieldDecorator} = this.props.form;
return ( return (
<Form onSubmit={this.handleSubmit} className="login-form"> <Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item> <Form.Item>
{getFieldDecorator('userName', { {getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }], rules: [{required: true, message: 'Please input your username!'}],
})( })(
<Input style={{height: 32}} prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" /> <Input style={{height: 32}} prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
placeholder="Username"/>
)} )}
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
{getFieldDecorator('password', { {getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }], rules: [{required: true, message: 'Please input your Password!'}],
})( })(
<Input style={{height: 32}} className={styles.input} prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" /> <Input style={{height: 32}} className={styles.input}
prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}}/>} type="password"
placeholder="Password"/>
)} )}
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
@ -65,19 +77,18 @@ class NormalLoginForm extends React.Component {
valuePropName: 'checked', valuePropName: 'checked',
initialValue: true, initialValue: true,
})( })(
<Checkbox>Remember me</Checkbox> <Checkbox>Remember me....</Checkbox>
)} )}
<a className="login-form-forgot" href="">Forgot password</a> <a className="login-form-forgot" href="">Forgot password</a>
<Button block type="primary" htmlType="submit" className="login-form-button"> <Button block type="primary" htmlType="submit" className="login-form-button">
Log in Log in
</Button> </Button>
Or <a href="">register now!</a>
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm); const WrappedNormalLoginForm = Form.create({name: 'normal_login'})(NormalLoginForm);
export default Login; export default Login;

@ -1,12 +1,33 @@
import React from "react"; import React from "react";
import "antd/dist/antd.css"; import "antd/dist/antd.css";
import {PageHeader, Typography, Card, Steps, Button, message, Row, Col} from "antd"; import {
import Step1 from "./Step1" PageHeader,
import Step2 from "./Step2" Typography,
import Step3 from "./Step3" Card,
Steps,
Button,
message,
Row,
Col,
Tag,
Tooltip,
Input,
Icon,
Select,
Switch,
Form,
Upload,
Divider
} from "antd";
import Step1 from "./Step1";
import Step2 from "./Step2";
import Step3 from "./Step3";
import styles from "./Style.less";
import IconImage from "./IconImg";
import UploadScreenshots from "./UploadScreenshots";
const Paragraph = Typography; const Paragraph = Typography;
const Dragger = Upload.Dragger;
const routes = [ const routes = [
{ {
path: 'index', path: 'index',
@ -22,6 +43,23 @@ const routes = [
}, },
]; ];
const props = {
name: 'file',
multiple: false,
action: '//jsonplaceholder.typicode.com/posts/',
onChange(info) {
const status = info.file.status;
if (status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
};
const Step = Steps.Step; const Step = Steps.Step;
const steps = [{ const steps = [{
@ -36,6 +74,96 @@ const steps = [{
}]; }];
const {Option} = Select;
const {TextArea} = Input;
const InputGroup = Input.Group;
const formItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
class EditableTagGroup extends React.Component {
state = {
tags: [],
inputVisible: false,
inputValue: '',
};
handleClose = (removedTag) => {
const tags = this.state.tags.filter(tag => tag !== removedTag);
console.log(tags);
this.setState({tags});
}
showInput = () => {
this.setState({inputVisible: true}, () => this.input.focus());
}
handleInputChange = (e) => {
this.setState({inputValue: e.target.value});
}
handleInputConfirm = () => {
const {inputValue} = this.state;
let {tags} = this.state;
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue];
}
console.log(tags);
this.setState({
tags,
inputVisible: false,
inputValue: '',
});
}
saveInputRef = input => this.input = input
render() {
const {tags, inputVisible, inputValue} = this.state;
return (
<div>
{tags.map((tag, index) => {
const isLongTag = tag.length > 20;
const tagElem = (
<Tag key={tag} closable={index !== 0} onClose={() => this.handleClose(tag)}>
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
</Tag>
);
return isLongTag ? <Tooltip title={tag} key={tag}>{tagElem}</Tooltip> : tagElem;
})}
{inputVisible && (
<Input
ref={this.saveInputRef}
type="text"
size="small"
style={{width: 78}}
value={inputValue}
onChange={this.handleInputChange}
onBlur={this.handleInputConfirm}
onPressEnter={this.handleInputConfirm}
/>
)}
{!inputVisible && (
<Tag
onClick={this.showInput}
style={{background: '#fff', borderStyle: 'dashed'}}
>
<Icon type="plus"/> New Tag
</Tag>
)}
</div>
);
}
}
class AddNewApp extends React.Component { class AddNewApp extends React.Component {
constructor(props) { constructor(props) {
@ -45,6 +173,12 @@ class AddNewApp extends React.Component {
}; };
} }
tags = [];
addTag(key, value){
this.tags.push(<Option key={key}>{value}</Option>);
}
next() { next() {
const current = this.state.current + 1; const current = this.state.current + 1;
this.setState({current}); this.setState({current});
@ -59,6 +193,8 @@ class AddNewApp extends React.Component {
render() { render() {
const {current} = this.state; const {current} = this.state;
const Content = steps[current].content; const Content = steps[current].content;
this.addTag('1','Lorem');
this.addTag('2','Ipsum');
return ( return (
<div> <div>
<PageHeader <PageHeader
@ -75,33 +211,116 @@ class AddNewApp extends React.Component {
</PageHeader> </PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> <div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
<Row> <Row>
<Col span={16} offset={4}> <Col span={20} offset={2}>
<Card> <Card>
<div> <Row>
<Steps current={current}> <Col span={12}>
{steps.map(item => <Step key={item.title} title={item.title}/>)} <div>
</Steps> <Form labelAlign="left" layout="horizontal" className={styles.stepForm}
<Content/> hideRequiredMark>
<div className="steps-action"> <Form.Item {...formItemLayout} label="Platform">
{ <Select placeholder="ex: android">
current < steps.length - 1 <Option value="Android">Android</Option>
&& <Button type="primary" onClick={() => this.next()}>Next</Button> <Option value="iOS">iOS</Option>
} </Select>
{ </Form.Item>
current === steps.length - 1 <Form.Item {...formItemLayout} label="Type">
&& <Button type="primary" <Select value="Enterprise">
onClick={() => message.success('Processing complete!')}>Done</Button> <Option value="Enterprise" selected>Enterprise</Option>
} </Select>
{ </Form.Item>
current > 0 <Form.Item {...formItemLayout} label="App Name">
&& ( <Input placeholder="ex: Lorem App"/>
<Button style={{marginLeft: 8}} onClick={() => this.prev()}> </Form.Item>
Previous <Form.Item {...formItemLayout} label="Description">
</Button> <TextArea placeholder="Enter the description..." rows={7}/>
) </Form.Item>
} <Form.Item {...formItemLayout} label="Category">
</div> <Select placeholder="Select a category">
</div> <Option value="travel">Travel</Option>
<Option value="entertainment">Entertainment</Option>
</Select>
</Form.Item>
<Form.Item {...formItemLayout} label="Price">
<Input prefix="$" placeholder="00.00"/>
</Form.Item>
<Form.Item {...formItemLayout} label="Is Sahred?">
<Switch checkedChildren={<Icon type="check" />} unCheckedChildren={<Icon type="close" />} defaultChecked />
</Form.Item>
<Divider/>
<Form.Item {...formItemLayout} label="Tags">
<InputGroup>
<Row gutter={8}>
<Col span={22}>
<Select
mode="multiple"
style={{ width: '100%' }}
placeholder="Tags Mode"
>
{this.tags}
</Select>
</Col>
<Col span={2}>
<Button type="dashed" shape="circle" icon="plus"/>
</Col>
</Row>
</InputGroup>
</Form.Item>
<Form.Item {...formItemLayout} label="Meta Daa">
<InputGroup>
<Row gutter={8}>
<Col span={10}>
<Input placeholder="Key"/>
</Col>
<Col span={12}>
<Input placeholder="value"/>
</Col>
<Col span={2}>
<Button type="dashed" shape="circle" icon="plus"/>
</Col>
</Row>
</InputGroup>
</Form.Item>
</Form>
</div>
</Col>
<Col span={12} style={{paddingTop: 40, paddingLeft: 20}}>
<p>Application</p>
<div style={{height: 170}}>
<Dragger {...props}>
<p className="ant-upload-drag-icon">
<Icon type="inbox"/>
</p>
<p className="ant-upload-text">Click or drag file to this area to
upload</p>
<p className="ant-upload-hint">Support for a single or bulk upload.
Strictly prohibit from uploading company data or other band
files</p>
</Dragger>
</div>
<Row style={{marginTop: 40}}>
<Col span={12}>
<p>Icon</p>
<IconImage/>
</Col>
<Col span={12}>
<p>Banner</p>
<IconImage/>
</Col>
</Row>
<Row style={{marginTop: 40}}>
<Col span={24}>
<p>Screenshots</p>
<UploadScreenshots/>
</Col>
</Row>
</Col>
</Row>
</Card> </Card>
</Col> </Col>
</Row> </Row>

@ -0,0 +1,66 @@
import React from "react";
import { Upload, Icon, message } from 'antd';
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJPG && isLt2M;
}
class IconImage extends React.Component {
state = {
loading: false,
};
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl => this.setState({
imageUrl,
loading: false,
}));
}
}
render() {
const uploadButton = (
<div>
<Icon type={this.state.loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">Upload</div>
</div>
);
const imageUrl = this.state.imageUrl;
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="//jsonplaceholder.typicode.com/posts/"
beforeUpload={beforeUpload}
onChange={this.handleChange}
>
{imageUrl ? <img src={imageUrl} alt="avatar" /> : uploadButton}
</Upload>
);
}
}
export default IconImage;

@ -0,0 +1,49 @@
import React from "react";
import { Upload, Icon, Modal} from 'antd';
class UploadScreenshots extends React.Component {
state = {
previewVisible: false,
previewImage: '',
fileList: [],
};
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = (file) => {
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
};
handleChange = ({ fileList }) => this.setState({ fileList });
render() {
const { previewVisible, previewImage, fileList } = this.state;
const uploadButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">Upload</div>
</div>
);
return (
<div className="clearfix">
<Upload
action="//jsonplaceholder.typicode.com/posts/"
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
>
{fileList.length >= 3 ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
}
}
export default UploadScreenshots;

@ -0,0 +1,49 @@
import React from "react";
import { Upload, Icon, Modal} from 'antd';
class AddTagModal extends React.Component {
state = {
previewVisible: false,
previewImage: '',
fileList: [],
};
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = (file) => {
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
};
handleChange = ({ fileList }) => this.setState({ fileList });
render() {
const { previewVisible, previewImage, fileList } = this.state;
const uploadButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">Upload</div>
</div>
);
return (
<div className="clearfix">
<Upload
action="//jsonplaceholder.typicode.com/posts/"
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
>
{fileList.length >= 3 ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
}
}
export default AddTagModal;
Loading…
Cancel
Save