@translationstudio/translationstudio-strapi-extension 4.1.1 → 4.3.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.
package/dist/Types.d.ts CHANGED
@@ -143,6 +143,9 @@ export interface ImportPayload {
143
143
  keep?: TranslatedDocumentReplaceFields;
144
144
  }[];
145
145
  }
146
+ export type HistoryDataMap = {
147
+ [id: string]: HistoryItem[];
148
+ };
146
149
  export interface HistoryItem {
147
150
  'time-intranslation': number;
148
151
  'project-name': string;
@@ -152,6 +155,7 @@ export interface HistoryItem {
152
155
  'time-updated': number;
153
156
  'element-name': string;
154
157
  'time-requested': number;
158
+ 'id': string;
155
159
  }
156
160
  export interface ContentType {
157
161
  uid: string;
@@ -167,7 +171,7 @@ export interface Entry {
167
171
  [key: string]: any;
168
172
  }
169
173
  export interface BulkTranslationMenuProps {
170
- historyData: HistoryItem[];
174
+ historyData: HistoryDataMap;
171
175
  isLoadingHistory: boolean;
172
176
  onTranslationComplete?: () => void;
173
177
  }
@@ -5,7 +5,7 @@ const admin = require("@strapi/strapi/admin");
5
5
  const reactRouterDom = require("react-router-dom");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const react = require("react");
8
- const index = require("./index-DkASHLO0.js");
8
+ const index = require("./index-Dgpsawos.js");
9
9
  const apiService = {
10
10
  async loadLicense(get) {
11
11
  try {
@@ -3,7 +3,7 @@ import { getFetchClient, Page } from "@strapi/strapi/admin";
3
3
  import { Routes, Route } from "react-router-dom";
4
4
  import { Main, Box, Alert, Grid, Typography, TextInput, Switch, Button } from "@strapi/design-system";
5
5
  import { useState, useCallback, useEffect } from "react";
6
- import { T as TranslationstudioLogo } from "./index-DF5d8Feh.mjs";
6
+ import { T as TranslationstudioLogo } from "./index-Csx0xlBL.mjs";
7
7
  const apiService = {
8
8
  async loadLicense(get) {
9
9
  try {
@@ -1,8 +1,9 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { Box, Typography, TextInput, Table, Thead, Tr, Tbody, Td, Badge, Th, Button, Radio, Checkbox, DatePicker, ProgressBar, Alert, Main, Grid, Flex } from "@strapi/design-system";
2
+ import { Modal, Typography, Button, Box, Alert, TextInput, Table, Thead, Tr, Th, Tbody, Td, Badge, SimpleMenu, IconButton, MenuItem, Radio, Checkbox, DatePicker, ProgressBar, Main, Grid, Flex } from "@strapi/design-system";
3
3
  import { useState, useEffect, useMemo } from "react";
4
4
  import { getFetchClient } from "@strapi/strapi/admin";
5
- import { g as groupHistoryData, f as formatDate, h as handleHistoryResponse, a as getThemeColors, u as useThemeMode, b as getStoredEmail, c as getSubmitLabel, s as setStoredEmail, v as validateDueDate, d as createEntryUid, e as determineEntryName, i as createTranslationPayload, j as createSuccessMessage, k as createErrorMessage, l as createGeneralErrorMessage, T as TranslationstudioLogo } from "./index-DF5d8Feh.mjs";
5
+ import { g as getThemeColors, a as groupHistoryData, G as GetStatusColor, b as GetStatusText, f as formatDate, h as handleHistoryResponse, u as useThemeMode, c as getStoredEmail, d as getSubmitLabel, s as setStoredEmail, v as validateDueDate, e as createEntryUid, i as determineEntryName, j as createTranslationPayload, k as createSuccessMessage, l as createErrorMessage, m as createGeneralErrorMessage, n as getElementStatus, T as TranslationstudioLogo } from "./index-Csx0xlBL.mjs";
6
+ import { Trash, More, Bell } from "@strapi/icons";
6
7
  const getSearchableText = (item) => {
7
8
  return `${item["project-name"]} ${item["element-name"]} ${item["element-uid"]} ${item.targetLanguages.join(" ")} ${item.combinedStatus.text}`.toLowerCase();
8
9
  };
@@ -12,19 +13,33 @@ const filterBySearchTerm = (items, searchTerm, getSearchableText2) => {
12
13
  return items.filter((item) => getSearchableText2(item).includes(lowerSearchTerm));
13
14
  };
14
15
  const getSortValue = (item, sortField) => {
15
- if (sortField === "status") {
16
- return item.combinedStatus.text.toLowerCase();
17
- }
18
- if (sortField === "target-language") {
19
- return item.targetLanguages.join(", ").toLowerCase();
16
+ switch (sortField) {
17
+ case "project-name":
18
+ case "element-uid":
19
+ return item["element-uid"] ?? "";
20
+ case "status":
21
+ return item["status"] ?? "";
22
+ case "element-name":
23
+ return item["element-name"];
24
+ case "target-language":
25
+ return item["targetLanguage"] ?? "";
26
+ case "time-imported":
27
+ return item.timeUpdated ?? 0;
28
+ default:
29
+ return "";
20
30
  }
21
- return item[sortField].toString().toLowerCase();
31
+ };
32
+ const compareValues = function(a, b) {
33
+ if (typeof a === "number" && typeof b === "number")
34
+ return a - b;
35
+ else
36
+ return a.localeCompare(b);
22
37
  };
23
38
  const sortItems = (items, sortState) => {
24
39
  return [...items].sort((a, b) => {
25
40
  const aValue = getSortValue(a, sortState.field);
26
41
  const bValue = getSortValue(b, sortState.field);
27
- const result = aValue.localeCompare(bValue);
42
+ const result = compareValues(aValue, bValue);
28
43
  return sortState.direction === "asc" ? result : -result;
29
44
  });
30
45
  };
@@ -48,6 +63,30 @@ const useDebounce = (value, delay) => {
48
63
  }, [value, delay]);
49
64
  return debouncedValue;
50
65
  };
66
+ function DeleteHistoryEntryRequest({ item, onDeleted, onClose }) {
67
+ return /* @__PURE__ */ jsx(Modal.Root, { open: true, onOpenChange: () => onClose(), children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
68
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Delete translation history information" }) }),
69
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
70
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Really delete this enty?" }) }),
71
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Typography, { children: "This cannot be undone. You can always translate the entry again." }) }),
72
+ /* @__PURE__ */ jsx("p", { children: " " }),
73
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs(Typography, { children: [
74
+ "Name: ",
75
+ item["element-name"],
76
+ /* @__PURE__ */ jsx("br", {}),
77
+ "Status: ",
78
+ item.status,
79
+ /* @__PURE__ */ jsx("br", {}),
80
+ "Language: ",
81
+ item.targetLanguage
82
+ ] }) })
83
+ ] }),
84
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
85
+ /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: "Cancel" }),
86
+ /* @__PURE__ */ jsx(Button, { onClick: onDeleted, startIcon: /* @__PURE__ */ jsx(Trash, {}), children: "Delete" })
87
+ ] })
88
+ ] }) });
89
+ }
51
90
  const DEBOUNCE_DELAY = 500;
