@deenruv/reviews-plugin 1.0.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 (88) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +58 -0
  3. package/dist/plugin-server/api/admin-api.resolver.d.ts +67 -0
  4. package/dist/plugin-server/api/admin-api.resolver.js +127 -0
  5. package/dist/plugin-server/api/review-asset.resolver.d.ts +11 -0
  6. package/dist/plugin-server/api/review-asset.resolver.js +37 -0
  7. package/dist/plugin-server/api/review-customer.resolver.d.ts +15 -0
  8. package/dist/plugin-server/api/review-customer.resolver.js +52 -0
  9. package/dist/plugin-server/api/review-order.resolver.d.ts +8 -0
  10. package/dist/plugin-server/api/review-order.resolver.js +47 -0
  11. package/dist/plugin-server/api/review-product.resolver.d.ts +15 -0
  12. package/dist/plugin-server/api/review-product.resolver.js +50 -0
  13. package/dist/plugin-server/api/shop-api.resolver.d.ts +28 -0
  14. package/dist/plugin-server/api/shop-api.resolver.js +57 -0
  15. package/dist/plugin-server/constants.d.ts +6 -0
  16. package/dist/plugin-server/constants.js +7 -0
  17. package/dist/plugin-server/entities/review-translation.entity.d.ts +9 -0
  18. package/dist/plugin-server/entities/review-translation.entity.js +37 -0
  19. package/dist/plugin-server/entities/review.entity.d.ts +24 -0
  20. package/dist/plugin-server/entities/review.entity.js +89 -0
  21. package/dist/plugin-server/events.d.ts +10 -0
  22. package/dist/plugin-server/events.js +11 -0
  23. package/dist/plugin-server/extensions/admin-api.extension.d.ts +2 -0
  24. package/dist/plugin-server/extensions/admin-api.extension.js +70 -0
  25. package/dist/plugin-server/extensions/shared.extension.d.ts +2 -0
  26. package/dist/plugin-server/extensions/shared.extension.js +87 -0
  27. package/dist/plugin-server/extensions/shop-api.extension.d.ts +2 -0
  28. package/dist/plugin-server/extensions/shop-api.extension.js +60 -0
  29. package/dist/plugin-server/index.d.ts +15 -0
  30. package/dist/plugin-server/index.js +86 -0
  31. package/dist/plugin-server/services/reviews.service.d.ts +85 -0
  32. package/dist/plugin-server/services/reviews.service.js +366 -0
  33. package/dist/plugin-server/state/reviews.state.d.ts +16 -0
  34. package/dist/plugin-server/state/reviews.state.js +63 -0
  35. package/dist/plugin-server/types.d.ts +33 -0
  36. package/dist/plugin-server/types.js +1 -0
  37. package/dist/plugin-server/zeus/const.d.ts +6 -0
  38. package/dist/plugin-server/zeus/const.js +4027 -0
  39. package/dist/plugin-server/zeus/index.d.ts +20410 -0
  40. package/dist/plugin-server/zeus/index.js +443 -0
  41. package/dist/plugin-ui/components/OrderInfo.d.ts +5 -0
  42. package/dist/plugin-ui/components/OrderInfo.js +42 -0
  43. package/dist/plugin-ui/components/ProductInfo.d.ts +5 -0
  44. package/dist/plugin-ui/components/ProductInfo.js +36 -0
  45. package/dist/plugin-ui/components/ReviewCustomer.d.ts +2 -0
  46. package/dist/plugin-ui/components/ReviewCustomer.js +166 -0
  47. package/dist/plugin-ui/components/ReviewOrder.d.ts +2 -0
  48. package/dist/plugin-ui/components/ReviewOrder.js +57 -0
  49. package/dist/plugin-ui/components/ReviewProductSidebar.d.ts +2 -0
  50. package/dist/plugin-ui/components/ReviewProductSidebar.js +29 -0
  51. package/dist/plugin-ui/components/ReviewStateChange.d.ts +5 -0
  52. package/dist/plugin-ui/components/ReviewStateChange.js +35 -0
  53. package/dist/plugin-ui/components/UniversalSelectDialog.d.ts +16 -0
  54. package/dist/plugin-ui/components/UniversalSelectDialog.js +35 -0
  55. package/dist/plugin-ui/components/index.d.ts +2 -0
  56. package/dist/plugin-ui/components/index.js +2 -0
  57. package/dist/plugin-ui/constants.d.ts +1 -0
  58. package/dist/plugin-ui/constants.js +1 -0
  59. package/dist/plugin-ui/graphql/index.d.ts +3 -0
  60. package/dist/plugin-ui/graphql/index.js +3 -0
  61. package/dist/plugin-ui/graphql/mutations.d.ts +35 -0
  62. package/dist/plugin-ui/graphql/mutations.js +28 -0
  63. package/dist/plugin-ui/graphql/queries.d.ts +293 -0
  64. package/dist/plugin-ui/graphql/queries.js +34 -0
  65. package/dist/plugin-ui/graphql/scalars.d.ts +16 -0
  66. package/dist/plugin-ui/graphql/scalars.js +14 -0
  67. package/dist/plugin-ui/graphql/selectors.d.ts +58 -0
  68. package/dist/plugin-ui/graphql/selectors.js +39 -0
  69. package/dist/plugin-ui/index.d.ts +7 -0
  70. package/dist/plugin-ui/index.js +69 -0
  71. package/dist/plugin-ui/locales/en/index.d.ts +108 -0
  72. package/dist/plugin-ui/locales/en/index.js +2 -0
  73. package/dist/plugin-ui/locales/en/reviews.json +107 -0
  74. package/dist/plugin-ui/locales/pl/index.d.ts +108 -0
  75. package/dist/plugin-ui/locales/pl/index.js +2 -0
  76. package/dist/plugin-ui/locales/pl/reviews.json +107 -0
  77. package/dist/plugin-ui/pages/Review.d.ts +2 -0
  78. package/dist/plugin-ui/pages/Review.js +263 -0
  79. package/dist/plugin-ui/pages/Reviews.d.ts +2 -0
  80. package/dist/plugin-ui/pages/Reviews.js +174 -0
  81. package/dist/plugin-ui/tsconfig.json +18 -0
  82. package/dist/plugin-ui/zeus/const.d.ts +6 -0
  83. package/dist/plugin-ui/zeus/const.js +4027 -0
  84. package/dist/plugin-ui/zeus/index.d.ts +20410 -0
  85. package/dist/plugin-ui/zeus/index.js +459 -0
  86. package/dist/plugin-ui/zeus/typedDocumentNode.d.ts +3 -0
  87. package/dist/plugin-ui/zeus/typedDocumentNode.js +9 -0
  88. package/package.json +61 -0
