@graphcommerce/magento-review 2.105.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/components/AccountReviews/AccountReviews.gql.ts +4 -0
- package/components/AccountReviews/AccountReviews.graphql +5 -0
- package/components/AccountReviews/index.tsx +64 -0
- package/components/CreateProductReviewForm/CreateProductReview.gql.ts +16 -0
- package/components/CreateProductReviewForm/CreateProductReview.graphql +15 -0
- package/components/CreateProductReviewForm/ProductReviewRatingsMetadata.gql.ts +10 -0
- package/components/CreateProductReviewForm/ProductReviewRatingsMetadata.graphql +12 -0
- package/components/CreateProductReviewForm/index.tsx +231 -0
- package/components/CustomerReview/CustomerReview.gql.ts +4 -0
- package/components/CustomerReview/CustomerReview.graphql +20 -0
- package/components/CustomerReview/index.tsx +103 -0
- package/components/CustomerReview/review_star_filled.svg +7 -0
- package/components/CustomerReview/review_star_outlined.svg +7 -0
- package/components/JsonLdProductReview/JsonLdProductReview.gql.ts +16 -0
- package/components/JsonLdProductReview/JsonLdProductReview.graphql +5 -0
- package/components/JsonLdProductReview/index.ts +28 -0
- package/components/ProductReviewChip/ProductReviewSummary.gql.ts +16 -0
- package/components/ProductReviewChip/ProductReviewSummary.graphql +4 -0
- package/components/ProductReviewChip/index.tsx +45 -0
- package/components/ProductReviews/ProductReviewProductName.gql.ts +12 -0
- package/components/ProductReviews/ProductReviewProductName.graphql +9 -0
- package/components/ProductReviews/ProductReviewRatingsMetadata.gql.ts +10 -0
- package/components/ProductReviews/ProductReviewRatingsMetadata.graphql +12 -0
- package/components/ProductReviews/ProductReviews.gql.ts +16 -0
- package/components/ProductReviews/ProductReviews.graphql +22 -0
- package/components/ProductReviews/ProductReviewsPage.gql.ts +14 -0
- package/components/ProductReviews/ProductReviewsPage.graphql +10 -0
- package/components/ProductReviews/index.tsx +205 -0
- package/components/index.ts +16 -0
- package/index.ts +2 -0
- package/inject/AccountDashboardCustomerReviews.gql.ts +4 -0
- package/inject/AccountDashboardCustomerReviews.graphql +8 -0
- package/package.json +38 -0
- package/queries/AccountDashboardReviews.gql.ts +10 -0
- package/queries/AccountDashboardReviews.graphql +7 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [2.105.1](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.105.0...@graphcommerce/magento-review@2.105.1) (2021-09-27)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @graphcommerce/magento-review
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 2.105.0 (2021-09-27)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* customer-order folder structure ([b7fabd1](https://github.com/ho-nl/m2-pwa/commit/b7fabd12014b2925d0b89c21f58e9974ce1c8b40))
|
|
20
|
+
* introduced SvgImageSimple and solve issue with review chips ([931d7fd](https://github.com/ho-nl/m2-pwa/commit/931d7fdcf0faa9d2264899b72e564138215b6bd8))
|
|
21
|
+
* make separate queries folder, create injectable for account and inject reviews ([5d82a5d](https://github.com/ho-nl/m2-pwa/commit/5d82a5d9162f687c2678cce215b77eedbaf1669e))
|
|
22
|
+
* remove coupon form style was too large ([30df274](https://github.com/ho-nl/m2-pwa/commit/30df274ecdffdcebd76710a5304d6fa248e81211))
|
|
23
|
+
* **review:** make sure chip is rendered correctly ([387df34](https://github.com/ho-nl/m2-pwa/commit/387df3456973290f9ce98d47823a7c71a6d95850))
|
|
24
|
+
* SvgSimpleImage sizing didn't use rem ([1ba07a5](https://github.com/ho-nl/m2-pwa/commit/1ba07a5694bd60ad3cee2e8102814643d2a7c79d))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* **playwright:** added new playwright package to enable browser testing ([6f49ec7](https://github.com/ho-nl/m2-pwa/commit/6f49ec7595563775b96ebf21c27e39da1282e8d9))
|
|
30
|
+
* renamed all packages to use [@graphcommerce](https://github.com/graphcommerce) instead of [@reachdigital](https://github.com/reachdigital) ([491e4ce](https://github.com/ho-nl/m2-pwa/commit/491e4cec9a2686472dac36b79f999257c0811ffe))
|
|
31
|
+
* **theme:** restructured fonts and applied to home and category page ([6adf5f1](https://github.com/ho-nl/m2-pwa/commit/6adf5f11321bdfbf499125f1161c5abf5a1bfe4a))
|
|
32
|
+
* upgraded to nextjs 11 ([0053beb](https://github.com/ho-nl/m2-pwa/commit/0053beb7ef597c190add7264256a0eaec35868da))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Reverts
|
|
36
|
+
|
|
37
|
+
* Revert "chore: upgrade @apollo/client" ([55ff24e](https://github.com/ho-nl/m2-pwa/commit/55ff24ede0e56c85b8095edadadd1ec5e0b1b8d2))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Change Log
|
|
44
|
+
|
|
45
|
+
All notable changes to this project will be documented in this file. See
|
|
46
|
+
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
47
|
+
|
|
48
|
+
# [2.104.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.103.2...@graphcommerce/magento-review@2.104.0) (2021-08-12)
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
- remove coupon form style was too large
|
|
53
|
+
([30df274](https://github.com/ho-nl/m2-pwa/commit/30df274ecdffdcebd76710a5304d6fa248e81211))
|
|
54
|
+
|
|
55
|
+
### Features
|
|
56
|
+
|
|
57
|
+
- upgraded to nextjs 11
|
|
58
|
+
([0053beb](https://github.com/ho-nl/m2-pwa/commit/0053beb7ef597c190add7264256a0eaec35868da))
|
|
59
|
+
|
|
60
|
+
## [2.103.2](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.103.1...@graphcommerce/magento-review@2.103.2) (2021-08-09)
|
|
61
|
+
|
|
62
|
+
### Bug Fixes
|
|
63
|
+
|
|
64
|
+
- SvgSimpleImage sizing didn't use rem
|
|
65
|
+
([1ba07a5](https://github.com/ho-nl/m2-pwa/commit/1ba07a5694bd60ad3cee2e8102814643d2a7c79d))
|
|
66
|
+
|
|
67
|
+
### Reverts
|
|
68
|
+
|
|
69
|
+
- Revert "chore: upgrade @apollo/client"
|
|
70
|
+
([55ff24e](https://github.com/ho-nl/m2-pwa/commit/55ff24ede0e56c85b8095edadadd1ec5e0b1b8d2))
|
|
71
|
+
|
|
72
|
+
## [2.103.1](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.103.0...@graphcommerce/magento-review@2.103.1) (2021-08-09)
|
|
73
|
+
|
|
74
|
+
### Bug Fixes
|
|
75
|
+
|
|
76
|
+
- **review:** make sure chip is rendered correctly
|
|
77
|
+
([387df34](https://github.com/ho-nl/m2-pwa/commit/387df3456973290f9ce98d47823a7c71a6d95850))
|
|
78
|
+
|
|
79
|
+
# [2.103.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.102.6...@graphcommerce/magento-review@2.103.0) (2021-08-06)
|
|
80
|
+
|
|
81
|
+
### Bug Fixes
|
|
82
|
+
|
|
83
|
+
- introduced SvgImageSimple and solve issue with review chips
|
|
84
|
+
([931d7fd](https://github.com/ho-nl/m2-pwa/commit/931d7fdcf0faa9d2264899b72e564138215b6bd8))
|
|
85
|
+
|
|
86
|
+
### Features
|
|
87
|
+
|
|
88
|
+
- **theme:** restructured fonts and applied to home and category page
|
|
89
|
+
([6adf5f1](https://github.com/ho-nl/m2-pwa/commit/6adf5f11321bdfbf499125f1161c5abf5a1bfe4a))
|
|
90
|
+
|
|
91
|
+
# [2.102.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-review@2.101.3...@graphcommerce/magento-review@2.102.0) (2021-07-26)
|
|
92
|
+
|
|
93
|
+
### Features
|
|
94
|
+
|
|
95
|
+
- **playwright:** added new playwright package to enable browser testing
|
|
96
|
+
([6f49ec7](https://github.com/ho-nl/m2-pwa/commit/6f49ec7595563775b96ebf21c27e39da1282e8d9))
|
|
97
|
+
|
|
98
|
+
## 2.101.1 (2021-07-23)
|
|
99
|
+
|
|
100
|
+
### Bug Fixes
|
|
101
|
+
|
|
102
|
+
- customer-order folder structure
|
|
103
|
+
([b7fabd1](https://github.com/ho-nl/m2-pwa/commit/b7fabd12014b2925d0b89c21f58e9974ce1c8b40))
|
|
104
|
+
- make separate queries folder, create injectable for account and inject reviews
|
|
105
|
+
([5d82a5d](https://github.com/ho-nl/m2-pwa/commit/5d82a5d9162f687c2678cce215b77eedbaf1669e))
|
|
106
|
+
|
|
107
|
+
# [2.101.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-product-review@2.100.14...@graphcommerce/magento-product-review@2.101.0) (2021-07-21)
|
|
108
|
+
|
|
109
|
+
### Bug Fixes
|
|
110
|
+
|
|
111
|
+
- write review button mobile styles
|
|
112
|
+
([8f6b883](https://github.com/ho-nl/m2-pwa/commit/8f6b883fa0a513f84b7c6d8ed376b0e8d4b8a3bd))
|
|
113
|
+
|
|
114
|
+
### Features
|
|
115
|
+
|
|
116
|
+
- **reviews:** no reviews written message
|
|
117
|
+
([8ade3db](https://github.com/ho-nl/m2-pwa/commit/8ade3dbe830f5a59af09c002dfa38fa5349a4b61))
|
|
118
|
+
|
|
119
|
+
## [2.100.11](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/magento-product-review@2.100.10...@graphcommerce/magento-product-review@2.100.11) (2021-07-20)
|
|
120
|
+
|
|
121
|
+
### Bug Fixes
|
|
122
|
+
|
|
123
|
+
- ignore md files from triggering version updates
|
|
124
|
+
([4f98392](https://github.com/ho-nl/m2-pwa/commit/4f9839250b3a32d3070da5290e5efcc5e2243fba))
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type AccountReviewsFragment = { items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, product: { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> }, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { makeStyles, Theme } from '@material-ui/core'
|
|
2
|
+
import { Skeleton } from '@material-ui/lab'
|
|
3
|
+
import { SectionContainer } from '@graphcommerce/next-ui'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import CustomerReview from '../CustomerReview'
|
|
6
|
+
import { AccountReviewsFragment } from './AccountReviews.gql'
|
|
7
|
+
|
|
8
|
+
type AccountReviewsProps = AccountReviewsFragment & { loading: boolean }
|
|
9
|
+
|
|
10
|
+
const useStyles = makeStyles(
|
|
11
|
+
(theme: Theme) => ({
|
|
12
|
+
reviewsContainer: {
|
|
13
|
+
marginBottom: theme.spacings.md,
|
|
14
|
+
},
|
|
15
|
+
olderReviewsContainer: {
|
|
16
|
+
[theme.breakpoints.up('md')]: {
|
|
17
|
+
marginTop: theme.spacings.lg,
|
|
18
|
+
marginBottom: theme.spacings.lg,
|
|
19
|
+
},
|
|
20
|
+
marginTop: theme.spacings.md,
|
|
21
|
+
marginBottom: theme.spacings.md,
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
{ name: 'AccountReviews' },
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
export default function AccountReviews(props: AccountReviewsProps) {
|
|
28
|
+
const { items, loading } = props
|
|
29
|
+
const classes = useStyles()
|
|
30
|
+
const showLatestReviews = 2
|
|
31
|
+
const latestReviews = items?.slice(0, Math.min(items?.length, showLatestReviews))
|
|
32
|
+
const olderReviews = items?.slice(Math.min(items?.length, showLatestReviews), items?.length)
|
|
33
|
+
|
|
34
|
+
if (loading) {
|
|
35
|
+
return (
|
|
36
|
+
<div className={classes.reviewsContainer}>
|
|
37
|
+
<SectionContainer labelLeft='Latest'>
|
|
38
|
+
<Skeleton height={196} />
|
|
39
|
+
<Skeleton height={196} />
|
|
40
|
+
</SectionContainer>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
return (
|
|
45
|
+
<div className={classes.reviewsContainer}>
|
|
46
|
+
<SectionContainer labelLeft='Latest'>
|
|
47
|
+
{latestReviews?.map(
|
|
48
|
+
(review) => review && <CustomerReview key={review.created_at} {...review} />,
|
|
49
|
+
)}
|
|
50
|
+
</SectionContainer>
|
|
51
|
+
|
|
52
|
+
{items && items.length >= showLatestReviews + 1 && (
|
|
53
|
+
<SectionContainer
|
|
54
|
+
labelLeft='Older'
|
|
55
|
+
classes={{ sectionContainer: classes.olderReviewsContainer }}
|
|
56
|
+
>
|
|
57
|
+
{olderReviews?.map(
|
|
58
|
+
(review, i) => review && <CustomerReview key={review.created_at} {...review} />,
|
|
59
|
+
)}
|
|
60
|
+
</SectionContainer>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const CreateProductReviewDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProductReview"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"nickname"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ratings"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProductReviewRatingInput"}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sku"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"summary"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createProductReview"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"nickname"},"value":{"kind":"Variable","name":{"kind":"Name","value":"nickname"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"ratings"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ratings"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"sku"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sku"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"summary"},"value":{"kind":"Variable","name":{"kind":"Name","value":"summary"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"review"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"average_rating"}},{"kind":"Field","name":{"kind":"Name","value":"created_at"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uid"}},{"kind":"Field","name":{"kind":"Name","value":"url_key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"ratings_breakdown"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CreateProductReviewMutation, CreateProductReviewMutationVariables>;
|
|
7
|
+
export type CreateProductReviewMutationVariables = Types.Exact<{
|
|
8
|
+
nickname: Types.Scalars['String'];
|
|
9
|
+
ratings: Array<Types.Maybe<Types.ProductReviewRatingInput>> | Types.Maybe<Types.ProductReviewRatingInput>;
|
|
10
|
+
sku: Types.Scalars['String'];
|
|
11
|
+
summary: Types.Scalars['String'];
|
|
12
|
+
text: Types.Scalars['String'];
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export type CreateProductReviewMutation = { createProductReview: { review: { average_rating: number, created_at: string, nickname: string, summary: string, text: string, product: { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> }, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> } } };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
mutation CreateProductReview(
|
|
2
|
+
$nickname: String!
|
|
3
|
+
$ratings: [ProductReviewRatingInput]!
|
|
4
|
+
$sku: String!
|
|
5
|
+
$summary: String!
|
|
6
|
+
$text: String!
|
|
7
|
+
) {
|
|
8
|
+
createProductReview(
|
|
9
|
+
input: { nickname: $nickname, ratings: $ratings, sku: $sku, summary: $summary, text: $text }
|
|
10
|
+
) {
|
|
11
|
+
review {
|
|
12
|
+
...CustomerReview
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const ProductReviewRatingsMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProductReviewRatingsMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productReviewRatingsMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"values"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"value_id"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ProductReviewRatingsMetadataQuery, ProductReviewRatingsMetadataQueryVariables>;
|
|
7
|
+
export type ProductReviewRatingsMetadataQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export type ProductReviewRatingsMetadataQuery = { productReviewRatingsMetadata: { items: Array<Types.Maybe<{ id: string, name: string, values: Array<Types.Maybe<{ value: string, value_id: string }>> }>> } };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client'
|
|
2
|
+
import { Box, makeStyles, TextField, Theme, Typography } from '@material-ui/core'
|
|
3
|
+
import { Alert } from '@material-ui/lab'
|
|
4
|
+
import { ProductReviewRatingInput } from '@graphcommerce/graphql'
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
Form,
|
|
8
|
+
UseStyles,
|
|
9
|
+
responsiveVal,
|
|
10
|
+
FormActions,
|
|
11
|
+
FormRow,
|
|
12
|
+
StarRatingField,
|
|
13
|
+
} from '@graphcommerce/next-ui'
|
|
14
|
+
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
15
|
+
import { useRouter } from 'next/router'
|
|
16
|
+
import React, { useEffect, useState } from 'react'
|
|
17
|
+
import ApolloCustomerErrorAlert from '../../../magento-customer/components/ApolloCustomerError/ApolloCustomerErrorAlert'
|
|
18
|
+
import { CreateProductReviewDocument } from './CreateProductReview.gql'
|
|
19
|
+
import { ProductReviewRatingsMetadataDocument } from './ProductReviewRatingsMetadata.gql'
|
|
20
|
+
|
|
21
|
+
const useStyles = makeStyles(
|
|
22
|
+
(theme: Theme) => ({
|
|
23
|
+
ratingContainer: {
|
|
24
|
+
marginBottom: theme.spacings.xxs,
|
|
25
|
+
},
|
|
26
|
+
rating: {
|
|
27
|
+
paddingBottom: 'unset',
|
|
28
|
+
gridTemplateColumns: `minmax(${responsiveVal(60, 80)}, 0.1fr) max-content`,
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
},
|
|
31
|
+
ratingLabel: {
|
|
32
|
+
fontWeight: 'normal',
|
|
33
|
+
justifySelf: 'left',
|
|
34
|
+
},
|
|
35
|
+
submitButton: {
|
|
36
|
+
width: responsiveVal(200, 250),
|
|
37
|
+
height: responsiveVal(40, 50),
|
|
38
|
+
borderRadius: responsiveVal(20, 25),
|
|
39
|
+
},
|
|
40
|
+
cancelButton: {
|
|
41
|
+
display: 'block',
|
|
42
|
+
maxWidth: 'max-content',
|
|
43
|
+
margin: '0 auto',
|
|
44
|
+
},
|
|
45
|
+
formActions: {
|
|
46
|
+
gridAutoFlow: 'row',
|
|
47
|
+
gap: 8,
|
|
48
|
+
marginTop: theme.spacings.xxs,
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
{
|
|
52
|
+
name: 'CreateProductReviewForm',
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
type CreateProductReviewFormProps = {
|
|
57
|
+
sku: string
|
|
58
|
+
nickname?: string
|
|
59
|
+
} & UseStyles<typeof useStyles>
|
|
60
|
+
|
|
61
|
+
export default function CreateProductReviewForm(props: CreateProductReviewFormProps) {
|
|
62
|
+
const { sku, nickname } = props
|
|
63
|
+
const classes = useStyles(props)
|
|
64
|
+
const router = useRouter()
|
|
65
|
+
const [ratings, setRatings] = useState<ProductReviewRatingInput[]>([])
|
|
66
|
+
|
|
67
|
+
const { data, loading } = useQuery(ProductReviewRatingsMetadataDocument)
|
|
68
|
+
|
|
69
|
+
const form = useFormGqlMutation(
|
|
70
|
+
CreateProductReviewDocument,
|
|
71
|
+
{
|
|
72
|
+
defaultValues: { sku, nickname },
|
|
73
|
+
onBeforeSubmit: (formData) => ({
|
|
74
|
+
...formData,
|
|
75
|
+
ratings: ratings.some((r) => r.value_id === '') ? [] : ratings,
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
{ errorPolicy: 'all' },
|
|
79
|
+
)
|
|
80
|
+
const { handleSubmit, muiRegister, formState, required, error } = form
|
|
81
|
+
const submitHandler = handleSubmit(() => {})
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (loading || !data) return
|
|
85
|
+
|
|
86
|
+
// set initial state
|
|
87
|
+
if (ratings.length === 0) {
|
|
88
|
+
const reviewMetadataRatings = data.productReviewRatingsMetadata.items.map((metadata) => ({
|
|
89
|
+
id: metadata?.id ?? '',
|
|
90
|
+
value_id: '',
|
|
91
|
+
}))
|
|
92
|
+
|
|
93
|
+
setRatings(reviewMetadataRatings)
|
|
94
|
+
}
|
|
95
|
+
}, [loading, data, ratings.length])
|
|
96
|
+
|
|
97
|
+
if (!data) return <></>
|
|
98
|
+
|
|
99
|
+
if (formState.isSubmitSuccessful && data) {
|
|
100
|
+
return (
|
|
101
|
+
<>
|
|
102
|
+
<Alert severity='success' variant='standard'>
|
|
103
|
+
Thank you! Your review was successfully submitted for approval
|
|
104
|
+
</Alert>
|
|
105
|
+
<Box mt={6}>
|
|
106
|
+
<Button
|
|
107
|
+
variant='contained'
|
|
108
|
+
color='primary'
|
|
109
|
+
text='bold'
|
|
110
|
+
size='large'
|
|
111
|
+
onClick={() => router.back()}
|
|
112
|
+
>
|
|
113
|
+
Continue shopping
|
|
114
|
+
</Button>
|
|
115
|
+
</Box>
|
|
116
|
+
</>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Form onSubmit={submitHandler} noValidate>
|
|
122
|
+
<FormRow>
|
|
123
|
+
<TextField
|
|
124
|
+
variant='outlined'
|
|
125
|
+
type='text'
|
|
126
|
+
error={!!formState.errors.nickname || !!error}
|
|
127
|
+
label='Name'
|
|
128
|
+
required={required.nickname}
|
|
129
|
+
{...muiRegister('nickname', { required: required.nickname })}
|
|
130
|
+
helperText={formState.errors.nickname?.message}
|
|
131
|
+
disabled={formState.isSubmitting}
|
|
132
|
+
InputProps={{
|
|
133
|
+
readOnly: typeof nickname !== 'undefined',
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
</FormRow>
|
|
137
|
+
|
|
138
|
+
<div className={classes.ratingContainer}>
|
|
139
|
+
{data?.productReviewRatingsMetadata?.items?.map((prrvm) => (
|
|
140
|
+
<FormRow key={prrvm?.id} className={classes.rating}>
|
|
141
|
+
<Typography variant='h5' component='span' className={classes.ratingLabel}>
|
|
142
|
+
{prrvm?.name}
|
|
143
|
+
</Typography>
|
|
144
|
+
{prrvm && (
|
|
145
|
+
<StarRatingField
|
|
146
|
+
id={prrvm?.id ?? ''}
|
|
147
|
+
onChange={(id, value) => {
|
|
148
|
+
const productReviewRatingInputValue =
|
|
149
|
+
data.productReviewRatingsMetadata.items.find((meta) => meta?.id === id)?.values[
|
|
150
|
+
value - 1
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
const ratingsArr = [...ratings]
|
|
154
|
+
|
|
155
|
+
const clonedProductReviewRatingInputValue = ratingsArr.find(
|
|
156
|
+
(meta) => meta.id === id,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if (
|
|
160
|
+
!clonedProductReviewRatingInputValue ||
|
|
161
|
+
typeof productReviewRatingInputValue?.value_id === undefined
|
|
162
|
+
) {
|
|
163
|
+
console.error('Cannot find product review rating input value in local state')
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
clonedProductReviewRatingInputValue.value_id =
|
|
168
|
+
productReviewRatingInputValue?.value_id ?? ''
|
|
169
|
+
|
|
170
|
+
setRatings(ratingsArr)
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
</FormRow>
|
|
175
|
+
))}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<FormRow>
|
|
179
|
+
<TextField
|
|
180
|
+
variant='outlined'
|
|
181
|
+
type='text'
|
|
182
|
+
error={!!formState.errors.summary || !!error}
|
|
183
|
+
label='Summary'
|
|
184
|
+
required={required.summary}
|
|
185
|
+
{...muiRegister('summary', { required: required.summary })}
|
|
186
|
+
helperText={formState.errors.summary?.message}
|
|
187
|
+
disabled={formState.isSubmitting}
|
|
188
|
+
/>
|
|
189
|
+
</FormRow>
|
|
190
|
+
|
|
191
|
+
<FormRow>
|
|
192
|
+
<TextField
|
|
193
|
+
variant='outlined'
|
|
194
|
+
type='text'
|
|
195
|
+
error={!!formState.errors.text || !!error}
|
|
196
|
+
label='Review'
|
|
197
|
+
required={required.text}
|
|
198
|
+
{...muiRegister('text', { required: required.text })}
|
|
199
|
+
helperText={formState.errors.text?.message}
|
|
200
|
+
disabled={formState.isSubmitting}
|
|
201
|
+
multiline
|
|
202
|
+
rows={8}
|
|
203
|
+
rowsMax={8}
|
|
204
|
+
/>
|
|
205
|
+
</FormRow>
|
|
206
|
+
|
|
207
|
+
<FormActions className={classes.formActions}>
|
|
208
|
+
<Button
|
|
209
|
+
variant='pill'
|
|
210
|
+
color='primary'
|
|
211
|
+
text='bold'
|
|
212
|
+
type='submit'
|
|
213
|
+
size='medium'
|
|
214
|
+
className={classes.submitButton}
|
|
215
|
+
>
|
|
216
|
+
Submit review
|
|
217
|
+
</Button>
|
|
218
|
+
<Button
|
|
219
|
+
variant='text'
|
|
220
|
+
color='primary'
|
|
221
|
+
onClick={() => router.back()}
|
|
222
|
+
className={classes.cancelButton}
|
|
223
|
+
>
|
|
224
|
+
Cancel
|
|
225
|
+
</Button>
|
|
226
|
+
</FormActions>
|
|
227
|
+
|
|
228
|
+
<ApolloCustomerErrorAlert error={error} />
|
|
229
|
+
</Form>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type CustomerReviewFragment = { average_rating: number, created_at: string, nickname: string, summary: string, text: string, product: { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> }, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client'
|
|
2
|
+
import { makeStyles, Theme } from '@material-ui/core'
|
|
3
|
+
import { Image } from '@graphcommerce/image'
|
|
4
|
+
import { StoreConfigDocument } from '@graphcommerce/magento-store'
|
|
5
|
+
import { responsiveVal } from '@graphcommerce/next-ui'
|
|
6
|
+
import { CustomerReviewFragment } from './CustomerReview.gql'
|
|
7
|
+
import filledStar from './review_star_filled.svg'
|
|
8
|
+
import outlinedStar from './review_star_outlined.svg'
|
|
9
|
+
|
|
10
|
+
type CustomerReviewProps = CustomerReviewFragment
|
|
11
|
+
|
|
12
|
+
const useStyles = makeStyles(
|
|
13
|
+
(theme: Theme) => ({
|
|
14
|
+
container: {
|
|
15
|
+
padding: `${theme.spacings.md} 0 ${theme.spacings.md} 0`,
|
|
16
|
+
display: 'grid',
|
|
17
|
+
gridTemplateAreas: `
|
|
18
|
+
'image stars'
|
|
19
|
+
'image title'
|
|
20
|
+
'image text'
|
|
21
|
+
'image date'`,
|
|
22
|
+
gridTemplateColumns: `${responsiveVal(96, 196)} 1fr`,
|
|
23
|
+
gridColumnGap: theme.spacings.md,
|
|
24
|
+
gridRowGap: theme.spacings.sm,
|
|
25
|
+
alignItems: 'start',
|
|
26
|
+
...theme.typography.body1,
|
|
27
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
28
|
+
[theme.breakpoints.up('sm')]: {
|
|
29
|
+
gridRowGap: theme.spacings.xxs,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
image: {
|
|
33
|
+
gridArea: 'image',
|
|
34
|
+
'& img': {
|
|
35
|
+
width: '100%',
|
|
36
|
+
height: 'auto',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
stars: {
|
|
40
|
+
gridArea: 'stars',
|
|
41
|
+
margin: '-6px 0 -6px -6px',
|
|
42
|
+
},
|
|
43
|
+
title: {
|
|
44
|
+
gridArea: 'title',
|
|
45
|
+
fontWeight: theme.typography.fontWeightBold,
|
|
46
|
+
},
|
|
47
|
+
text: {
|
|
48
|
+
gridArea: 'text',
|
|
49
|
+
},
|
|
50
|
+
date: {
|
|
51
|
+
gridArea: 'date',
|
|
52
|
+
fontStyle: 'italic',
|
|
53
|
+
color: theme.palette.primary.mutedText,
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
{ name: 'CustomerReview' },
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
export default function CustomerReview(props: CustomerReviewProps) {
|
|
60
|
+
const { product, text, average_rating, created_at } = props
|
|
61
|
+
const classes = useStyles()
|
|
62
|
+
|
|
63
|
+
const maxAverageRating = 100
|
|
64
|
+
const totalStars = 5
|
|
65
|
+
const valuePerStar = maxAverageRating / totalStars
|
|
66
|
+
const totalFilledStars = (average_rating / maxAverageRating / valuePerStar) * 100
|
|
67
|
+
|
|
68
|
+
const { data: config } = useQuery(StoreConfigDocument)
|
|
69
|
+
const locale = config?.storeConfig?.locale?.replace('_', '-')
|
|
70
|
+
|
|
71
|
+
const dateFormatter = new Intl.DateTimeFormat(locale, {
|
|
72
|
+
year: 'numeric',
|
|
73
|
+
month: 'long',
|
|
74
|
+
day: 'numeric',
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={classes.container}>
|
|
79
|
+
<div className={classes.image}>
|
|
80
|
+
{product && product.thumbnail && (
|
|
81
|
+
<Image
|
|
82
|
+
src={product.thumbnail?.url ?? ''}
|
|
83
|
+
width={196}
|
|
84
|
+
height={196}
|
|
85
|
+
alt={product.thumbnail?.label ?? ''}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
<div className={classes.stars}>
|
|
90
|
+
{[...new Array(totalStars)].map((value, index) => (
|
|
91
|
+
<Image
|
|
92
|
+
src={index < totalFilledStars ? filledStar : outlinedStar}
|
|
93
|
+
alt='star'
|
|
94
|
+
loading='eager'
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
<div className={classes.title}>{product?.name}</div>
|
|
99
|
+
<div className={classes.text}>{text}</div>
|
|
100
|
+
<div className={classes.date}>{dateFormatter.format(new Date(created_at ?? ''))}</div>
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>desktop_Star_Filled</title>
|
|
4
|
+
<g id="desktop_Star_Filled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
|
5
|
+
<polygon id="Path" stroke="#FF4A55" stroke-width="0.5" fill="#FF4A55" points="16.3216665 21.1249997 11.1783333 23.8291663 12.1608332 18.1016665 8 14.0458332 13.7499999 13.2108332 16.3216665 8 18.8933331 13.2108332 24.643333 14.0458332 20.4824998 18.1016665 21.4649997 23.8291663"></polygon>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>desktop_Star_Outline</title>
|
|
4
|
+
<g id="desktop_Star_Outline" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
|
5
|
+
<polygon id="Path" stroke="#FF4A55" stroke-width="0.5" points="16.3216665 21.1249997 11.1783333 23.8291663 12.1608332 18.1016665 8 14.0458332 13.7499999 13.2108332 16.3216665 8 18.8933331 13.2108332 24.643333 14.0458332 20.4824998 18.1016665 21.4649997 23.8291663"></polygon>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type JsonLdProductReview_BundleProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
5
|
+
|
|
6
|
+
export type JsonLdProductReview_ConfigurableProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
7
|
+
|
|
8
|
+
export type JsonLdProductReview_DownloadableProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
9
|
+
|
|
10
|
+
export type JsonLdProductReview_GroupedProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
11
|
+
|
|
12
|
+
export type JsonLdProductReview_SimpleProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
13
|
+
|
|
14
|
+
export type JsonLdProductReview_VirtualProduct_Fragment = { review_count: number, rating_summary: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
15
|
+
|
|
16
|
+
export type JsonLdProductReviewFragment = JsonLdProductReview_BundleProduct_Fragment | JsonLdProductReview_ConfigurableProduct_Fragment | JsonLdProductReview_DownloadableProduct_Fragment | JsonLdProductReview_GroupedProduct_Fragment | JsonLdProductReview_SimpleProduct_Fragment | JsonLdProductReview_VirtualProduct_Fragment;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Product } from 'schema-dts'
|
|
2
|
+
import { JsonLdProductReviewFragment } from './JsonLdProductReview.gql'
|
|
3
|
+
|
|
4
|
+
export function jsonLdProductReview(props: JsonLdProductReviewFragment): Partial<Product> {
|
|
5
|
+
const { reviews, review_count, rating_summary } = props
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
aggregateRating: {
|
|
9
|
+
'@type': 'AggregateRating',
|
|
10
|
+
reviewCount: review_count ?? undefined,
|
|
11
|
+
ratingValue: rating_summary ? Math.max(rating_summary * 0.5 * 0.1, 1) : undefined,
|
|
12
|
+
},
|
|
13
|
+
review: reviews.items.map((review) => ({
|
|
14
|
+
'@type': 'Review',
|
|
15
|
+
reviewRating: {
|
|
16
|
+
'@type': 'Rating',
|
|
17
|
+
ratingValue: Math.max((review?.average_rating || 1) * 0.5 * 0.1, 1),
|
|
18
|
+
},
|
|
19
|
+
name: review?.summary,
|
|
20
|
+
author: {
|
|
21
|
+
'@type': 'Person',
|
|
22
|
+
name: review?.nickname,
|
|
23
|
+
},
|
|
24
|
+
datePublished: review?.created_at,
|
|
25
|
+
reviewBody: review?.text,
|
|
26
|
+
})),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type ProductReviewSummary_BundleProduct_Fragment = { rating_summary: number };
|
|
5
|
+
|
|
6
|
+
export type ProductReviewSummary_ConfigurableProduct_Fragment = { rating_summary: number };
|
|
7
|
+
|
|
8
|
+
export type ProductReviewSummary_DownloadableProduct_Fragment = { rating_summary: number };
|
|
9
|
+
|
|
10
|
+
export type ProductReviewSummary_GroupedProduct_Fragment = { rating_summary: number };
|
|
11
|
+
|
|
12
|
+
export type ProductReviewSummary_SimpleProduct_Fragment = { rating_summary: number };
|
|
13
|
+
|
|
14
|
+
export type ProductReviewSummary_VirtualProduct_Fragment = { rating_summary: number };
|
|
15
|
+
|
|
16
|
+
export type ProductReviewSummaryFragment = ProductReviewSummary_BundleProduct_Fragment | ProductReviewSummary_ConfigurableProduct_Fragment | ProductReviewSummary_DownloadableProduct_Fragment | ProductReviewSummary_GroupedProduct_Fragment | ProductReviewSummary_SimpleProduct_Fragment | ProductReviewSummary_VirtualProduct_Fragment;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Chip, ChipProps } from '@material-ui/core'
|
|
2
|
+
import { SvgImageSimple, iconStarYellow } from '@graphcommerce/next-ui'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
export type ProductReviewChipProps = {
|
|
6
|
+
rating?: number
|
|
7
|
+
reviewSectionId?: string
|
|
8
|
+
max?: number
|
|
9
|
+
} & ChipProps
|
|
10
|
+
|
|
11
|
+
export default function ProductReviewChip(props: ProductReviewChipProps) {
|
|
12
|
+
const { rating, reviewSectionId = '', max = 5, ...chipProps } = props
|
|
13
|
+
|
|
14
|
+
if (!rating) return null
|
|
15
|
+
|
|
16
|
+
const normalizedRating = Math.round(rating / (10 / max)) / 10
|
|
17
|
+
|
|
18
|
+
const handleClick: React.MouseEventHandler<HTMLDivElement> = (e) => {
|
|
19
|
+
const element = document.getElementById(reviewSectionId)
|
|
20
|
+
e.preventDefault()
|
|
21
|
+
if (!element) return
|
|
22
|
+
|
|
23
|
+
window.scrollTo({
|
|
24
|
+
top: element.offsetTop - 50,
|
|
25
|
+
left: 0,
|
|
26
|
+
behavior: 'smooth',
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const chip = (
|
|
31
|
+
<Chip
|
|
32
|
+
variant='outlined'
|
|
33
|
+
clickable={!!reviewSectionId}
|
|
34
|
+
onClick={handleClick}
|
|
35
|
+
icon={<SvgImageSimple src={iconStarYellow} alt='Stars' />}
|
|
36
|
+
color='default'
|
|
37
|
+
label={`${normalizedRating}/5`}
|
|
38
|
+
{...chipProps}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if (!reviewSectionId) return chip
|
|
43
|
+
|
|
44
|
+
return chip
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const ProductReviewProductNameDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProductReviewProductName"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sku"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"products"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"sku"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sku"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"uid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode<ProductReviewProductNameQuery, ProductReviewProductNameQueryVariables>;
|
|
7
|
+
export type ProductReviewProductNameQueryVariables = Types.Exact<{
|
|
8
|
+
sku: Types.Scalars['String'];
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export type ProductReviewProductNameQuery = { products?: Types.Maybe<{ items?: Types.Maybe<Array<Types.Maybe<{ __typename: 'BundleProduct', uid: string, name?: Types.Maybe<string> } | { __typename: 'ConfigurableProduct', uid: string, name?: Types.Maybe<string> } | { __typename: 'DownloadableProduct', uid: string, name?: Types.Maybe<string> } | { __typename: 'GroupedProduct', uid: string, name?: Types.Maybe<string> } | { __typename: 'SimpleProduct', uid: string, name?: Types.Maybe<string> } | { __typename: 'VirtualProduct', uid: string, name?: Types.Maybe<string> }>>> }> };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const ProductReviewRatingsMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProductReviewRatingsMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productReviewRatingsMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"values"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"value_id"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ProductReviewRatingsMetadataQuery, ProductReviewRatingsMetadataQueryVariables>;
|
|
7
|
+
export type ProductReviewRatingsMetadataQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export type ProductReviewRatingsMetadataQuery = { productReviewRatingsMetadata: { items: Array<Types.Maybe<{ id: string, name: string, values: Array<Types.Maybe<{ value: string, value_id: string }>> }>> } };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type ProductReviews_BundleProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
5
|
+
|
|
6
|
+
export type ProductReviews_ConfigurableProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
7
|
+
|
|
8
|
+
export type ProductReviews_DownloadableProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
9
|
+
|
|
10
|
+
export type ProductReviews_GroupedProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
11
|
+
|
|
12
|
+
export type ProductReviews_SimpleProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
13
|
+
|
|
14
|
+
export type ProductReviews_VirtualProduct_Fragment = { review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
|
15
|
+
|
|
16
|
+
export type ProductReviewsFragment = ProductReviews_BundleProduct_Fragment | ProductReviews_ConfigurableProduct_Fragment | ProductReviews_DownloadableProduct_Fragment | ProductReviews_GroupedProduct_Fragment | ProductReviews_SimpleProduct_Fragment | ProductReviews_VirtualProduct_Fragment;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
fragment ProductReviews on ProductInterface @inject(into: ["ProductPageItem"]) {
|
|
2
|
+
reviews(pageSize: $reviewPageSize, currentPage: $reviewPage) {
|
|
3
|
+
page_info {
|
|
4
|
+
total_pages
|
|
5
|
+
current_page
|
|
6
|
+
}
|
|
7
|
+
items {
|
|
8
|
+
average_rating
|
|
9
|
+
created_at
|
|
10
|
+
nickname
|
|
11
|
+
ratings_breakdown {
|
|
12
|
+
name
|
|
13
|
+
value
|
|
14
|
+
}
|
|
15
|
+
summary
|
|
16
|
+
text
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
review_count
|
|
20
|
+
url_key
|
|
21
|
+
sku
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const ProductReviewsPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProductReviewsPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"urlKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"reviewPageSize"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"reviewPage"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"products"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"url_key"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"urlKey"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"uid"}},{"kind":"Field","name":{"kind":"Name","value":"review_count"}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pageSize"},"value":{"kind":"Variable","name":{"kind":"Name","value":"reviewPageSize"}}},{"kind":"Argument","name":{"kind":"Name","value":"currentPage"},"value":{"kind":"Variable","name":{"kind":"Name","value":"reviewPage"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"page_info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total_pages"}},{"kind":"Field","name":{"kind":"Name","value":"current_page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"average_rating"}},{"kind":"Field","name":{"kind":"Name","value":"created_at"}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"ratings_breakdown"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"summary"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"url_key"}},{"kind":"Field","name":{"kind":"Name","value":"sku"}}]}}]}}]}}]} as unknown as DocumentNode<ProductReviewsPageQuery, ProductReviewsPageQueryVariables>;
|
|
7
|
+
export type ProductReviewsPageQueryVariables = Types.Exact<{
|
|
8
|
+
urlKey: Types.Scalars['String'];
|
|
9
|
+
reviewPageSize?: Types.Maybe<Types.Scalars['Int']>;
|
|
10
|
+
reviewPage: Types.Scalars['Int'];
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export type ProductReviewsPageQuery = { products?: Types.Maybe<{ items?: Types.Maybe<Array<Types.Maybe<{ __typename: 'BundleProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } } | { __typename: 'ConfigurableProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } } | { __typename: 'DownloadableProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } } | { __typename: 'GroupedProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } } | { __typename: 'SimpleProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } } | { __typename: 'VirtualProduct', uid: string, review_count: number, url_key?: Types.Maybe<string>, sku?: Types.Maybe<string>, reviews: { page_info: { total_pages?: Types.Maybe<number>, current_page?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } }>>> }> };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client'
|
|
2
|
+
import { Chip, makeStyles, Theme, Typography } from '@material-ui/core'
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
iconStarFilledMuted,
|
|
6
|
+
iconStarYellow,
|
|
7
|
+
Pagination,
|
|
8
|
+
responsiveVal,
|
|
9
|
+
StarRatingField,
|
|
10
|
+
SvgImage,
|
|
11
|
+
} from '@graphcommerce/next-ui'
|
|
12
|
+
import Link from 'next/link'
|
|
13
|
+
import React, { useState } from 'react'
|
|
14
|
+
import ProductReviewChip from '../ProductReviewChip'
|
|
15
|
+
import { ProductReviewsFragment } from './ProductReviews.gql'
|
|
16
|
+
import { ProductReviewsPageDocument } from './ProductReviewsPage.gql'
|
|
17
|
+
|
|
18
|
+
const useStyles = makeStyles(
|
|
19
|
+
(theme: Theme) => ({
|
|
20
|
+
review: {
|
|
21
|
+
display: 'grid',
|
|
22
|
+
gap: theme.spacings.sm,
|
|
23
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
24
|
+
padding: `${theme.spacings.md} 0`,
|
|
25
|
+
...theme.typography.body1,
|
|
26
|
+
},
|
|
27
|
+
title: {
|
|
28
|
+
display: 'grid',
|
|
29
|
+
gridAutoFlow: 'column',
|
|
30
|
+
justifyContent: 'start',
|
|
31
|
+
gap: theme.spacings.xs,
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
},
|
|
34
|
+
meta: {
|
|
35
|
+
color: theme.palette.text.disabled,
|
|
36
|
+
display: 'grid',
|
|
37
|
+
gridAutoFlow: 'column',
|
|
38
|
+
justifyContent: 'space-between',
|
|
39
|
+
},
|
|
40
|
+
nickname: {},
|
|
41
|
+
date: {},
|
|
42
|
+
reviewsBottomContainer: {
|
|
43
|
+
display: 'flex',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
justifyContent: 'space-between',
|
|
46
|
+
marginTop: theme.spacings.sm,
|
|
47
|
+
},
|
|
48
|
+
paginationRoot: {
|
|
49
|
+
margin: `0 -16px 0`,
|
|
50
|
+
},
|
|
51
|
+
paginationButton: {
|
|
52
|
+
padding: 0,
|
|
53
|
+
minWidth: 'unset',
|
|
54
|
+
borderRadius: '100%',
|
|
55
|
+
'& > .MuiButton-label': {
|
|
56
|
+
padding: 0,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
ratingRow: {
|
|
60
|
+
display: 'flex',
|
|
61
|
+
flexWrap: 'wrap',
|
|
62
|
+
gap: theme.spacings.sm,
|
|
63
|
+
color: theme.palette.text.disabled,
|
|
64
|
+
...theme.typography.body2,
|
|
65
|
+
},
|
|
66
|
+
rating: {
|
|
67
|
+
display: 'grid',
|
|
68
|
+
gridAutoFlow: 'column',
|
|
69
|
+
gridTemplateColumns: '0.4fr 0.6fr',
|
|
70
|
+
justifyContent: 'space-between',
|
|
71
|
+
marginRight: theme.spacings.xxs,
|
|
72
|
+
rowGap: responsiveVal(8, 16),
|
|
73
|
+
gap: 8,
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
},
|
|
76
|
+
writeReviewButton: {
|
|
77
|
+
[theme.breakpoints.down('xs')]: {
|
|
78
|
+
padding: '8px 16px 8px',
|
|
79
|
+
whiteSpace: 'nowrap',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
container: {
|
|
83
|
+
marginTop: `calc(${theme.spacings.xxs} * -1)`,
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
{ name: 'ProductReviews' },
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
export type ProductReviewsProps = ProductReviewsFragment
|
|
90
|
+
|
|
91
|
+
export default function ProductReviews(props: ProductReviewsProps) {
|
|
92
|
+
const { reviews, url_key, sku } = props
|
|
93
|
+
const classes = useStyles()
|
|
94
|
+
const config = 'en_US'
|
|
95
|
+
const locale = config.replace('_', '-')
|
|
96
|
+
|
|
97
|
+
const [reviewPage, setPage] = useState<number>(1)
|
|
98
|
+
|
|
99
|
+
const { data: otherReviewsPage, loading } = useQuery(ProductReviewsPageDocument, {
|
|
100
|
+
skip: reviewPage === 1,
|
|
101
|
+
variables: {
|
|
102
|
+
urlKey: url_key ?? '',
|
|
103
|
+
reviewPage,
|
|
104
|
+
reviewPageSize: 3,
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const myReviews = otherReviewsPage?.products?.items?.[0]?.reviews ?? reviews
|
|
109
|
+
|
|
110
|
+
const { current_page, total_pages } = myReviews.page_info
|
|
111
|
+
|
|
112
|
+
const formatter = new Intl.DateTimeFormat(locale, {
|
|
113
|
+
year: 'numeric',
|
|
114
|
+
month: 'long',
|
|
115
|
+
day: 'numeric',
|
|
116
|
+
hour: 'numeric',
|
|
117
|
+
minute: 'numeric',
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
if (!reviews) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const actions = (
|
|
125
|
+
<div className={classes.reviewsBottomContainer}>
|
|
126
|
+
<Link href={`/account/reviews/add?sku=${sku}`} passHref>
|
|
127
|
+
<Button
|
|
128
|
+
variant='pill'
|
|
129
|
+
color='primary'
|
|
130
|
+
text='bold'
|
|
131
|
+
size='large'
|
|
132
|
+
className={classes.writeReviewButton}
|
|
133
|
+
>
|
|
134
|
+
Write a review
|
|
135
|
+
</Button>
|
|
136
|
+
</Link>
|
|
137
|
+
|
|
138
|
+
{!!total_pages && total_pages > 1 && (
|
|
139
|
+
<Pagination
|
|
140
|
+
count={total_pages ?? 1}
|
|
141
|
+
page={current_page ?? 1}
|
|
142
|
+
classes={{ root: classes.paginationRoot }}
|
|
143
|
+
renderLink={(p: number, icon: React.ReactNode) => (
|
|
144
|
+
<Button onClick={() => setPage(p)} className={classes.paginationButton}>
|
|
145
|
+
{icon}
|
|
146
|
+
</Button>
|
|
147
|
+
)}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if (reviews?.items.length === 0) {
|
|
154
|
+
return (
|
|
155
|
+
<div className={classes.container}>
|
|
156
|
+
<div className={classes.review}>
|
|
157
|
+
<div className={classes.title}>
|
|
158
|
+
<Typography variant='subtitle1'>Be the first to write a review!</Typography>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
{actions}
|
|
162
|
+
</div>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div className={classes.container}>
|
|
168
|
+
{!loading &&
|
|
169
|
+
myReviews.items.map((review) => (
|
|
170
|
+
<div key={review?.summary} className={classes.review}>
|
|
171
|
+
<div className={classes.title}>
|
|
172
|
+
<ProductReviewChip rating={review?.average_rating} variant='default' />
|
|
173
|
+
<Typography variant='h5'> {review?.summary}</Typography>
|
|
174
|
+
</div>
|
|
175
|
+
<Typography variant='body1'>{review?.text}</Typography>
|
|
176
|
+
|
|
177
|
+
{(review?.ratings_breakdown ?? 0) > 1 && (
|
|
178
|
+
<div className={classes.ratingRow}>
|
|
179
|
+
{review?.ratings_breakdown.map((ratingBreakdown) => (
|
|
180
|
+
<div key={`rating-${ratingBreakdown?.value}`} className={classes.rating}>
|
|
181
|
+
<span>{ratingBreakdown?.name}</span>
|
|
182
|
+
<StarRatingField
|
|
183
|
+
iconSize={16}
|
|
184
|
+
readOnly
|
|
185
|
+
size='small'
|
|
186
|
+
defaultValue={Number(ratingBreakdown?.value ?? 0)}
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
))}
|
|
190
|
+
</div>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
<div className={classes.meta}>
|
|
194
|
+
<div className={classes.nickname}>Written by {review?.nickname}</div>
|
|
195
|
+
<time className={classes.date} dateTime={review?.created_at}>
|
|
196
|
+
{review?.created_at &&
|
|
197
|
+
formatter.format(new Date(review?.created_at.replace(/-/g, '/')))}
|
|
198
|
+
</time>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
))}
|
|
202
|
+
{actions}
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { default as AccountReviews } from './AccountReviews'
|
|
2
|
+
export { default as CreateProductReviewForm } from './CreateProductReviewForm'
|
|
3
|
+
export { default as CustomerReview } from './CustomerReview'
|
|
4
|
+
export { default as ProductReviews } from './ProductReviews'
|
|
5
|
+
export { default as ProductReviewChip } from './ProductReviewChip'
|
|
6
|
+
|
|
7
|
+
export * from './AccountReviews/AccountReviews.gql'
|
|
8
|
+
export * from './ProductReviews'
|
|
9
|
+
export * from './CustomerReview/CustomerReview.gql'
|
|
10
|
+
export * from './ProductReviewChip/ProductReviewSummary.gql'
|
|
11
|
+
export * from './ProductReviews/ProductReviewProductName.gql'
|
|
12
|
+
export * from './CreateProductReviewForm/CreateProductReview.gql'
|
|
13
|
+
export * from './CreateProductReviewForm/ProductReviewRatingsMetadata.gql'
|
|
14
|
+
|
|
15
|
+
export * from './JsonLdProductReview/JsonLdProductReview.gql'
|
|
16
|
+
export * from './JsonLdProductReview'
|
package/index.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
export type AccountDashboardCustomerReviewsFragment = { reviews: { page_info: { total_pages?: Types.Maybe<number> }, items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, product: { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> }, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/magento-review",
|
|
3
|
+
"version": "2.105.1",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
6
|
+
"browserslist": [
|
|
7
|
+
"extends @graphcommerce/browserslist-config-pwa"
|
|
8
|
+
],
|
|
9
|
+
"eslintConfig": {
|
|
10
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"project": "./tsconfig.json"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@graphcommerce/browserslist-config-pwa": "^3.0.1",
|
|
17
|
+
"@graphcommerce/eslint-config-pwa": "^3.0.1",
|
|
18
|
+
"@graphcommerce/prettier-config-pwa": "^3.0.1",
|
|
19
|
+
"@graphcommerce/typescript-config-pwa": "^3.0.1",
|
|
20
|
+
"@playwright/test": "^1.14.1"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@apollo/client": "^3.3.21",
|
|
24
|
+
"@graphcommerce/graphql": "^2.103.1",
|
|
25
|
+
"@graphcommerce/image": "^2.104.1",
|
|
26
|
+
"@graphcommerce/magento-product": "^3.0.1",
|
|
27
|
+
"@graphcommerce/magento-store": "^3.0.1",
|
|
28
|
+
"@graphcommerce/next-ui": "^3.0.1",
|
|
29
|
+
"@graphql-typed-document-node/core": "^3.1.0",
|
|
30
|
+
"@material-ui/core": "^4.12.3",
|
|
31
|
+
"@material-ui/lab": "^4.0.0-alpha.60",
|
|
32
|
+
"clsx": "^1.1.1",
|
|
33
|
+
"next": "^11.1.2",
|
|
34
|
+
"react": "^17.0.2",
|
|
35
|
+
"react-dom": "^17.0.2",
|
|
36
|
+
"schema-dts": "^1.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const AccountDashboardReviewsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AccountDashboardReviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"average_rating"}},{"kind":"Field","name":{"kind":"Name","value":"created_at"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uid"}},{"kind":"Field","name":{"kind":"Name","value":"url_key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"ratings_breakdown"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<AccountDashboardReviewsQuery, AccountDashboardReviewsQueryVariables>;
|
|
7
|
+
export type AccountDashboardReviewsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export type AccountDashboardReviewsQuery = { customer?: Types.Maybe<{ reviews: { items: Array<Types.Maybe<{ average_rating: number, created_at: string, nickname: string, summary: string, text: string, product: { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> } | { uid: string, url_key?: Types.Maybe<string>, name?: Types.Maybe<string>, thumbnail?: Types.Maybe<{ url?: Types.Maybe<string>, label?: Types.Maybe<string> }> }, ratings_breakdown: Array<Types.Maybe<{ name: string, value: string }>> }>> } }> };
|