@deenruv/reviews-plugin 1.0.17-dev.31 → 1.0.17-dev.32

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.
@@ -6,7 +6,7 @@ import { REVIEWS_ROUTES } from "../index.js";
6
6
  import { Check, ReplaceAllIcon, ReplaceIcon } from "lucide-react";
7
7
  import { UniversalSelectDialog } from "./UniversalSelectDialog";
8
8
  export const ReviewCustomer = () => {
9
- const { t } = useTranslation(TRANSLATION_NAMESPACE);
9
+ const { t, i18n } = useTranslation(TRANSLATION_NAMESPACE);
10
10
  const [fetch] = useLazyQuery(ListReviewQuery);
11
11
  const [changeReviewState] = useMutation(ChangeReviewStateMutation);
12
12
  const [changeReviewsState] = useMutation(ChangeReviewsStateMutation);
@@ -136,7 +136,9 @@ export const ReviewCustomer = () => {
136
136
  const responseCreatedAt = row.original.responseCreatedAt;
137
137
  return (React.createElement("div", null, responseCreatedAt
138
138
  ? t("list.responseCreatedAtValue", {
139
- date: formatDate(responseCreatedAt),
139
+ date: formatDate(responseCreatedAt, {
140
+ locale: i18n.resolvedLanguage ?? i18n.language,
141
+ }),
140
142
  })
141
143
  : t("list.noResponse")));
142
144
  },
@@ -1,2 +1,4 @@
1
1
  import React from "react";
2
+ import { ReviewState } from "../zeus";
3
+ export declare const getReviewStateLabel: (state: ReviewState, translate: (key: string) => string) => string;
2
4
  export declare const ReviewOrder: () => React.JSX.Element | null;
@@ -23,8 +23,18 @@ const getStatusColor = (status) => {
23
23
  return "bg-gray-100 text-gray-800 border-gray-200";
24
24
  }
25
25
  };
26
+ export const getReviewStateLabel = (state, translate) => {
27
+ switch (state) {
28
+ case "PENDING" /* ReviewState.PENDING */:
29
+ return translate("state.pending");
30
+ case "ACCEPTED" /* ReviewState.ACCEPTED */:
31
+ return translate("state.accepted");
32
+ case "DECLINED" /* ReviewState.DECLINED */:
33
+ return translate("state.declined");
34
+ }
35
+ };
26
36
  export const ReviewOrder = () => {
27
- const { t } = useTranslation(TRANSLATION_NAMESPACE);
37
+ const { t, i18n } = useTranslation(TRANSLATION_NAMESPACE);
28
38
  const { order } = useOrder();
29
39
  const [getReviewForOrder, { data }] = useLazyQuery(GetReviewForOrderQuery);
30
40
  useEffect(() => {
@@ -41,7 +51,7 @@ export const ReviewOrder = () => {
41
51
  React.createElement(CardTitle, { className: "flex items-center gap-2" },
42
52
  React.createElement(MessageSquare, { className: "h-5 w-5 text-orange-500" }),
43
53
  t("reviewOrder.titleWithId", { orderId: order.id }))),
44
- React.createElement(Badge, { className: getStatusColor(data.getReviewForOrder.state) }, data.getReviewForOrder.state))),
54
+ React.createElement(Badge, { className: getStatusColor(data.getReviewForOrder.state) }, getReviewStateLabel(data.getReviewForOrder.state, t)))),
45
55
  React.createElement(CardContent, { className: "space-y-4 p-0" },
46
56
  React.createElement("div", { className: "space-y-4" },
47
57
  React.createElement("div", { className: "flex items-center justify-between" },
@@ -50,5 +60,7 @@ export const ReviewOrder = () => {
50
60
  React.createElement("p", { className: "text-sm leading-relaxed text-foreground" }, data.getReviewForOrder.body)),
51
61
  React.createElement("div", { className: "flex items-center gap-4 pt-2 border-t text-xs text-muted-foreground" }, data.getReviewForOrder.createdAt && (React.createElement("div", { className: "flex items-center gap-1" },
52
62
  React.createElement(Calendar, { className: "h-3 w-3" }),
53
- formatDate(data.getReviewForOrder.createdAt))))))));
63
+ formatDate(data.getReviewForOrder.createdAt, {
64
+ locale: i18n.resolvedLanguage ?? i18n.language,
65
+ }))))))));
54
66
  };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { createDeenruvUIPlugin, DEENRUV_UI_VERSION } from "@deenruv/react-ui-devkit";