@@ -0,0 +1,107 @@
1
+ {
2
+ "dialog": {
3
+ "bulkStateChangeTitle": "Bulk change review state",
4
+ "bulkStateChangeDescription": "Are you sure you want to change the state of the selected reviews?",
5
+ "bulkStateChangeSelectLabel": "Select review state",
6
+ "bulkStateChangeSelectPlaceholder": "Select review state",
7
+ "bulkStateChangeSuccess": "Review state has been changed",
8
+ "bulkStateChangeError": "An error occurred while changing the review state",
9
+ "bulkStateChangeCancelled": "Bulk change of review state has been cancelled",
10
+ "bulkStateChangeNoSelection": "No reviews selected for state change",
11
+ "singleStateChangeTitle": "Single change review state",
12
+ "singleStateChangeDescription": "Are you sure you want to change the state of this review?",
13
+ "singleStateChangeSelectLabel": "Select review state",
14
+ "singleStateChangeSelectPlaceholder": "Select review state",
15
+ "singleStateChangeSuccess": "Review state has been changed",
16
+ "singleStateChangeError": "An error occurred while changing the review state",
17
+ "singleStateChangeCancelled": "Single change of review state has been cancelled",
18
+ "singleStateChangeNoSelection": "No review selected for state change",
19
+ "cancel": "Cancel",
20
+ "submit": "Submit",
21
+ "selectError": "Please select a review state"
22
+ },
23
+ "state": {
24
+ "pending": "Pending",
25
+ "accepted": "Accepted",
26
+ "declined": "Declined"
27
+ },
28
+ "detail": {
29
+ "viewCustomer": "View customer",
30
+ "id": "Review {{id}}",
31
+ "changeState": "Change review state",
32
+ "changeStateSuccess": "Review state has been changed",
33
+ "changeStateError": "An error occurred while changing the review state",
34
+ "cancel": "Cancel",
35
+ "confirm": "Confirm",
36
+ "selectState": "Select review state",
37
+ "businessResponsePlaceholder": "Optional response to the review",
38
+ "businessResponse": "Business response",
39
+ "createdAt": "Created at",
40
+ "updatedAt": "Updated at",
41
+ "noFound": "No review found",
42
+ "reviewTranslations": "Translations",
43
+ "reviewContent": "Review content",
44
+ "generateAllMissingTranslations": "Generate all missing translations",
45
+ "generateAllMissingTranslationsSuccess": "All missing translations have been generated",
46
+ "generateAllMissingTranslationsError": "An error occurred while generating missing translations",
47
+ "updateReview": "Update review",
48
+ "noTranslation": "No translation available",
49
+ "generateTranslation": "Generate translation",
50
+ "manualTranslation": "Create manual translation",
51
+ "reviewBody": "Review body",
52
+ "review": "{{review}} out of 5",
53
+ "stateRequired": "Review state is required",
54
+ "rating": "Rating {{rating}} out of 5",
55
+ "assets": "Attachments",
56
+ "manualTranslationPlaceholder": "Manual translation body",
57
+ "generatedTranslationBody": "Generated translation body"
58
+ },
59
+ "productInfo": {
60
+ "productReview": "Product review",
61
+ "viewProduct": "View product",
62
+ "productId": "Product ID",
63
+ "productName": "Product name",
64
+ "variantName": "Variant name"
65
+ },
66
+ "orderInfo": {
67
+ "orderReview": "Order review",
68
+ "viewOrder": "View order",
69
+ "orderId": "Order ID",
70
+ "orderCode": "Order code",
71
+ "orderTotal": "Order total",
72
+ "totalItems": "Total items"
73
+ },
74
+ "reviewOrder": {
75
+ "title": "Order review",
76
+ "titleWithId": "Order review #{{orderId}}"
77
+ },
78
+ "productReviewSidebar": {
79
+ "averageRating": "Average rating",
80
+ "noAverageRating": "No average rating",
81
+ "totalReviews": "Total reviews",
82
+ "noTotalReviews": "No total reviews",
83
+ "totalReviewsForProduct": "Total reviews for product",
84
+ "noTotalReviewsForProduct": "No total reviews for product",
85
+ "viewAllReviews": "See all reviews for this product"
86
+ },
87
+ "nav": {
88
+ "reviews": "Reviews",
89
+ "reviewsList": "Reviews List"
90
+ },
91
+ "list": {
92
+ "bulkStateChange": "Bulk change state",
93
+ "stateChange": "Change state",
94
+ "productId": "Product ID",
95
+ "orderId": "Order ID",
96
+ "customerId": "Customer ID",
97
+ "reviewFor": "Review for",
98
+ "shopReview": "Review for shop",
99
+ "orderReview": "Review for order #{{orderId}}",
100
+ "responseDate": "Response date",
101
+ "state": "State",
102
+ "rating": "Rating",
103
+ "noResponse": "No response",
104
+ "responseCreatedAt": "Response created at",
105
+ "responseCreatedAtValue": "Responded {{date}}"
106
+ }
107
+ }
@@ -0,0 +1,108 @@
1
+ declare const _default: {
2
+ dialog: {
3
+ bulkStateChangeTitle: string;
4
+ bulkStateChangeDescription: string;
5
+ bulkStateChangeSelectLabel: string;
6
+ bulkStateChangeSelectPlaceholder: string;
7
+ bulkStateChangeSuccess: string;
8
+ bulkStateChangeError: string;
9
+ bulkStateChangeCancelled: string;
10
+ bulkStateChangeNoSelection: string;
11
+ singleStateChangeTitle: string;
12
+ singleStateChangeDescription: string;
13
+ singleStateChangeSelectLabel: string;
14
+ singleStateChangeSelectPlaceholder: string;
15
+ singleStateChangeSuccess: string;
16
+ singleStateChangeError: string;
17
+ singleStateChangeCancelled: string;
18
+ singleStateChangeNoSelection: string;
19
+ cancel: string;
20
+ submit: string;
21
+ selectError: string;
22
+ };
23
+ state: {
24
+ pending: string;
25
+ accepted: string;
26
+ declined: string;
27
+ };
28
+ detail: {
29
+ viewCustomer: string;
30
+ id: string;
31
+ changeState: string;
32
+ changeStateSuccess: string;
33
+ changeStateError: string;
34
+ cancel: string;
35
+ confirm: string;
36
+ selectState: string;
37
+ businessResponsePlaceholder: string;
38
+ businessResponse: string;
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ noFound: string;
42
+ reviewTranslations: string;
43
+ reviewContent: string;
44
+ generateAllMissingTranslations: string;
45
+ generateAllMissingTranslationsSuccess: string;
46
+ generateAllMissingTranslationsError: string;
47
+ updateReview: string;
48
+ noTranslation: string;
49
+ generateTranslation: string;
50
+ manualTranslation: string;
51
+ reviewBody: string;
52
+ review: string;
53
+ stateRequired: string;
54
+ rating: string;
55
+ assets: string;
56
+ manualTranslationPlaceholder: string;
57
+ generatedTranslationBody: string;
58
+ };
59
+ productInfo: {
60
+ productReview: string;
61
+ viewProduct: string;
62
+ productId: string;
63
+ productName: string;
64
+ variantName: string;
65
+ };
66
+ orderInfo: {
67
+ orderReview: string;
68
+ viewOrder: string;
69
+ orderId: string;
70
+ orderCode: string;
71
+ orderTotal: string;
72
+ totalItems: string;
73
+ };
74
+ reviewOrder: {
75
+ title: string;
76
+ titleWithId: string;
77
+ };
78
+ productReviewSidebar: {
79
+ averageRating: string;
80
+ noAverageRating: string;
81
+ totalReviews: string;
82
+ noTotalReviews: string;
83
+ totalReviewsForProduct: string;
84
+ noTotalReviewsForProduct: string;
85
+ viewAllReviews: string;
86
+ };
87
+ nav: {
88
+ reviews: string;
89
+ reviewsList: string;
90
+ };
91
+ list: {
92
+ bulkStateChange: string;
93
+ stateChange: string;
94
+ productId: string;
95
+ orderId: string;
96
+ customerId: string;
97
+ reviewFor: string;
98
+ shopReview: string;
99
+ orderReview: string;
100
+ responseDate: string;
101
+ state: string;
102
+ rating: string;
103
+ noResponse: string;
104
+ responseCreatedAt: string;
105
+ responseCreatedAtValue: string;
106
+ };
107
+ }[];
108
+ export default _default;
@@ -0,0 +1,2 @@
1
+ import reviews from "./reviews.json";
2
+ export default [reviews];
@@ -0,0 +1,107 @@
1
+ {
2
+ "dialog": {
3
+ "bulkStateChangeTitle": "Zmiana stanu opinii",
4
+ "bulkStateChangeDescription": "Czy na pewno chcesz zmienić stan zaznaczonych opinii?",
5
+ "bulkStateChangeSelectLabel": "Wybierz stan opinii",
6
+ "bulkStateChangeSelectPlaceholder": "Wybierz stan opinii",
7
+ "bulkStateChangeSuccess": "Stan opinii został zmieniony",
8
+ "bulkStateChangeError": "Wystąpił błąd podczas zmiany stanu opinii",
9
+ "bulkStateChangeCancelled": "Zmiana stanu opinii została anulowana",
10
+ "bulkStateChangeNoSelection": "Nie wybrano opinii do zmiany stanu",
11
+ "singleStateChangeTitle": "Zmiana stanu opinii",
12
+ "singleStateChangeDescription": "Czy na pewno chcesz zmienić stan tej opinii?",
13
+ "singleStateChangeSelectLabel": "Wybierz stan opinii",
14
+ "singleStateChangeSelectPlaceholder": "Wybierz stan opinii",
15
+ "singleStateChangeSuccess": "Stan opinii został zmieniony",
16
+ "singleStateChangeError": "Wystąpił błąd podczas zmiany stanu opinii",
17
+ "singleStateChangeCancelled": "Zmiana stanu opinii została anulowana",
18
+ "singleStateChangeNoSelection": "Nie wybrano opinii do zmiany stanu",
19
+ "cancel": "Anuluj",
20
+ "submit": "Zatwierdź",
21
+ "selectError": "Wybierz stan opinii"
22
+ },
23
+ "state": {
24
+ "pending": "Oczekująca",
25
+ "accepted": "Zaakceptowana",
26
+ "declined": "Odrzucona"
27
+ },
28
+ "detail": {
29
+ "viewCustomer": "Zobacz klienta",
30
+ "id": "Opinia {{id}}",
31
+ "changeState": "Zmień stan opinii",
32
+ "changeStateSuccess": "Stan opinii został zmieniony",
33
+ "changeStateError": "Wystąpił błąd podczas zmiany stanu opinii",
34
+ "cancel": "Anuluj",
35
+ "confirm": "Potwierdź",
36
+ "selectState": "Wybierz stan opinii",
37
+ "businessResponsePlaceholder": "Opcjonalna odpowiedź na opinię",
38
+ "businessResponse": "Odpowiedź firmy",
39
+ "createdAt": "Utworzono",
40
+ "updatedAt": "Zaktualizowano",
41
+ "noFound": "Nie znaleziono opinii",
42
+ "reviewTranslations": "Tłumaczenia",
43
+ "reviewContent": "Treść opinii",
44
+ "generateAllMissingTranslations": "Generuj brakujące tłumaczenia",
45
+ "generateAllMissingTranslationsSuccess": "Wszystkie brakujące tłumaczenia zostały wygenerowane",
46
+ "generateAllMissingTranslationsError": "Wystąpił błąd podczas generowania brakujących tłumaczeń",
47
+ "updateReview": "Zaktualizuj opinię",
48
+ "noTranslation": "Brak dostępnego tłumaczenia",
49
+ "generateTranslation": "Generuj tłumaczenie",
50
+ "manualTranslation": "Utwórz ręczne tłumaczenie",
51
+ "reviewBody": "Treść opinii",
52
+ "review": "{{review}} z 5",
53
+ "stateRequired": "Stan opinii jest wymagany",
54
+ "rating": "Ocena {{rating}} z 5",
55
+ "assets": "Załączniki",
56
+ "manualTranslationPlaceholder": "Ręczne tłumaczenie treści",
57
+ "generatedTranslationBody": "Wygenerowana treść tłumaczenia"
58
+ },
59
+ "productInfo": {
60
+ "productReview": "Recenzja produktu",
61
+ "viewProduct": "Zobacz produkt",
62
+ "productId": "ID produktu",
63
+ "productName": "Nazwa produktu",
64
+ "variantName": "Nazwa wariantu"
65
+ },
66
+ "orderInfo": {
67
+ "orderReview": "Recenzja zamówienia",
68
+ "viewOrder": "Zobacz zamówienie",
69
+ "orderId": "ID zamówienia",
70
+ "orderCode": "Kod zamówienia",
71
+ "orderTotal": "Łączna kwota zamówienia",
72
+ "totalItems": "Łączna liczba przedmiotów"
73
+ },
74
+ "reviewOrder": {
75
+ "title": "Recenzja zamówienia",
76
+ "titleWithId": "Recenzja zamówienia #{{orderId}}"
77
+ },
78
+ "productReviewSidebar": {
79
+ "averageRating": "Średnia ocena",
80
+ "noAverageRating": "Brak średniej oceny",
81
+ "totalReviews": "Łączna liczba ocen",
82
+ "noTotalReviews": "Brak ocen",
83
+ "totalReviewsForProduct": "Łączna liczba ocen dla produktu",
84
+ "noTotalReviewsForProduct": "Brak ocen dla produktu",
85
+ "viewAllReviews": "Zobacz wszystkie opinie o produkcie"
86
+ },
87
+ "nav": {
88
+ "reviews": "Oceny",
89
+ "reviewsList": "Lista ocen"
90
+ },
91
+ "list": {
92
+ "bulkStateChange": "Zmiana stanu dla wielu",
93
+ "stateChange": "Zmień stan",
94
+ "productId": "ID produktu",
95
+ "orderId": "ID zamówienia",
96
+ "customerId": "ID klienta",
97
+ "reviewFor": "Recenzja dla",
98
+ "shopReview": "Recenzja sklepu",
99
+ "orderReview": "Recenzja zamówienia #{{orderId}}",
100
+ "responseDate": "Data odpowiedzi",
101
+ "state": "Stan",
102
+ "rating": "Ocena",
103
+ "noResponse": "Nie zareagowano",
104
+ "responseCreatedAt": "Odpowiedź utworzona",
105
+ "responseCreatedAtValue": "Zareagowano {{date}}"
106
+ }
107
+ }
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const Review: () => React.JSX.Element;
@@ -0,0 +1,263 @@
1
+ import { Card, CardContent, CardHeader, CardTitle, PageBlock, Textarea, useLazyQuery, useQuery, Label, Badge, Separator, formatDate, Button, useMutation, ImageWithPreview, Routes, } from "@deenruv/react-ui-devkit";
2
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { ChangeReviewStateMutation, GetReviewQuery, GetReviewsConfigQuery, UpdateTranslationsReviewMutation, TranslateReviewsQuery, } from "../graphql";
4
+ import { Link, useParams } from "react-router-dom";
5
+ import { ArrowLeft, Calendar, Mail, Star, User } from "lucide-react";
6
+ import { useNavigate } from "react-router-dom";
7
+ import { OrderInfo, ProductInfo } from "../components";
8
+ import { TRANSLATION_NAMESPACE } from "../constants";
9
+ import { useTranslation } from "react-i18next";
10
+ import { toast } from "sonner";
11
+ import { ReviewStateChange } from "../components/ReviewStateChange";
12
+ export const Review = () => {
13
+ const { t } = useTranslation(TRANSLATION_NAMESPACE, {
14
+ i18n: window.__DEENRUV_SETTINGS__.i18n,
15
+ });
16
+ const { id } = useParams();
17
+ const { data: config } = useQuery(GetReviewsConfigQuery);
18
+ const [get] = useLazyQuery(GetReviewQuery);
19
+ const [originalReview, setOriginalReview] = useState();
20
+ const [review, setReview] = useState();
21
+ const [updateTranslationsReview] = useMutation(UpdateTranslationsReviewMutation);
22
+ const [changeReviewState] = useMutation(ChangeReviewStateMutation);
23
+ const [translate] = useLazyQuery(TranslateReviewsQuery);
24
+ const navigate = useNavigate();
25
+ const refetch = useCallback(async () => {
26
+ if (!id)
27
+ return;
28
+ try {
29
+ const res = await get({ id });
30
+ setReview(res.getReview);
31
+ setOriginalReview(res.getReview);
32
+ }
33
+ catch (error) {
34
+ console.error("Error fetching review:", error);
35
+ }
36
+ }, [id]);
37
+ useEffect(() => {
38
+ if (config && id)
39
+ refetch();
40
+ }, [id, config]);
41
+ const getStateColor = (state) => {
42
+ let variant = "default";
43
+ if (state === "ACCEPTED" /* ReviewState.ACCEPTED */) {
44
+ variant = "success";
45
+ }
46
+ if (state === "DECLINED" /* ReviewState.DECLINED */) {
47
+ variant = "destructive";
48
+ }
49
+ return variant;
50
+ };
51
+ const renderStars = (rating) => {
52
+ return Array.from({ length: 5 }, (_, i) => (React.createElement(Star, { key: i, className: `w-5 h-5 ${i < rating ? "text-yellow-500" : "text-gray-300"}`, fill: i < rating ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 1.5 })));
53
+ };
54
+ const handleGenerateAllMissingTranslations = async () => {
55
+ if (!review || !config)
56
+ return;
57
+ try {
58
+ const languages = config.getReviewsConfig.reviewsLanguages;
59
+ const existingLanguages = review.translations.map((t) => t.languageCode);
60
+ const missingLanguages = languages.filter((lang) => !existingLanguages.includes(lang));
61
+ const { translateReviews } = await translate({
62
+ input: { id: review.id, languages: missingLanguages },
63
+ });
64
+ const newTranslations = translateReviews.map((translation) => ({
65
+ id: `generate-${translation.languageCode}`,
66
+ languageCode: translation.languageCode,
67
+ body: translation.body || t("detail.generatedTranslationBody"),
68
+ }));
69
+ setReview((prev) => {
70
+ if (!prev)
71
+ return prev;
72
+ return {
73
+ ...prev,
74
+ translations: [...(prev.translations || []), ...newTranslations],
75
+ };
76
+ });
77
+ toast.success(t("generateAllMissingTranslationsSuccess"));
78
+ }
79
+ catch (error) {
80
+ toast.error(t("generateAllMissingTranslationsError"));
81
+ }
82
+ };
83
+ const handleMissingTranslation = async (action, language) => {
84
+ if (!review || !config)
85
+ return;
86
+ let body = "";
87
+ if (action === "generate") {
88
+ const { translateReviews } = await translate({
89
+ input: { id: review.id, languages: [language] },
90
+ });
91
+ const translatedReview = translateReviews.find((t) => t.languageCode === language);
92
+ body = translatedReview?.body || t("detail.generatedTranslationBody");
93
+ }
94
+ else {
95
+ body = t("detail.manualTranslationPlaceholder");
96
+ }
97
+ setReview((prev) => {
98
+ if (!prev)
99
+ return prev;
100
+ const newTranslation = {
101
+ id: `${action}-${language}`,
102
+ languageCode: language,
103
+ body,
104
+ };
105
+ return {
106
+ ...prev,
107
+ translations: [...(prev.translations || []), newTranslation],
108
+ };
109
+ });
110
+ };
111
+ const handleUpdateReviewTranslation = async () => {
112
+ if (!review || !id)
113
+ return;
114
+ const mappedTranslations = review.translations.map((translation) => ({
115
+ languageCode: translation.languageCode,
116
+ body: translation.body,
117
+ }));
118
+ const translations = mappedTranslations.filter((translation) => translation.languageCode !== undefined &&
119
+ translation.body !== undefined);
120
+ await updateTranslationsReview({ input: { id: review.id, translations } });
121
+ await refetch();
122
+ };
123
+ const haveMissingTranslations = useMemo(() => {
124
+ if (!review || !config)
125
+ return false;
126
+ const languages = config.getReviewsConfig.reviewsLanguages;
127
+ const existingLanguages = review.translations.map((t) => t.languageCode);
128
+ return languages.some((lang) => !existingLanguages.includes(lang));
129
+ }, [review, config]);
130
+ const handleChangeReviewState = async (state, message) => {
131
+ if (!review || !id)
132
+ return;
133
+ try {
134
+ await changeReviewState({
135
+ input: { id: review.id, state, message },
136
+ });
137
+ toast.success(t("detail.changeStateSuccess"));
138
+ await refetch();
139
+ }
140
+ catch (error) {
141
+ console.error("Error changing review state:", error);
142
+ toast.error(t("detail.changeStateError"));
143
+ }
144
+ };
145
+ const InfoComponent = useMemo(() => {
146
+ if (review?.productVariant) {
147
+ return React.createElement(ProductInfo, { review: review });
148
+ }
149
+ else if (review?.order) {
150
+ return React.createElement(OrderInfo, { review: review });
151
+ }
152
+ return null;
153
+ }, [review]);
154
+ const isDirty = useMemo(() => {
155
+ if (!review || !originalReview)
156
+ return false;
157
+ return JSON.stringify(review) !== JSON.stringify(originalReview);
158
+ }, [review, originalReview]);
159
+ return (React.createElement(PageBlock, { className: "flex flex-col gap-4" },
160
+ React.createElement(Card, null,
161
+ React.createElement(CardHeader, null,
162
+ React.createElement("div", { className: "flex items-start justify-between" },
163
+ React.createElement("div", { className: "flex gap-2 items-center" },
164
+ React.createElement(Button, { variant: "ghost", size: "icon", onClick: () => navigate(-1) },
165
+ React.createElement(ArrowLeft, { className: "w-5 h-5" })),
166
+ React.createElement("p", { className: "text-md font-semibold" }, review?.id ? t("detail.id", { id: review.id }) : null)),
167
+ React.createElement("div", { className: "flex flex-col gap-2 items-end" }, "PENDING" /* ReviewState.PENDING */ === review?.state && (React.createElement(ReviewStateChange, { onSubmit: handleChangeReviewState }))))),
168
+ React.createElement(CardContent, null, review ? (React.createElement("div", { className: "space-y-6" },
169
+ React.createElement("div", { className: "flex gap-4 justify-between items-start" },
170
+ React.createElement(Card, { className: "w-full" },
171
+ React.createElement(CardContent, { className: "flex items-center gap-4 p-4 rounded-lg" },
172
+ React.createElement("div", { className: "w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center" },
173
+ React.createElement(User, { className: "w-6 h-6 text-blue-600" })),
174
+ React.createElement("div", { className: "flex-1" },
175
+ React.createElement("h3", { className: "font-semibold text-lg" }, review.authorName),
176
+ React.createElement("div", { className: "flex items-center gap-2 text-sm text-gray-600" },
177
+ React.createElement(Mail, { className: "w-4 h-4" }),
178
+ review.authorEmailAddress),
179
+ React.createElement("div", { className: "flex items-center gap-2 text-sm text-gray-600" },
180
+ React.createElement(Calendar, { className: "w-4 h-4" }),
181
+ formatDate(review.createdAt, {
182
+ dateStyle: "medium",
183
+ timeStyle: "short",
184
+ })),
185
+ review.author?.id ? (React.createElement("div", { className: "text-sm text-gray-600" },
186
+ React.createElement(Link, { to: Routes.customers.to(review.author.id) }, t("detail.viewCustomer")))) : null),
187
+ React.createElement("div", { className: "flex flex-col gap-2 items-end" },
188
+ React.createElement("div", { className: "flex items-center gap-1 mb-1" }, renderStars(review.rating)),
189
+ React.createElement("div", { className: "text-sm text-gray-500 text-right" }, t("detail.rating", { rating: review.rating })),
190
+ React.createElement(Badge, { variant: getStateColor(review?.state), className: "mt-4 text-xs font-semibold w-fit" }, t(`state.${review.state.toLowerCase()}`))))),
191
+ React.createElement("div", { className: "w-full" }, InfoComponent)),
192
+ React.createElement("div", { className: "space-y-4" }, review.assets?.length ? (React.createElement("div", { className: "flex flex-col gap-2" },
193
+ React.createElement(Label, { className: "text-base font-semibold" }, t("detail.assets")),
194
+ review.assets.map((asset) => (React.createElement("div", { key: asset.key, className: "flex flex-col items-start gap-2" },
195
+ React.createElement(ImageWithPreview, { src: asset.url, alt: asset.key, imageClassName: "w-16 h-16 object-cover rounded", previewClassName: "w-full h-full object-cover rounded max-w-[400px] max-h-[400px]" })))))) : null),
196
+ review.response && review.responseCreatedAt && (React.createElement(React.Fragment, null,
197
+ React.createElement(Separator, null),
198
+ React.createElement("div", { className: "bg-primary/90 p-4 rounded-lg" },
199
+ React.createElement("div", { className: "flex items-center gap-2 mb-2" },
200
+ React.createElement(Label, { className: "text-base font-semibold text-secondary" }, t("detail.businessResponse")),
201
+ React.createElement(Badge, { variant: "outline", className: "text-xs text-secondary" }, formatDate(review.responseCreatedAt, {
202
+ dateStyle: "medium",
203
+ timeStyle: "short",
204
+ }))),
205
+ React.createElement("p", { className: "text-secondary" }, review.response)))),
206
+ React.createElement("div", { className: "grid grid-cols-2 gap-4 pt-4 border-t text-sm text-gray-600" },
207
+ React.createElement("div", null,
208
+ React.createElement("span", { className: "font-medium" },
209
+ t("detail.createdAt"),
210
+ ":"),
211
+ " ",
212
+ formatDate(review.createdAt, {
213
+ dateStyle: "medium",
214
+ timeStyle: "short",
215
+ })),
216
+ React.createElement("div", null,
217
+ React.createElement("span", { className: "font-medium" },
218
+ t("detail.updatedAt"),
219
+ ":"),
220
+ " ",
221
+ formatDate(review.updatedAt, {
222
+ dateStyle: "medium",
223
+ timeStyle: "short",
224
+ }))))) : (React.createElement("div", { className: "text-center py-8" },
225
+ React.createElement("p", { className: "text-gray-500" }, t("detail.noFound")))))),
226
+ React.createElement(Card, null,
227
+ React.createElement(CardHeader, null,
228
+ React.createElement("div", { className: "flex items-center justify-between" },
229
+ React.createElement(CardTitle, { className: "text-xl font-bold" }, config?.getReviewsConfig.reviewsLanguages &&
230
+ config.getReviewsConfig.reviewsLanguages.length > 1
231
+ ? t("detail.reviewTranslations")
232
+ : t("detail.reviewContent")),
233
+ config?.getReviewsConfig.reviewsLanguages &&
234
+ config.getReviewsConfig.reviewsLanguages.length > 1 ? (React.createElement("div", { className: "flex items-center gap-4" },
235
+ config?.getReviewsConfig.canTranslate ? (React.createElement(Button, { disabled: !haveMissingTranslations, variant: "secondary", onClick: handleGenerateAllMissingTranslations }, t("detail.generateAllMissingTranslations"))) : null,
236
+ React.createElement(Button, { variant: "action", disabled: !isDirty, onClick: handleUpdateReviewTranslation }, t("detail.updateReview")))) : null)),
237
+ React.createElement(CardContent, null,
238
+ React.createElement("div", { className: "space-y-6" }, config?.getReviewsConfig.reviewsLanguages.map((lang, index) => {
239
+ const matchingTranslation = review?.translations.find((t) => t.languageCode === lang);
240
+ const isOriginal = !(matchingTranslation?.id.startsWith("manual-") ||
241
+ matchingTranslation?.id.startsWith("generate-"));
242
+ return (React.createElement("div", { key: lang },
243
+ index > 0 && React.createElement(Separator, { className: "mb-6" }),
244
+ React.createElement("div", { className: "space-y-4" },
245
+ React.createElement("div", { className: "flex items-center gap-2" },
246
+ React.createElement(Badge, { variant: "outline", className: "font-mono" }, lang.toUpperCase())),
247
+ matchingTranslation ? (React.createElement(TranslationComponent, { language: lang, matchingTranslation: matchingTranslation, isOriginal: isOriginal })) : (React.createElement("div", { className: "text-center py-6 rounded-lg" },
248
+ React.createElement("p", { className: "text-gray-500" }, t("detail.noTranslation")),
249
+ React.createElement("div", { className: "flex items-center justify-center gap-4 mt-4" },
250
+ config?.getReviewsConfig.canTranslate ? (React.createElement(Button, { variant: "action", onClick: () => handleMissingTranslation("generate", lang) }, t("detail.generateTranslation"))) : null,
251
+ React.createElement(Button, { variant: "outline", onClick: () => handleMissingTranslation("manual", lang) }, t("detail.manualTranslation"))))))));
252
+ }))))));
253
+ };
254
+ const TranslationComponent = ({ language, matchingTranslation, isOriginal, }) => {
255
+ const { t } = useTranslation(TRANSLATION_NAMESPACE, {
256
+ i18n: window.__DEENRUV_SETTINGS__.i18n,
257
+ });
258
+ const [body, setBody] = useState(matchingTranslation.body);
259
+ return (React.createElement("div", { className: "space-y-4" },
260
+ React.createElement("div", null,
261
+ React.createElement(Label, { htmlFor: `body-${language}`, className: "text-sm font-medium" }, t("detail.reviewBody")),
262
+ React.createElement(Textarea, { id: `body-${language}`, value: body, readOnly: isOriginal, className: "mt-1 min-h-[100px] resize-none", onChange: (e) => setBody(e.target.value) }))));
263
+ };
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const Reviews: () => React.JSX.Element;