52
91
  const LoadingState = () => /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Loading history..." }) });
53
92
  const EmptyState = ({ hasSearchTerm }) => /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: "#666" }, children: hasSearchTerm ? "No matching translation history found." : "No translation history available." }) });
@@ -85,7 +124,9 @@ const SortableHeader = ({
85
124
  const HistoryTable = ({
86
125
  data,
87
126
  sortState,
88
- onSort
127
+ onSort,
128
+ onDelete,
129
+ secondaryColor
89
130
  }) => /* @__PURE__ */ jsx(
90
131
  Box,
91
132
  {
@@ -94,35 +135,63 @@ const HistoryTable = ({
94
135
  },
95
136
  children: /* @__PURE__ */ jsxs(Table, { colCount: 6, rowCount: data.length + 1, children: [
96
137
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
97
- /* @__PURE__ */ jsx(SortableHeader, { field: "project-name", currentSort: sortState, onSort, children: "Content Type" }),
98
138
  /* @__PURE__ */ jsx(SortableHeader, { field: "element-name", currentSort: sortState, onSort, children: "Entry" }),
99
- /* @__PURE__ */ jsx(SortableHeader, { field: "element-uid", currentSort: sortState, onSort, children: "ID" }),
139
+ /* @__PURE__ */ jsx(SortableHeader, { field: "project-name", currentSort: sortState, onSort, children: "Content Type" }),
100
140
  /* @__PURE__ */ jsx(SortableHeader, { field: "status", currentSort: sortState, onSort, children: "Status" }),
101
141
  /* @__PURE__ */ jsx(SortableHeader, { field: "target-language", currentSort: sortState, onSort, children: "Target Language" }),
102
- /* @__PURE__ */ jsx(SortableHeader, { field: "time-imported", currentSort: sortState, onSort, children: "Date" })
142
+ /* @__PURE__ */ jsx(SortableHeader, { field: "time-imported", currentSort: sortState, onSort, children: "Date" }),
143
+ /* @__PURE__ */ jsx(Th, {})
103
144
  ] }) }),
104
- /* @__PURE__ */ jsx(Tbody, { children: data.map((item, index) => /* @__PURE__ */ jsx(HistoryRow, { item }, `${item["element-uid"]}-${index}`)) })
145
+ /* @__PURE__ */ jsx(Tbody, { children: data.map((item, index) => /* @__PURE__ */ jsx(
146
+ HistoryRow,
147
+ {
148
+ item,
149
+ onDelete,
150
+ secondaryColor
151
+ },
152
+ `${item["element-uid"]}-${index}`
153
+ )) })
105
154
  ] })
