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

Add UI improvements to APPM UI

See merge request entgra/carbon-device-mgt!281
feature/appm-store/pbac
Dharmakeerthi Lasantha 5 years ago
commit 220aba8e90

@ -1,7 +1,7 @@
{ {
"theme": { "theme": {
"logo": "https://entgra.io/assets/images/svg/logo.svg", "logo": "https://entgra.io/assets/images/svg/logo.svg",
"primaryColor": "#badc58" "primaryColor": "rgb(24, 144, 255)"
}, },
"serverConfig": { "serverConfig": {
"invoker": { "invoker": {

@ -22,6 +22,7 @@ import StarRatings from "react-star-ratings";
import "./DetailedRating.css"; import "./DetailedRating.css";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const { Text } = Typography; const { Text } = Typography;
@ -60,16 +61,7 @@ class DetailedRating extends React.Component{
} }
}).catch(function (error) { }).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load rating for the release.");
window.location.href = window.location.origin+'/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load rating for the release.",
});
}
}); });
}; };

@ -42,6 +42,7 @@ import pSBC from "shade-blend-color";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import ManagedConfigurationsIframe import ManagedConfigurationsIframe
from "../../../manage/android-enterprise/ManagedConfigurationsIframe/ManagedConfigurationsIframe"; from "../../../manage/android-enterprise/ManagedConfigurationsIframe/ManagedConfigurationsIframe";
import {handleApiError} from "../../../../js/Utils";
const {Meta} = Card; const {Meta} = Card;
const {Text, Title} = Typography; const {Text, Title} = Typography;
@ -135,16 +136,7 @@ class AppDetailsDrawer extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load app details.");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load app details.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -175,16 +167,7 @@ class AppDetailsDrawer extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load tags.");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load tags.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });

@ -34,6 +34,7 @@ import {
} from "antd"; } from "antd";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const {Option} = Select; const {Option} = Select;
const {Title} = Typography; const {Title} = Typography;
@ -92,16 +93,7 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load categories.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load categories.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -123,16 +115,7 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load tags.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load tags.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -154,16 +137,7 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load device types.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load device types.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });

@ -17,10 +17,11 @@
*/ */
import React from "react"; import React from "react";
import {Card, Col, Row, Typography, Input, Divider} from "antd"; import {Card, Col, Row, Typography, Input, Divider, notification} from "antd";
import AppsTable from "./appsTable/AppsTable"; import AppsTable from "./appsTable/AppsTable";
import Filters from "./Filters"; import Filters from "./Filters";
import AppDetailsDrawer from "./AppDetailsDrawer/AppDetailsDrawer"; import AppDetailsDrawer from "./AppDetailsDrawer/AppDetailsDrawer";
import axios from "axios";
const {Title} = Typography; const {Title} = Typography;
const Search = Input.Search; const Search = Input.Search;

@ -23,6 +23,7 @@ import pSBC from 'shade-blend-color';
import "./AppsTable.css"; import "./AppsTable.css";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import AppDetailsDrawer from "../AppDetailsDrawer/AppDetailsDrawer"; import AppDetailsDrawer from "../AppDetailsDrawer/AppDetailsDrawer";
import {handleApiError} from "../../../../js/Utils";
let config = null; let config = null;
@ -214,18 +215,7 @@ class AppsTable extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load apps.");
message.error('You are not logged in');
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load the apps.",
});
}
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };

