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

Create component to add new app in APPM UI

See merge request entgra/carbon-device-mgt!149
4.x.x
Dharmakeerthi Lasantha 6 years ago
commit e846207a49

@ -15,6 +15,7 @@
"axios": "^0.18.0",
"d3": "^5.9.2",
"dagre": "^0.8.4",
"fetch": "^1.1.0",
"keymirror": "^0.1.1",
"rc-tween-one": "^2.4.1",
"react": "^16.8.4",

@ -10,12 +10,12 @@ const {Option} = Select;
const {Title, Text} = Typography;
const Search = Input.Search;
// connecting state.apps with the component
const mapStateToProps = state => {
return {apps: state.apps}
};
// const mapStateToProps = state => {
// return {apps: state.apps}
// };
class ConnectedListApps extends React.Component {
class ListApps extends React.Component {
constructor(props) {
super(props);
this.state = {
@ -70,6 +70,6 @@ class ConnectedListApps extends React.Component {
}
}
const ListApps = connect(mapStateToProps, {getApps})(ConnectedListApps);
// const ListApps = connect(mapStateToProps, {getApps})(ConnectedListApps);
export default ListApps;

@ -0,0 +1,569 @@
import React from "react";
import "antd/dist/antd.css";
import {
PageHeader,
Typography,
Card,
Steps,
Button,
message,
Row,
Col,
Tag,
Tooltip,
Input,
Icon,
Select,
Switch,
Form,
Upload,
Divider, notification
} from "antd";
import IconImage from "./IconImg";
import UploadScreenshots from "./UploadScreenshots";
import axios from "axios";
import config from "../../../public/conf/config.json";
const Paragraph = Typography;
const Dragger = Upload.Dragger;
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 steps = [{
// title: 'First',
// content: Step1
// }, {
// title: 'Second',
// content: Step2,
// }, {
// title: 'Last',
// content: Step3,
// }];
const {Option} = Select;
const {TextArea} = Input;
const InputGroup = Input.Group;
const formItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
class AddNewAppFormComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
current: 0,
categories: [],
tags: [],
icons: [],
screenshots: []
};
}
componentDidMount() {
this.getCategories();
this.getTags();
}
getCategories = () => {
axios.get(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
{
headers: {'X-Platform': config.serverConfig.platform}
}).then(res => {
if (res.status === 200) {
let categories = JSON.parse(res.data.data);
this.setState({
categories: categories,
loading: false
});
}
}).catch((error) => {
if (error.response.status === 401) {
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({
loading: false
});
});
};
getTags = () => {
axios.get(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
{
headers: {'X-Platform': config.serverConfig.platform}
}).then(res => {
if (res.status === 200) {
let tags = JSON.parse(res.data.data);
this.setState({
tags: tags,
loading: false,
});
}
}).catch((error) => {
if (error.response.status === 401) {
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({
loading: false
});
});
};
handleCategoryChange = (value) => {
console.log(`selected ${value}`);
};
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
const {name, description, appCategories, tags, deviceType, price, isSharedWithAllTenants, binaryFile, icon, screenshots} = values;
const payload = {
binaryFile: binaryFile[0].originFileObj,
icon: icon[0].originFileObj,
screenshot1: screenshots[0].originFileObj,
screenshot2: screenshots[1].originFileObj,
screenshot3: screenshots[2].originFileObj,
application: {
name,
description,
appCategories,
subType: (price === undefined || parseInt(price) === 0) ? "FREE" : "PAID",
tags,
unrestrictedRoles: [],
deviceType,
entAppReleaseWrappers: [{
description,
price: (price === undefined) ? 0 : parseInt(price),
isSharedWithAllTenants,
metaData: "string",
supportedOsVersions: "4.0-10.0"
}]
}
};
console.log(payload);
// let data = new FormData();
//
// const url = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/ent-app";
//
// data.append('binaryFile', binaryFile[0].originFileObj);
// data.append('icon', icon[0].originFileObj);
// data.append('screenshot1', screenshots[0].originFileObj);
// data.append('screenshot2', screenshots[1].originFileObj);
// data.append('screenshot3', screenshots[2].originFileObj);
// data.append('application', JSON.toString(payload.application));
// let request = new XMLHttpRequest();
// request.open('POST', url);
// request.send(data);
// var xhr = new XMLHttpRequest();
// // xhr.withCredentials = true;
//
// xhr.addEventListener("readystatechange", function () {
// if (this.readyState === 4) {
// console.log(this.responseText);
// }
// });
//
// xhr.open("POST", "https://localhost:9443/ui-request-handler/invoke/application-mgt-publisher/v1.0/applications/ent-app");
// xhr.open("GET", "https://localhost:9443/ui-request-handler/invoke/application-mgt-publisher/v1.0/applications/tags");
// xhr.setRequestHeader("Content-Type", "multipart/mixed");
// xhr.setRequestHeader("X-Platform", "publisher");
//
// // xhr.setRequestHeader("Accept", "*/*");
//
// xhr.send(data);
// xhr.send();
// const options = {method: 'POST', body: data};
//
// fetch(url, options).then(function (response) {
// console.log(response);
// });
// axios.post(
// url,
// data,
// {
// headers:{
// 'X-Platform': config.serverConfig.platform,
// 'Content-Type': 'multipart/mixed',
// 'content-type': 'multipart/form-data'
// },
// 'Content-Type': 'multipart/mixed',
// 'content-type': 'multipart/form-data'
// }
// ).then(res => {
// if (res.status === 201) {
// this.setState({
// loading: false,
// });
//
// notification["success"]({
// message: "Done!",
// description:
// "New app was added successfully",
// });
// }
//
// }).catch((error) => {
// if (error.response.status === 401) {
// window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
// } else {
// message.warning('Something went wrong');
//
// }
// this.setState({
// loading: false
// });
// });
var data = new FormData();
// data.append("binaryFile", "/Users/jayasanka/Desktop/gov/viber/a.apk");
// data.append("application", "{\n\t\"name\": \"Tub1111\",\n\t\"description\": \"Watch thousands of hit movies and TV series for free. Tubi is 100% legal unlimited streaming, with no credit cards and no subscription required. Choose what you want to watch, when you want to watch it, with fewer ads than regular TV. Tubi is the largest free streaming service featuring award-winning movies and TV series. There is something for everybody; from comedy to drama, kids to classics, and niche favorites such as Korean dramas, anime, and British series. Download now and start streaming entertainment for free, today!\",\n\t\"appCategories\": [\"EMM\"],\n\t\"subType\": \"FREE\",\n\t\"tags\": [\"tv\", \"movies\"],\n\t\"unrestrictedRoles\": [],\n\t\"deviceType\": \"android\",\n\t\"entAppReleaseWrappers\": [{\n\t\t\"description\": \" SEND A MESSAGE -Skip exchanging phone numbers, just send a message. It's seamless across devices. * SHOW YOUR REACTION - Add a silly sticker\",\n\t\t\"releaseType\": \"GA\",\n\t\t\"price\": 0.0,\n\t\t\"isSharedWithAllTenants\": false,\n\t\t\"metaData\": \"{\\\"developer\\\":\\\"Facebook, Inc.\\\",\\\"Copyright\\\":\\\"\\u00A9 Facebook, Inc.\\\",\\\"Location\\\":\\\"This app may use your location even when it isn't open, which can decrease battery life.\\\"}\",\n\t\t\"ratedUsers\": 0,\n\t\t\"rating\": 0,\n\t\t\"supportedOsVersions\": \"4.0-10.0\"\n\t}]\n}");
// data.append("icon", "/Users/jayasanka/Desktop/gov/angular/i.png");
// data.append("screenshot1", "/Users/jayasanka/Desktop/gov/angular/1.webp");
// data.append("screenshot2", "/Users/jayasanka/Desktop/gov/angular/2.webp");
// data.append("screenshot3", "/Users/jayasanka/Desktop/gov/angular/3.webp");
data.append('binaryFile', binaryFile[0].originFileObj);
data.append('icon', icon[0].originFileObj);
data.append('screenshot1', screenshots[0].originFileObj);
data.append('screenshot2', screenshots[1].originFileObj);
data.append('screenshot3', screenshots[2].originFileObj);
data.append('application', JSON.toString(payload.application));
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://localhost:9443/ui-request-handler/invoke/application-mgt-publisher/v1.0/applications/ent-app");
xhr.setRequestHeader("Content-Type", "multipart/mixed");
xhr.setRequestHeader("X-Platform", "publisher");
xhr.send(data);
}
});
};
normFile = e => {
console.log('Upload event:', e);
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
};
handleIconChange = ({fileList}) => this.setState({icons: fileList});
handleScreenshotChange = ({fileList}) => this.setState({screenshots: fileList});
validateIcon = (rule, value, callback) => {
const {icons} = this.state;
if (icons.length !== 1) {
callback("Please select icon file");
}
callback();
};
render() {
const {categories, tags, icons, screenshots} = this.state;
const {getFieldDecorator} = this.props.form;
return (
<div>
<Row>
<Col span={20} offset={2}>
<Card>
<Form labelAlign="left" layout="horizontal"
hideRequiredMark
onSubmit={this.handleSubmit}>
<Row>
<Col span={12}>
<div>
{/*device type*/}
<Form.Item {...formItemLayout} label="Device Type">
{getFieldDecorator('deviceType', {
rules: [{
required: false,
message: 'Please select device type'
},
{
validator: this.validateIcon
}],
})(
<Select placeholder="select device type">
<Option key="android">Android</Option>
<Option key="ios">iOS</Option>
</Select>
)}
</Form.Item>
{/*app name*/}
<Form.Item {...formItemLayout} label="App Name">
{getFieldDecorator('name', {
rules: [{
required: false,
message: 'Please input a name'
}],
})(
<Input placeholder="ex: Lorem App"/>
)}
</Form.Item>
{/*description*/}
<Form.Item {...formItemLayout} label="Description">
{getFieldDecorator('description', {
rules: [{
required: false,
message: 'Please enter a description'
}],
})(
<TextArea placeholder="Enter the description..." rows={7}/>
)}
</Form.Item>
<Form.Item {...formItemLayout} label="Categories">
{getFieldDecorator('appCategories', {
rules: [{
required: false,
message: 'Please select categories'
}],
})(
<Select
mode="multiple"
style={{width: '100%'}}
placeholder="Select a Category"
onChange={this.handleCategoryChange}
>
{
categories.map(category => {
return (
<Option
key={category.categoryName}>
{category.categoryName}
</Option>
)
})
}
</Select>
)}
</Form.Item>
<Form.Item {...formItemLayout} label="Price">
{getFieldDecorator('price', {
rules: [{
required: false
}],
})(
<Input prefix="$" placeholder="00.00"/>
)}
</Form.Item>
<Form.Item {...formItemLayout} label="Is Shared?">
{getFieldDecorator('isSharedWithAllTenants', {
rules: [{
required: false,
message: 'Please select'
}],
})(
<Switch checkedChildren={<Icon type="check"/>}
unCheckedChildren={<Icon type="close"/>} defaultChecked/>
)}
</Form.Item>
<Divider/>
<Form.Item {...formItemLayout} label="Tags">
{getFieldDecorator('tags', {
rules: [{
required: false,
message: 'Please select tags'
}],
})(
<Select
mode="tags"
style={{width: '100%'}}
placeholder="Tags"
>
{
tags.map(tag => {
return (
<Option
key={tag.tagName}>
{tag.tagName}
</Option>
)
})
}
</Select>
)}
</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.Item wrapperCol={{span: 12, offset: 5}}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</div>
</Col>
<Col span={12} style={{paddingLeft: 20}}>
<Form.Item label="Application">
<div className="dropbox">
{getFieldDecorator('binaryFile', {
valuePropName: 'fileList',
getValueFromEvent: this.normFile,
required: false,
message: 'Please select tags'
})(
<Upload.Dragger
name="files"
beforeUpload={() => false}
multiple={false}
>
<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.</p>
</Upload.Dragger>,
)}
</div>
</Form.Item>
<Row>
<Col span={12}>
<Form.Item label="Icon">
{getFieldDecorator('icon', {
valuePropName: 'icon',
getValueFromEvent: this.normFile,
required: false,
message: 'Please select a icon'
})(
<Upload
name="logo"
onChange={this.handleIconChange}
beforeUpload={() => false}
>
{icons.length !== 1 && (
<Button>
<Icon type="upload"/> Click to upload
</Button>
)}
</Upload>,
)}
</Form.Item>
</Col>
</Row>
<Row style={{marginTop: 40}}>
<Col span={24}>
<Form.Item label="Screenshots">
{getFieldDecorator('screenshots', {
valuePropName: 'icon',
getValueFromEvent: this.normFile,
required: false,
message: 'Please select a icon'
})(
<Upload
name="screenshots"
onChange={this.handleScreenshotChange}
beforeUpload={() => false}
multiple
>
{screenshots.length < 3 && (
<Button>
<Icon type="upload"/> Click to upload
</Button>
)}
</Upload>,
)}
</Form.Item>
</Col>
</Row>
</Col>
</Row>
</Form>
</Card>
</Col>
</Row>
</div>
);
}
}
const AddNewAppForm = Form.create({name: 'add-new-app'})(AddNewAppFormComponent);
export default AddNewAppForm;

