Add component to display reviews in APPM Store

feature/appm-store/pbac
Jayasanka Weerasinghe 6 years ago committed by Dharmakeerthi Lasantha
parent 443a083048
commit b6174243c6

@ -1,57 +0,0 @@
/*
* Copyright (c) 2018, 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.
*
*/
package org.wso2.carbon.device.application.mgt.common;
import java.io.Serializable;
/**
* This class represents the Enterprise ApplicationDTO information.
*/
public class EnterpriseApplication extends AndroidApplication implements Serializable {
private String url;
private String schedule;
private String packageName;
private static final long serialVersionUID = 660343716452348222L;
public String getSchedule() {
return schedule;
}
public void setSchedule(String schedule) {
this.schedule = schedule;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}

@ -224,6 +224,7 @@ public class ReviewManagerImpl implements ReviewManager {
review.setCreatedAt(reviewDTO.getCreatedAt());
review.setModifiedAt(reviewDTO.getModifiedAt());
review.setRating(reviewDTO.getRating());
review.setUsername(reviewDTO.getUsername());
review.setReplies(new ArrayList<>());
return review;
}

@ -1,53 +0,0 @@
/*
* Copyright (c) 2018, 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.
*
*/
package org.wso2.carbon.device.application.mgt.core.util;
import org.wso2.carbon.device.application.mgt.common.AppOperation;
import org.wso2.carbon.device.application.mgt.common.EnterpriseApplication;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation;
public class AndroidApplicationOperationUtil {
// public static Operation installApp(AppOperation appOperation) {
// ProfileOperation operation = new ProfileOperation();
// operation.setCode(MDMAppConstants.AndroidConstants.OPCODE_INSTALL_APPLICATION);
// operation.setType(Operation.Type.PROFILE);
// switch (appOperation.getApplication().getType()) {
// case "ENTERPRISE"://TODO: fix with ENUM
// EnterpriseApplication enterpriseApplication = new EnterpriseApplication();
// enterpriseApplication.setType(appOperation.getApplication().toString());
// enterpriseApplication.setUrl(application.getLocation());
// enterpriseApplication.setSchedule(appOperation.getScheduledDateTime());
// enterpriseApplication.setPackageName(appOperation.getApplication().get);
// operation.setPayLoad(enterpriseApplication.toJSON());
// break;
// case "PUBLIC":
// setOperationForPublicApp(operation, application);
// break;
// case "WEBAPP":
// setOperationForWebApp(operation, application);
// break;
// default:
// String errorMessage = "Invalid application type.";
// throw new DeviceApplicationException(errorMessage);
// }
// return operation;
// }
}

@ -56,7 +56,7 @@ import java.util.List;
@SwaggerDefinition(
info = @Info(
version = "1.0.0",
title = "Store Management Service",
title = "Review Management Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = "name", value = "ReviewManagementService"),
@ -86,7 +86,7 @@ import java.util.List;
)
@Path("/reviews")
@Api(value = "Review Management")
@Api(value = "Review Management API")
@Produces(MediaType.APPLICATION_JSON)
public interface ReviewManagementAPI {
String SCOPE = "scope";

@ -49,7 +49,7 @@ import javax.ws.rs.core.Response;
@SwaggerDefinition(
info = @Info(
version = "1.0.0",
title = "Store Management Admin Service",
title = "Admin Review Management Admin Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = "name", value = "ReviewManagementAdminService"),
@ -73,7 +73,7 @@ scopes = {
)
@Path("/admin/reviews")
@Api(value = "Review Management")
@Api(value = "Review Management Admin API")
@Produces(MediaType.APPLICATION_JSON)
public interface ReviewManagementAdminAPI {
String SCOPE = "scope";

@ -26,6 +26,7 @@
<jaxrs:serviceBeans>
<ref bean="applicationMgtServiceBean"/>
<ref bean="reviewMgtServiceBean"/>
<ref bean="reviewMgtAdminServiceBean"/>
<ref bean="subscriptionMgtServiceBean"/>
<ref bean="swaggerResource"/>
</jaxrs:serviceBeans>
@ -55,6 +56,7 @@
<bean id="applicationMgtServiceBean" class="org.wso2.carbon.device.application.mgt.store.api.services.impl.ApplicationManagementAPIImpl"/>
<bean id="reviewMgtServiceBean" class="org.wso2.carbon.device.application.mgt.store.api.services.impl.ReviewManagementAPIImpl" />
<bean id="reviewMgtAdminServiceBean" class="org.wso2.carbon.device.application.mgt.store.api.services.impl.admin.ReviewManagementAdminAPIImpl" />
<bean id="subscriptionMgtServiceBean" class="org.wso2.carbon.device.application.mgt.store.api.services.impl.SubscriptionManagementAPIImpl"/>
<bean id="jsonProvider" class="org.wso2.carbon.device.application.mgt.addons.JSONMessageHandler"/>
<bean id="multipartProvider" class="org.wso2.carbon.device.application.mgt.addons.MultipartCustomProvider"/>

@ -22,12 +22,15 @@
"react-dom": "^16.8.4",
"react-highlight-words": "^0.16.0",
"react-image-viewer-zoom": "^1.0.36",
"react-infinite-scroller": "^1.2.4",
"react-router": "latest",
"react-router-config": "^5.0.0",
"react-router-dom": "latest",
"react-scripts": "2.1.8",
"react-star-ratings": "^2.3.0",
"react-virtualized": "^9.21.1",
"redux-thunk": "^2.3.0",
"reqwest": "^2.0.5",
"storm-react-diagrams": "^5.2.1"
},
"devDependencies": {

@ -4,6 +4,7 @@ import "../../../App.css";
import ImgViewer from "../../apps/release/images/ImgViewer";
import StarRatings from "react-star-ratings";
import DetailedRating from "./DetailedRating";
import Reviews from "./review/Reviews";
const {Title, Text, Paragraph} = Typography;
class ReleaseView extends React.Component {
@ -52,6 +53,7 @@ class ReleaseView extends React.Component {
<DetailedRating uuid={release.uuid}/>
</Col>
</Row>
<Reviews uuid={release.uuid}/>
</div>
</div>
);

@ -0,0 +1,16 @@
.demo-infinite-container {
overflow: auto;
padding: 8px 24px;
}
.demo-loading-container {
position: absolute;
bottom: 40px;
width: 100%;
text-align: center;
}
.demo-loading {
position: absolute;
bottom: -40px;
left: 50%;
}

@ -0,0 +1,127 @@
import React from "react";
import {List, message, Avatar, Spin, Button} from 'antd';
import "./Reviews.css";
import InfiniteScroll from 'react-infinite-scroller';
import SingleReview from "./SingleReview";
import axios from "axios";
import config from "../../../../../public/conf/config.json";
const limit = 5;
class Reviews extends React.Component {
state = {
data: [],
loading: false,
hasMore: false,
loadMore: false
};
componentDidMount() {
this.fetchData(0, limit, res => {
this.setState({
data: res,
});
});
}
fetchData = (offset, limit, callback) => {
const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/reviews/" + this.props.uuid+"?offset="+offset+"%26limit="+limit;
axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
).then(res => {
if (res.status === 200) {
let reviews = res.data.data.data;
callback(reviews);
}
}).catch(function (error) {
if (error.response.status === 401) {
window.location.href = 'https://localhost:9443/store/login';
message.warning('Something went wrong');
}
});
};
handleInfiniteOnLoad = (count) => {
const offset = count*limit;
let data = this.state.data;
this.setState({
loading: true,
});
if (data.length > 149) {
this.setState({
hasMore: false,
loading: false,
});
return;
}
this.fetchData(offset, limit, res => {
if(res.length>0){
data = data.concat(res);
this.setState({
data,
loading: false,
});
}else {
this.setState({
hasMore: false,
loading: false
});
}
});
};
enableLoading = () => {
this.setState({
hasMore: true,
loadMore: true
});
};
render() {
const review = {
id: 2,
content: "Btw, it was clear to me that I can cancel the 1 year subscription before the free trial week and so I did. Dont understand the negative reviews about that. It has a good collection of excercises, meditations etc. You just answer 5 questions and you get challenges assigned to you. I would have liked something even more personalized. I didnt like the interface. It is a bit messy and difficult to follow your tasks. So, I didnt want to do a full-year subscription. There could be more options.",
rootParentI: -1,
immediateParentId: -1,
createdAt: "Fri, 24 May 2019 17:27:22 IST",
modifiedAt: "Fri, 24 May 2019 17:27:22 IST",
rating: 4,
replies: []
};
console.log(this.state.loadMore);
console.log(this.state.data.length);
return (
<div className="demo-infinite-container">
<InfiniteScroll
initialLoad={false}
pageStart={0}
loadMore={this.handleInfiniteOnLoad}
hasMore={!this.state.loading && this.state.hasMore}
useWindow={true}
>
<List
dataSource={this.state.data}
renderItem={item => (
<List.Item key={item.id}>
<SingleReview review={item}/>
</List.Item>
)}
>
{this.state.loading && this.state.hasMore && (
<div className="demo-loading-container">
<Spin/>
</div>
)}
</List>
</InfiniteScroll>
{!this.state.loadMore && (this.state.data.length >= limit) && (<div style={{textAlign: "center"}}>
<Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
</div>)}
</div>
);
}
}
export default Reviews;

@ -0,0 +1,46 @@
import React from "react";
import {Avatar} from "antd";
import {List,Typography} from "antd";
import StarRatings from "react-star-ratings";
const {Text, Paragraph} = Typography;
const colorList = ['#f0932b','#badc58','#6ab04c','#eb4d4b','#0abde3', '#9b59b6','#3498db','#22a6b3'];
class SingleReview extends React.Component {
render() {
const review = this.props.review;
const randomColor = colorList[Math.floor(Math.random() * (colorList.length))];
const avatarLetter = review.username.charAt(0).toUpperCase();
const content = (
<div style={{marginTop: -5}}>
<StarRatings
rating={review.rating}
starRatedColor="#777"
starDimension = "12px"
starSpacing = "2px"
numberOfStars={5}
name='rating'
/>
<Text style={{fontSize: 12, color: "#aaa"}} type="secondary"> {review.createdAt}</Text><br/>
<Paragraph ellipsis={{ rows: 3, expandable: true }} style={{color: "#777"}}>{review.content}</Paragraph>
</div>
);
return (
<div>
<List.Item.Meta
avatar={
<Avatar style={{ backgroundColor: randomColor, verticalAlign: 'middle' }} size="large">
{avatarLetter}
</Avatar>
}
title={review.username}
description={content}
/>
</div>
);
}
}
export default SingleReview;
Loading…
Cancel
Save