@ -117,7 +117,7 @@ class EditReleaseModal extends React.Component {
showModal = () => { showModal = () => {
const {release} = this.props; const {app, release} = this.props;
const {formConfig} = this.state; const {formConfig} = this.state;
const {specificElements} = formConfig; const {specificElements} = formConfig;
let metaData = []; let metaData = [];
@ -143,6 +143,14 @@ class EditReleaseModal extends React.Component {
} }
}); });
// if (specificElements.hasOwnProperty("packageName")) {
// this.props.form.setFields({
// packageName: {
// value: app.packageName
// }
// });
// }
if (specificElements.hasOwnProperty("version")) { if (specificElements.hasOwnProperty("version")) {
this.props.form.setFields({ this.props.form.setFields({
version: { version: {
@ -238,10 +246,10 @@ class EditReleaseModal extends React.Component {
if (specificElements.hasOwnProperty("url")) { if (specificElements.hasOwnProperty("url")) {
release.url = values.url; release.url = values.url;
} }
//
if (specificElements.hasOwnProperty("packageName")) { // if (specificElements.hasOwnProperty("packageName")) {
release.packageName = values.packageName; // release.packageName = values.packageName;
} // }
if (icons.length === 1) { if (icons.length === 1) {
data.append('icon', icons[0].originFileObj); data.append('icon', icons[0].originFileObj);
@ -362,18 +370,18 @@ class EditReleaseModal extends React.Component {
</Form.Item> </Form.Item>
)} )}
{formConfig.specificElements.hasOwnProperty("packageName") && ( {/*{formConfig.specificElements.hasOwnProperty("packageName") && (*/}
<Form.Item {...formItemLayout} label="Package Name"> {/* <Form.Item {...formItemLayout} label="Package Name">*/}
{getFieldDecorator('packageName', { {/* {getFieldDecorator('packageName', {*/}
rules: [{ {/* rules: [{*/}
required: true, {/* required: true,*/}
message: 'Please input the package name' {/* message: 'Please input the package name'*/}
}], {/* }],*/}
})( {/* })(*/}
<Input placeholder="Package Name"/> {/* <Input placeholder="Package Name"/>*/}
)} {/* )}*/}
</Form.Item> {/* </Form.Item>*/}
)} {/*)}*/}
{formConfig.specificElements.hasOwnProperty("url") && ( {formConfig.specificElements.hasOwnProperty("url") && (
<Form.Item {...formItemLayout} label="URL"> <Form.Item {...formItemLayout} label="URL">

@ -24,6 +24,7 @@ import 'react-quill/dist/quill.snow.css';
import './LifeCycle.css'; import './LifeCycle.css';
import LifeCycleDetailsModal from "./lifeCycleDetailsModal/lifeCycleDetailsModal"; import LifeCycleDetailsModal from "./lifeCycleDetailsModal/lifeCycleDetailsModal";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils";
const {Text, Title, Paragraph} = Typography; const {Text, Title, Paragraph} = Typography;
const {Option} = Select; const {Option} = Select;
@ -120,15 +121,7 @@ class LifeCycle extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to add lifecycle");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "Error",
description:
"Error occurred while trying to add lifecycle",
});
}
this.setState({ this.setState({
isConfirmButtonLoading: false isConfirmButtonLoading: false
}); });

@ -1,54 +0,0 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 from "react";
import "./CustomNode.css";
/**
* Component that renders a person's name and gender, along with icons
* representing if they have a driver license for bike and / or car.
* @param {Object} props component props to render.
*/
function CustomNode({ node }) {
return (
<div className="node" style={{backgroundColor: node.color}}>
<div className="name">{node.id}</div>
{/*<div className="flex-container fill-space flex-container-row">*/}
{/*<div className="fill-space">*/}
{/*<div*/}
{/*className="icon"*/}
{/*style={{ backgroundImage: `url('${isMale ? ICON_TYPES.MAN : ICON_TYPES.WOMAN}')` }}*/}
{/*/>*/}
{/*</div>*/}
{/*<div className="icon-bar">*/}
{/*{person.hasBike && (*/}
{/*<div className="icon" style={{ backgroundImage: `url('${ICON_TYPES.BIKE}')` }} />*/}
{/*)}*/}
{/*{person.hasCar && <div className="icon" style={{ backgroundImage: `url('${ICON_TYPES.CAR}')` }} />}*/}
{/*</div>*/}
{/*</div>*/}
</div>
);
}
export default CustomNode;

@ -24,6 +24,7 @@ import InfiniteScroll from 'react-infinite-scroller';
import SingleReview from "./SingleReview"; import SingleReview from "./SingleReview";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils";
const limit = 5; const limit = 5;
@ -58,16 +59,7 @@ class Reviews extends React.Component {
} }
}).catch(function (error) { }).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load reviews.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load reviews.",
});
}
}); });
}; };