2
+ import { createDeenruvUIPlugin, DEENRUV_UI_VERSION, } from "@deenruv/react-ui-devkit";
3
3
  import { Reviews } from "./pages/Reviews";
4
4
  import { ListIcon } from "lucide-react";
5
5
  import { Review } from "./pages/Review";
@@ -46,6 +46,7 @@ export const ReviewsUIPlugin = createDeenruvUIPlugin({
46
46
  id: "customers-detail-view",
47
47
  name: "reviews",
48
48
  label: "Reviews",
49
+ labelId: "nav.reviews",
49
50
  component: React.createElement(ReviewCustomer, null),
50
51
  hideSidebar: true,
51
52
  },
@@ -9,7 +9,7 @@ import { TRANSLATION_NAMESPACE } from "../constants";
9
9
  import { toast } from "sonner";
10
10
  import { ReviewStateChange } from "../components/ReviewStateChange";
11
11
  export const Review = () => {
12
- const { t } = useTranslation(TRANSLATION_NAMESPACE);
12
+ const { t, i18n } = useTranslation(TRANSLATION_NAMESPACE);
13
13
  const { id } = useParams();
14
14
  const { data: config } = useQuery(GetReviewsConfigQuery);
15
15
  const [get] = useLazyQuery(GetReviewQuery);
@@ -178,6 +178,7 @@ export const Review = () => {
178
178
  React.createElement("div", { className: "flex items-center gap-2 text-sm text-gray-600" },
179
179
  React.createElement(Calendar, { className: "w-4 h-4" }),
180
180
  formatDate(review.createdAt, {
181
+ locale: i18n.language,
181
182
  dateStyle: "medium",
182
183
  timeStyle: "short",
183
184
  })),
@@ -198,6 +199,7 @@ export const Review = () => {
198
199
  React.createElement("div", { className: "flex items-center gap-2 mb-2" },
199
200
  React.createElement(Label, { className: "text-base font-semibold text-secondary" }, t("detail.businessResponse")),
200
201
  React.createElement(Badge, { variant: "outline", className: "text-xs text-secondary" }, formatDate(review.responseCreatedAt, {
202
+ locale: i18n.language,
201
203
  dateStyle: "medium",
202
204
  timeStyle: "short",
203
205
  }))),
@@ -209,6 +211,7 @@ export const Review = () => {
209
211
  ":"),
210
212
  " ",
211
213
  formatDate(review.createdAt, {
214
+ locale: i18n.language,
212
215
  dateStyle: "medium",
213
216
  timeStyle: "short",
214
217
  })),
@@ -218,6 +221,7 @@ export const Review = () => {
218
221
  ":"),
219
222
  " ",
220
223
  formatDate(review.updatedAt, {
224
+ locale: i18n.language,
221
225
  dateStyle: "medium",
222
226
  timeStyle: "short",
223
227
  }))))) : (React.createElement("div", { className: "text-center py-8" },
@@ -7,7 +7,7 @@ import { Check, ReplaceIcon, ReplaceAllIcon } from "lucide-react";
7
7
  import { UniversalSelectDialog } from "../components/UniversalSelectDialog.js";
8
8
  import { ChangeReviewsStateMutation, ChangeReviewStateMutation, } from "../graphql/mutations.js";
9
9
  export const Reviews = () => {
10
- const { t } = useTranslation(TRANSLATION_NAMESPACE);
10
+ const { t, i18n } = useTranslation(TRANSLATION_NAMESPACE);
11
11
  const [fetch] = useLazyQuery(ListReviewQuery);
12
12
  const [changeReviewState] = useMutation(ChangeReviewStateMutation);
13
13
  const [changeReviewsState] = useMutation(ChangeReviewsStateMutation);
@@ -142,6 +142,7 @@ export const Reviews = () => {
142
142
  return (React.createElement("div", null, responseCreatedAt
143
143
  ? t("list.responseCreatedAtValue", {
144
144
  date: formatDate(responseCreatedAt, {
145
+ locale: i18n.resolvedLanguage ?? i18n.language,
145
146
  dateStyle: "medium",
146
147
  timeStyle: "short",
147
148
  }),
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import en from "./locales/en/reviews.json";
3
+ import pl from "./locales/pl/reviews.json";
4
+ import { getReviewStateLabel } from "./components/ReviewOrder";
5
+ import { readFileSync } from "node:fs";
6
+ import { fileURLToPath, URL } from "node:url";
7
+ const leafKeys = (value, prefix = "") => Object.entries(value).flatMap(([key, child]) => {
8
+ const path = prefix ? `${prefix}.${key}` : key;
9
+ return child && typeof child === "object"
10
+ ? leafKeys(child, path)
11
+ : [path];
12
+ });
13
+ describe("Reviews admin translations", () => {
14
+ it("keeps English and Polish locale keys in parity", () => {
15
+ expect(leafKeys(en).sort()).toEqual(leafKeys(pl).sort());
16
+ });
17
+ it("localizes every backend review state without changing enum values", () => {
18
+ const translate = (key) => `translated:${key}`;
19
+ expect(getReviewStateLabel("PENDING" /* ReviewState.PENDING */, translate)).toBe("translated:state.pending");
20
+ expect(getReviewStateLabel("ACCEPTED" /* ReviewState.ACCEPTED */, translate)).toBe("translated:state.accepted");
21
+ expect(getReviewStateLabel("DECLINED" /* ReviewState.DECLINED */, translate)).toBe("translated:state.declined");
22
+ });
23
+ it("declares an SSR-safe customer tab translation key", async () => {
24
+ const { ReviewsUIPlugin } = await import("./index");
25
+ expect(ReviewsUIPlugin.tabs?.[0]).toMatchObject({
26
+ name: "reviews",
27
+ label: "Reviews",
28
+ labelId: "nav.reviews",
29
+ });
30
+ expect(globalThis.window).toBeUndefined();
31
+ });
32
+ it("formats every visible review date in the active UI language", () => {
33
+ const sources = [
34
+ "./pages/Review.tsx",
35
+ "./pages/Reviews.tsx",
36
+ "./components/ReviewCustomer.tsx",
37
+ "./components/ReviewOrder.tsx",
38
+ ].map((path) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), "utf8"));
39
+ expect(sources.reduce((count, source) => count + (source.match(/formatDate\(/g)?.length ?? 0), 0)).toBe(7);
40
+ for (const source of sources) {
41
+ expect(source.match(/locale: i18n\.(?:resolvedLanguage|language)/g)).toHaveLength(source.match(/formatDate\(/g)?.length ?? 0);
42
+ }
43
+ });
44
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deenruv/reviews-plugin",
3
- "version": "1.0.17-dev.31",
3
+ "version": "1.0.17-dev.32",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -30,9 +30,9 @@
30
30
  "react-router": "^7.13.0",
31
31
  "sonner": "^1.4.41",
32
32
  "recharts": "^2.12.7",
33
- "@deenruv/admin-types": "^1.0.17-dev.31",
34
- "@deenruv/common": "^1.0.17-dev.31",
35
- "@deenruv/react-ui-devkit": "^1.0.17-dev.31"
33
+ "@deenruv/admin-types": "^1.0.17-dev.32",
34
+ "@deenruv/common": "^1.0.17-dev.32",
35
+ "@deenruv/react-ui-devkit": "^1.0.17-dev.32"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@graphql-typed-document-node/core": "3.2.0",
@@ -44,7 +44,7 @@
44
44
  "rimraf": "^5.0.10",
45
45
  "tslib": "^2.6.2",
46
46
  "typescript": "5.3.3",
47
- "@deenruv/core": "^1.0.17-dev.31"
47
+ "@deenruv/core": "^1.0.17-dev.32"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@deenruv/core": "^1.0.0 || ^1.0.17-dev.0"