106
155
  }
107
156
  );
108
- const HistoryRow = ({ item }) => /* @__PURE__ */ jsxs(Tr, { children: [
109
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: item["project-name"] }) }),
110
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: item["element-name"] }) }),
111
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { fontFamily: "monospace", fontSize: "12px" }, children: item["element-uid"] }) }),
112
- /* @__PURE__ */ jsx(Td, { children: item.combinedStatus.text && /* @__PURE__ */ jsx(Badge, { variant: item.combinedStatus.variant, children: item.combinedStatus.text }) }),
113
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: item.targetLanguages.map((lang) => lang.toUpperCase()).join(", ") }) }),
114
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: formatDate(item["time-imported"]) }) })
157
+ const getContentType = function(input) {
158
+ const left = input.indexOf(".");
159
+ const right = input.lastIndexOf("#");
160
+ if (left === -1 || left > right || right === -1)
161
+ return "-";
162
+ return input.substring(left + 1, right);
163
+ };
164
+ const getElementId = function(input) {
165
+ const right = input.lastIndexOf("#");
166
+ if (right === -1)
167
+ return "-";
168
+ return input.substring(right + 1);
169
+ };
170
+ const HistoryRow = ({ item, onDelete, secondaryColor }) => /* @__PURE__ */ jsxs(Tr, { children: [
171
+ /* @__PURE__ */ jsxs(Td, { children: [
172
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: item["element-name"] }) }),
173
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { fontFamily: "monospace", fontSize: "12px", color: secondaryColor }, children: getElementId(item["element-uid"]) })
174
+ ] }),
175
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: getContentType(item["element-uid"]) }) }),
176
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { variant: GetStatusColor(item.status), children: GetStatusText(item.status) }) }),
177
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: item.targetLanguage.toUpperCase() }) }),
178
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: formatDate(item.timeUpdated) }) }),
179
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(SimpleMenu, { label: "Actions", tag: IconButton, icon: /* @__PURE__ */ jsx(More, {}), children: /* @__PURE__ */ jsx(MenuItem, { startIcon: /* @__PURE__ */ jsx(Bell, {}), variant: "danger", onSelect: () => onDelete(item), children: "Delete history entry" }) }) })
115
180
  ] });