@ -1,116 +0,0 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 from "react";
import * as SRD from "storm-react-diagrams";
import "storm-react-diagrams/dist/style.min.css";
import "./LifeCycle.css";
import {distributeElements} from "../../../js/utils/dagre-utils.ts";
const inPortName = "IN";
const outPortName = "OUT";
class LifeCycleGraph extends React.Component {
render() {
const lifecycle = this.props.lifecycle;
const nodes = [];
const links = [];
const engine = new SRD.DiagramEngine();
engine.installDefaultFactories();
const model = new SRD.DiagramModel();
const nextStates = lifecycle[this.props.currentStatus].proceedingStates;
Object.keys(lifecycle).forEach((stateName) => {
let color = "rgb(83, 92, 104)";
if (stateName === this.props.currentStatus) {
color = "rgb(192,255,0)";
} else if (nextStates.includes(stateName)) {
color = "rgb(0,192,255)";
}
const node = createNode(stateName, color);
nodes.push(node);
lifecycle[stateName].node = node;
});
Object.keys(lifecycle).forEach((stateName) => {
const state = lifecycle[stateName];
//todo: remove checking property
if (state.hasOwnProperty("proceedingStates")) {
state.proceedingStates.forEach((proceedingState) => {
links.push(connectNodes(state.node, lifecycle[proceedingState].node));
});
}
});
nodes.forEach((node) => {
model.addNode(node);
// node.addListener({
// selectionChanged: (node, isSelected) => {
// console.log(isSelected);
// }
// });
});
links.forEach((link) => {
model.addLink(link);
});
let distributedModel = getDistributedModel(engine, model);
engine.setDiagramModel(distributedModel);
return (
<div style={{height: 500}}>
<SRD.DiagramWidget diagramEngine={engine} maxNumberPointsPerLink={10} smartRouting={true}/>
</div>
);
}
}
function getDistributedModel(engine, model) {
const serialized = model.serializeDiagram();
const distributedSerializedDiagram = distributeElements(serialized);
//deserialize the model
let deSerializedModel = new SRD.DiagramModel();
deSerializedModel.deSerializeDiagram(distributedSerializedDiagram, engine);
return deSerializedModel;
}
function createNode(name, color) {
const node = new SRD.DefaultNodeModel(name, color);
node.addPort(new SRD.DefaultPortModel(true, inPortName, " "));
node.addPort(new SRD.DefaultPortModel(false, outPortName, " "));
return node;
}
let count = 0;
function connectNodes(nodeFrom, nodeTo) {
return nodeFrom.getPort(outPortName).link(nodeTo.getPort(inPortName));
}
function f() {
// console.log(1);
}
export default LifeCycleGraph;

@ -75,18 +75,7 @@ class ManagedConfigurationsIframe extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load configurations.");
message.error('You are not logged in');
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load configurations.",
});
}
this.setState({loading: false, visible: false}); this.setState({loading: false, visible: false});
}); });
}; };
@ -154,18 +143,7 @@ class ManagedConfigurationsIframe extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to update configurations.");
message.error('You are not logged in');
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to update configurations.",
});
}
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };

