@ecomplus/widget-martan 1.1.0

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +11 -0
  3. package/cms.config.js +264 -0
  4. package/dist/public/widget-martan.1.min.js +2 -0
  5. package/dist/public/widget-martan.1.min.js.map +1 -0
  6. package/dist/public/widget-martan.2.min.js +2 -0
  7. package/dist/public/widget-martan.2.min.js.map +1 -0
  8. package/dist/public/widget-martan.3.min.js +2 -0
  9. package/dist/public/widget-martan.3.min.js.map +1 -0
  10. package/dist/public/widget-martan.4.min.js +2 -0
  11. package/dist/public/widget-martan.4.min.js.map +1 -0
  12. package/dist/public/widget-martan.5.min.js +2 -0
  13. package/dist/public/widget-martan.5.min.js.map +1 -0
  14. package/dist/public/widget-martan.var.min.js +71 -0
  15. package/dist/public/widget-martan.var.min.js.map +1 -0
  16. package/dist/widget-martan.1.min.js +2 -0
  17. package/dist/widget-martan.1.min.js.map +1 -0
  18. package/dist/widget-martan.2.min.js +2 -0
  19. package/dist/widget-martan.2.min.js.map +1 -0
  20. package/dist/widget-martan.3.min.js +2 -0
  21. package/dist/widget-martan.3.min.js.map +1 -0
  22. package/dist/widget-martan.4.min.js +2 -0
  23. package/dist/widget-martan.4.min.js.map +1 -0
  24. package/dist/widget-martan.5.min.js +2 -0
  25. package/dist/widget-martan.5.min.js.map +1 -0
  26. package/dist/widget-martan.min.js +71 -0
  27. package/dist/widget-martan.min.js.map +1 -0
  28. package/package.json +36 -0
  29. package/src/append/body.ejs +79 -0
  30. package/src/append/head.ejs +5 -0
  31. package/src/append/product-block.ejs +5 -0
  32. package/src/append/product-card-slots.ejs +3 -0
  33. package/src/append/product-slots.ejs +25 -0
  34. package/src/append/stamps.ejs +3 -0
  35. package/src/index.js +22 -0
  36. package/src/utils/get-width.js +11 -0
  37. package/src/utils/textToNumber.js +31 -0
  38. package/src/utils/time-ago.js +35 -0
  39. package/src/widgets/reviews/AuthorAndRating.vue +30 -0
  40. package/src/widgets/reviews/AverageScore.vue +160 -0
  41. package/src/widgets/reviews/AverageTotal.vue +35 -0
  42. package/src/widgets/reviews/CardReview.vue +87 -0
  43. package/src/widgets/reviews/GridView.vue +113 -0
  44. package/src/widgets/reviews/HeaderExpanded.vue +84 -0
  45. package/src/widgets/reviews/HeaderMinimal.vue +110 -0
  46. package/src/widgets/reviews/ListView.vue +47 -0
  47. package/src/widgets/reviews/Quickview.vue +398 -0
  48. package/src/widgets/reviews/ReviewBody.vue +39 -0
  49. package/src/widgets/reviews/ReviewReply.vue +57 -0
  50. package/src/widgets/reviews/Reviews.vue +496 -0
  51. package/src/widgets/reviews/Score.vue +186 -0
  52. package/src/widgets/reviews/Sort.vue +76 -0
  53. package/src/widgets/reviews/ThumbsPictures.vue +135 -0
  54. package/src/widgets/reviews/Total.vue +79 -0
  55. package/src/widgets/reviews/VerifiedPurchase.vue +111 -0
  56. package/src/widgets/reviews/VideoPlayer.vue +136 -0
  57. package/src/widgets/reviews/index.js +52 -0
  58. package/src/widgets/reviews/isRecommended.vue +44 -0
  59. package/src/widgets/snippets/Rating.vue +71 -0
  60. package/src/widgets/snippets/Snippets.vue +311 -0
  61. package/src/widgets/snippets/index.js +45 -0
  62. package/webpack.config.js +1 -0
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@ecomplus/widget-martan",
3
+ "version": "1.1.0",
4
+ "description": "Storefront plugin for martan product reviews, product q&a",
5
+ "main": "dist/widget-martan.min.js",
6
+ "module": "src/index.js",
7
+ "browser": "dist/widget-martan.min.js",
8
+ "jsdelivr": "dist/public/widget-martan.var.min.js",
9
+ "unpkg": "dist/public/widget-martan.var.min.js",
10
+ "scripts": {
11
+ "build": "cross-env NODE_ENV=production webpack",
12
+ "serve": "webpack-dev-server"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/ecomplus/storefront.git",
17
+ "directory": "@ecomplus/widget-martan"
18
+ },
19
+ "keywords": [
20
+ "ecomplus",
21
+ "storefront",
22
+ "widget",
23
+ "martan",
24
+ "reviews"
25
+ ],
26
+ "author": "Talisson Trindade <tt@martan.app>",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/ecomplus/storefront/issues"
30
+ },
31
+ "homepage": "https://github.com/ecomplus/storefront/tree/master/@ecomplus/widget-martan#readme",
32
+ "webpackOutput": {
33
+ "library": "widgetMartan",
34
+ "filename": "widget-martan.min.js"
35
+ }
36
+ }
@@ -0,0 +1,79 @@
1
+ <script>
2
+ document.addEventListener("DOMContentLoaded", function (event) {
3
+ const scriptUrl =
4
+ "<%- options.widgets_src || 'https://unpkg.com/@martan-app/widgets-js@1.1.23/dist/martan.umd.js' %>"
5
+
6
+ const startWidget = () => {
7
+ const color =
8
+ "<%- options.widget_rating && options.widget_rating.star_color || '#000' %>"
9
+ const fontSize =
10
+ "<%- options.widget_rating && options.widget_rating.font_size || 14 %>"
11
+
12
+ const storeId = "<%- options.store_id %>"
13
+ const widgetKey = "<%- options.widget_key %>"
14
+ const webId = "<%- options.web_id %>"
15
+
16
+ setTimeout(() => {
17
+ try {
18
+ const martan = new window.Martan({
19
+ storeId: parseInt(storeId, 10),
20
+ widgetKey,
21
+ webId
22
+ })
23
+
24
+ const init = () =>
25
+ martan
26
+ .average({
27
+ fontSize,
28
+ color,
29
+ classes: ['.product-card__rating']
30
+ })
31
+ .start()
32
+
33
+ const $searchEngine = document.getElementById("search-engine")
34
+ if (!$searchEngine) {
35
+ init()
36
+ } else {
37
+ const displayOnSearchPage =
38
+ "<%- options.widget_rating && options.widget_rating.search_page %>"
39
+
40
+ if (displayOnSearchPage === "true") {
41
+ const callback = () => {
42
+ searchEngineObserver.disconnect()
43
+ const $retailRow = $searchEngine.querySelectorAll(
44
+ ".search-engine__retail > .row"
45
+ )[0]
46
+
47
+ const rowObserver = new MutationObserver(() => {
48
+ setTimeout(() => init(), 100)
49
+ rowObserver.disconnect()
50
+ })
51
+ rowObserver.observe($retailRow, {
52
+ childList: true,
53
+ subtree: true,
54
+ })
55
+ }
56
+ const searchEngineObserver = new MutationObserver(callback)
57
+ searchEngineObserver.observe($searchEngine, {
58
+ attributes: true,
59
+ childList: true,
60
+ })
61
+ }
62
+ }
63
+ } catch (error) {}
64
+ }, 50)
65
+ }
66
+
67
+ const isEnabled =
68
+ "<%- options.widget_rating && options.widget_rating.is_enabled %>"
69
+
70
+ if (isEnabled === "true") {
71
+ const script = document.createElement("script")
72
+ script.setAttribute("src", scriptUrl)
73
+ script.setAttribute("id", "martan-scripts")
74
+ script.async = true
75
+ document.body.appendChild(script)
76
+ script.onload = startWidget
77
+ }
78
+ })
79
+ </script>
@@ -0,0 +1,5 @@
1
+ <style>
2
+ #reviews_widget {
3
+ min-height: 230px;
4
+ }
5
+ </style>
@@ -0,0 +1,5 @@
1
+ <section id="reviews">
2
+ <div class="container" id="<%- options.martanReviewsTarget || 'reviews_widget' %>" data-product="<%- _.state.sku %>" style="margin-bottom: 1rem; display: <%- options && options.widget_rating && options.widget_rating.is_enabled
3
+ ? 'initial' : 'none' %>;">
4
+ </div>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <div data-slot="rating">
2
+ <div data-total="0" data-martan-display="<%- options && options.widget_rating && options.widget_rating.display %>" data-martan-product-id="<%- opt.item && opt.item.sku %>" class="mt-review-average"></div>
3
+ </div>
@@ -0,0 +1,25 @@
1
+ <div data-slot="rating" class="d-none">
2
+ <div
3
+ id="martan-rating__action"
4
+ style="margin-bottom: 1rem; display: <%- options && options.widget_rating && options.widget_rating.is_enabled ? 'initial' : 'none' %>;"
5
+ >
6
+ <a
7
+ class="martan-widget-rating"
8
+ title="Pergunte e veja opiniões de quem já comprou"
9
+ href="#reviews"
10
+ >
11
+ <div
12
+ data-martan-style="completed"
13
+ data-martan-product-id="<%- _.state.sku %>"
14
+ class="mt-review-average d-inline-block"
15
+ ></div>
16
+ </a>
17
+ </div>
18
+
19
+ <div
20
+ class="d-none"
21
+ id="snippet_widget"
22
+ data-sku="<%- _.state.sku %>"
23
+ style="margin-bottom: 1rem; height: 60px"
24
+ ></div>
25
+ </div>
@@ -0,0 +1,3 @@
1
+ <% if (!options.disable_certificate) { %>
2
+ <div />
3
+ <% } %>
package/src/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import Reviews from './widgets/reviews'
2
+ import Snippets from './widgets/snippets'
3
+
4
+ export const MARTAN_API = 'https://widgets.martan.app/v1'
5
+
6
+ export default (options) => {
7
+ if (options && options.store_id && options.web_id) {
8
+ if (options.widget_snippet && options.widget_snippet.is_enabled) {
9
+ Snippets(options)
10
+ }
11
+
12
+ if (options.widget_review && options.widget_review.is_enabled) {
13
+ Reviews(options)
14
+ }
15
+ } else {
16
+ console.error(
17
+ new Error(
18
+ "Can't setup Martan widget without `martanStoreId` and `martanWidgetKey`"
19
+ )
20
+ )
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ import { numberToText } from './textToNumber'
2
+
3
+ // calcula a % usando a média de avaliacoes / total de avaliacoes do produto
4
+ export function getWidth(reviews, rating) {
5
+ if (reviews.total === 0) return '0%'
6
+
7
+ return `${(
8
+ (100 * reviews.average[numberToText(rating)]) /
9
+ reviews.total
10
+ ).toFixed()}%`
11
+ }
@@ -0,0 +1,31 @@
1
+ const textToNumber = (number) => {
2
+ switch (number) {
3
+ case 'one':
4
+ return 1
5
+ case 'two':
6
+ return 2
7
+ case 'three':
8
+ return 3
9
+ case 'four':
10
+ return 4
11
+ case 'five':
12
+ return 5
13
+ }
14
+ }
15
+
16
+ const numberToText = (text) => {
17
+ switch (text) {
18
+ case 1:
19
+ return 'one'
20
+ case 2:
21
+ return 'two'
22
+ case 3:
23
+ return 'three'
24
+ case 4:
25
+ return 'four'
26
+ case 5:
27
+ return 'five'
28
+ }
29
+ }
30
+
31
+ export { textToNumber, numberToText }
@@ -0,0 +1,35 @@
1
+ // Retorna uma hora em formato humanizade
2
+ export function timeAgo (date) {
3
+ const segundos = Math.floor((new Date() - new Date(date)) / 1000)
4
+
5
+ const intervalos = {
6
+ ano: 31536000,
7
+ mês: 2592000,
8
+ semana: 604800,
9
+ dia: 86400,
10
+ hora: 3600,
11
+ minuto: 60,
12
+ segundo: 1
13
+ }
14
+
15
+ let contador
16
+
17
+ if (segundos === 0) {
18
+ return 'Agora'
19
+ }
20
+
21
+ for (const intervalo in intervalos) {
22
+ contador = Math.floor(segundos / intervalos[intervalo])
23
+
24
+ if (contador > 0) {
25
+ if (contador === 1) {
26
+ return `${contador} ${intervalo} atrás`
27
+ } else {
28
+ if (intervalo === 'mês') {
29
+ return `${contador} meses atrás`
30
+ }
31
+ return `${contador} ${intervalo}s atrás`
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <div style="display: flex;align-items: baseline; justify-content: space-between">
3
+ <div style="display: flex; height: 20px; align-items: center; gap: 5px">
4
+ <div class="mt-review__author" style="padding-right: 2px">
5
+ <span>{{ isAnonymous ? "Anônimo" : author }}</span>
6
+ </div>
7
+
8
+ <rating :rating="rating" :color="starColor" />
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import Rating from "../snippets/Rating.vue";
15
+
16
+ export default {
17
+ name: "AuthorAndRating",
18
+
19
+ props: {
20
+ isAnonymous: Boolean,
21
+ author: String,
22
+ rating: Number,
23
+ starColor: String,
24
+ },
25
+
26
+ components: {
27
+ Rating,
28
+ },
29
+ };
30
+ </script>
@@ -0,0 +1,160 @@
1
+ <template>
2
+ <ul class="mt-rating__options" ref="options">
3
+ <li class="mt-rating__list" v-for="index in maxReviews" :key="index"
4
+ v-on:click="(e) => (reviews.total === 0 ? null : setRating(e, index))">
5
+ <div class="mt-rating-star">
6
+ <span>{{ index }}</span>
7
+
8
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-star" width="14" height="14"
9
+ viewBox="0 0 24 24" stroke-width="2" stroke="#e3e6e6" fill="none" stroke-linecap="round"
10
+ stroke-linejoin="round">
11
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
12
+ <path
13
+ d="M12 17.75l-6.172 3.245l1.179 -6.873l-5 -4.867l6.9 -1l3.086 -6.253l3.086 6.253l6.9 1l-5 4.867l1.179 6.873z" />
14
+ </svg>
15
+ </div>
16
+
17
+ <div class="mt-rating-meter">
18
+ <div class="mt-rating-bar" v-bind:style="{
19
+ width: reviews.getWidth(index),
20
+ 'background-color': config.star_color || 'red',
21
+ }" />
22
+ </div>
23
+
24
+ <div class="mt-rating-total">
25
+ <span>{{ reviews.average[numberToText(index)] }}</span>
26
+ <span>({{ reviews.getWidth(index) }})</span>
27
+
28
+ <span class="mt-rating-remove"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x"
29
+ width="15" height="15" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
30
+ stroke-linecap="round" stroke-linejoin="round">
31
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
32
+ <path d="M18 6l-12 12"></path>
33
+ <path d="M6 6l12 12"></path>
34
+ </svg></span>
35
+ </div>
36
+ </li>
37
+ </ul>
38
+ </template>
39
+
40
+
41
+ <script>
42
+ export default {
43
+ name: 'AverageScore',
44
+
45
+ props: {
46
+ },
47
+
48
+ data() {
49
+ return {
50
+ maxReviews: 5
51
+ };
52
+ },
53
+
54
+ methods: {
55
+ async setRating(e, rating) {
56
+ const removeClass = () =>
57
+ new Promise((resolve) => {
58
+ const res = Array.from(options.value.children).map((option) => {
59
+ option.style.removeProperty("opacity")
60
+ option.children[0].childNodes[1].style.stroke = "rgb(227, 230, 230)"
61
+ return option.classList.remove("is-active")
62
+ })
63
+
64
+ resolve(res)
65
+ })
66
+
67
+ await removeClass().then(() => {
68
+ if (rating !== reviews.orderRating) {
69
+ const el = Array.from(options.value.children)[rating - 1]
70
+ el.classList.add("is-active")
71
+ el.style.opacity = 1
72
+ el.children[0].childNodes[1].style.stroke = config.star_color
73
+ Array.from(options.value.children).forEach((option, index) => {
74
+ if (index !== rating - 1) {
75
+ option.style.opacity = ".5"
76
+ }
77
+ })
78
+ }
79
+ })
80
+
81
+ if (rating === reviews.orderRating) {
82
+ reviews.orderRating = null
83
+ } else {
84
+ reviews.offset = 0
85
+ reviews.orderRating = rating
86
+ }
87
+ }
88
+ },
89
+ };
90
+ </script>
91
+
92
+ <style>
93
+ .mt-rating-remove {
94
+ visibility: hidden;
95
+ cursor: pointer;
96
+ display: flex;
97
+ align-items: center;
98
+ }
99
+
100
+ .mt-rating__list.is-active .mt-rating-remove {
101
+ visibility: visible;
102
+ }
103
+
104
+ .mt-rating__options li:hover {
105
+ cursor: pointer;
106
+ }
107
+
108
+ .mt-rating__list {
109
+ display: flex;
110
+ align-items: center;
111
+ height: 23px;
112
+ }
113
+
114
+ .mt-rating__list.is-active .mt-rating-total,
115
+ .mt-rating__list:hover .mt-rating-total {
116
+ visibility: visible;
117
+ }
118
+
119
+ .mt-rating__list:hover {
120
+ opacity: 0.9;
121
+ }
122
+
123
+ .mt-rating-meter {
124
+ overflow: hidden;
125
+ box-shadow: inset 0 0 0 1px #e3e6e6;
126
+ background-color: #e3e6e6;
127
+ height: 10px;
128
+ width: 100%;
129
+ border-radius: 50px;
130
+ display: flex;
131
+ }
132
+
133
+ .mt-rating__options {
134
+ margin: 0;
135
+ list-style: none;
136
+ padding: 0;
137
+ max-width: 800px;
138
+ width: 100%;
139
+ }
140
+
141
+ .mt-rating-star {
142
+ display: flex;
143
+ width: 30px;
144
+ justify-content: space-around;
145
+ font-size: 12px;
146
+ color: #757373;
147
+ align-items: center;
148
+ margin-left: 5px;
149
+ }
150
+
151
+ .mt-rating-total {
152
+ visibility: hidden;
153
+ width: 50px;
154
+ font-size: 12px;
155
+ color: #757373;
156
+ margin-left: 2px;
157
+ display: flex;
158
+ gap: 3px;
159
+ }
160
+ </style>
@@ -0,0 +1,35 @@
1
+ <script>
2
+ export default {
3
+ name: 'AverageTotal',
4
+
5
+ props: {
6
+ average: {
7
+ type: Number,
8
+ default: 0
9
+ }
10
+ },
11
+ };
12
+ </script>
13
+
14
+ <template>
15
+ <div class="mt-rating__resume" :title="'Nota média ' + average">
16
+ <div class="mt-rating__average">
17
+ <div class="mt-rating__average__wrapper">
18
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-star-filled" width="24" height="24"
19
+ viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
20
+ stroke-linejoin="round">
21
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
22
+ <path
23
+ d="M8.243 7.34l-6.38 .925l-.113 .023a1 1 0 0 0 -.44 1.684l4.622 4.499l-1.09 6.355l-.013 .11a1 1 0 0 0 1.464 .944l5.706 -3l5.693 3l.1 .046a1 1 0 0 0 1.352 -1.1l-1.091 -6.355l4.624 -4.5l.078 -.085a1 1 0 0 0 -.633 -1.62l-6.38 -.926l-2.852 -5.78a1 1 0 0 0 -1.794 0l-2.853 5.78z"
24
+ stroke-width="0" fill="currentColor" />
25
+ </svg>
26
+ </div>
27
+
28
+ <div style="margin-left: 10px">
29
+ <p class="mt-rating__average--total">
30
+ {{ average }}
31
+ </p>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </template>
@@ -0,0 +1,87 @@
1
+ <template>
2
+ <div class="mt-review">
3
+ <div class="mt-rating__group">
4
+ <author-and-rating
5
+ v-if="review"
6
+ :author="review.display_name"
7
+ :isAnonymous="review.is_anonymous"
8
+ :rating="review.rating"
9
+ :starColor="starColor"
10
+ />
11
+
12
+ <is-recommended v-if="review" :recommended="review.is_recommended" />
13
+
14
+ <verified-purchase v-if="isVerified" />
15
+ </div>
16
+
17
+ <thumbs-pictures
18
+ :review="review"
19
+ @onClick="(e) => $emit('openQuickview', e)"
20
+ />
21
+
22
+ <review-body
23
+ v-if="review"
24
+ :body="review.body"
25
+ :createdAt="review.created_at"
26
+ />
27
+
28
+ <review-reply :reply="review.reply" />
29
+ </div>
30
+ </template>
31
+
32
+ <script>
33
+ import Rating from "../snippets/Rating.vue";
34
+ import ThumbsPictures from "./ThumbsPictures.vue";
35
+ import VerifiedPurchase from "./VerifiedPurchase.vue";
36
+ import AuthorAndRating from "./AuthorAndRating.vue";
37
+ import IsRecommended from "./isRecommended.vue";
38
+ import ReviewBody from "./ReviewBody.vue";
39
+ import ReviewReply from "./ReviewReply.vue";
40
+
41
+ export default {
42
+ name: "CardReview",
43
+
44
+ props: {
45
+ review: {
46
+ type: Object,
47
+ default () {
48
+ return {}
49
+ }
50
+ },
51
+
52
+ starColor: {
53
+ type: String,
54
+ default: "#212529",
55
+ },
56
+ },
57
+
58
+ components: {
59
+ ThumbsPictures,
60
+ Rating,
61
+ VerifiedPurchase,
62
+ AuthorAndRating,
63
+ IsRecommended,
64
+ ReviewBody,
65
+ ReviewReply,
66
+ },
67
+
68
+ computed: {
69
+ isVerified: function () {
70
+ return this.review && this.review.verified_purchase;
71
+ },
72
+ },
73
+ };
74
+ </script>
75
+
76
+ <style>
77
+ .mt-reply__wrapper {
78
+ display: flex;
79
+ align-items: center;
80
+ }
81
+
82
+ @media (max-width: 580px) {
83
+ .body-wrapper {
84
+ padding: 0px;
85
+ }
86
+ }
87
+ </style>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <div class="mt-gridview">
3
+ <div class="mt-gridview__card" v-for="review in reviews.list" :key="review.id" @click="(e) => openQuickview(review)">
4
+ <div class="mt-gridview__thumb" v-if="getPictureURL(review)">
5
+ <img :src="getPictureURL(review)" :alt="review.title" @click="() => openQuickview(review)" />
6
+ </div>
7
+
8
+ <div class="mt-gridview__detail">
9
+ <span class="mt-gridview__author">{{ review.display_name.substr(0, 16) }}</span>
10
+ <rating :rating="review.rating" :color="starColor" />
11
+ <verified-purchase v-if="review.verified_purchase" />
12
+ <span class="mt-gridview__body">{{ review.body.substr(0, 250) }}</span>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ import Rating from '../snippets/Rating.vue';
20
+ import CardReview from "./CardReview.vue";
21
+ import VerifiedPurchase from './VerifiedPurchase.vue';
22
+
23
+ export default {
24
+ name: "GridView",
25
+
26
+ components: {
27
+ CardReview,
28
+ Rating,
29
+ VerifiedPurchase,
30
+ },
31
+
32
+ props: {
33
+ starColor: {
34
+ type: String,
35
+ default: "#212529",
36
+ },
37
+
38
+ reviews: {
39
+ type: Object,
40
+ default: {
41
+ list: [],
42
+ orderRating: null,
43
+ total: 0,
44
+ },
45
+ },
46
+ },
47
+
48
+ methods: {
49
+ openQuickview: function (review) {
50
+ this.$emit('openQuickview', { review })
51
+ },
52
+
53
+ getPictureURL(review) {
54
+ if (review && review.pictures && review.pictures.length && review.pictures[0].normal) {
55
+ return review.pictures[0].normal
56
+ }
57
+
58
+ return false
59
+ },
60
+ },
61
+ };
62
+ </script>
63
+
64
+ <style lang="scss">
65
+ .mt-gridview {
66
+ transition: all 0.7s ease;
67
+ column-count: 2;
68
+ column-gap: 10px;
69
+ margin-right: auto;
70
+ margin-left: auto;
71
+
72
+ @media only screen and (min-width: 600px) {
73
+ column-count: 3;
74
+ }
75
+
76
+ @media only screen and (min-width: 768px) {
77
+ column-count: 4;
78
+ }
79
+
80
+ @media only screen and (min-width: 992px) {
81
+ column-count: 5;
82
+ }
83
+
84
+ &__card {
85
+ width: 100%;
86
+ border: 1px #d5d9d9 solid;
87
+ border-radius: 6px;
88
+ cursor: pointer;
89
+ margin: 0;
90
+ display: grid;
91
+ grid-template-rows: 1fr auto;
92
+ margin-bottom: 10px;
93
+ break-inside: avoid;
94
+ }
95
+
96
+ &__detail {
97
+ padding: 10px;
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: 8px;
101
+ }
102
+
103
+ &__thumb {
104
+ img {
105
+ max-width: 250px;
106
+ width: 100%;
107
+ object-fit: contain;
108
+ border-top-right-radius: 5px;
109
+ border-top-left-radius: 5px;
110
+ }
111
+ }
112
+ }
113
+ </style>