@ -9,8 +9,6 @@ import Release from "./pages/dashboard/apps/release/Release";
import AddNewApp from "./pages/dashboard/add-new-app/AddNewApp";
import Mange from "./pages/dashboard/manage/Manage";
import './index.css';
import store from "./js/store/index";
import {Provider} from "react-redux";
const routes = [
@ -20,7 +18,7 @@ const routes = [
component: Login
},
{
path: '/publisher/apps',
path: '/publisher/',
exact: false,
component: Dashboard,
routes: [
@ -30,21 +28,25 @@ const routes = [
exact: true
},
{
path: '/publisher/apps/new-app',
path: '/publisher/apps/releases/:uuid',
exact: true,
component: Release
},
{
path: '/publisher/add-new-app/enterprise',
component: AddNewApp,
exact: true
},
{
path: '/publisher/apps/releases/:uuid',
exact: true,
component: Release
}
]
},{
path: '/publisher/manage',
exact: false,
component: Dashboard,
routes: [
path: '/publisher/add-new-app/public',
component: AddNewApp,
exact: true
},
{
path: '/publisher/add-new-app/web-clip',
component: AddNewApp,
exact: true
},
{
path: '/publisher/manage',
component: Mange,
@ -56,9 +58,7 @@ const routes = [
ReactDOM.render(
<Provider store={store}>
<App routes={routes}/>
</Provider>,
<App routes={routes}/>,
document.getElementById('root'));
// If you want your app to work offline and load faster, you can change

@ -1,14 +1,14 @@
import React from "react";
import {Layout, Menu, Icon} from 'antd';
const {Header, Content, Footer} = Layout;
import Logo from "../../../public/images/logo.svg";
import {BrowserRouter,Switch,Link, NavLink} from "react-router-dom";
import {Switch, Link} from "react-router-dom";
import RouteWithSubRoutes from "../../components/RouteWithSubRoutes"
import {Redirect} from 'react-router'
import "../../App.css";
const {Header, Content, Footer} = Layout;
const {SubMenu} = Menu;
class Dashboard extends React.Component {
constructor(props) {
super(props);
@ -32,8 +32,18 @@ class Dashboard extends React.Component {
style={{lineHeight: '64px'}}
>
<Menu.Item key="1"><Link to="/publisher/apps"><Icon type="appstore"/>Apps</Link></Menu.Item>
<Menu.Item key="3"><Link to="/publisher/apps/new-app"><Icon type="upload"/>Add New
App</Link></Menu.Item>
<SubMenu
title={
<span className="submenu-title-wrapper">
<Icon type="plus"/>
Add New App
</span>
}
>
<Menu.Item key="setting:1"><Link to="/publisher/add-new-app/public">Public APP</Link></Menu.Item>
<Menu.Item key="setting:2"><Link to="/publisher/add-new-app/enterprise">Enterprise APP</Link></Menu.Item>
<Menu.Item key="setting:3"><Link to="/publisher/add-new-app/web-clip">Web Clip</Link></Menu.Item>
</SubMenu>
<Menu.Item key="2"><Link to="/publisher/manage"><Icon
type="control"/>Manage</Link></Menu.Item>
</Menu>
@ -41,7 +51,6 @@ class Dashboard extends React.Component {
</Layout>
<Layout>
<Content style={{padding: '0 0'}}>
<BrowserRouter>
<Switch>
<Redirect exact from="/publisher" to="/publisher/apps"/>
{this.state.routes.map((route) => (
@ -49,7 +58,6 @@ class Dashboard extends React.Component {
))}
</Switch>
</BrowserRouter>
</Content>
<Footer style={{textAlign: 'center'}}>

@ -19,29 +19,12 @@ import {
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";
import axios from "axios";
import AddNewAppForm from "../../../components/new-app/AddNewAppForm"
import config from "../../../../public/conf/config.json";
const Paragraph = Typography;
const Dragger = Upload.Dragger;
const routes = [
{
path: 'index',
breadcrumbName: 'publisher',
},
{
path: 'first',
breadcrumbName: 'dashboard',
},
{
path: 'second',
breadcrumbName: 'add new app',
},
];
const props = {
name: 'file',
@ -60,18 +43,17 @@ const props = {
},
};
const Step = Steps.Step;
const steps = [{
title: 'First',
content: Step1
}, {
title: 'Second',
content: Step2,
}, {
title: 'Last',
content: Step3,
}];
//
// const steps = [{
// title: 'First',
// content: Step1
// }, {
// title: 'Second',
// content: Step2,
// }, {
// title: 'Last',
// content: Step3,
// }];
const {Option} = Select;
@ -87,95 +69,18 @@ const formItemLayout = {
},
};
class EditableTagGroup extends React.Component {
state = {
tags: [],
inputVisible: false,
inputValue: '',
};
handleClose = (removedTag) => {
const tags = this.state.tags.filter(tag => tag !== removedTag);
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 {
constructor(props) {
super(props);
this.state = {
current: 0,
categories: []
};
}
tags = [];
addTag(key, value){
this.tags.push(<Option key={key}>{value}</Option>);
componentDidMount() {
// this.getCategories();
}
next() {
@ -188,17 +93,44 @@ class AddNewApp extends React.Component {
this.setState({current});
}
getCategories = () => {
axios.get(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
{
headers: {'X-Platform': config.serverConfig.platform}
}).then(res => {
if (res.status === 200) {
let categories = JSON.parse(res.data.data);
this.setState({
categories: categories,
loading: false
});
}
}).catch((error) => {
if (error.response.status === 401) {
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({
loading: false
});
});
};
handleCategoryChange = (value) => {
console.log(`selected ${value}`);
};
render() {
const {current} = this.state;
const Content = steps[current].content;
this.addTag('1','Lorem');
this.addTag('2','Ipsum');
const {categories} = this.state;
return (
<div>
<PageHeader
title="Add New App"
breadcrumb={{routes}}
>
<div className="wrap">
<div className="content">
@ -209,121 +141,7 @@ class AddNewApp extends React.Component {
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
<Row>
<Col span={20} offset={2}>
<Card>
<Row>
<Col span={12}>
<div>
<Form labelAlign="left" layout="horizontal" className={styles.stepForm}
hideRequiredMark>
<Form.Item {...formItemLayout} label="Platform">
<Select placeholder="ex: android">
<Option value="Android">Android</Option>
<Option value="iOS">iOS</Option>
</Select>
</Form.Item>
<Form.Item {...formItemLayout} label="Type">
<Select value="Enterprise">
<Option value="Enterprise" selected>Enterprise</Option>
</Select>
</Form.Item>
<Form.Item {...formItemLayout} label="App Name">
<Input placeholder="ex: Lorem App"/>
</Form.Item>
<Form.Item {...formItemLayout} label="Description">
<TextArea placeholder="Enter the description..." rows={7}/>
</Form.Item>
<Form.Item {...formItemLayout} label="Category">
<Select placeholder="Select a category">
<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>
</Col>
</Row>
<AddNewAppForm/>
</div>
</div>

@ -33,11 +33,7 @@ class Apps extends React.Component {
render() {
return (
<div>
<PageHeader
breadcrumb={{routes}}
/>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
<ReleaseModal/>
<ListApps/>
</div>

Loading…
Cancel
Save