@ -38,6 +38,7 @@ import axios from "axios";
import {TweenOneGroup} from 'rc-tween-one'; import {TweenOneGroup} from 'rc-tween-one';
import pSBC from "shade-blend-color"; import pSBC from "shade-blend-color";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const {Title} = Typography; const {Title} = Typography;
@ -69,12 +70,7 @@ class ManageCategories extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occured while trying to load categories");
window.location.href = window.location.origin+ '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -116,17 +112,7 @@ class ManageCategories extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load categories.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load categories.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -264,17 +250,7 @@ class ManageCategories extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to add categories.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add categories.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -331,17 +307,7 @@ class ManageCategories extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to delete the category.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete the category.",
});
}
this.setState({ this.setState({
loading: false, loading: false,
editingValue: null editingValue: null

@ -36,6 +36,7 @@ import {
import axios from "axios"; import axios from "axios";
import {TweenOneGroup} from 'rc-tween-one'; import {TweenOneGroup} from 'rc-tween-one';
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const {Title} = Typography; const {Title} = Typography;
@ -67,16 +68,7 @@ class ManageTags extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load tags.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load tags.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -120,17 +112,7 @@ class ManageTags extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to delete the tag.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete the tag.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -265,17 +247,7 @@ class ManageTags extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to delete tag.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete tag.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -332,17 +304,7 @@ class ManageTags extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to edit tag.");
message.error('You are not logged in');
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to edit tag.",
});
}
this.setState({ this.setState({
loading: false, loading: false,
editingValue: null editingValue: null

@ -33,6 +33,7 @@ import {withRouter} from 'react-router-dom';
import NewAppDetailsForm from "./subForms/NewAppDetailsForm"; import NewAppDetailsForm from "./subForms/NewAppDetailsForm";
import NewAppUploadForm from "./subForms/NewAppUploadForm"; import NewAppUploadForm from "./subForms/NewAppUploadForm";
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import {handleApiError} from "../../js/Utils";
const {Step} = Steps; const {Step} = Steps;
@ -103,16 +104,7 @@ class AddNewAppFormComponent extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Sorry, we were unable to complete your request.")
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "Something went wrong!",
description:
"Sorry, we were unable to complete your request.",
});
}
this.setState({ this.setState({
loading: false, loading: false,
isError: true, isError: true,

@ -1,84 +0,0 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 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;

@ -1,67 +0,0 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 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;

@ -20,6 +20,7 @@ import React from "react";
import {Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Switch, Upload} from "antd"; import {Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Switch, Upload} from "antd";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
@ -80,8 +81,6 @@ class NewAppDetailsForm extends React.Component {
componentDidMount() { componentDidMount() {
this.getCategories(); this.getCategories();
this.getTags();
this.getDeviceTypes();
} }
getCategories = () => { getCategories = () => {
@ -96,18 +95,10 @@ class NewAppDetailsForm extends React.Component {
loading: false loading: false
}); });
} }
this.getTags();
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load categories.");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load categories.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -126,18 +117,10 @@ class NewAppDetailsForm extends React.Component {
loading: false, loading: false,
}); });
} }
this.getDeviceTypes();
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load tags.");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load tags.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });
@ -180,17 +163,7 @@ class NewAppDetailsForm extends React.Component {
}).catch((error) => { }).catch((error) => {
console.log(error); handleApiError(error, "Error occurred while trying to load device types.");
if (error.hasOwnProperty("response") && error.response.status === 401) {
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load device types.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });

@ -36,6 +36,7 @@ import {
import axios from "axios"; import axios from "axios";
import {withRouter} from 'react-router-dom' import {withRouter} from 'react-router-dom'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import {handleApiError} from "../../js/Utils";
const {Option} = Select; const {Option} = Select;
const {TextArea} = Input; const {TextArea} = Input;
@ -127,16 +128,7 @@ class AddNewReleaseFormComponent extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Sorry, we were unable to complete your request.");
window.location.href = window.location.origin+ '/publisher/login';
} else {
notification["error"]({
message: "Something went wrong!",
description:
"Sorry, we were unable to complete your request.",
});
}
this.setState({ this.setState({
loading: false loading: false
}); });

@ -16,16 +16,16 @@
* under the License. * under the License.
*/ */
import {message, notification} from "antd"; import {notification} from "antd";
export const handleApiError = (error, message) => { export const handleApiError = (error, message) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in'); const redirectUrl = encodeURI(window.location.href);
window.location.href = window.location.origin + '/publisher/login'; window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
} else { } else {
notification["error"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 0, duration: 10,
description: message, description: message,
}); });
} }

@ -22,6 +22,7 @@ import './Login.css';
import axios from 'axios'; import axios from 'axios';
import "./Login.css"; import "./Login.css";
import {withConfigContext} from "../context/ConfigContext"; import {withConfigContext} from "../context/ConfigContext";
import {handleApiError} from "../js/Utils";
const {Title} = Typography; const {Title} = Typography;
const {Text} = Typography; const {Text} = Typography;
@ -98,21 +99,15 @@ class NormalLoginForm extends React.Component {
axios.post(window.location.origin+ config.serverConfig.loginUri, request axios.post(window.location.origin+ config.serverConfig.loginUri, request
).then(res=>{ ).then(res=>{
if (res.status === 200) { if (res.status === 200) {
window.location = window.location.origin+"/publisher"; let redirectUrl = window.location.origin+"/publisher";
const searchParams = new URLSearchParams(window.location.search);
if(searchParams.has("redirect")){
redirectUrl = searchParams.get("redirect");
}
window.location = redirectUrl;
} }
}).catch(function (error) { }).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 400) { handleApiError(error, "Error occurred while trying to login.");
thisForm.setState({
inValid: true
});
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to login.",
});
}
thisForm.setState({ thisForm.setState({
loading: false loading: false
}); });
@ -162,7 +157,7 @@ class NormalLoginForm extends React.Component {
)} )}
<br/> <br/>
<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 loading={this.state.loading} block type="primary" htmlType="submit" className="login-form-button">
Log in Log in
</Button> </Button>
</Form.Item> </Form.Item>

