2018-07-09 15:46:31 +00:00
|
|
|
/** @format */
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2018-08-01 12:21:51 +00:00
|
|
|
import { __, sprintf } from '@wordpress/i18n';
|
2019-03-25 09:08:25 +00:00
|
|
|
import classnames from 'classnames';
|
2018-07-20 18:24:39 +00:00
|
|
|
import { Component, Fragment } from '@wordpress/element';
|
2018-12-12 13:35:56 +00:00
|
|
|
import { compose } from '@wordpress/compose';
|
2018-07-25 14:25:08 +00:00
|
|
|
import Gridicon from 'gridicons';
|
2018-08-01 12:21:51 +00:00
|
|
|
import interpolateComponents from 'interpolate-components';
|
2019-03-25 09:08:25 +00:00
|
|
|
import { get, noop, isNull } from 'lodash';
|
2018-12-12 13:35:56 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2019-03-21 11:35:46 +00:00
|
|
|
import { withDispatch } from '@wordpress/data';
|
2018-07-09 15:46:31 +00:00
|
|
|
|
|
|
|
/**
|
2018-12-12 13:35:56 +00:00
|
|
|
* WooCommerce dependencies
|
2018-07-09 15:46:31 +00:00
|
|
|
*/
|
2018-09-21 15:19:05 +00:00
|
|
|
import {
|
2018-12-12 13:35:56 +00:00
|
|
|
EmptyContent,
|
2018-09-21 15:19:05 +00:00
|
|
|
Gravatar,
|
|
|
|
Link,
|
|
|
|
ProductImage,
|
|
|
|
ReviewRating,
|
|
|
|
Section,
|
|
|
|
SplitButton,
|
|
|
|
} from '@woocommerce/components';
|
2018-07-09 15:46:31 +00:00
|
|
|
|
2018-12-12 13:35:56 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { ActivityCard, ActivityCardPlaceholder } from '../activity-card';
|
|
|
|
import ActivityHeader from '../activity-header';
|
2019-03-28 22:20:17 +00:00
|
|
|
import { DEFAULT_REVIEW_STATUSES, QUERY_DEFAULTS } from 'wc-api/constants';
|
2018-12-12 13:35:56 +00:00
|
|
|
import sanitizeHTML from 'lib/sanitize-html';
|
|
|
|
import withSelect from 'wc-api/with-select';
|
2018-08-01 12:21:51 +00:00
|
|
|
|
2018-07-09 15:46:31 +00:00
|
|
|
class ReviewsPanel extends Component {
|
2019-03-21 11:35:46 +00:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.mountTime = new Date().getTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
const userDataFields = {
|
|
|
|
[ 'activity_panel_reviews_last_read' ]: this.mountTime,
|
|
|
|
};
|
|
|
|
this.props.updateCurrentUserData( userDataFields );
|
|
|
|
}
|
|
|
|
|
|
|
|
renderReview( review, props ) {
|
|
|
|
const { lastRead } = props;
|
2018-12-12 13:35:56 +00:00
|
|
|
const product =
|
|
|
|
( review && review._embedded && review._embedded.up && review._embedded.up[ 0 ] ) || null;
|
|
|
|
|
|
|
|
if ( isNull( product ) ) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-08-01 12:21:51 +00:00
|
|
|
|
|
|
|
const title = interpolateComponents( {
|
|
|
|
mixedString: sprintf(
|
|
|
|
__(
|
|
|
|
'{{productLink}}%s{{/productLink}} reviewed by {{authorLink}}%s{{/authorLink}}',
|
2019-03-13 17:14:02 +00:00
|
|
|
'woocommerce-admin'
|
2018-08-01 12:21:51 +00:00
|
|
|
),
|
|
|
|
product.name,
|
|
|
|
review.reviewer
|
|
|
|
),
|
|
|
|
components: {
|
|
|
|
productLink: <Link href={ product.permalink } type="external" />,
|
|
|
|
authorLink: <Link href={ 'mailto:' + review.reviewer_email } type="external" />,
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
|
|
|
const subtitle = (
|
2018-07-20 18:24:39 +00:00
|
|
|
<Fragment>
|
2018-08-01 12:21:51 +00:00
|
|
|
<ReviewRating review={ review } />
|
|
|
|
{ review.verified && (
|
|
|
|
<span className="woocommerce-review-activity-card__verified">
|
|
|
|
<Gridicon icon="checkmark" size={ 18 } />
|
2019-03-13 17:14:02 +00:00
|
|
|
{ __( 'Verified customer', 'woocommerce-admin' ) }
|
2018-08-01 12:21:51 +00:00
|
|
|
</span>
|
|
|
|
) }
|
|
|
|
</Fragment>
|
|
|
|
);
|
2018-07-25 14:25:08 +00:00
|
|
|
|
2019-03-25 09:08:25 +00:00
|
|
|
const productImage = get( product, [ 'images', 0 ] ) || get( product, [ 'image' ] );
|
|
|
|
const productImageClasses = classnames(
|
|
|
|
'woocommerce-review-activity-card__image-overlay__product',
|
|
|
|
{
|
|
|
|
'is-placeholder': ! productImage || ! productImage.src,
|
|
|
|
}
|
|
|
|
);
|
2018-08-01 12:21:51 +00:00
|
|
|
const icon = (
|
|
|
|
<div className="woocommerce-review-activity-card__image-overlay">
|
|
|
|
<Gravatar user={ review.reviewer_email } size={ 24 } />
|
2019-03-25 09:08:25 +00:00
|
|
|
<div className={ productImageClasses }>
|
|
|
|
<ProductImage product={ product } />
|
|
|
|
</div>
|
2018-08-01 12:21:51 +00:00
|
|
|
</div>
|
|
|
|
);
|
2018-07-25 14:25:08 +00:00
|
|
|
|
2018-08-01 12:21:51 +00:00
|
|
|
const cardActions = () => {
|
|
|
|
const mainLabel =
|
2019-03-13 17:14:02 +00:00
|
|
|
'approved' === review.status
|
|
|
|
? __( 'Unapprove', 'woocommerce-admin' )
|
|
|
|
: __( 'Approve', 'woocommerce-admin' );
|
2018-08-01 12:21:51 +00:00
|
|
|
return (
|
2018-07-25 14:25:08 +00:00
|
|
|
<SplitButton
|
2018-08-01 12:21:51 +00:00
|
|
|
mainLabel={ mainLabel }
|
2019-03-13 17:14:02 +00:00
|
|
|
menuLabel={ __( 'Select an action', 'woocommerce-admin' ) }
|
2018-08-01 12:21:51 +00:00
|
|
|
onClick={ noop }
|
2018-07-25 14:25:08 +00:00
|
|
|
controls={ [
|
|
|
|
{
|
2019-03-13 17:14:02 +00:00
|
|
|
label: __( 'Reply', 'woocommerce-admin' ),
|
2018-08-01 12:21:51 +00:00
|
|
|
onClick: noop,
|
2018-07-25 14:25:08 +00:00
|
|
|
},
|
|
|
|
{
|
2019-03-13 17:14:02 +00:00
|
|
|
label: __( 'Spam', 'woocommerce-admin' ),
|
2018-08-01 12:21:51 +00:00
|
|
|
onClick: noop,
|
2018-07-25 14:25:08 +00:00
|
|
|
},
|
|
|
|
{
|
2019-03-13 17:14:02 +00:00
|
|
|
label: __( 'Trash', 'woocommerce-admin' ),
|
2018-08-01 12:21:51 +00:00
|
|
|
onClick: noop,
|
2018-07-25 14:25:08 +00:00
|
|
|
},
|
|
|
|
] }
|
|
|
|
/>
|
2018-08-01 12:21:51 +00:00
|
|
|
);
|
|
|
|
};
|
2018-07-25 14:25:08 +00:00
|
|
|
|
2018-08-01 12:21:51 +00:00
|
|
|
return (
|
|
|
|
<ActivityCard
|
|
|
|
className="woocommerce-review-activity-card"
|
|
|
|
key={ review.id }
|
|
|
|
title={ title }
|
|
|
|
subtitle={ subtitle }
|
2019-04-09 07:50:00 +00:00
|
|
|
date={ review.date_created_gmt }
|
2018-08-01 12:21:51 +00:00
|
|
|
icon={ icon }
|
|
|
|
actions={ cardActions() }
|
2019-03-21 11:35:46 +00:00
|
|
|
unread={
|
2019-03-28 22:20:17 +00:00
|
|
|
review.status === 'hold' ||
|
2019-03-21 11:35:46 +00:00
|
|
|
! lastRead ||
|
|
|
|
! review.date_created_gmt ||
|
|
|
|
new Date( review.date_created_gmt + 'Z' ).getTime() > lastRead
|
|
|
|
}
|
2018-08-01 12:21:51 +00:00
|
|
|
>
|
2018-12-12 13:35:56 +00:00
|
|
|
<span dangerouslySetInnerHTML={ sanitizeHTML( review.review ) } />
|
2018-08-01 12:21:51 +00:00
|
|
|
</ActivityCard>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
renderPlaceholders() {
|
|
|
|
const { numberOfReviews } = this.props;
|
|
|
|
const placeholders = new Array( numberOfReviews );
|
|
|
|
return placeholders
|
|
|
|
.fill( 0 )
|
|
|
|
.map( ( p, i ) => (
|
|
|
|
<ActivityCardPlaceholder
|
|
|
|
className="woocommerce-review-activity-card"
|
|
|
|
key={ i }
|
|
|
|
hasAction
|
|
|
|
hasDate
|
|
|
|
lines={ 2 }
|
|
|
|
/>
|
|
|
|
) );
|
|
|
|
}
|
|
|
|
|
2018-08-01 12:21:51 +00:00
|
|
|
render() {
|
2018-12-12 13:35:56 +00:00
|
|
|
const { isError, isRequesting, reviews } = this.props;
|
|
|
|
|
|
|
|
if ( isError ) {
|
2019-03-13 17:14:02 +00:00
|
|
|
const title = __(
|
|
|
|
'There was an error getting your reviews. Please try again.',
|
|
|
|
'woocommerce-admin'
|
|
|
|
);
|
|
|
|
const actionLabel = __( 'Reload', 'woocommerce-admin' );
|
2018-12-12 13:35:56 +00:00
|
|
|
const actionCallback = () => {
|
|
|
|
window.location.reload();
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<EmptyContent
|
|
|
|
title={ title }
|
|
|
|
actionLabel={ actionLabel }
|
|
|
|
actionURL={ null }
|
|
|
|
actionCallback={ actionCallback }
|
|
|
|
/>
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-08-01 12:21:51 +00:00
|
|
|
return (
|
|
|
|
<Fragment>
|
2019-03-13 17:14:02 +00:00
|
|
|
<ActivityHeader title={ __( 'Reviews', 'woocommerce-admin' ) } />
|
2018-08-01 12:21:51 +00:00
|
|
|
<Section>
|
2018-12-12 13:35:56 +00:00
|
|
|
{ isRequesting ? (
|
2019-03-21 11:35:46 +00:00
|
|
|
this.renderPlaceholders()
|
2018-08-01 12:21:51 +00:00
|
|
|
) : (
|
2019-03-21 11:35:46 +00:00
|
|
|
<Fragment>
|
|
|
|
{ reviews.map( review => this.renderReview( review, this.props ) ) }
|
|
|
|
</Fragment>
|
2018-08-01 12:21:51 +00:00
|
|
|
) }
|
|
|
|
</Section>
|
2018-07-20 18:24:39 +00:00
|
|
|
</Fragment>
|
|
|
|
);
|
2018-07-09 15:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-12 13:35:56 +00:00
|
|
|
ReviewsPanel.propTypes = {
|
|
|
|
reviews: PropTypes.array.isRequired,
|
|
|
|
isError: PropTypes.bool,
|
|
|
|
isRequesting: PropTypes.bool,
|
2019-03-21 11:35:46 +00:00
|
|
|
numberOfReviews: PropTypes.number,
|
2018-12-12 13:35:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ReviewsPanel.defaultProps = {
|
|
|
|
reviews: [],
|
|
|
|
isError: false,
|
|
|
|
isRequesting: false,
|
2019-03-21 11:35:46 +00:00
|
|
|
numberOfReviews: 0,
|
2018-12-12 13:35:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default compose(
|
2019-03-21 11:35:46 +00:00
|
|
|
withSelect( ( select, props ) => {
|
|
|
|
const { numberOfReviews } = props;
|
|
|
|
const { getCurrentUserData, getReviews, getReviewsError, isGetReviewsRequesting } = select(
|
|
|
|
'wc-api'
|
|
|
|
);
|
|
|
|
if ( numberOfReviews === 0 ) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const userData = getCurrentUserData();
|
2018-12-12 13:35:56 +00:00
|
|
|
const reviewsQuery = {
|
|
|
|
page: 1,
|
|
|
|
per_page: QUERY_DEFAULTS.pageSize,
|
2019-03-28 22:20:17 +00:00
|
|
|
status: DEFAULT_REVIEW_STATUSES,
|
2018-12-12 13:35:56 +00:00
|
|
|
_embed: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
const reviews = getReviews( reviewsQuery );
|
2018-12-15 12:38:54 +00:00
|
|
|
const isError = Boolean( getReviewsError( reviewsQuery ) );
|
2018-12-12 13:35:56 +00:00
|
|
|
const isRequesting = isGetReviewsRequesting( reviewsQuery );
|
|
|
|
|
2019-03-21 11:35:46 +00:00
|
|
|
return { reviews, isError, isRequesting, lastRead: userData.activity_panel_reviews_last_read };
|
|
|
|
} ),
|
|
|
|
withDispatch( dispatch => {
|
|
|
|
const { updateCurrentUserData } = dispatch( 'wc-api' );
|
|
|
|
|
|
|
|
return {
|
|
|
|
updateCurrentUserData,
|
|
|
|
};
|
2018-12-12 13:35:56 +00:00
|
|
|
} )
|
|
|
|
)( ReviewsPanel );
|