PageSourceSearch

https://vietcare.com.vn/wp-content/plugins/reviewx/resources/js/reviewx-storefront.js?ver=6.8.10

js vietcare.com.vn collected 2026-10-06 07:32:37 UTC 44,845 bytes, 1,065 lines download raw bytes

1window.__reviewXState__ = function () {
2    return {
3        rvxAttributes: {
4            // settings: {
5            //     isVerified: 'no',
6            // },
7            product: {
8                id: null,
9                postType: 'product',
10            },
11            userInfo: {
12                isLoggedIn: null,
13                id: null,
14                name: null,
15                email: null,
16                isVerified: false,
17            },
18            baseDomain: '',
19            baseRestUrl: '',
20            formLevelData: {}
21        },
22        newReview: {
23            rating: 5,
24            reviewer_email: '',
25            reviewer_name: '',
26            is_anonymous: false,
27            consent: false,
28            title: '',
29            feedback: '',
30            is_recommended: 'true'
31        },
32        isRvxUserLoggedIn: false,
33        isRvxUserVerified: false,
34        fetchReviewsIsLoading: true,
35        isReviewSubmitting: false,
36        fetchReviewsSettingsIsLoading: false,
37        haveReviews: false,
38        isFiltering: false,
39        reviewsData: null,
40        reviewsAllData: null,
41        reviewListShortCodeData: null,
42        reviewWithReviewIdsShortCodeData: null,
43        reviewSettingsData: undefined,
44        reviewAggregationData: null,
45        isLoadMoreReviews: false,
46        isLoadMoreAllReviews: false,
47        layoutView: 'list',
48        formatAggregations: [],
49        formatMultiCriteriaAggregations: [],
50        multiCriteriaRatings: [],
51        attachmentFiles: [],
52        showReviewSuccessModal: false,
53        showReviewDetailsModal: false,
54        selectedReviewDetails: {},
55        storeFrontReviewQuery: {},
56        storeFrontAllReviewQuery: {},
57        storeFrontAllReviewParams: {},
58        storeFrontReviewListShortcodeQuery: {},
59        storeFrontValidation: {
60            isValidReviewTitle: false,
61            isValidReviewFeedback: false,
62            isValidReviewerName: false,
63            isValidReviewerEmail: false,
64            isNotRobot: false,
65            reviewSubmitFailed: false,
66            isAcceptConsent: false
67        },
68        showErrorToastMessage: false,
69        errorToastMessage: '',
70
71        rvxAttributesDataSetHandler(rvxAttributes) {
72            this.rvxAttributes = {
73                ...this.rvxAttributes,
74                ...rvxAttributes,
75                baseDomain: `${rvxAttributes.domain.baseDomain}/wp-json/reviewx/api/v1/storefront`,
76                baseRestUrl: `${rvxAttributes.domain.baseRestUrl}/api/v1/storefront`
77            }
78            this.isRvxUserLoggedIn = this.rvxAttributes?.userInfo?.isLoggedIn ? true : false
79            this.isRvxUserVerified = this.rvxAttributes?.userInfo?.isVerified ? true : false
80        },
81        selectedReviewDetailsHandler(review) {
82            this.selectedReviewDetails = review
83            this.showReviewDetailsModal = true
84        },
85        appendReviews(newRes) {
86            this.reviewsData = {
87                ...this.reviewsData,
88                data: {
89                    ...this.reviewsData.data,
90                    reviews: [
91                        ...this.reviewsData.data.reviews,
92                        ...newRes.data.reviews],
93                    meta: { ...this.reviewsData.data.meta, ...newRes.data.meta },
94                }
95            };
96        },
97        appendAllReviews(newRes) {
98            this.reviewsAllData = {
99                ...this.reviewsAllData,
100                data: {
101                    ...this.reviewsAllData.data,
102                    reviews: [
103                        ...this.reviewsAllData.data.reviews,
104                        ...newRes.data.reviews],
105                    meta: { ...this.reviewsAllData.data.meta, ...newRes.data.meta },
106                }
107            };
108        },
109        async fetchReviews({ query, loadMoreReview, productId }) {
110            if (loadMoreReview) {
111                this.isLoadMoreReviews = true
112            } else {
113                this.fetchReviewsIsLoading = true;
114            }
115            let queryParams;
116            if (query) {
117                const newQuery = {
118                    ...query
119                }
120                queryParams = this.generateQueryParams(newQuery)
121            }
122            try {
123                const url = `${this.rvxAttributes.baseRestUrl}/${productId}/reviews${queryParams ? '?' + queryParams : ''}`;
124                const data = await fetch(url);
125                const res = await data.json();
126                if (loadMoreReview && this.reviewsData?.data?.reviews?.length) {
127                    this.appendReviews(res);
128                } else {
129                    this.reviewsData = res;
130                    this.reviewListShortCodeData = res;
131                }
132
133                if (res?.data?.reviews?.length) {
134                    this.haveReviews = true;
135                }
136            } catch (e) {
137                // console.log('Error fetching reviews:', e);
138            } finally {
139                this.fetchReviewsIsLoading = false;
140                this.isLoadMoreReviews = false
141            }
142        },
143        async fetchReviewsListShortcode({ query, loadMoreReview, productId }) {
144            if (loadMoreReview) {
145                this.isLoadMoreReviews = true
146            } else {
147                this.fetchReviewsIsLoading = true;
148            }
149            let queryParams;
150            if (query) {
151                const newQuery = {
152                    ...query
153                }
154                queryParams = this.generateQueryParams(newQuery)
155            }
156            try {
157                const url = `${this.rvxAttributes.baseRestUrl}/${productId}/reviews/shortcode${queryParams ? '?' + queryParams : ''}`;
158                // console.log('URL =====', url)
159                const data = await fetch(url);
160                const res = await data.json();
161                if (loadMoreReview && this.reviewsData?.data?.reviews?.length) {
162                    this.appendReviews(res);
163                } else {
164                    this.reviewsData = res;
165                    this.reviewListShortCodeData = res;
166                }
167
168                if (res?.data?.reviews?.length) {
169                    this.haveReviews = true;
170                }
171            } catch (e) {
172                // console.log('Error fetching reviews:', e);
173            } finally {
174                this.fetchReviewsIsLoading = false;
175                this.isLoadMoreReviews = false
176            }
177        },
178
179        // //////////////////////////
180        // async fetchReviewListShortCodes(ids) {
181        //     this.fetchReviewsIsLoading = true;
182        //     const reviewListShortCodeIds = ids?.[0]
183        //     if (!reviewListShortCodeIds) return
184        //     try {
185        //         const url = `${this.rvxAttributes.baseRestUrl}/${reviewListShortCodeIds}/reviews`;
186        //         const data = await fetch(url);
187        //         const res = await data.json();
188        //         if (!res?.data?.reviews?.length) {
189        //             this.haveReviews = true; // No more reviews available
190        //         } else {
191        //             this.reviewListShortCodeData = res;
192        //         }
193        //     } catch (e) {
194        //         console.log('Error fetching reviews:', e);
195        //     } finally {
196        //         this.fetchReviewsIsLoading = false;
197        //     }
198        // },
199        // //////////////////////////
200
201        async fetchAllReviewListShortCodes({ query, loadMoreReview }) {
202            if (loadMoreReview) {
203                this.isLoadMoreAllReviews = true
204            } else {
205                this.fetchReviewsIsLoading = true;
206            }
207            let queryParams;
208            if (query) {
209                const newQuery = {
210                    ...query
211                }
212                queryParams = this.generateQueryParams(newQuery)
213            }
214            try {
215                const params = {}
216
217                // Only include if value is a non-empty string
218                if (typeof this.storeFrontAllReviewParams?.post_type === 'string' && this.storeFrontAllReviewParams.post_type.trim() !== '') {
219                    params.post_type = this.storeFrontAllReviewParams.post_type.trim()
220                }
221                if (typeof this.storeFrontAllReviewParams?.sort_by === 'string' && this.storeFrontAllReviewParams.sort_by.trim() !== '') {
222                    params.sort_by = this.storeFrontAllReviewParams.sort_by.trim()
223                }
224                if (typeof this.storeFrontAllReviewParams?.rating === 'string' && this.storeFrontAllReviewParams.rating.trim() !== '') {
225                    params.rating = this.storeFrontAllReviewParams.rating.trim()
226                }
227                if (typeof this.storeFrontAllReviewParams?.attachment === 'string' && this.storeFrontAllReviewParams.attachment.trim() !== '') {
228                    params.attachment = this.storeFrontAllReviewParams.attachment.trim()
229                }
230
231                // Build query string for dynamic params
232                const dynamicParams = new URLSearchParams(params).toString()
233
234                let queryString = ''
235
236                if (dynamicParams && queryParams) {
237                    queryString = `?${dynamicParams}&${queryParams}`
238                } else if (dynamicParams) {
239                    queryString = `?${dynamicParams}`
240                } else if (queryParams) {
241                    queryString = `?${queryParams}`
242                }
243
244                const url = `${this.rvxAttributes.baseRestUrl}/all/reviews/shortcode${queryString}`
245
246                const data = await fetch(url, {
247                    method: 'POST',
248                    headers: { 'Content-Type': 'application/json' },
249                    body: JSON.stringify(query),
250                })
251                const res = await data.json()
252
253
254                if (loadMoreReview && this.reviewsAllData?.data?.reviews?.length) {
255                    this.appendAllReviews(res);
256                } else {
257                    this.reviewsAllData = res;
258                }
259
260                if (res?.data?.reviews?.length) {
261                    this.haveReviews = true;
262                }
263            } catch (e) {
264                console.log('Error fetching reviews:', e);
265            } finally {
266                this.fetchReviewsIsLoading = false;
267                this.isLoadMoreAllReviews = false
268            }
269        },
270
271        async fetchReviewListWithIdsShortCodes(ids) {
272            this.fetchReviewsIsLoading = true;
273            const reviewsShortCodeIds = ids
274            if (!reviewsShortCodeIds?.length) return
275            const payload = {
276                review_ids: reviewsShortCodeIds
277            }
278            try {
279                const url = `${this.rvxAttributes.baseRestUrl}/widgets/short/code/reviews`;
280                const data = await fetch(url, {
281                    method: 'POST',
282                    body: JSON.stringify(payload),
283                    headers: {
284                        'Content-Type': 'application/json'
285                    }
286                });
287                const res = await data.json();
288                // console.log('res =====', res)
289                if (!res?.data?.reviews?.length) {
290                    this.haveReviews = true; // No more reviews available
291                } else {
292                    this.reviewWithReviewIdsShortCodeData = res;
293                }
294            } catch (e) {
295                console.log('Error fetching reviews:', e);
296            } finally {
297                this.fetchReviewsIsLoading = false;
298            }
299        },
300        loadMoreReviewHandler() {
301            // this.isLoadMoreReviews = true;
302            const cursor = this.reviewsData?.data?.meta?.next_cursor;
303            if (cursor) {
304                this.storeFrontReviewQuery = {
305                    ...this.storeFrontReviewQuery,
306                    cursor
307                };
308                this.fetchReviews({
309                    query: this.storeFrontReviewQuery,
310                    loadMoreReview: true,
311                    productId: this.rvxAttributes?.product?.id
312                });
313            }
314        },
315        loadMoreReviewAllHandler() {
316            // this.isLoadMoreAllReviews = true;
317            const cursor = this.reviewsAllData?.data?.meta?.next_cursor;
318            if (cursor) {
319                this.storeFrontAllReviewQuery = {
320                    ...this.storeFrontAllReviewQuery,
321                    cursor
322                };
323                this.fetchAllReviewListShortCodes({
324                    query: this.storeFrontAllReviewQuery,
325                    loadMoreReview: true,
326                });
327            }
328        },
329        async fetchReviewListShortcodeReviews({ query, loadMoreReview, productId }) {
330            if (loadMoreReview) {
331                this.isLoadMoreReviews = true;
332            } else {
333                this.fetchReviewsIsLoading = true;
334            }
335            let queryParams;
336            if (query) {
337                const newQuery = { ...query };
338                queryParams = this.generateQueryParams(newQuery);
339            }
340            try {
341                const url = `${this.rvxAttributes.baseRestUrl}/${productId}/reviews${queryParams ? '?' + queryParams : ''}`;
342                const data = await fetch(url);
343                const res = await data.json();
344
345                if (loadMoreReview) {
346                    // Append new reviews to existing data
347                    if (this.reviewListShortCodeData?.data?.reviews) {
348                        this.reviewListShortCodeData = {
349                            ...this.reviewListShortCodeData,
350                            data: {
351                                ...this.reviewListShortCodeData.data,
352                                reviews: [...this.reviewListShortCodeData.data.reviews, ...res.data.reviews],
353                                meta: res.data.meta // Update next_cursor
354                            }
355                        };
356                    } else {
357                        this.reviewListShortCodeData = res;
358                    }
359                } else {
360                    // Initial load
361                    this.reviewListShortCodeData = res;
362                }
363
364                if (res?.data?.reviews?.length) {
365                    this.haveReviews = true;
366                }
367            } catch (e) {
368                console.log('Error fetching reviews:', e);
369            } finally {
370                this.fetchReviewsIsLoading = false;
371                this.isLoadMoreReviews = false;
372            }
373        },
374        async fetchAllReviewListShortcodeReviews({ query, loadMoreReview }) {
375            if (loadMoreReview) {
376                this.isLoadMoreAllReviews = true;
377            } else {
378                this.fetchReviewsIsLoading = true;
379            }
380            let queryParams;
381            if (query) {
382                queryParams = query;
383            }
384            try {
385                const url = `${this.rvxAttributes.baseRestUrl}/all/reviews/shortcode`;
386                const data = await fetch(url, {
387                    method: 'POST', // Use POST instead of GET
388                    headers: {
389                        'Content-Type': 'application/json',
390                    },
391                    body: JSON.stringify(queryParams),
392                });
393                const res = await data.json();
394
395                if (loadMoreReview) {
396                    // Append new reviews to existing data
397                    if (this.reviewListShortCodeData?.data?.reviews) {
398                        this.reviewListShortCodeData = {
399                            ...this.reviewListShortCodeData,
400                            data: {
401                                ...this.reviewListShortCodeData.data,
402                                reviews: [...this.reviewListShortCodeData.data.reviews, ...res.data.reviews],
403                                meta: res.data.meta // Update next_cursor
404                            }
405                        };
406                    } else {
407                        this.reviewListShortCodeData = res;
408                    }
409                } else {
410                    // Initial load
411                    this.reviewListShortCodeData = res;
412                }
413
414                if (res?.data?.reviews?.length) {
415                    this.haveReviews = true;
416                }
417            } catch (e) {
418                console.log('Error fetching reviews:', e);
419            } finally {
420                this.fetchReviewsIsLoading = false;
421                this.isLoadMoreAllReviews = false;
422            }
423        },
424        loadMoreReviewListShortcodeHandler() {
425            const cursor = this.reviewListShortCodeData?.data?.meta?.next_cursor;
426            if (cursor) {
427                this.storeFrontReviewListShortcodeQuery = {
428                    ...this.storeFrontReviewListShortcodeQuery,
429                    cursor
430                };
431                this.fetchReviewListShortcodeReviews({
432                    query: this.storeFrontReviewListShortcodeQuery,
433                    loadMoreReview: true,
434                    productId: this.reviewListShortCodeData?.data?.reviews[0]?.product_wp_id
435                });
436                console.log('Product ID: ', storeFrontReviewListShortcodeQuery);
437            }
438        },
439        loadMoreAllReviewListShortcodeHandler() {
440            const cursor = this.reviewListShortCodeData?.data?.meta?.next_cursor;
441            const per_page = this.reviewListShortCodeData?.data?.meta?.per_page;
442
443            if (cursor) {
444                this.storeFrontReviewListShortcodeQuery = {
445                    ...this.storeFrontReviewListShortcodeQuery,
446                    cursor,
447                    per_page
448                };
449                this.fetchAllReviewListShortcodeReviews({
450                    query: this.storeFrontReviewListShortcodeQuery,
451                    loadMoreReview: true
452                });
453                console.log('Product ID: ', storeFrontReviewListShortcodeQuery);
454            }
455        },
456        async fetchReviewsSettings() {
457            this.fetchReviewsSettingsIsLoading = true;
458            let postType = this.rvxAttributes?.product?.postType ? this.rvxAttributes?.product?.postType : 'product';
459            try {
460                const data = await fetch(`${this.rvxAttributes.baseRestUrl}/wp/settings${'?cpt_type=' + postType}`);
461                const res = await data.json();
462                this.reviewSettingsData = res;
463                this.layoutView = res?.data.setting?.widget_settings?.outline?.layout_type
464                // console.log('Settings:', res);
465                if (res?.data?.setting?.review_settings) {
466                    this.multiCriteriaRatings = this.createMultiCriteriaRatingObject({
467                        criterias: res?.data.setting.review_settings.reviews.multicriteria.criterias
468                    })
469                }
470
471                if (res.data.setting.review_settings?.reviews?.recaptcha?.enabled) {
472                    const script = document.createElement('script');
473                    script.src = `https://www.google.com/recaptcha/api.js?render=${res.data.setting.review_settings?.reviews?.recaptcha.site_key}`
474                    document.head.appendChild(script);
475                }
476            } catch (e) {
477                console.log('Error fetching settings:', e);
478            } finally {
479                this.fetchReviewsSettingsIsLoading = false;
480            }
481        },
482        async fetchReviewsAggregation({ productId }) {
483            // console.log('Fetching review Aggregation');
484            this.fetchReviewsSettingsIsLoading = true;
485            try {
486                const data = await fetch(`${this.rvxAttributes.baseRestUrl}/${productId}/insight`);
487                const res = await data.json();
488                if (res.data?.aggregation) {
489                    this.formatAggregations = [
490                        {
491                            rating: 5,
492                            total: res?.data?.aggregation.five.total,
493                            percent: res?.data.aggregation.five.percentage
494                        },
495                        {
496                            rating: 4,
497                            total: res?.data?.aggregation.four.total,
498                            percent: res?.data.aggregation.four.percentage
499                        },
500                        {
501                            rating: 3,
502                            total: res?.data?.aggregation.three.total,
503                            percent: res?.data.aggregation.three.percentage
504                        },
505                        {
506                            rating: 2,
507                            total: res?.data?.aggregation.two.total,
508                            percent: res?.data.aggregation.two.percentage
509                        },
510                        {
511                            rating: 1,
512                            total: res?.data?.aggregation.one.total,
513                            percent: res?.data.aggregation.one.percentage
514                        },
515                    ]
516                    this.formatMultiCriteriaAggregations = this.formattedMultiCriteriaAggregation({
517                        criteriaStats: res.data.criteria_stats,
518                        criterias: this.reviewSettingsData.data.setting.review_settings.reviews.multicriteria.criterias,
519                        totalAvaReview: res.data?.aggregation.total_avg_review
520                    })
521                }
522                this.reviewAggregationData = res;
523
524            } catch (e) {
525                console.log('Error fetching settings:', e);
526            } finally {
527                this.fetchReviewsSettingsIsLoading = false;
528            }
529        },
530        formattedMultiCriteriaAggregation({ criteriaStats, criterias, totalAvaReview, percentDefaultValue = 5 }) {
531            return criterias?.map((criteria) => {
532                const total = criteriaStats[criteria.key] || 0;
533                let modifyTotal
534                if (total === 0 && totalAvaReview > 0) {
535                    modifyTotal = totalAvaReview
536                } else {
537                    modifyTotal = total
538                }
539                const percent = (modifyTotal / percentDefaultValue) * 100;
540                return {
541                    key: criteria.key,
542                    name: criteria.name,
543                    total: modifyTotal,
544                    // total,
545                    percent,
546                };
547            });
548        },
549        createMultiCriteriaRatingObject({ criterias, criteriaStats, existingRating }) {
550            const newCriterial = criterias.map((criteria) => {
551                if (criteriaStats) {
552                    let rating;
553                    const getRating = criteriaStats?.[criteria.key];
554                    if (getRating) {
555                        rating = getRating;
556                    } else if (existingRating) {
557                        rating = Number(existingRating);
558                    } else {
559                        rating = 5
560                    }
561                    return {
562                        ...criteria,
563                        rating,
564                    };
565                } else {
566                    return {
567                        ...criteria,
568                        rating: 5,
569                    };
570                }
571            });
572            return newCriterial;
573        },
574        notifyAttachmentAdded(files) {
575            this.attachmentFiles = files.blobFiles
576        },
577        multiCriteriaRatingsToObject(array) {
578            return array.reduce((acc, curr) => {
579                acc[curr.key] = curr.rating;
580                return acc;
581            }, {});
582        },
583        googleRecaptchaVerify() {
584            return new Promise((resolve, reject) => {
585                const siteKey = this.reviewSettingsData?.data?.setting.review_settings?.reviews?.recaptcha.site_key;
586                if (!siteKey || !grecaptcha) {
587                    return reject('Recaptcha or site key is not defined');
588                }
589                const baseAPIEndPoint = this.rvxAttributes.baseRestUrl
590                grecaptcha.ready(function () {
591                    grecaptcha.execute(siteKey, { action: 'submit' }).then(async function (token) {
592                        try {
593                            const payload = {
594                                token
595                            }
596                            const data = await fetch(`${baseAPIEndPoint}/google/recaptcha/verify`, {
597                                method: 'post',
598                                body: JSON.stringify(payload),
599                                headers: {
600                                    'Content-Type': 'application/json',
601                                }
602                            })
603                            const res = await data.json()
604                            resolve(res.result);
605                        } catch (e) {
606                            reject(e);
607                        }
608                    }).catch(function (error) {
609                        console.error('Recaptcha error:', error);
610                        reject(error);
611                    });
612                });
613            });
614        },
615        checkValidity(field) {
616            this.storeFrontValidation[field] = !!this.newReview[field];
617        },
618        async fetchUserIPAddress() {
619            try {
620                const response = await fetch('https://api.ipify.org?format=json');
621                const data = await response.json();
622                // console.log('Your IP address is:', data.ip);
623                return data.ip;
624            } catch (error) {
625                console.error('Error fetching IP address:', error);
626                return null; // Handle error as needed
627            }
628        },
629        async reviewFormValidation() {
630            let isValid = true;
631            const allowReviewTitle = this.reviewSettingsData?.data?.setting?.review_settings.reviews.allow_review_titles
632            this.storeFrontValidation = {
633                isValidReviewTitle: false,
634                isValidReviewFeedback: false,
635                isValidReviewerName: false,
636                isValidReviewerEmail: false,
637                isNotRobot: false,
638                reviewSubmitFailed: false,
639                isAcceptConsent: false
640            }
641
642            if (this.reviewSettingsData?.data?.setting.review_settings?.reviews?.recaptcha?.enabled) {
643                const res = await this.googleRecaptchaVerify()
644                this.storeFrontValidation.isNotRobot = res.result;
645                isValid = !res.result
646            }
647            if (this.reviewSettingsData?.data?.setting.review_settings?.reviews?.show_consent_checkbox?.enabled && !this.newReview.consent) {
648                this.storeFrontValidation.isAcceptConsent = true;
649                isValid = false;
650            }
651
652            if (this.isRvxUserLoggedIn) {
653                if (allowReviewTitle && !this.newReview.title) {
654                    this.storeFrontValidation.isValidReviewTitle = true;
655                    isValid = false;
656                }
657                if (!this.newReview.feedback) {
658                    this.storeFrontValidation.isValidReviewFeedback = true;
659                    isValid = false;
660                }
661            } else {
662                if (allowReviewTitle && !this.newReview.title) {
663                    this.storeFrontValidation.isValidReviewTitle = true;
664
665                    isValid = false;
666                }
667                if (!this.newReview.feedback) {
668                    this.storeFrontValidation.isValidReviewFeedback = true;
669                    isValid = false;
670                }
671
672                if (!this.newReview.reviewer_name) {
673                    this.storeFrontValidation.isValidReviewerName = true;
674                    isValid = false;
675                }
676                if (!this.newReview.reviewer_email) {
677                    this.storeFrontValidation.isValidReviewerEmail = true;
678                    isValid = false;
679                }
680            }
681
682            return isValid
683        },
684        async reviewSubmitHandler() {
685            const result = await this.reviewFormValidation()
686            console.log('validate result ===========', result)
687            const userIP = await this.fetchUserIPAddress()
688            if (!result) return
689            this.isReviewSubmitting = true
690            const newCriterias = this.multiCriteriaRatingsToObject(this.multiCriteriaRatings)
691            const updatedReviewPayload = {
692                feedback: this.newReview.feedback,
693                reviewer_name: this.isRvxUserLoggedIn ? this.rvxAttributes.userInfo?.name : this.newReview.reviewer_name,
694                reviewer_email: this.isRvxUserLoggedIn ? this.rvxAttributes.userInfo?.email : this.newReview.reviewer_email,
695                user_id: this.isRvxUserLoggedIn ? this.rvxAttributes.userInfo?.id : 0,
696                criterias: newCriterias,
697                rating: this.newReview.rating,
698                is_anonymous: this.newReview.is_anonymous,
699                wp_post_id: this.reviewAggregationData?.data?.product?.wp_id,
700                ip: userIP ?? '0.0.0.0'
701            }
702            if (this.reviewSettingsData?.data?.setting?.review_settings.reviews.allow_recommendations) {
703                updatedReviewPayload.is_recommended = this.newReview.is_recommended;
704            }
705
706            if (this.reviewSettingsData?.data?.setting?.review_settings.reviews.allow_review_titles) {
707                updatedReviewPayload.title = this.newReview.title;
708            }
709
710            if (this.reviewSettingsData?.data?.setting?.review_settings.reviews.photo_reviews_allowed && this.attachmentFiles.length) {
711                updatedReviewPayload.attachments = this.attachmentFiles;
712            }
713
714            const payload = this.generateFormData({ objectData: updatedReviewPayload })
715            // payload.forEach((value, key) => {
716            //     if (value instanceof File) {
717            //         console.log(
718            //             `${key}: [File] Name: ${value.name}, Size: ${value.size}, Type: ${value.type}`
719            //         );
720            //     } else {
721            //         console.log(`${key}: ${value}`);
722            //     }
723            // });
724            try {
725                const data = await fetch(`${this.rvxAttributes.baseRestUrl}/reviews`, {
726                    method: 'post',
727                    body: payload,
728                })
729                const res = await data.json()
730                // console.log('res =====', res.code)
731                // if(res.code)
732                this.notifyReviewAdded()
733
734                this.newReview = {
735                    rating: 5,
736                    reviewer_name: this.isRvxUserLoggedIn ? this.rvxAttributes.userInfo?.name : '',
737                    reviewer_email: this.isRvxUserLoggedIn ? this.rvxAttributes.userInfo?.email : '',
738                    is_anonymous: false,
739                    consent: false,
740                    title: '',
741                    feedback: '',
742                    is_recommended: 'true'
743                }
744
745                this.showReviewSuccessModal = true
746                document.getElementById('rvx-storefront-widget').scrollIntoView({ behavior: 'smooth' });
747                if (this.reviewSettingsData?.data?.setting?.review_settings?.reviews?.auto_approve_reviews) {
748                    await this.fetchReviews({ productId: this.rvxAttributes?.product?.id });
749                }
750            } catch (error) {
751                console.log('error ======', error);
752            } finally {
753                this.isReviewSubmitting = false
754            }
755        },
756        notifyReviewAdded() {
757            this.$dispatch('notify-review-added', { message: 'Success' });
758        },
759        generateQueryParams(params) {
760            const queryParams = new URLSearchParams();
761            for (const key in params) {
762                if (params.hasOwnProperty(key)) {
763                    let value = params[key];
764                    // Handle array values by appending multiple times with the same key
765                    if (Array.isArray(value)) {
766                        value.forEach((item) => queryParams.append(key, item));
767                    } else if (value !== null && value !== undefined) {
768                        // Only append non-null and non-undefined values
769                        queryParams.append(key, value);
770                    }
771                }
772            }
773            return queryParams.toString();
774        },
775        isObject(value) {
776            return (
777                typeof value === 'object' &&
778                value !== null &&
779                !Array.isArray(value) &&
780                !(value instanceof File)
781            );
782        },
783        avatarComponent({ src = '', alt = 'Default Name' }) {
784            return {
785                hasError: false,
786                src: src,
787                alt: alt,
788                showImage() {
789                    return this.src && !this.hasError;
790                },
791                onError() {
792                    this.hasError = true;
793                }
794            };
795        },
796        copyClipboardComponent() {
797            return {
798                copySuccess: false,
799                copyClipboard(input) {
800                    navigator.clipboard.writeText(input)
801                        .then(() => {
802                            this.copySuccess = true;
803                            setTimeout(() => this.copySuccess = false, 1000);
804                        })
805                        .catch(err => {
806                            console.error('Failed to copy: ', err);
807                        });
808                }
809            };
810        },
811        reviewHelpInfoComponent() {
812            return {
813                async likeDislikeHandler({ preference, uid, review }) {
814                    if (!this.isRvxUserLoggedIn) {
815                        this.errorToastMessage = 'Please login first';
816                        this.showErrorToastMessage = true
817                        setTimeout(() => this.showErrorToastMessage = false, 3000);
818                        return;
819                    }
820
821                    // Save original state for possible rollback
822                    const originalLikes = review.likes;
823                    const originalDislikes = review.dislikes;
824                    const originalPreference = review.preference ? { ...review.preference } : null;
825
826                    const prevPreference = review.preference?.preference;
827
828                    // Optimistic update
829                    if (prevPreference === preference) {
830                        // Toggle off
831                        if (preference === 1) review.likes--;
832                        if (preference === 2) review.dislikes--;
833                        review.preference = null;
834                    } else {
835                        // Change preference
836                        if (prevPreference === 1) review.likes--;
837                        if (prevPreference === 2) review.dislikes--;
838
839                        if (preference === 1) review.likes++;
840                        if (preference === 2) review.dislikes++;
841
842                        review.preference = { preference };
843                    }
844
845                    const payload = {
846                        preference,
847                    };
848                    try {
849                        const res = await fetch(`${this.rvxAttributes.baseRestUrl}/reviews/${uid}/preference`, {
850                            method: 'POST',
851                            headers: {
852                                'Content-Type': 'application/json',
853                            },
854                            body: JSON.stringify(payload)
855                        });
856
857                        if (!res.ok) {
858                            throw new Error('API request failed');
859                        }
860                    } catch (error) {
861                        console.log('error =======', error);
862                        // Rollback on error
863                        review.likes = originalLikes;
864                        review.dislikes = originalDislikes;
865                        review.preference = originalPreference;
866
867                        this.errorToastMessage = 'Something went wrong. Please try again later.';
868                        this.showErrorToastMessage = true;
869                        setTimeout(() => this.showErrorToastMessage = false, 3000);
870                    }
871                }
872            }
873        },
874        generateFormData({ objectData, fileKey }) {
875            const formData = new FormData();
876            const appendFormData = (key, value) => {
877                if (value instanceof File && fileKey) {
878                    formData.append(fileKey, value);
879                } else if (value instanceof File) {
880                    formData.append(key, value);
881                } else if (this.isObject(value)) {
882                    for (const subKey in value) {
883                        if (Object.prototype.hasOwnProperty.call(value, subKey)) {
884                            appendFormData(`${key}[${subKey}]`, value[subKey]);
885                        }
886                    }
887                } else if (Array.isArray(value)) {
888                    value.forEach((item, index) => {
889                        if (item instanceof File) {
890                            const fileArrayKey = fileKey
891                                ? `${fileKey}[${index}]`
892                                : `${key}[${index}]`;
893                            formData.append(fileArrayKey, item);
894                        } else if (this.isObject(item)) {
895                            for (const subKey in item) {
896                                if (Object.prototype.hasOwnProperty.call(item, subKey)) {
897                                    appendFormData(`${key}[${index}][${subKey}]`, item[subKey]);
898                                }
899                            }
900                        } else {
901                            formData.append(`${key}[${index}]`, item);
902                        }
903                    });
904                } else {
905                    formData.append(key, value);
906                }
907            };
908
909            for (const key in objectData) {
910                if (Object.prototype.hasOwnProperty.call(objectData, key)) {
911                    const value = objectData[key];
912                    appendFormData(key, value);
913                }
914            }
915
916            return formData;
917        },
918
919        async initialize(data) {
920            const parseAttData = JSON.parse(data.data)
921            const parseFormLabels = JSON.parse(data.formLevelData)
922            const concatValue = {
923                ...parseAttData,
924                formLevelData: {
925                    ...parseFormLabels
926                }
927            }
928            this.rvxAttributesDataSetHandler(concatValue)
929            try {
930                await this.fetchReviewsSettings();
931                if (this.rvxAttributes?.product?.id) {
932                    await this.fetchReviewsAggregation({ productId: this.rvxAttributes?.product?.id });
933                    await this.fetchReviews({ productId: this.rvxAttributes?.product?.id });
934                }
935            } catch (error) {
936                console.error('Error during initialization:', error);
937            }
938        },
939
940        async initializeReviewsListShortcode(data) {
941            const parseAttData = JSON.parse(data.data)
942            const parseFormLabels = JSON.parse(data.formLevelData)
943            const parseAttributes = JSON.parse(data.attributes)
944            const concatValue = {
945                ...parseAttData,
946                formLevelData: {
947                    ...parseFormLabels
948                }
949            }
950            this.rvxAttributesDataSetHandler(concatValue)
951            try {
952                await this.fetchReviewsSettings();
953                if (this.rvxAttributes?.product?.id && (parseAttributes?.graph == 'on' || parseAttributes?.form == 'on')) {
954                    await this.fetchReviewsAggregation({ productId: this.rvxAttributes?.product?.id });
955                }
956                if (this.rvxAttributes?.product?.id && parseAttributes?.list == 'on') {
957                    await this.fetchReviewsListShortcode({ productId: this.rvxAttributes?.product?.id });
958                }
959            } catch (error) {
960                console.error('Error during initialization:', error);
961            }
962        },
963
964        async initializeMyAccountReviewForm(data) {
965            // const parseData = JSON.parse(data.data)
966            // console.log('init 2')
967            const parseAttData = JSON.parse(data.data)
968            const parseFormLabels = JSON.parse(data?.formLevelData)
969            // console.log('parseAttData', parseAttData)
970            // console.log('parseFormLabels', parseFormLabels)
971            const concatValue = {
972                ...parseAttData,
973                formLevelData: {
974                    ...parseFormLabels
975                }
976            }
977            // console.log('concatValue', concatValue)
978            this.rvxAttributesDataSetHandler(concatValue)
979            // this.rvxAttributesDataSetHandler(parseData)
980            try {
981                await this.fetchReviewsSettings();
982            } catch (error) {
983                console.error('Error during initialization:', error);
984            }
985        },
986        async initializeMyAccountReviewFormOnProductChange(productId) {
987            try {
988                // console.log('init 3')
989                await this.fetchReviewsAggregation({ productId: productId });
990            } catch (error) {
991                console.error('Error during initialization:', error);
992            }
993        },
994        async initializeReviewGraphShortCodes(data) {
995            const parseData = JSON.parse(data.data)
996            this.rvxAttributesDataSetHandler(parseData)
997            // console.log('init 4')
998            try {
999                await this.fetchReviewsSettings();
1000                await this.fetchReviewsAggregation({ productId: parseData.product.id });
1001            } catch (error) {
1002                console.error('Error during initialization:', error);
1003            }
1004        },
1005
1006        // //////////////////////////
1007        // async initializeReviewListShortCodes(data) {
1008        //     const parseData = JSON.parse(data.data)
1009        //     this.rvxAttributesDataSetHandler(parseData)
1010        //     try {
1011        //         // console.log('init 5')
1012        //         await this.fetchReviewsSettings();
1013        //         await this.fetchReviewListShortCodes(parseData.ids);
1014        //     } catch (error) {
1015        //         console.error('Error during initialization:', error);
1016        //     }
1017        // },
1018        // ///////////////////////////
1019
1020        async initializeAllReviewListShortCodes(data) {
1021            const parseData = JSON.parse(data.data)
1022            this.storeFrontAllReviewParams = parseData.params
1023            this.rvxAttributesDataSetHandler(parseData)
1024            try {
1025                await this.fetchReviewsSettings();
1026                await this.fetchAllReviewListShortCodes(parseData);
1027            } catch (error) {
1028                console.error('
1028Error during initialization:', error);
1029            }
1030        },
1031        async initializeReviewWithReviewIdsShortCodes(data) {
1032            const parseData = JSON.parse(data.data)
1033            this.rvxAttributesDataSetHandler(parseData)
1034            try {
1035                // console.log('init 6')
1036                await this.fetchReviewsSettings();
1037                await this.fetchReviewListWithIdsShortCodes(parseData.ids);
1038            } catch (error) {
1039                console.error('Error during initialization:', error);
1040            }
1041        },
1042        async initializeReviewStatsShortCodes(data) {
1043            const parseData = JSON.parse(data.data)
1044            this.rvxAttributesDataSetHandler(parseData)
1045            try {
1046                // console.log('init 7')
1047                await this.fetchReviewsSettings();
1048                await this.fetchReviewsAggregation({ productId: parseData.product.id });
1049            } catch (error) {
1050                console.error('Error during initialization:', error);
1051            }
1052        },
1053        async initializeReviewSummaryShortCodes(data) {
1054            const parseData = JSON.parse(data.data)
1055            this.rvxAttributesDataSetHandler(parseData)
1056            try {
1057                // console.log('init 8')
1058                await this.fetchReviewsSettings();
1059                await this.fetchReviewsAggregation({ productId: parseData.product.id });
1060            } catch (error) {
1061                console.error('Error during initialization:', error);
1062            }
1063        },
1064    }
1065}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.