@ -81,12 +81,12 @@ class Dashboard extends React.Component {
> >
<Menu.Item key="add-new-public-app"> <Menu.Item key="add-new-public-app">
<Link to="/publisher/add-new-app/public"> <Link to="/publisher/add-new-app/public">
Public APP Public App
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="add-new-enterprise-app"> <Menu.Item key="add-new-enterprise-app">
<Link to="/publisher/add-new-app/enterprise"> <Link to="/publisher/add-new-app/enterprise">
Enterprise APP Enterprise App
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="add-new-web-clip"> <Menu.Item key="add-new-web-clip">
@ -94,6 +94,11 @@ class Dashboard extends React.Component {
Web Clip Web Clip
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="add-new-custom-app">
<Link to="/publisher/add-new-app/custom-app">
Custom App
</Link>
</Menu.Item>
</SubMenu> </SubMenu>
<SubMenu <SubMenu

@ -1,67 +0,0 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 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;

@ -23,6 +23,7 @@ import axios from 'axios';
import ReleaseView from "../../../../components/apps/release/ReleaseView"; import ReleaseView from "../../../../components/apps/release/ReleaseView";
import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle"; import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils";
const {Title} = Typography; const {Title} = Typography;
@ -87,18 +88,7 @@ class Release extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load the release.");
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load the release.",
});
}
this.setState({loading: false}); this.setState({loading: false});
}); });
@ -118,16 +108,7 @@ class Release extends React.Component {
} }
}).catch(function (error) { }).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to load lifecycle configuration.");
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load lifecycle configuration.",
});
}
}); });
}; };

@ -52,7 +52,6 @@ class Logout extends React.Component {
window.location = window.location.origin + "/publisher/login"; window.location = window.location.origin + "/publisher/login";
} }
}).catch(function (error) { }).catch(function (error) {
if (error.hasOwnProperty("response") && error.response.status === 400) { if (error.hasOwnProperty("response") && error.response.status === 400) {
thisForm.setState({ thisForm.setState({
inValid: true inValid: true

@ -37,6 +37,7 @@ import {withConfigContext} from "../../../../../context/ConfigContext";
import axios from "axios"; import axios from "axios";
import Cluster from "../../../../../components/manage/android-enterprise/Pages/Cluster/Cluster"; import Cluster from "../../../../../components/manage/android-enterprise/Pages/Cluster/Cluster";
import EditLinks from "../../../../../components/manage/android-enterprise/Pages/EditLinks/EditLinks"; import EditLinks from "../../../../../components/manage/android-enterprise/Pages/EditLinks/EditLinks";
import {handleApiError} from "../../../../../js/Utils";
const {Option} = Select; const {Option} = Select;
const {Title, Text} = Typography; const {Title, Text} = Typography;
@ -110,18 +111,7 @@ class Page extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Error occurred while trying to save the page name.");
message.error('You are not logged in');
window.location.href = window.location.origin + '/publisher/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to save the page name.",
});
}
this.setState({loading: false}); this.setState({loading: false});
}); });
} }

