Merge pull request #954 from menakaj/application-mgt

Publisher UI modifications.
feature/appm-store/pbac
Megala Uthayakumar 7 years ago committed by GitHub
commit 56ce03927b

@ -14,7 +14,6 @@
"history": "^4.6.3", "history": "^4.6.3",
"latest-version": "^3.1.0", "latest-version": "^3.1.0",
"material-ui": "^0.19.0", "material-ui": "^0.19.0",
"material-ui-datatables": "^0.18.2",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qs": "^6.5.0", "qs": "^6.5.0",
"react": "^15.6.1", "react": "^15.6.1",

@ -25,8 +25,8 @@
manifest.json provides metadata used when your web app is added to the manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/publisher/manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/publisher/images/favicon.png"> <link rel="shortcut icon" href="images/favicon.png">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build. It will be replaced with the URL of the `public` folder during the build.
@ -39,6 +39,7 @@
<title>WSO2 IoT App Publisher</title> <title>WSO2 IoT App Publisher</title>
</head> </head>
<body> <body>
<script src='./dist/index.js'></script>
<noscript> <noscript>
You need to enable JavaScript to run this app. You need to enable JavaScript to run this app.
</noscript> </noscript>

@ -16,11 +16,19 @@
* under the License. * under the License.
*/ */
import './App.css' import './App.css';
import React, {Component} from 'react'; import React, {Component} from 'react';
import createHistory from 'history/createHashHistory'; import createHistory from 'history/createBrowserHistory';
import {HashRouter as Router, Redirect, Route, Switch} from 'react-router-dom' import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom'
import {BaseLayout, ApplicationCreate, Login, NotFound, PublisherOverview, PlatformCreate} from './components' import {
ApplicationCreate,
ApplicationListing,
BaseLayout,
Login,
NotFound,
PlatformCreate,
PlatformListing
} from './components';
const history = createHistory({basename: '/publisher'}); const history = createHistory({basename: '/publisher'});
@ -33,6 +41,9 @@ const history = createHistory({basename: '/publisher'});
* The Router and Route is used for navigation. * The Router and Route is used for navigation.
* We specify the component which needs to be rendered for an URL. * We specify the component which needs to be rendered for an URL.
* Ex: When navigate to publisher/overview, the overview component will be rendered inside the main layout. * Ex: When navigate to publisher/overview, the overview component will be rendered inside the main layout.
*
* HashRouter is used because the other router types need the server to serve those urls. In hashRouter, server does
* not want to serve the URL.
* */ * */
class Base extends Component { class Base extends Component {
@ -49,15 +60,15 @@ class Base extends Component {
<div className="container"> <div className="container">
<BaseLayout> <BaseLayout>
<Switch> <Switch>
<Redirect exact path={"/"} to={"/overview"}/> <Redirect exact path={"/"} to={"/assets/apps"}/>
<Route exact path={"/overview"} component={PublisherOverview}/> <Route exact path={"/assets/apps"} component={ApplicationListing}/>
<Route exact path={"/assets/apps/create"} component={ApplicationCreate}/> <Route exact path={"/assets/apps/create"} component={ApplicationCreate}/>
<Route exact path={"/assets/platforms"} component={PlatformListing}/>
<Route exact path={"/assets/platforms/create"} component={PlatformCreate}/> <Route exact path={"/assets/platforms/create"} component={PlatformCreate}/>
<Route exact path={"/assets/apps"} />
<Route exact path={"/assets/apps/:app"} /> <Route exact path={"/assets/apps/:app"} />
<Route exact path={"/assets/apps/edit/:app"} /> <Route exact path={"/assets/apps/:app/edit"} />
<Route exact path={"/assets/platforms/:platform"}/> <Route exact path={"/assets/platforms/:platform"}/>
<Route exact path={"/assets/platforms/edit/:platform"}/> <Route exact path={"/assets/platforms/:platform/edit"}/>
<Route exact path={"/assets/reviews"}/> <Route exact path={"/assets/reviews"}/>
<Route exact path={"/assets/reviews/:review"}/> <Route exact path={"/assets/reviews/:review"}/>
<Route component={NotFound}/> <Route component={NotFound}/>
@ -66,13 +77,15 @@ class Base extends Component {
</div> </div>
) )
} }
return (<Redirect to={"/login"}/>) return (<Redirect to={"/login"}/>)
} }
} }
/** /**
* This component is referred by the index.js to initiate the application. * This component is referred by the index.js to initiate the application.
* TODO: Currently the URL shows like https://localhost:9443/publisher/#/publisher/assets/apps/create. this needs to
* be fixed as https://localhost:9443/publisher/#/assets/apps/create
*
* */ * */
class Publisher extends Component { class Publisher extends Component {
render() { render() {
@ -90,8 +103,4 @@ class Publisher extends Component {
} }
} }
Publisher.propTypes = {
user: Object
};
export default Publisher; export default Publisher;

@ -19,8 +19,8 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import Dialog from 'material-ui/Dialog'; import Dialog from 'material-ui/Dialog';
import {withRouter} from 'react-router-dom'; import {withRouter} from 'react-router-dom';
import {Step1, Step2, Step3} from './Forms';
import FlatButton from 'material-ui/FlatButton'; import FlatButton from 'material-ui/FlatButton';
import {Step1, Step2, Step3} from './CreateSteps';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import {Card, CardActions, CardTitle} from 'material-ui/Card'; import {Card, CardActions, CardTitle} from 'material-ui/Card';
import {Step, StepLabel, Stepper,} from 'material-ui/Stepper'; import {Step, StepLabel, Stepper,} from 'material-ui/Stepper';
@ -55,6 +55,7 @@ class ApplicationCreate extends Component {
* Handles next button click event. * Handles next button click event.
* */ * */
handleNext = () => { handleNext = () => {
console.log("Handle Next");
const {stepIndex} = this.state; const {stepIndex} = this.state;
this.setState({ this.setState({
stepIndex: stepIndex + 1, stepIndex: stepIndex + 1,
@ -97,7 +98,7 @@ class ApplicationCreate extends Component {
let tmpStepData = this.state.stepData; let tmpStepData = this.state.stepData;
tmpStepData.push({step: step, data: data}); tmpStepData.push({step: step, data: data});
this.setState({stepData: tmpStepData}) this.setState({stepData: tmpStepData}, this.handleNext())
}; };
/** /**
@ -179,7 +180,7 @@ class ApplicationCreate extends Component {
return ( return (
<div className="middle" style={{width: '95%', height: '100%', marginTop: '1%'}}> <div className="middle" style={{width: '95%', height: '100%', marginTop: '1%'}}>
<Card> <Card style={{maxHeight: '700px', overflow: 'auto'}}>
<CardTitle title="Create Application"/> <CardTitle title="Create Application"/>
{/** {/**
@ -228,4 +229,6 @@ class ApplicationCreate extends Component {
} }
} }
ApplicationCreate.propTypes = {};
export default withRouter(ApplicationCreate); export default withRouter(ApplicationCreate);

@ -1,30 +0,0 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
/**
* Application List Component.
* */
class ApplicationListing extends Component{
}
export default withRouter(ApplicationListing);

@ -0,0 +1,180 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import TextField from 'material-ui/TextField';
import DataTable from '../UIComponents/DataTable';
import {Card, CardActions, CardTitle} from 'material-ui/Card';
/**
* The App Create Component.
*
* Application creation is handled through a Wizard. (We use Material UI Stepper.)
*
* In each step, data will be set to the state separately.
* When the wizard is completed, data will be arranged and sent to the api.
* */
class ApplicationListing extends Component {
constructor() {
super();
this.state = {
data: [],
asc: true
}
}
data = [
{
id: Math.random(),
applicationName:"Cne",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"Gone",
platform:'IOS',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"Ane",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"one",
platform:'Android',
category:"Public",
status: "Created"
},
{
id: Math.random(),
applicationName:"one",
platform:'Android',
category:"Public",
status: "Created"
},
];
headers = [
{
data_id: "image",
data_type: "image",
sortable: false,
label: ""},
{
data_id: "applicationName",
data_type: "string",
sortable: true,
label: "Application Name",
sort: this._sortData.bind(this)
},
{
data_id: "platform",
data_type: "image_array",
sortable: false,
label: "Platform"},
{
data_id: "category",
data_type: "string",
sortable: false,
label: "Category"
},
{
data_id: "status",
data_type: "string",
sortable: false,
label: "Status"
}
];
componentWillMount() {
//Fetch all the applications from backend and create application objects.
this.setState({data: this.data});
}
/**
* Handles the search action.
* When typing in the search bar, this method will be invoked.
* */
_searchApplications(event, word) {
let searchedData;
if (word){
searchedData = this.data.filter((dataItem) => {
return dataItem.applicationName.includes(word);
});
} else {
searchedData = this.data;
}
this.setState({data: searchedData}, console.log("Searched data ", this.state.data));
}
/**
* Handles sort data function and toggles the asc state.
* asc: true : sort in ascending order.
* */
_sortData() {
let isAsc = this.state.asc;
let datas = isAsc?this.data.sort(this._compare):this.data.reverse();
this.setState({data: datas, asc: !isAsc});
}
_compare(a, b) {
if (a.applicationName < b.applicationName)
return -1;
if (a.applicationName > b.applicationName)
return 1;
return 0;
}
_onRowClick(id) {
this.props.history.push("apps/"+id);
}
render() {
return (
<div className="middle" style={{width: '95%', height: '100%', marginTop: '1%'}}>
<Card style={{display: 'flex', flexWrap: 'wrap'}}>
<TextField hintText="Search"
style={{float:'right', paddingRight: '2px'}}
onChange={this._searchApplications.bind(this)}/>
<CardTitle title="Applications" style={{display: 'flex', flexWrap: 'wrap'}}/>
<CardActions>
</CardActions>
<DataTable headers={this.headers}
data={this.state.data}
handleRowClick={this._onRowClick.bind(this)}
noDataMessage={{type: 'button', text: 'Create Application'}}/>
</Card>
</div>);
}
}
ApplicationListing.propTypes = {};
export default withRouter(ApplicationListing);

@ -16,10 +16,10 @@
* under the License. * under the License.
*/ */
import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import MenuItem from 'material-ui/MenuItem'; import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField'; import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import SelectField from 'material-ui/SelectField'; import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
@ -44,23 +44,32 @@ class Step1 extends Component {
stepIndex: 0, stepIndex: 0,
store: 1, store: 1,
platform: 1, platform: 1,
stepData: [] stepData: [],
title: "",
titleError: ""
}; };
} }
componentWillMount() {
//Get the list of available platforms and set to the state.
}
/** /**
* Invokes the handleNext function in Create component. * Invokes the handleNext function in Create component.
* */ * */
handleNext = () => { _handleNext = () => {
this.props.handleNext(); this.props.handleNext();
}; };
/** /**
* Persist the current form data to the state. * Persist the current form data to the state.
* */ * */
setStepData() { _setStepData() {
this.props.setData("step1", {step: "Dfds"}); var step = {
this.handleNext.bind(this); store: this.state.store,
platform: this.state.platform
};
this.props.setData("step1", {step: step});
} }
/** /**
@ -69,25 +78,32 @@ class Step1 extends Component {
* Sets the data to the state. * Sets the data to the state.
* Invokes the handleNext method of Create component. * Invokes the handleNext method of Create component.
* */ * */
handleClick() { _handleClick() {
this.setStepData(); this._setStepData();
this.handleNext();
} }
/** /**
* Triggers when changing the Platform selection. * Triggers when changing the Platform selection.
* */ * */
onChangePlatform = (event, index, value) => { _onChangePlatform = (event, index, value) => {
console.log(value);
this.setState({platform: value}); this.setState({platform: value});
}; };
/** /**
* Triggers when changing the Store selection. * Triggers when changing the Store selection.
* */ * */
onChangeStore = (event, index, value) => { _onChangeStore = (event, index, value) => {
this.setState({store: value}); this.setState({store: value});
}; };
/**
* Triggers when user types on Title text field.
* */
_onChangeTitle = (event, value) => {
this.setState({title: value});
};
render() { render() {
const contentStyle = {margin: '0 16px'}; const contentStyle = {margin: '0 16px'};
return ( return (
@ -95,16 +111,11 @@ class Step1 extends Component {
<div style={contentStyle}> <div style={contentStyle}>
<div> <div>
<div> <div>
<TextField
hintText="Enter a title for your application."
floatingLabelText="Title*"
floatingLabelFixed={true}
/><br/>
<SelectField <SelectField
floatingLabelText="Store Type*" floatingLabelText="Store Type*"
value={this.state.store} value={this.state.store}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this.onChangeStore.bind(this)} onChange={this._onChangeStore.bind(this)}
> >
<MenuItem value={1} primaryText="Enterprise"/> <MenuItem value={1} primaryText="Enterprise"/>
<MenuItem value={2} primaryText="Public"/> <MenuItem value={2} primaryText="Public"/>
@ -113,11 +124,11 @@ class Step1 extends Component {
floatingLabelText="Platform*" floatingLabelText="Platform*"
value={this.state.platform} value={this.state.platform}
floatingLabelFixed={true} floatingLabelFixed={true}
onChange={this.onChangePlatform.bind(this)} onChange={this._onChangePlatform.bind(this)}
> >
<MenuItem value={1} primaryText="Android"/> <MenuItem value={1} primaryText="Android"/>
<MenuItem value={2} primaryText="iOS"/> <MenuItem value={2} primaryText="iOS"/>
<MenuItem value={3} primaryText="Web"/> <MenuItem value={{name: "Web", id:3}} primaryText="Web"/>
</SelectField> </SelectField>
</div> </div>
@ -127,7 +138,7 @@ class Step1 extends Component {
<RaisedButton <RaisedButton
label="Next >" label="Next >"
primary={true} primary={true}
onClick={this.handleClick.bind(this)} onClick={this._handleClick.bind(this)}
/> />
</div> </div>
</div> </div>
@ -137,4 +148,10 @@ class Step1 extends Component {
} }
} }
Step1.propTypes = {
handleNext: PropTypes.func,
setData: PropTypes.func,
removeData: PropTypes.func
};
export default Step1; export default Step1;

@ -0,0 +1,489 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import Chip from 'material-ui/Chip';
import Dropzone from 'react-dropzone';
import React, {Component} from 'react';
import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton';
import Clear from 'material-ui/svg-icons/content/clear';
import {GridList, GridTile} from 'material-ui/GridList';
/**
* The Second step of application create wizard.
* This contains following components.
* * App Title
* * Short Description
* * Application Description
* * Application Visibility
* * Application Tags : {Used Material UI Chip component}
* * Application Category.
* * Platform Specific properties.
* * Screenshots
* * Banner
* * Icon
*
* Parent Component: Create
* Props:
* * handleNext : {type: function, Invokes handleNext function in Parent.}
* * handlePrev : {type: function, Invokes handlePrev function in Parent}
* * setData : {type: function, Invokes setStepData function in Parent}
* * removeData : {type: Invokes removeStepData function in Parent}
* */
class Step2 extends Component {
constructor() {
super();
this.state = {
tags: [],
defValue: "",
category: 0,
visibility: 0,
errors: {},
title: "",
shortDescription: "",
description: "",
banner: [],
screenshots: [],
icon: []
};
this.styles = {
chip: {
margin: 4,
},
wrapper: {
display: 'flex',
flexWrap: 'wrap',
},
};
}
/**
* Create a tag on Enter key press and set it to the state.
* Clears the tags text field.
* Chip gets two parameters: Key and value.
* */
_addTags(event) {
let tags = this.state.tags;
if (event.charCode === 13) {
event.preventDefault();
tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value});
this.setState({tags, defValue: ""});
}
}
/**
* Set the value for tag.
* */
_handleTagChange(event) {
let defaultValue = this.state.defValue;
defaultValue = event.target.value;
this.setState({defValue: defaultValue})
}
/**
* Invokes the handleNext function in Create component.
* */
_handleNext() {
let fields = [{name: "Title", value: this.state.title},
{name: "Short Description", value: this.state.shortDescription},
{name: "Description", value: this.state.description},
{name: "Banner", value: this.state.banner},
{name: "Screenshots", value: this.state.screenshots},
{name: "Icon", value: this.state.icon}];
this._validate(fields);
// this.props.handleNext();
}
/**
* Invokes the handlePrev function in Create component.
* */
_handlePrev() {
this.props.handlePrev();
}
/**
* Handles Chip delete function.
* Removes the tag from state.tags
* */
_handleRequestDelete = (key) => {
this.chipData = this.state.tags;
const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key);
this.chipData.splice(chipToDelete, 1);
this.setState({tags: this.chipData});
};
/**
* Creates Chip array from state.tags.
* */
_renderChip(data) {
return (
<Chip
key={data.key}
onRequestDelete={() => this._handleRequestDelete(data.key)}
style={this.styles.chip}
>
{data.value}
</Chip>
);
}
_onVisibilitySelect = (event, index, value) => {
console.log(value);
let comp = <SelectField> <MenuItem value={0} primaryText="Public"/>
<MenuItem value={1} primaryText="Roles"/>
<MenuItem value={2} primaryText="Devices"/> </SelectField>;
if (value === 1) {
this.setState({visibilityComponent: comp});
} else if (value === 2) {
} else {
}
};
/**
* Validate the form.
* */
_validate(fields) {
let errors = {};
let errorsPresent = false;
fields.forEach(function (field) {
switch (field.name) {
case 'Title': {
if (field.value === "") {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Short Description': {
if (field.value === "") {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Description': {
if (field.value === "") {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Banner': {
if (field.value.length === 0) {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Icon': {
if (field.value.length === 0) {
errors[field.name] = field.name + " is required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
case 'Screenshots': {
if (field.value.length < 3) {
errors[field.name] = "3 " +field.name + " are required!";
errorsPresent = true;
} else {
errorsPresent = false;
}
break;
}
}
});
console.log(errorsPresent);
if (!errorsPresent) {
this._setStepData();
} else {
this.setState({errors: errors}, console.log(errors));
}
}
/**
* Creates an object with the current step data and persist in the parent.
* */
_setStepData() {
let stepData = {
title: this.state.title,
description: this.state.description,
shortDescription: this.state.shortDescription,
tags: this.state.tags,
banner: this.state.banner,
screenshots: this.state.screenshots,
icon: this.state.icon
};
this.props.setData("step2", {step: stepData});
}
/**
* Set text field values to state.
* */
_onTextFieldChange(event, value) {
let field = event.target.id;
switch (field) {
case "title": {
this.setState({title: value});
break;
}
case "shortDescription": {
this.setState({shortDescription: value});
break;
}
case "description": {
this.setState({description: value});
break;
}
}
}
/**
* Removed user uploaded banner.
* */
_removeBanner(event, d) {
console.log(event, d);
this.setState({banner: []});
}
/**
* Removes uploaded icon.
* */
_removeIcon(event) {
this.setState({icon: []});
}
/**
* Removes selected screenshot.
* */
_removeScreenshot(event) {
console.log(event.target)
}
render() {
console.log(this.state.visibilityComponent);
const contentStyle = {margin: '0 16px'};
return (
<div style={contentStyle}>
<div>
<div>
<TextField
id="title"
hintText="Enter a title for your application."
errorText={this.state.errors["Title"]}
floatingLabelText="Title*"
floatingLabelFixed={true}
onChange={this._onTextFieldChange.bind(this)}
/><br/>
<TextField
id="shortDescription"
hintText="Enter a short description for your application."
errorText={this.state.errors["Short Description"]}
floatingLabelText="Short Description*"
floatingLabelFixed={true}
multiLine={true}
rows={2}
onChange={this._onTextFieldChange.bind(this)}
/><br/>
<TextField
id="description"
errorText={this.state.errors["Description"]}
hintText="Enter the description."
floatingLabelText="Description*"
floatingLabelFixed={true}
multiLine={true}
rows={4}
onChange={this._onTextFieldChange.bind(this)}
/><br/>
<SelectField
floatingLabelText="Visibility*"
value={this.state.visibility}
floatingLabelFixed={true}
onChange={this._onVisibilitySelect.bind(this)}
>
<MenuItem value={0} primaryText="Public"/>
<MenuItem value={1} primaryText="Roles"/>
<MenuItem value={2} primaryText="Devices"/>
</SelectField><br/>
<TextField
id="tags"
errorText={this.state.errors["tags"]}
hintText="Enter application tags.."
floatingLabelText="Tags*"
floatingLabelFixed={true}
value={this.state.defValue}
onChange={this._handleTagChange.bind(this)}
onKeyPress={this._addTags.bind(this)}
/><br/>
<div style={this.styles.wrapper}>
{this.state.tags.map(this._renderChip, this)}
</div>
<br/>
<SelectField
floatingLabelText="Category*"
value={this.state.category}
floatingLabelFixed={true}
>
<MenuItem value={0} primaryText="Business"/>
</SelectField> <br/>
{/*Platform Specific Properties.*/}
<div style={{border: 'solid #BDBDBD 1px'}}>
<p style={{color: '#BDBDBD'}}>Platform Specific Properties</p>
</div>
<br/>
<div>
<p style={{color: '#f44336'}}>{this.state.errors["Banner"]}</p>
<p style={{color: '#BDBDBD'}}>Banner*:</p>
<GridList style={{
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
}} cols={1.1}>
{this.state.banner.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)}
title={tile.name}
actionIcon={
<IconButton onClick={this._removeBanner.bind(this)}>
<Clear />
</IconButton>}>
<img src={tile.preview}/></GridTile>
))}
{this.state.banner.length === 0 ?
<Dropzone style={{width: '300px', height: '150px', border: 'dashed #BDBDBD 1px'}}
accept="image/jpeg, image/png"
onDrop={(banner, rejected) => {
this.setState({banner, rejected});
}}>
<p style={{margin: '70px 40px 40px 150px'}}>+</p>
</Dropzone> : <div />}
</GridList>
</div>
<br/>
<div>
<p style={{color: '#f44336'}}>{this.state.errors["Screenshots"]}</p>
<p style={{color: '#BDBDBD'}}>Screenshots*:</p>
<GridList style={{
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
}} cols={1.1}>
{this.state.screenshots.map((file) => (
<GridTile key={Math.floor(Math.random() * 1000)}
title={file[0].name}
actionIcon={
<IconButton onClick={this._removeScreenshot.bind(this)}>
<Clear/>
</IconButton>}>
<img src={file[0].preview}/></GridTile>
))}
{this.state.screenshots.length < 3 ?
<Dropzone style={{width: '150px', height: '150px', border: 'dashed #BDBDBD 1px'}}
accept="image/jpeg, image/png"
onDrop={(screenshots, rejected) => {
let tmpScreenshots = this.state.screenshots;
tmpScreenshots.push(screenshots);
this.setState({
screenshots: tmpScreenshots});
}}>
<p style={{margin: '70px 40px 70px 70px'}}>+</p>
</Dropzone> : <div />}
</GridList>
</div>
<br/>
<div>
<p style={{color: '#f44336'}}>{this.state.errors["Icon"]}</p>
<p style={{color: '#BDBDBD'}}>Icon*:</p>
<GridList style={{
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
}} cols={1.1}>
{this.state.icon.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)}
title={tile.name}
actionIcon={
<IconButton onClick={this._removeIcon.bind(this)}>
<Clear />
</IconButton>}>
<img src={tile.preview}/></GridTile>
))}
{this.state.icon.length === 0 ?
<Dropzone style={{width: '150px', height: '150px', border: 'dashed #BDBDBD 1px'}}
accept="image/jpeg, image/png"
onDrop={(icon, rejected) => {this.setState({icon, rejected});}}>
<p style={{margin: '70px 40px 70px 70px'}}>+</p>
</Dropzone> : <div />}
</GridList>
</div>
<br/>
</div>
<br/>
<br/>
<div style={{marginTop: 12}}>
<FlatButton
label="< Back"
disabled={false}
onClick={this._handlePrev.bind(this)}
style={{marginRight: 12}}
/>
<RaisedButton
label="Next >"
primary={true}
onClick={this._handleNext.bind(this)}
/>
</div>
</div>
</div>
);
}
}
Step2.prototypes = {
handleNext: PropTypes.func,
handlePrev: PropTypes.func,
setData: PropTypes.func,
removeData: PropTypes.func
};
export default Step2;

@ -16,6 +16,7 @@
* under the License. * under the License.
*/ */
import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Toggle from 'material-ui/Toggle'; import Toggle from 'material-ui/Toggle';
import MenuItem from 'material-ui/MenuItem'; import MenuItem from 'material-ui/MenuItem';
@ -51,7 +52,8 @@ class Step3 extends Component {
super(); super();
this.state = { this.state = {
showForm: false, showForm: false,
releaseChannel: 1 releaseChannel: 1,
errors: {}
} }
} }
@ -59,26 +61,25 @@ class Step3 extends Component {
* Handles finish button click. * Handles finish button click.
* This invokes handleNext function in parent component. * This invokes handleNext function in parent component.
* */ * */
handleFinish() { _handleFinish() {
this.props.handleFinish(); this.props.handleFinish();
} }
/** /**
* Invokes Prev button click. * Invokes Prev button click.
* */ * */
handlePrev() { _handlePrev() {
this.props.handlePrev(); this.props.handlePrev();
} }
/** /**
* Handles release application selection. * Handles release application selection.
* */ * */
handleToggle() { _handleToggle() {
let hide = this.state.showForm; let hide = this.state.showForm;
this.setState({showForm: !hide}); this.setState({showForm: !hide});
} }
render() { render() {
const contentStyle = {margin: '0 16px'}; const contentStyle = {margin: '0 16px'};
return ( return (
@ -87,10 +88,9 @@ class Step3 extends Component {
<Toggle <Toggle
label="Release the Application" label="Release the Application"
labelPosition="right" labelPosition="right"
onToggle={this.handleToggle.bind(this)} onToggle={this._handleToggle.bind(this)}
defaultToggled={this.state.showForm} defaultToggled={this.state.showForm}
/> />
{/*If toggle is true, the release form will be shown.*/} {/*If toggle is true, the release form will be shown.*/}
{!this.state.showForm ? <div/> : <div> {!this.state.showForm ? <div/> : <div>
<SelectField <SelectField
@ -105,6 +105,7 @@ class Step3 extends Component {
<TextField <TextField
hintText="1.0.0" hintText="1.0.0"
floatingLabelText="Version*" floatingLabelText="Version*"
errorText={this.state.errors["title"]}
floatingLabelFixed={true} floatingLabelFixed={true}
/><br/> /><br/>
</div>} </div>}
@ -113,13 +114,13 @@ class Step3 extends Component {
<FlatButton <FlatButton
label="< Back" label="< Back"
disabled={false} disabled={false}
onClick={this.handlePrev.bind(this)} onClick={this._handlePrev.bind(this)}
style={{marginRight: 12}} style={{marginRight: 12}}
/> />
<RaisedButton <RaisedButton
label="Finish" label="Finish"
primary={true} primary={true}
onClick={this.handleFinish.bind(this)} onClick={this._handleFinish.bind(this)}
/> />
</div> </div>
</div> </div>
@ -128,4 +129,11 @@ class Step3 extends Component {
} }
} }
Step3.propTypes = {
handleFinish: PropTypes.func,
handlePrev: PropTypes.func,
setData: PropTypes.func,
removeData: PropTypes.func
};
export default Step3; export default Step3;

@ -1,209 +0,0 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Chip from 'material-ui/Chip';
import React, {Component} from 'react';
import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton';
/**
* The Second step of application create wizard.
* This contains following components.
* * App Title
* * Short Description
* * Application Description
* * Application Visibility
* * Application Tags : {Used Material UI Chip component}
* * Application Category.
* * Platform Specific properties.
* * Screenshots
* * Banner
* * Icon
*
* Parent Component: Create
* Props:
* * handleNext : {type: function, Invokes handleNext function in Parent.}
* * handlePrev : {type: function, Invokes handlePrev function in Parent}
* * setData : {type: function, Invokes setStepData function in Parent}
* * removeData : {type: Invokes removeStepData function in Parent}
* */
class Step2 extends Component {
constructor() {
super();
this.state = {
tags: [],
defValue: "",
category: 1
};
this.styles = {
chip: {
margin: 4,
},
wrapper: {
display: 'flex',
flexWrap: 'wrap',
},
};
}
/**
* Create a tag on Enter key press and set it to the state.
* Clears the tags text field.
* Chip gets two parameters: Key and value.
* */
addTags(event) {
let tags = this.state.tags;
if (event.charCode === 13) {
event.preventDefault();
tags.push({key: Math.floor(Math.random() * 1000), value: event.target.value});
this.setState({tags, defValue: ""}, console.log(this.state.tags));
}
}
/**
*
* */
handleTagChange(event) {
let defaultValue = this.state.defValue;
defaultValue = event.target.value;
this.setState({defValue: defaultValue})
}
/**
* Invokes the handleNext function in Create component.
* */
handleNext() {
this.props.handleNext();
}
/**
* Invokes the handlePrev function in Create component.
* */
handlePrev() {
this.props.handlePrev();
}
/**
* Handles Chip delete function.
* Removes the tag from state.tags
* */
handleRequestDelete = (key) => {
this.chipData = this.state.tags;
const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key);
this.chipData.splice(chipToDelete, 1);
this.setState({tags: this.chipData});
};
/**
* Creates Chip array from state.tags.
* */
renderChip(data) {
console.log(data);
return (
<Chip
key={data.key}
onRequestDelete={() => this.handleRequestDelete(data.key)}
style={this.styles.chip}
>
{data.value}
</Chip>
);
}
render() {
const contentStyle = {margin: '0 16px'};
return (
<div style={contentStyle}>
<div>
<div>
<TextField
hintText="Enter a title for your application."
floatingLabelText="Title*"
floatingLabelFixed={true}
/><br/>
<TextField
hintText="Enter a short description for your application."
floatingLabelText="Short Description*"
floatingLabelFixed={true}
multiLine={true}
rows={2}
/><br/>
<TextField
hintText="Enter the description."
floatingLabelText="Description*"
floatingLabelFixed={true}
multiLine={true}
rows={4}
/><br/>
<TextField
hintText="Select the application visibility"
floatingLabelText="Visibility*"
floatingLabelFixed={true}
/><br/>
<TextField
hintText="Enter application tags.."
floatingLabelText="Tags*"
floatingLabelFixed={true}
value={this.state.defValue}
onChange={this.handleTagChange.bind(this)}
onKeyPress={this.addTags.bind(this)}
/><br/>
<div style={this.styles.wrapper}>
{this.state.tags.map(this.renderChip, this)}
</div>
<br/>
<SelectField
floatingLabelText="Category*"
value={this.state.category}
floatingLabelFixed={true}
>
<MenuItem value={1} primaryText="Business"/>
</SelectField> <br/>
{/*Platform Specific Properties.*/}
<div style={{border: '1px'}}>
fdfdfd
</div>
</div>
<br />
<br />
<div style={{marginTop: 12}}>
<FlatButton
label="< Back"
disabled={false}
onClick={this.handlePrev.bind(this)}
style={{marginRight: 12}}
/>
<RaisedButton
label="Next >"
primary={true}
onClick={this.handleNext.bind(this)}
/>
</div>
</div>
</div>
);
}
}
export default Step2;

@ -16,6 +16,7 @@
* under the License. * under the License.
*/ */
import PropTypes from 'prop-types';
import Badge from 'material-ui/Badge'; import Badge from 'material-ui/Badge';
import React, {Component} from 'react'; import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar'; import AppBar from 'material-ui/AppBar';
@ -109,9 +110,6 @@ class BaseLayout extends Component {
<div> <div>
<Drawer containerStyle={{height: 'calc(100% - 64px)', width: '15%', top: '10%'}} open={true}> <Drawer containerStyle={{height: 'calc(100% - 64px)', width: '15%', top: '10%'}} open={true}>
<List> <List>
<ListItem primaryText="Overview"
onClick={this.handleOverviewClick.bind(this)}
leftIcon={<Dashboard/>}/>
<ListItem primaryText="Applications" <ListItem primaryText="Applications"
leftIcon={<Apps/>} leftIcon={<Apps/>}
initiallyOpen={false} initiallyOpen={false}
@ -161,4 +159,8 @@ class BaseLayout extends Component {
} }
BaseLayout.propTypes = {
children: PropTypes.element
};
export default withRouter(BaseLayout); export default withRouter(BaseLayout);

@ -16,6 +16,7 @@
* under the License. * under the License.
*/ */
import React, {Component} from 'react'; import React, {Component} from 'react';
import DataTable from '../UIComponents/DataTable';
/** /**
* *
@ -39,6 +40,7 @@ class PublisherOverview extends Component {
<div> <div>
Overview Overview
</div> </div>
); );
} }

@ -1,38 +0,0 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
/**
* Platform Create component
* */
class PlatformCreate extends Component {
constructor() {
super();
}
render() {
return (
<div>
Create Platform
</div>
);
}
}
export default PlatformCreate;

@ -0,0 +1,308 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import Dropzone from 'react-dropzone';
import React, {Component} from 'react';
import Toggle from 'material-ui/Toggle';
import MenuItem from 'material-ui/MenuItem';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import SelectField from 'material-ui/SelectField';
import RaisedButton from 'material-ui/RaisedButton';
import Clear from 'material-ui/svg-icons/content/clear';
import {GridList, GridTile} from 'material-ui/GridList';
import Close from 'material-ui/svg-icons/navigation/close';
import {Card, CardActions, CardTitle} from 'material-ui/Card';
import AddCircleOutline from 'material-ui/svg-icons/content/add-circle-outline';
/**
* Platform Create component.
* Contains following components:
* * Platform Name
* * Platform Description
* * Platform Icon
* * Whether the platform needs an app to be installed.
* * Whether the platform is enabled by default.
* * Whether the platform is shared with tenants.
* */
class PlatformCreate extends Component {
constructor() {
super();
this.state = {
enabled: true,
allTenants: false,
files: [],
platformProperties: [],
selectedProperty: 0,
name: "",
description: "",
property: "",
icon: [],
propertyTypes: [
{key: 0, value: 'String'},
{key: 1, value: 'Number'},
{key: 2, value: 'Boolean'},
{key: 3, value: 'File'}]
}
}
/**
* Handles toggle button actions.
* One method is used for all the toggle buttons and, each toggle is identified by the id.
* */
_handleToggle(event) {
switch (event.target.id) {
case "enabled" : {
let enabled = this.state.enabled;
this.setState({enabled: !enabled});
break;
}
case "tenant" : {
let allTenants = this.state.allTenants;
this.setState({allTenants: !allTenants});
break;
}
}
}
/**
* Triggers the onChange action on property type selection.
* */
_onPropertySelect = (event, index, value) => {
console.log(this.state.propertyTypes[value]);
this.setState({selectedProperty: value});
};
/**
* Remove the selected property from the property list.
* */
_removeProperty(property) {
let properties = this.state.platformProperties;
properties.splice(properties.indexOf(property), 1);
this.setState({platformProperties: properties});
}
/**
* Add a new platform property.
* */
_addProperty() {
let property = this.state.property;
let selected = this.state.selectedProperty;
this.setState({platformProperties:
this.state.platformProperties.concat([
{
key: property,
value: this.state.propertyTypes[selected].value
}]),
property: "",
selectedProperty: 0});
}
/**
* Triggers in onChange event of text fields.
* Text fields are identified by their ids and the value will be persisted in the component state.
* */
_onTextChange = (event, value) => {
let property = this.state.property;
let name = this.state.name;
let description = this.state.description;
switch (event.target.id) {
case "name": {
name = value;
this.setState({name: name});
break;
}
case "description": {
description = value;
this.setState({description: description});
break;
}
case "property": {
property = value;
this.setState({property: property});
break;
}
}
};
_onCreatePlatform() {
}
/**
* Remove the uploaded icon.
* */
_removeIcon(event) {
this.setState({icon: []});
}
/**
* Clears the user entered values in the form.
* */
_clearForm() {
this.setState({enabled: true,
allTenants: false,
files: [],
platformProperties: [],
selectedProperty: 0,
name: "",
description: "",
property: "",})
}
render() {
const {
platformProperties,
allTenants,
enabled,
selectedProperty,
propertyTypes,
name,
description,
property} = this.state;
return (
<div className="middle" style={{width: '95%', height: '100%', marginTop: '1%'}}>
<Card>
<CardTitle title="Create Platform"/>
<CardActions>
<div style={{width: '100%', margin: 'auto', paddingLeft: '10px'}}>
<form>
<TextField
hintText="Enter the Platform Name."
id="name"
floatingLabelText="Name*"
floatingLabelFixed={true}
value={name}
onChange={this._onTextChange.bind(this)}
/><br/>
<TextField
id="description"
hintText="Enter the Platform Description."
floatingLabelText="Description*"
floatingLabelFixed={true}
multiLine={true}
rows={2}
value={description}
onChange={this._onTextChange.bind(this)}
/><br/><br/>
<Toggle
id="tenant"
label="Shared with all Tenants"
labelPosition="right"
onToggle={this._handleToggle.bind(this)}
toggled={allTenants}
/> <br/>
<Toggle
id="enabled"
label="Enabled"
labelPosition="right"
onToggle={this._handleToggle.bind(this)}
toggled={enabled}
/> <br/>
<div>
<p style={{color: '#BaBaBa'}}>Platform Properties</p>
<div id="property-container">
{platformProperties.map((p) => {
return <div key={p.key}>{p.key} : {p.value}
<IconButton onClick={this._removeProperty.bind(this, p)}>
<Close style={{height: '10px', width: '10px'}}/>
</IconButton>
</div>
})}
</div>
<div style={{display: 'flex'}}>
<TextField
id="property"
hintText="Property Name"
floatingLabelText="Platform Property*"
floatingLabelFixed={true}
value={this.state.property}
onChange={this._onTextChange.bind(this)}
/> <em/>
<SelectField
style={{flex: '1 1 23% 1', margin: '0 1%'}}
floatingLabelText="Property Type"
value={selectedProperty}
floatingLabelFixed={true}
onChange={this._onPropertySelect.bind(this)}>
{propertyTypes.map((type) => {
return <MenuItem key={type.key}
value={type.key}
primaryText={type.value}/>
})}
</SelectField>
<IconButton onClick={this._addProperty.bind(this)}>
<AddCircleOutline/>
</IconButton>
<br/>
</div>
</div>
<div>
{/*<p style={{color: '#f44336'}}>{this.state.errors["Icon"]}</p>*/}
<p style={{color: '#BDBDBD'}}>Platform Icon*:</p>
<GridList style={{
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
}} cols={1.1}>
{this.state.icon.map((tile) => (
<GridTile key={Math.floor(Math.random() * 1000)}
title={tile.name}
actionIcon={
<IconButton onClick={this._removeIcon.bind(this)}>
<Clear />
</IconButton>}>
<img src={tile.preview}/>
</GridTile>
))}
{this.state.icon.length === 0 ?
<Dropzone style={
{width: '150px', height: '150px', border: 'dashed #BDBDBD 1px'}
}
accept="image/jpeg, image/png"
onDrop={(icon, rejected) => {this.setState({icon, rejected})}}>
<p style={{margin: '70px 40px 70px 70px'}}>+</p>
</Dropzone> : <div />}
</GridList>
</div>
<br/>
<RaisedButton primary={true} label="Create"
onClick={this._onCreatePlatform.bind(this)}/>
<FlatButton label="Cancel" onClick={this._clearForm.bind(this)}/>
</form>
</div>
</CardActions>
</Card>
</div>
);
}
}
PlatformCreate.prototypes = {
};
export default PlatformCreate;

@ -1,38 +0,0 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
/**
* Platform Listing component.
* */
class PlatformListing extends Component {
constructor() {
super();
}
render() {
return (
<div>
Platform View
</div>
);
}
}
export default PlatformListing;

@ -0,0 +1,99 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import TextField from 'material-ui/TextField';
import DataTable from '../UIComponents/DataTable';
import {Card, CardActions, CardTitle} from 'material-ui/Card';
/**
* The App Create Component.
*
* Application creation is handled through a Wizard. (We use Material UI Stepper.)
*
* In each step, data will be set to the state separately.
* When the wizard is completed, data will be arranged and sent to the api.
* */
class PlatformListing extends Component {
constructor() {
super();
this.state = {
data: [],
asc: true
}
}
componentWillMount() {
//Fetch all the applications from backend and create application objects.
}
/**
* Handles the search action.
* When typing in the search bar, this method will be invoked.
* */
_searchApplications(word) {
let searchedData = [];
}
/**
* Handles sort data function and toggles the asc state.
* asc: true : sort in ascending order.
* */
_sortData() {
let isAsc = this.state.asc;
let datas = isAsc?this.data.sort(this._compare):this.data.reverse();
this.setState({data: datas, asc: !isAsc});
}
_compare(a, b) {
if (a.applicationName < b.applicationName)
return -1;
if (a.applicationName > b.applicationName)
return 1;
return 0;
}
_onRowClick(id) {
console.log(id)
}
render() {
return (
<div className="middle" style={{width: '95%', height: '100%', marginTop: '1%'}}>
<Card style={{display: 'flex', flexWrap: 'wrap'}}>
<TextField hintText="Search"
style={{float:'right', paddingRight: '2px'}}
onChange={this._searchApplications.bind(this)}/>
<CardTitle title="Platforms" style={{display: 'flex', flexWrap: 'wrap'}}/>
<CardActions>
</CardActions>
<DataTable headers={this.headers}
data={this.data}
handleRowClick={this._onRowClick.bind(this)}
noDataMessage={{type: 'button', text: 'Create Platform'}}/>
</Card>
</div>);
}
}
PlatformListing.propTypes = {};
export default withRouter(PlatformListing);

@ -1,38 +0,0 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, {Component} from 'react';
/**
* Error page.
* */
class DataTable extends Component {
constructor() {
super();
}
render() {
return (
<div>
Data Table
</div>
);
}
}
export default DataTable;

@ -0,0 +1,125 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import DataTableRow from './DataTableRow';
import DataTableHeader from './DataTableHeader';
import RaisedButton from 'material-ui/RaisedButton';
import {Table, TableBody, TableHeader, TableRow} from 'material-ui/Table';
/**
* The Custom Table Component.
* This component wraps the material-ui Table component and add some extra functionalities.
* 1. Table header click. (For sorting)
* 2. Table row click.
*
* The main sort function is defined in the component where the data table is created and passed to the
* DataTable component via props.
*
* Following are the DataTable proptypes.
* 1. Headers: Table headers. This is an array of Json Objects.
* An Header Object contains the properties of each header. Currently following properties
* are supported.
* * sortable: boolean : whether the table column is sortable or not.
* * sort: func : If sortable, the sort function.
* * sort: func : If sortable, the sort function.
* * sort: func : If sortable, the sort function.
* * label: String: The Table header string.
* * id: String: Unique id for header.
*
* 2. Data: The list of data that needs to be displayed in the table.
* This is also a json array of data objects.
* The Json object should contain key: value pair where the key is the header id.
*
* */
class DataTable extends Component {
constructor() {
super();
this.state = {
data: [],
headers: [],
}
};
componentWillMount() {
this.setState({data: this.props.data, headers: this.props.headers})
}
shouldComponentUpdate(nextProps, nextState) {
console.log("next Props", nextProps.data);
this.setState({data: nextProps.data});
return true;
}
/**
* Triggers when user click on table row.
* This method invokes the parent method handleRowClick, which is passed via props.
* */
_handleRowClick(id) {
this.props.handleRowClick(id);
}
render() {
const {data, headers} = this.state;
console.log(data);
let noDataContent = null;
if (this.props.noDataMessage.type === 'button') {
noDataContent = <div><RaisedButton label={this.props.noDataMessage.text}/></div>
}
if (data) {
return (<Table
selectable={ false }>
<TableHeader displaySelectAll={ false }
adjustForCheckbox={ false }>
<TableRow>
{headers.map((header) => {
return (<DataTableHeader key={header.data_id}
style={{display: 'flex'}} header={header}/>)
}
)}
</TableRow>
</TableHeader>
<TableBody>
{data.map((dataItem) =>{
return (<DataTableRow key={dataItem.id}
dataItem={dataItem}
handleClick={this._handleRowClick.bind(this)}/>)
})}
</TableBody>
</Table>)
}
return (<div>{noDataContent}</div>);
}
}
DataTable.prototypes = {
data: PropTypes.arrayOf(Object),
headers: PropTypes.arrayOf(Object),
sortData: PropTypes.func,
handleRowClick: PropTypes.func,
noDataMessage: PropTypes.object
};
export default DataTable;

@ -0,0 +1,75 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import FlatButton from 'material-ui/FlatButton';
import {TableHeaderColumn} from 'material-ui/Table';
/**
* Data Table header component.
* This component creates the header elements of the table.
* */
class DataTableHeader extends Component {
constructor() {
super();
}
/**
* The onClick function of the table header.
* Invokes the function passed in the header object.
* */
_tableHeaderClick() {
this.props.header.sort();
}
render() {
let headerCell = null;
/**
* If the header is sortable, create a button with onClick handler.
* else create a span element with label as the table header.
* */
if (this.props.header.sortable) {
headerCell = <FlatButton label={this.props.header.label}
onClick={this._tableHeaderClick.bind(this)}
style={{color: '#bdbdbd'}}/>;
} else {
headerCell = <span style={{position: 'relative',
paddingLeft: '16px',
paddingRight: '16px',
textTransform: 'uppercase',
fontWeight: 'normal',
color: '#bdbdbd',
fontSize: '14px'}}>{this.props.header.label}</span>;
}
return (
<TableHeaderColumn key={this.props.header.id} style={{paddingLeft: '0px'}} >
{headerCell}
</TableHeaderColumn>
);
}
}
DataTableHeader.prototypes = {
header: PropTypes.object
};
export default DataTableHeader;

@ -0,0 +1,70 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {TableRow, TableRowColumn} from 'material-ui/Table';
/**
* Data table row component.
* This component created a row in the data table according to the props.
* */
class DataTableRow extends Component {
constructor() {
super();
this.state = {
dataItem: {}
}
}
componentWillMount() {
this.setState({dataItem: this.props.dataItem})
}
/**
* Triggers the click event on the data table row.
* */
_handleClick() {
this.props.handleClick(this.state.dataItem.id);
}
render() {
const {dataItem} = this.state;
return (
<TableRow key={this.props.key} onClick={this._handleClick.bind(this)} >
{Object.keys(dataItem).map((key) => {
if (key !== 'id') {
return <TableRowColumn style={{alignItems: 'center'}}
key={key}>{dataItem[key]}</TableRowColumn>
} else {
return <TableRowColumn key={key}/>
}
} )}
</TableRow>
);
}
}
DataTableRow.propTypes = {
onClick: PropTypes.func,
data: PropTypes.object
};
export default DataTableRow;

@ -17,6 +17,7 @@
*/ */
import qs from 'qs'; import qs from 'qs';
import PropTypes from 'prop-types';
import React, {Component} from 'react'; import React, {Component} from 'react';
import Checkbox from 'material-ui/Checkbox'; import Checkbox from 'material-ui/Checkbox';
import {Redirect, Switch} from 'react-router-dom'; import {Redirect, Switch} from 'react-router-dom';

@ -20,11 +20,14 @@ import Login from './User/Login/Login';
import NotFound from './Error/NotFound'; import NotFound from './Error/NotFound';
import BaseLayout from './Base/BaseLayout'; import BaseLayout from './Base/BaseLayout';
import PlatformCreate from './Platform/PlatformCreate'; import PlatformCreate from './Platform/PlatformCreate';
import PlatformListing from './Platform/PlatformListing';
import PublisherOverview from './Overview/PublisherOverview'; import PublisherOverview from './Overview/PublisherOverview';
import ApplicationCreate from './Application/ApplicationCreate'; import ApplicationCreate from './Application/ApplicationCreate';
import ApplicationListing from './Application/ApplicationListing';
/** /**
* Contains all UI components related to Application, Login and Platform * Contains all UI components related to Application, Login and Platform
*/ */
export {Login, BaseLayout, ApplicationCreate, NotFound, PublisherOverview, PlatformCreate}; export {Login, BaseLayout, ApplicationCreate, ApplicationListing, PlatformListing, NotFound, PublisherOverview,
PlatformCreate};

@ -122,5 +122,4 @@
<npm.build.command>build_prod</npm.build.command> <npm.build.command>build_prod</npm.build.command>
<npm.working.dir>./src/main/</npm.working.dir> <npm.working.dir>./src/main/</npm.working.dir>
</properties> </properties>
</project> </project>
Loading…
Cancel
Save