116
181
  const HistoryMenu = () => {
117
182
  const [historyData, setHistoryData] = useState([]);
118
183
  const [isLoading, setIsLoading] = useState(true);
119
184
  const [error, setError] = useState(null);
185
+ const [errorMessage, setErrorMessage] = useState("");
186
+ const [succcessMessage, setSucccessMessage] = useState("");
120
187
  const [searchTerm, setSearchTerm] = useState("");
188
+ const [deleteItem, setDeleteItem] = useState(null);
121
189
  const [sortState, setSortState] = useState({
122
- field: "project-name",
123
- direction: "asc"
190
+ field: "time-imported",
191
+ direction: "desc"
124
192
  });
125
- const { get } = getFetchClient();
193
+ const { get, post } = getFetchClient();
194
+ const themeColors = getThemeColors();
126
195
  const debouncedSearchTerm = useDebounce(searchTerm, DEBOUNCE_DELAY);
127
196
  useEffect(() => {
128
197
  const fetchHistory = async () => {
@@ -133,8 +202,10 @@ const HistoryMenu = () => {
133
202
  const result = handleHistoryResponse(response.data);
134
203
  if (result.isError) {
135
204
  setError(result.errorMessage || "Failed to fetch translation history.");
136
- } else {
137
- setHistoryData(result.historyData);
205
+ } else if (result.historyData && Array.isArray(result.historyData) && result.historyData.length > 0) {
206
+ const res = result.historyData;
207
+ res.sort((a, b) => a["time-updated"] - b["time-updated"]);
208
+ setHistoryData(res);
138
209
  }
139
210
  } catch (error2) {
140
211
  console.error("Failed to fetch history:", error2);
@@ -163,11 +234,49 @@ const HistoryMenu = () => {
163
234
  if (error) {
164
235
  return /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "beta", textColor: "danger600", children: error }) });
165
236
  }
237
+ const removeHistoryEntry = function(item) {
238
+ if (!item || historyData.length === 0)
239
+ return;
240
+ if (historyData.length === 1) {
241
+ setHistoryData([]);
242
+ return;
243
+ }
244
+ let index = -1;
245
+ for (let i = 0; i < historyData.length && index === -1; i++) {
246
+ if (historyData[i].id === item.id)
247
+ index = i;
248
+ }
249
+ if (index === -1)
250
+ return;
251
+ historyData.splice(index, 1);
252
+ setHistoryData([...historyData]);
253
+ };
254
+ const onDeleteHistoyInfo = function(elem) {
255
+ if (!errorMessage)
256
+ setErrorMessage("");
257
+ if (!succcessMessage)
258
+ setSucccessMessage("");
259
+ if (!deleteItem)
260
+ return;
261
+ post("/translationstudio/history", {
262
+ uid: deleteItem.id
263
+ }).then((res) => {
264
+ if (res.status !== 204)
265
+ throw new Error("Could not delete entry");
266
+ removeHistoryEntry(deleteItem);
267
+ setSucccessMessage("Successfully removed history entry.");
268
+ }).catch(() => setErrorMessage("Could not delete history entry " + deleteItem["element-name"])).finally(() => setDeleteItem(null));
269
+ };
166
270
  const hasData = processedData.length > 0;
167
271
  const hasSearchTerm = Boolean(debouncedSearchTerm.trim());
168
272
  return /* @__PURE__ */ jsx(Box, { paddingTop: 4, paddingBottom: 4, style: { width: "100%" }, children: !hasData && !hasSearchTerm ? /* @__PURE__ */ jsx(EmptyState, { hasSearchTerm: false }) : /* @__PURE__ */ jsxs(Fragment, { children: [
169
273
  historyData.length > 10 && /* @__PURE__ */ jsx(SearchInput, { value: searchTerm, onChange: handleSearchChange }),
170
- hasData ? /* @__PURE__ */ jsx(HistoryTable, { data: processedData, sortState, onSort: handleSort }) : /* @__PURE__ */ jsx(EmptyState, { hasSearchTerm: true })
274
+ errorMessage && /* @__PURE__ */ jsx(Alert, { title: errorMessage, variant: "danger", style: { marginBottom: "2em" }, children: errorMessage }),
275
+ succcessMessage && /* @__PURE__ */ jsx(Alert, { title: succcessMessage, variant: "success", style: { marginBottom: "2em" }, children: succcessMessage }),
276
+ hasData ? /* @__PURE__ */ jsxs(Fragment, { children: [
277
+ deleteItem && /* @__PURE__ */ jsx(DeleteHistoryEntryRequest, { item: deleteItem, onClose: () => setDeleteItem(null), onDeleted: onDeleteHistoyInfo }),
278
+ /* @__PURE__ */ jsx(HistoryTable, { data: processedData, sortState, onSort: handleSort, onDelete: setDeleteItem, secondaryColor: themeColors.secondaryText })
279
+ ] }) : /* @__PURE__ */ jsx(EmptyState, { hasSearchTerm: true })
171
280
  ] }) });
172
281
  };
173
282
  const LoadingSpinner = () => /* @__PURE__ */ jsxs(Box, { paddingBottom: 4, style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
@@ -213,7 +322,7 @@ const LanguageSelector = ({
213
322
  tag: "label",
214
323
  paddingBottom: 2,
215
324
  style: { color: themeColors.primaryText },
216
- children: "Languages / Connectors"
325
+ children: "Translation Options"
217
326
  }
218
327
  ),
219
328
  languages.map((lang) => /* @__PURE__ */ jsx(Radio.Item, { value: lang.name, children: lang.name }, lang.id))
@@ -539,6 +648,7 @@ const BulkTranslationPanel = ({
539
648
  children: /* @__PURE__ */ jsx("path", { d: "M2,21L23,12L2,3V10L17,12L2,14V21Z", fill: "currentColor" })
540
649
  }
541
650
  ),
651
+ style: { height: "auto" },
542
652
  children: getSubmitLabel(selectedEntries.length, isUrgent, isMachineTranslation)
543
653
  }
544
654
  )
@@ -572,65 +682,6 @@ const getEntryTitle = (entry) => {
572
682
  const getEntryId = (entry) => {
573
683
  return entry.documentId || entry.id;
574
684
  };
575
- const getTranslationStatus = (entryId, historyData) => {
576
- if (!Array.isArray(historyData) || historyData.length === 0) {
577
- return "";
578
- }
579
- const matchingItems = historyData.filter((h) => h["element-uid"].includes(entryId));
580
- if (matchingItems.length === 0) {
581
- return "not_translated";
582
- }
583
- const hasInTranslation = matchingItems.some((item) => {
584
- const timeInTranslation = item["time-intranslation"] || 0;
585
- const timeImported = item["time-imported"] || 0;
586
- return timeInTranslation > timeImported;
587
- });
588
- if (hasInTranslation) {
589
- return "in_translation";
590
- }
591
- const hasTranslated = matchingItems.some((item) => {
592
- const timeImported = item["time-imported"] || 0;
593
- return timeImported > 0;
594
- });
595
- return hasTranslated ? "translated" : "not_translated";
596
- };
597
- const getTargetLanguages = (entryId, historyData) => {
598
- if (!Array.isArray(historyData) || historyData.length === 0) return [];
599
- const matchingItems = historyData.filter((h) => h["element-uid"].includes(entryId));
600
- const languages = [...new Set(matchingItems.map((item) => item["target-language"]))];
601
- return languages.sort();
602
- };
603
- const getTranslationDate = (entryId, historyData) => {
604
- if (!Array.isArray(historyData) || historyData.length === 0) return "";
605
- const matchingItems = historyData.filter((h) => h["element-uid"].includes(entryId));
606
- if (matchingItems.length === 0) return "";
607
- const latestImportTime = Math.max(
608
- ...matchingItems.map((item) => item["time-imported"] || 0)
609
- );
610
- return latestImportTime > 0 ? formatDate(latestImportTime) : "";
611
- };
612
- const getStatusBadgeVariant = (status) => {
613
- switch (status) {
614
- case "translated":
615
- return "success";
616
- case "in_translation":
617
- return "secondary";
618
- default:
619
- return "neutral";
620
- }
621
- };
622
- const getStatusDisplayText = (status) => {
623
- switch (status) {
624
- case "translated":
625
- return "Translated";
626
- case "in_translation":
627
- return "In Translation";
628
- case "not_translated":
629
- return "Not translated";
630
- default:
631
- return "n.a.";
632
- }
633
- };
634
685
  const ContentTypesList = ({
635
686
  contentTypes,
636
687
  isLoading,
@@ -740,60 +791,13 @@ const EntriesTable = ({
740
791
  maxHeight: "fit-content",
741
792
  backgroundColor: themeColors.cardBackground
742
793
  },
743
- children: /* @__PURE__ */ jsxs(Table, { colCount: 6, rowCount: entries.length + 1, children: [
794
+ children: /* @__PURE__ */ jsxs(Table, { colCount: 5, children: [
744
795
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
745
- /* @__PURE__ */ jsx(Th, { style: { width: "50px" }, children: /* @__PURE__ */ jsx(
746
- Checkbox,
747
- {
748
- checked: checkedType,
749
- onCheckedChange: onSelectAll
750
- }
751
- ) }),
752
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(
753
- Typography,
754
- {
755
- variant: "sigma",
756
- fontWeight: "bold",
757
- style: { color: themeColors.primaryText },
758
- children: "Title"
759
- }
760
- ) }),
761
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(
762
- Typography,
763
- {
764
- variant: "sigma",
765
- fontWeight: "bold",
766
- style: { color: themeColors.primaryText },
767
- children: "ID"
768
- }
769
- ) }),
770
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(
771
- Typography,
772
- {
773
- variant: "sigma",
774
- fontWeight: "bold",
775
- style: { color: themeColors.primaryText },
776
- children: "Status"
777
- }
778
- ) }),
779
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(
780
- Typography,
781
- {
782
- variant: "sigma",
783
- fontWeight: "bold",
784
- style: { color: themeColors.primaryText },
785
- children: "Target Language"
786
- }
787
- ) }),
788
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(
789
- Typography,
790
- {
791
- variant: "sigma",
792
- fontWeight: "bold",
793
- style: { color: themeColors.primaryText },
794
- children: "Date"
795
- }
796
- ) })
796
+ /* @__PURE__ */ jsx(Th, { style: { width: "50px" }, children: /* @__PURE__ */ jsx(Checkbox, { checked: checkedType, onCheckedChange: onSelectAll }) }),
797
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { color: themeColors.primaryText }, children: "Title" }) }),
798
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { color: themeColors.primaryText }, children: "Translated" }) }),
799
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { color: themeColors.primaryText }, children: "In translation" }) }),
800
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { color: themeColors.primaryText }, children: "Queued" }) })
797
801
  ] }) }),