@ -15,25 +15,42 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
.app-icon {
min-width: 100%;
height: auto;
}
.content {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* --- Shape for the nodes --- */ .box {
position: relative;
width: 100%; /* desired width */
}
.node { .box:before {
width: 75px; content: "";
height: 30px; display: block;
border-radius: 20% 20% 20% 20%; padding-top: 100%; /* initial ratio of 1:1*/
overflow: hidden;
box-sizing: border-box;
display: flex;
} }
.release .release-icon {
margin-right: 15px;
}
.release .release-icon img {
width: 100%;
border-radius: 28%;
}
.node .name { .release .app-name{
padding: 5%; white-space: nowrap;
font-size: 0.5rem; overflow: hidden;
font-weight: bold; width: 100%;
text-align: center; text-overflow: ellipsis;
text-transform: uppercase;
color: white;
} }

@ -19,7 +19,7 @@
import {Card, Typography, Col, Row} from 'antd'; import {Card, Typography, Col, Row} from 'antd';
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import "../../App.css"; import "./AppCard.css";
import StarRatings from 'react-star-ratings'; import StarRatings from 'react-star-ratings';
const {Meta} = Card; const {Meta} = Card;
@ -29,11 +29,6 @@ class AppCard extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleReleasesClick = this.handleReleasesClick.bind(this);
}
handleReleasesClick() {
this.props.openReleasesModal(this.props.app);
} }
render() { render() {
@ -45,12 +40,17 @@ class AppCard extends React.Component {
<Link to={"/store/"+app.deviceType+"/apps/" + release.uuid}> <Link to={"/store/"+app.deviceType+"/apps/" + release.uuid}>
<Row className="release"> <Row className="release">
<Col span={24} className="release-icon"> <Col span={24} className="release-icon">
<img src={release.iconPath} alt="icon"/> <div className='box'>
<div className='content'>
<img className='app-icon' src={release.iconPath} alt="icon"/>
</div>
</div>
{/*<img src={release.iconPath} alt="icon"/>*/}
{/*<Avatar shape="square" size={128} src={release.iconPath} />*/} {/*<Avatar shape="square" size={128} src={release.iconPath} />*/}
</Col> </Col>
<Col span={24}> <Col span={24} style={{paddingTop:10}}>
<Text strong level={4}>{app.name}</Text><br/> <Text className="app-name" strong level={4}>{app.name}</Text><br/>
<Text type="secondary" level={4}>{app.deviceType}</Text><br/> <Text type="secondary" level={4}>{app.subMethod.toLowerCase()}</Text><br/>
<StarRatings <StarRatings
rating={app.rating} rating={app.rating}
starRatedColor="#777" starRatedColor="#777"

@ -42,7 +42,7 @@ class ReleaseView extends React.Component {
} }
} }
appOperation = (type, payload, operation) => { appOperation = (type, payload, operation, timestamp=null) => {
const config = this.props.context; const config = this.props.context;
const release = this.props.app.applicationReleases[0]; const release = this.props.app.applicationReleases[0];
const {uuid} = release; const {uuid} = release;
@ -50,7 +50,11 @@ class ReleaseView extends React.Component {
this.setState({ this.setState({
loading: true, loading: true,
}); });
const url = window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + type + "/" + operation; let url = window.location.origin+ config.serverConfig.invoker.uri +
config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + type + "/" + operation;
if(timestamp!= null){
url += `?timestamp=${timestamp}`;
}
axios.post( axios.post(
url, url,
payload, payload,
@ -61,7 +65,8 @@ class ReleaseView extends React.Component {
if (res.status === 200) { if (res.status === 200) {
this.setState({ this.setState({
loading: false, loading: false,
appInstallModalVisible: false appInstallModalVisible: false,
appUnInstallModalVisible: false,
}); });
notification["success"]({ notification["success"]({
message: 'Done!', message: 'Done!',

@ -18,13 +18,14 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Button, message, notification, Table, Typography} from "antd"; import {Button, message, DatePicker, Table, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const columns = [ const columns = [
@ -109,7 +110,9 @@ class DeviceInstall extends React.Component {
data: [], data: [],
pagination: {}, pagination: {},
loading: false, loading: false,
selectedRows: [] selectedRows: [],
scheduledTime: null,
isScheduledInstallVisible: false
}; };
} }
@ -163,11 +166,10 @@ class DeviceInstall extends React.Component {
data: res.data.data.devices, data: res.data.data.devices,
pagination, pagination,
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.");
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };
@ -187,7 +189,7 @@ class DeviceInstall extends React.Component {
}); });
}; };
install = () => { install = (timestamp=null) => {
const {selectedRows} = this.state; const {selectedRows} = this.state;
const payload = []; const payload = [];
selectedRows.map(device => { selectedRows.map(device => {
@ -196,11 +198,11 @@ class DeviceInstall extends React.Component {
type: device.type type: device.type
}); });
}); });
this.props.onInstall("devices", payload, "install"); this.props.onInstall("devices", payload, "install",timestamp);
}; };
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows, scheduledTime,isScheduledInstallVisible} = this.state;
return ( return (
<div> <div>
<Text> <Text>
@ -224,12 +226,7 @@ class DeviceInstall extends React.Component {
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}} scroll={{x: 1000}}
/> />
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={selectedRows.length === 0}/>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary"
onClick={this.install}>
Install
</Button>
</div>
</div> </div>
); );
} }

