Completed edit release functionality in APPM UI

merge-requests/159/head
Jayasanka 5 years ago
parent 3e3d8fe1ac
commit d0ca2ec16b

@ -22,6 +22,7 @@
"react-d3-graph": "^2.0.2", "react-d3-graph": "^2.0.2",
"react-dom": "^16.8.4", "react-dom": "^16.8.4",
"react-highlight-words": "^0.16.0", "react-highlight-words": "^0.16.0",
"react-html-parser": "^2.0.2",
"react-infinite-scroller": "^1.2.4", "react-infinite-scroller": "^1.2.4",
"react-quill": "^1.3.3", "react-quill": "^1.3.3",
"react-router": "latest", "react-router": "latest",

@ -1,15 +1,371 @@
import React from 'react'; import React from 'react';
import {Drawer, Row, Col, Typography, Divider, Tag, Avatar, List, Button, Icon} from 'antd'; import {Drawer, Select, Col, Typography, Divider, Tag, notification, List, Button, Spin, message, Icon} from 'antd';
import "../../../App.css"; import "../../../App.css";
import DetailedRating from "../detailed-rating/DetailedRating"; import DetailedRating from "../detailed-rating/DetailedRating";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import axios from "axios";
import config from "../../../../public/conf/config.json";
import ReactQuill from "react-quill";
import ReactHtmlParser, {processNodes, convertNodeToElement, htmlparser2} from 'react-html-parser';
const {Text, Title, Paragraph} = Typography;
const {Text, Title} = Typography;
const {Option} = Select;
const modules = {
toolbar: [
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}],
['link']
],
};
const formats = [
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet',
'link'
];
class AppDetailsDrawer extends React.Component { class AppDetailsDrawer extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
name: null,
description: null,
globalCategories: [],
globalTags: [],
categories: [],
tags: [],
temporaryDescription: null,
temporaryCategories: [],
temporaryTags: [],
isDescriptionEditEnabled: false,
isCategoriesEditEnabled: false,
isTagsEditEnabled: false,
};
}
componentDidMount() {
this.getCategories();
this.getTags();
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.app !== this.props.app) {
const {name, description, tags, categories} = this.props.app;
this.setState({
name,
description,
tags,
categories,
isDescriptionEditEnabled: false,
isCategoriesEditEnabled: false,
isTagsEditEnabled: false,
});
}
}
getCategories = () => {
axios.get(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
{
headers: {'X-Platform': config.serverConfig.platform}
}).then(res => {
if (res.status === 200) {
const categories = JSON.parse(res.data.data);
const globalCategories = categories.map(category => {
return (
<Option
key={category.categoryName}>
{category.categoryName}
</Option>
)
});
this.setState({
globalCategories,
loading: false
});
}
}).catch((error) => {
if (error.response.status === 401) {
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({
loading: false
});
});
};
getTags = () => {
axios.get(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
{
headers: {'X-Platform': config.serverConfig.platform}
}).then(res => {
if (res.status === 200) {
const tags = JSON.parse(res.data.data);
const globalTags = tags.map(tag => {
return (
<Option
key={tag.tagName}>
{tag.tagName}
</Option>
)
});
this.setState({
globalTags,
loading: false
});
}
}).catch((error) => {
if (error.response.status === 401) {
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.warning('Something went wrong');
}
this.setState({
loading: false
});
});
};
// change the app name
handleNameSave = name => {
const {id} = this.props.app;
if (name !== this.state.name && name !== "") {
const data = {name: name};
axios.put(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
data,
{
headers: {'X-Platform': config.serverConfig.platform}
}
).then(res => {
if (res.status === 200) {
notification["success"]({
message: 'Saved!'
});
this.setState({
loading: false,
name: name,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.error('Something went wrong... :(');
}
this.setState({loading: false});
});
}
};
// handle description change
handleDescriptionChange = (temporaryDescription) => {
this.setState({temporaryDescription})
};
enableDescriptionEdit = () => {
this.setState({
isDescriptionEditEnabled: true,
temporaryDescription: this.state.description
});
};
disableDescriptionEdit = () => {
this.setState({
isDescriptionEditEnabled: false,
});
};
enableCategoriesEdit = () => {
this.setState({
isCategoriesEditEnabled: true,
temporaryCategories: this.state.categories
});
};
disableCategoriesEdit = () => {
this.setState({
isCategoriesEditEnabled: false,
});
};
// handle description change
handleCategoryChange = (temporaryCategories) => {
this.setState({temporaryCategories})
};
// change app categories
handleCategorySave = () => {
const {id} = this.props.app;
const {temporaryCategories, categories} = this.state;
console.log(temporaryCategories);
const difference = temporaryCategories
.filter(x => !categories.includes(x))
.concat(categories.filter(x => !temporaryCategories.includes(x)));
if (difference.length !== 0 && temporaryCategories.length !== 0) {
const data = {categories: temporaryCategories};
axios.put(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
data,
{
headers: {'X-Platform': config.serverConfig.platform}
}
).then(res => {
if (res.status === 200) {
notification["success"]({
message: 'Saved!'
});
this.setState({
loading: false,
categories: temporaryCategories,
isCategoriesEditEnabled: false
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.error('Something went wrong... :(');
}
this.setState({loading: false});
});
}
};
enableTagsEdit = () => {
this.setState({
isTagsEditEnabled: true,
temporaryTags: this.state.tags
});
};
disableTagsEdit = () => {
this.setState({
isTagsEditEnabled: false,
});
};
// handle description change
handleTagsChange = (temporaryTags) => {
this.setState({temporaryTags})
};
// change app categories
handleTagsSave = () => {
const {id} = this.props.app;
const {temporaryTags, tags} = this.state;
console.log(temporaryTags);
const difference = temporaryTags
.filter(x => !tags.includes(x))
.concat(tags.filter(x => !temporaryTags.includes(x)));
if (difference.length !== 0 && temporaryTags.length !== 0) {
const data = {tags: temporaryTags};
axios.put(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
data,
{
headers: {'X-Platform': config.serverConfig.platform}
}
).then(res => {
if (res.status === 200) {
notification["success"]({
message: 'Saved!'
});
this.setState({
loading: false,
tags: temporaryTags,
isTagsEditEnabled: false
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.error('Something went wrong... :(');
}
this.setState({loading: false});
});
}
};
//handle description save
handleDescriptionSave = () => {
const {id} = this.props.app;
const {description, temporaryDescription} = this.state;
if (temporaryDescription !== description && temporaryDescription !== "<p><br></p>") {
const data = {description: temporaryDescription};
axios.put(
config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/" + id,
data,
{
headers: {'X-Platform': config.serverConfig.platform}
}
).then(res => {
if (res.status === 200) {
notification["success"]({
message: 'Saved!'
});
this.setState({
loading: false,
description: temporaryDescription,
isDescriptionEditEnabled: false
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = config.serverConfig.protocol + "://" + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + '/publisher/login';
} else {
message.error('Something went wrong... :(');
}
this.setState({loading: false});
});
} else {
this.setState({isDescriptionEditEnabled: false});
}
};
render() { render() {
const {app, visible, onClose} = this.props; const {app, visible, onClose} = this.props;
const {
name, loading, description, isDescriptionEditEnabled, isCategoriesEditEnabled,
isTagsEditEnabled, temporaryDescription, temporaryCategories, temporaryTags,
globalCategories, globalTags, categories, tags
} = this.state;
if (app == null) { if (app == null) {
return null; return null;
} }
@ -23,6 +379,7 @@ class AppDetailsDrawer extends React.Component {
onClose={onClose} onClose={onClose}
visible={visible} visible={visible}
> >
<Spin spinning={loading} delay={500}>
<div style={{textAlign: "center"}}> <div style={{textAlign: "center"}}>
<img <img
style={{ style={{
@ -33,41 +390,141 @@ class AppDetailsDrawer extends React.Component {
}} }}
src={app.applicationReleases[0].iconPath} src={app.applicationReleases[0].iconPath}
/> />
<Title level={2}>{app.name}</Title> <Title editable={{onChange: this.handleNameSave}} level={2}>{name}</Title>
</div> </div>
<Divider/> <Divider/>
<Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>{app.description}</Paragraph> <Text strong={true}>Description </Text>
{!isDescriptionEditEnabled && (
<Text
style={{
color: "#1890ff",
cursor: "pointer"
}}
onClick={this.enableDescriptionEdit}>
<Icon type="edit"/>
</Text>
)}
{!isDescriptionEditEnabled && (
<div>{ReactHtmlParser(description)}</div>
)}
{isDescriptionEditEnabled && (
<div>
<ReactQuill
theme="snow"
value={temporaryDescription}
onChange={this.handleDescriptionChange}
modules={modules}
formats={formats}
placeholder="Add description"
style={{
marginBottom: 10,
marginTop: 10
}}
/>
<Button style={{marginRight: 10}} size="small" htmlType="button"
onClick={this.disableDescriptionEdit}>Cancel</Button>
<Button size="small" type="primary" htmlType="button"
onClick={this.handleDescriptionSave}>Save</Button>
</div>
)}
<Divider dashed={true}/> <Divider dashed={true}/>
<Text strong={true}>Categories</Text> <Text strong={true}>Categories </Text>
{!isCategoriesEditEnabled && (<Text
style={{color: "#1890ff", cursor: "pointer"}}
onClick={this.enableCategoriesEdit}>
<Icon type="edit"/>
</Text>
)}
<br/> <br/>
<br/> <br/>
<span> {isCategoriesEditEnabled && (
{app.categories.map(category => { <div>
<Select
mode="multiple"
style={{width: '100%'}}
placeholder="Please select categories"
onChange={this.handleCategoryChange}
value={temporaryCategories}
>
{globalCategories}
</Select>
<div style={{marginTop: 10}}>
<Button style={{marginRight: 10}} size="small" htmlType="button"
onClick={this.disableCategoriesEdit}>Cancel</Button>
<Button
size="small"
type="primary"
htmlType="button"
onClick={this.handleCategorySave}>Save</Button>
</div>
</div>
)}
{!isCategoriesEditEnabled && (
<span>{
categories.map(category => {
return ( return (
<Tag color="blue" key={category} style={{paddingBottom: 5}}> <Tag color="blue" key={category} style={{paddingBottom: 5}}>
{category} {category}
</Tag> </Tag>
); );
})} })
</span> }</span>
)}
<Divider dashed={true}/> <Divider dashed={true}/>
<Text strong={true}>Tags</Text> <Text strong={true}>Tags </Text>
{!isTagsEditEnabled && (<Text
style={{color: "#1890ff", cursor: "pointer"}}
onClick={this.enableTagsEdit}>
<Icon type="edit"/>
</Text>
)}
<br/> <br/>
<br/> <br/>
<span> {isTagsEditEnabled && (
{app.tags.map(category => { <div>
<Select
mode="tags"
style={{width: '100%'}}
placeholder="Please select categories"
onChange={this.handleTagsChange}
value={temporaryTags}
>
{globalTags}
</Select>
<div style={{marginTop: 10}}>
<Button style={{marginRight: 10}} size="small" htmlType="button"
onClick={this.disableTagsEdit}>Cancel</Button>
<Button
size="small"
type="primary"
htmlType="button"
onClick={this.handleTagsSave}>Save</Button>
</div>
</div>
)}
{!isTagsEditEnabled && (
<span>{
tags.map(tag => {
return ( return (
<Tag color="gold" key={category} style={{paddingBottom: 5}}> <Tag color="gold" key={tag} style={{paddingBottom: 5}}>
{category} {tag}
</Tag> </Tag>
); );
})} })
</span> }</span>
)}
<Divider dashed={true}/> <Divider dashed={true}/>
<Text strong={true}>Releases </Text> <Text strong={true}>Releases </Text>
{/*display add new release only if app type is enterprise*/} {/*display add new release only if app type is enterprise*/}
{(app.type ==="ENTERPRISE") && (<Link to={`/publisher/apps/${app.id}/add-release`}><Button htmlType="button" size="small">Add new release</Button></Link>)} {(app.type === "ENTERPRISE") && (
<Link to={`/publisher/apps/${app.id}/add-release`}><Button htmlType="button" size="small">Add
new release</Button></Link>)}
<br/> <br/>
<List <List
itemLayout="horizontal" itemLayout="horizontal"
@ -75,10 +532,11 @@ class AppDetailsDrawer extends React.Component {
renderItem={release => ( renderItem={release => (
<List.Item> <List.Item>
<List.Item.Meta <List.Item.Meta
title={<a href={"apps/releases/"+release.uuid}>{release.version}</a>} title={<a href={"apps/releases/" + release.uuid}>{release.version}</a>}
description={ description={
<div> <div>
Status : <Tag>{release.currentStatus}</Tag> Release Type <Tag color="green">{release.releaseType}</Tag> Status : <Tag>{release.currentStatus}</Tag> Release Type <Tag
color="green">{release.releaseType}</Tag>
</div> </div>
} }
/> />
@ -88,6 +546,7 @@ class AppDetailsDrawer extends React.Component {
<Divider dashed={true}/> <Divider dashed={true}/>
<DetailedRating type="app" uuid={app.applicationReleases[0].uuid}/> <DetailedRating type="app" uuid={app.applicationReleases[0].uuid}/>
</Spin>
</Drawer> </Drawer>
</div> </div>
); );

Loading…
Cancel
Save