798
802
  /* @__PURE__ */ jsx(Tbody, { children: entries.map((entry) => /* @__PURE__ */ jsx(
799
803
  EntryRow,
@@ -811,6 +815,15 @@ const EntriesTable = ({
811
815
  }
812
816
  );
813
817
  };
818
+ const getLangQueued = function(list) {
819
+ return list.filter((e) => getElementStatus(e) === "queued").map((e) => e["target-language"]).sort().join(", ");
820
+ };
821
+ const getLangInTranslation = function(list) {
822
+ return list.filter((e) => getElementStatus(e) === "intranslation").map((e) => e["target-language"]).sort().join(", ");
823
+ };
824
+ const getLangTranslated = function(list) {
825
+ return list.filter((e) => getElementStatus(e) === "translated").map((e) => e["target-language"]).sort().join(", ");
826
+ };
814
827
  const EntryRow = ({
815
828
  entry,
816
829
  isSelected,
@@ -820,9 +833,7 @@ const EntryRow = ({
820
833
  themeColors
821
834
  }) => {
822
835
  const entryId = getEntryId(entry);
823
- const status = getTranslationStatus(entryId, historyData);
824
- const targetLanguages = getTargetLanguages(entryId, historyData);
825
- const date = getTranslationDate(entryId, historyData);
836
+ const history = historyData[entryId] ?? [];
826
837
  return /* @__PURE__ */ jsxs(Tr, { children: [
827
838
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
828
839
  Checkbox,
@@ -831,18 +842,14 @@ const EntryRow = ({
831
842
  onCheckedChange: (checked) => onEntrySelection(entryId, checked)
832
843
  }
833
844
  ) }),
834
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: getEntryTitle(entry) }) }),
835
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
836
- Typography,
837
- {
838
- variant: "omega",
839
- style: { fontFamily: "monospace", fontSize: "12px", color: themeColors.secondaryText },
840
- children: entryId
841
- }
842
- ) }),
843
- /* @__PURE__ */ jsx(Td, { children: isLoadingHistory ? /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.mutedText }, children: "Loading..." }) : /* @__PURE__ */ jsx(Badge, { variant: getStatusBadgeVariant(status), children: getStatusDisplayText(status) }) }),
844
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: targetLanguages.map((lang) => lang.toUpperCase()).join(", ") || "-" }) }),
845
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: date }) })
845
+ /* @__PURE__ */ jsxs(Td, { children: [
846
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: getEntryTitle(entry) }),
847
+ /* @__PURE__ */ jsx("br", {}),
848
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { fontFamily: "monospace", fontSize: "12px", color: themeColors.secondaryText }, children: entryId })
849
+ ] }),
850
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: getLangTranslated(history) }) }),
851
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: getLangInTranslation(history) }) }),
852
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: themeColors.primaryText }, children: getLangQueued(history) }) })
846
853
  ] });