@ -18,13 +18,14 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Button,Table, Typography} from "antd"; import {Button, Select, Table, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const columns = [ const columns = [
@ -115,8 +116,8 @@ class DeviceUninstall extends React.Component {
rowSelection = { rowSelection = {
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
this.setState({ this.setState({
selectedRows: selectedRows selectedRows: selectedRows
}) })
}, },
getCheckboxProps: record => ({ getCheckboxProps: record => ({
disabled: record.name === 'Disabled User', // Column configuration not to be checked disabled: record.name === 'Disabled User', // Column configuration not to be checked
@ -151,7 +152,7 @@ class DeviceUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( axios.get(
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/"+ window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
"/devices?" + encodedExtraParams, "/devices?" + encodedExtraParams,
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -163,7 +164,7 @@ class DeviceUninstall extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.");
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };
@ -172,59 +173,54 @@ class DeviceUninstall extends React.Component {
const pager = {...this.state.pagination}; const pager = {...this.state.pagination};
pager.current = pagination.current; pager.current = pagination.current;
this.setState({ this.setState({
pagination: pager, pagination: pager,
}); });
this.fetch({ this.fetch({
results: pagination.pageSize, results: pagination.pageSize,
page: pagination.current, page: pagination.current,
sortField: sorter.field, sortField: sorter.field,
sortOrder: sorter.order, sortOrder: sorter.order,
...filters, ...filters,
}); });
}; };
uninstall = () => { uninstall = (timestamp = null) => {
const {selectedRows} = this.state; const {selectedRows} = this.state;
const payload = []; const payload = [];
selectedRows.map(device => { selectedRows.map(device => {
payload.push({ payload.push({
id: device.deviceIdentifier, id: device.deviceIdentifier,
type: device.type type: device.type
}); });
}); });
this.props.onUninstall("devices", payload, "uninstall"); this.props.onUninstall("devices", payload, "uninstall", null);
}; };
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows} = this.state;
return ( return (
<div> <div>
<Text> <Text>
Start uninstalling the application for devices by selecting the corresponding devices. Start uninstalling the application for devices by selecting the corresponding devices.
Select uninstall to automatically start uninstalling the application for the respective devices. Select uninstall to automatically start uninstalling the application for the respective devices.
</Text> </Text>
<Table <Table
style={{paddingTop: 20}} style={{paddingTop: 20}}
columns={columns} columns={columns}
rowKey={record => record.deviceIdentifier} rowKey={record => record.deviceIdentifier}
dataSource={data} dataSource={data}
pagination={{ pagination={{
...pagination, ...pagination,
size: "small", size: "small",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices` showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
}} }}
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}} scroll={{x: 1000}}
/> />
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={selectedRows.length === 0}/>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary" </div>
onClick={this.uninstall}>
Uninstall
</Button>
</div>
</div>
); );
} }
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -112,9 +113,7 @@ class GroupInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -75,19 +76,19 @@ class GroupUninstall extends React.Component {
handleChange = value => { handleChange = value => {
this.setState({ this.setState({
value, value,
data: [], data: [],
fetching: false, fetching: false,
}); });
}; };
install = () =>{ uninstall = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("group", data, "uninstall"); this.props.onUninstall("group", data, "uninstall",null);
}; };
render() { render() {
@ -108,15 +109,12 @@ class GroupUninstall extends React.Component {
filterOption={false} filterOption={false}
onSearch={this.fetchUser} onSearch={this.fetchUser}
onChange={this.handleChange} onChange={this.handleChange}
style={{width: '100%'}} style={{width: '100%'}}>
>
{data.map(d => ( {data.map(d => (
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -79,13 +80,13 @@ class RoleInstall extends React.Component {
}); });
}; };
install = () =>{ install = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("role", data, "install"); this.props.onInstall("role", data, "install", timestamp);
}; };
render() { render() {
@ -112,9 +113,7 @@ class RoleInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -80,17 +81,16 @@ class RoleUninstall extends React.Component {
}); });
}; };
install = () =>{ uninstall = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("role", data, "uninstall"); this.props.onUninstall("role", data, "uninstall",null);
}; };
render() { render() {
const {fetching, data, value} = this.state; const {fetching, data, value} = this.state;
return ( return (
@ -113,9 +113,7 @@ class RoleUninstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -81,13 +82,13 @@ class UserInstall extends React.Component {
}); });
}; };
install = () => { install = (timestamp=null) => {
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val => { value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("user", data, "install"); this.props.onInstall("user", data, "install",timestamp);
}; };
render() { render() {
@ -112,9 +113,7 @@ class UserInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -49,9 +50,8 @@ class UserUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( axios.get(
window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
"/USER?", "/USER?",
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
if (fetchId !== this.lastFetchId) { if (fetchId !== this.lastFetchId) {
@ -67,54 +67,53 @@ class UserUninstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load users."); handleApiError(error, "Error occurred while trying to load users.");
this.setState({fetching: false}); this.setState({fetching: false});
}); });
}; };
handleChange = value => { handleChange = value => {
this.setState({ this.setState({
value, value,
data: [], data: [],
fetching: false, fetching: false,
}); });
}; };
uninstall = () => { uninstall = (timestamp=null) => {
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val => { value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onUninstall("user", data, "uninstall"); this.props.onUninstall("user", data, "uninstall",null);
}; };
render() { render() {
const {fetching, data, value} = this.state; const {fetching, data, value} = this.state;
return ( return (
<div> <div>
<Text>Start uninstalling the application for one or more users by entering the corresponding user name. Select uninstall to automatically start uninstalling the application for the respective user/users. </Text> <Text>Start uninstalling the application for one or more users by entering the corresponding user name.
<p>Select users</p> Select uninstall to automatically start uninstalling the application for the respective
<Select user/users. </Text>
mode="multiple" <p>Select users</p>
labelInValue <Select
value={value} mode="multiple"
placeholder="Enter the username" labelInValue
notFoundContent={fetching ? <Spin size="small"/> : null} value={value}
filterOption={false} placeholder="Enter the username"
onSearch={this.fetchUser} notFoundContent={fetching ? <Spin size="small"/> : null}
onChange={this.handleChange} filterOption={false}
style={{width: '100%'}} onSearch={this.fetchUser}
> onChange={this.handleChange}
{data.map(d => ( style={{width: '100%'}}>
<Option key={d.value}>{d.text}</Option> {data.map(d => (
))} <Option key={d.value}>{d.text}</Option>
</Select> ))}
<div style={{paddingTop: 10, textAlign: "right"}}> </Select>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.uninstall}>Uninstall</Button> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length === 0}/>
</div> </div>
</div>
); );
} }
} }

@ -0,0 +1,94 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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 from "react";
import {Button, DatePicker} from "antd";
class InstallModalFooter extends React.Component{
constructor(props) {
super(props);
this.state={
scheduledTime: null,
isScheduledInstallVisible: false
}
}
onDateTimeChange = (value, dateString) => {
this.setState({
scheduledTime: dateString
});
};
showScheduledInstall = ()=>{
this.setState({
isScheduledInstallVisible: true
})
};
hideScheduledInstall = ()=>{
this.setState({
isScheduledInstallVisible: false
})
};
render() {
const {scheduledTime,isScheduledInstallVisible} =this.state;
const {disabled, type} = this.props;
return (
<div>
<div style={{
textAlign: "right",
display: (!isScheduledInstallVisible)?'block':'none'
}}>
<Button style={{margin: 5}} disabled={disabled} htmlType="button" type="primary"
onClick={this.props.operation}>
{type}
</Button>
<Button style={{margin: 5}} disabled={disabled} htmlType="button"
onClick={this.showScheduledInstall}>
Scheduled {type}
</Button>
</div>
<div style={{
textAlign: "right",
display: (isScheduledInstallVisible)?'block':'none'
}}>
<DatePicker showTime
placeholder="Select Time"
format="YYYY-MM-DDTHH:mm"
onChange={this.onDateTimeChange}/>
<Button disabled={scheduledTime == null}
style={{margin: 5}}
htmlType="button"
type="primary"
onClick={()=>{
this.props.operation(scheduledTime);
}}>
Schedule
</Button>
<Button style={{margin: 5}} htmlType="button"
onClick={this.hideScheduledInstall}>
Cancel
</Button>
</div>
</div>
);
}
}
export default InstallModalFooter;

@ -25,7 +25,7 @@ export const handleApiError = (error, message) => {
} else { } else {
notification["error"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 0, duration: 2,
description: message, description: message,
}); });
} }

Loading…
Cancel
Save