847
854
  };
848
855
  const fetchContentEntriesData = async function(selectedContentType) {
@@ -976,7 +983,7 @@ const BulkTranslationMenu = ({
976
983
  }
977
984
  ),
978
985
  /* @__PURE__ */ jsx(Box, { style: { width: "400px", flexShrink: 0, display: "flex", flexDirection: "column" }, children: selectedEntries.size > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
979
- /* @__PURE__ */ jsx(Typography, { variant: "omega", paddingBottom: 3, style: { fontWeight: "bold" }, children: "Translation Settings" }),
986
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", paddingBottom: 3, style: { fontWeight: "bold" }, children: "Translation Options" }),
980
987
  /* @__PURE__ */ jsx(Box, { style: { borderRadius: "4px", overflow: "hidden" }, children: /* @__PURE__ */ jsx(
981
988
  BulkTranslationPanel,
982
989
  {
@@ -988,8 +995,21 @@ const BulkTranslationMenu = ({
988
995
  ] }) })
989
996
  ] }) });
990
997
  };
998
+ const processHistoryData = function(list) {
999
+ const map = {};
1000
+ for (const elem of list) {
1001
+ const id = elem["element-uid"].split("#");
1002
+ if (id.length !== 2)
1003
+ continue;
1004
+ if (!map[id[0]])
1005
+ map[id[0]] = [elem];
1006
+ else
1007
+ map[id[0]].push(elem);
1008
+ }
1009
+ return map;
1010
+ };
991
1011
  const HistoryPage = () => {
992
- const [historyData, setHistoryData] = useState([]);
1012
+ const [historyData, setHistoryData] = useState({});
993
1013
  const [isLoadingHistory, setIsLoadingHistory] = useState(true);
994
1014
  const [activeTab, setActiveTab] = useState("history");
995
1015
  const { get } = getFetchClient();
@@ -998,13 +1018,13 @@ const HistoryPage = () => {
998
1018
  try {
999
1019
  const response = await get("/translationstudio/history");
1000
1020
  const result = handleHistoryResponse(response.data);
1001
- if (result.isError) {
1002
- } else {
1003
- setHistoryData(result.historyData);
1004
- }
1021
+ if (result.isError)
1022
+ throw new Error("Cold not fetch data");
1023
+ if (result.historyData && Array.isArray(result.historyData))
1024
+ setHistoryData(processHistoryData(result.historyData));
1005
1025
  } catch (error) {
1006
1026
  console.error("Failed to fetch history:", error);
1007
- setHistoryData([]);
1027
+ setHistoryData({});
1008
1028
  } finally {
1009
1029
  setIsLoadingHistory(false);
1010
1030
  }
@@ -1016,13 +1036,12 @@ const HistoryPage = () => {
1016
1036
  try {
1017
1037
  const response = await get("/translationstudio/history");
1018
1038
  const result = handleHistoryResponse(response.data);
1019
- if (result.isError) {
1020
- setHistoryData([]);
1021
- } else {
1022
- setHistoryData(result.historyData);
1023
- }
1039
+ if (result.isError)
1040
+ setHistoryData({});
1041
+ else if (result.historyData && Array.isArray(result.historyData))
1042
+ setHistoryData(processHistoryData(result.historyData));
1024
1043
  } catch (error) {
1025
- setHistoryData([]);
1044
+ setHistoryData({});
1026
1045
  } finally {
1027
1046
  setIsLoadingHistory(false);
1028
1047
  }