@translationstudio/translationstudio-strapi-extension 4.3.0 → 5.0.1

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.
@@ -4,7 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const designSystem = require("@strapi/design-system");
5
5
  const react = require("react");
6
6
  const admin = require("@strapi/strapi/admin");
7
- const index = require("./index-Dgpsawos.js");
7
+ const index = require("./index-CgX-X1nF.js");
8
8
  const icons = require("@strapi/icons");
9
9
  const getSearchableText = (item) => {
10
10
  return `${item["project-name"]} ${item["element-name"]} ${item["element-uid"]} ${item.targetLanguages.join(" ")} ${item.combinedStatus.text}`.toLowerCase();
@@ -90,7 +90,6 @@ function DeleteHistoryEntryRequest({ item, onDeleted, onClose }) {
90
90
  ] }) });
91
91
  }
92
92
  const DEBOUNCE_DELAY = 500;
93
- const LoadingState = () => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: "Loading history..." }) });
94
93
  const EmptyState = ({ hasSearchTerm }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", style: { color: "#666" }, children: hasSearchTerm ? "No matching translation history found." : "No translation history available." }) });
95
94
  const SearchInput = ({ value, onChange }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
96
95
  designSystem.TextInput,
@@ -180,10 +179,7 @@ const HistoryRow = ({ item, onDelete, secondaryColor }) => /* @__PURE__ */ jsxRu
180
179
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: index.formatDate(item.timeUpdated) }) }),
181
180
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.SimpleMenu, { label: "Actions", tag: designSystem.IconButton, icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.MenuItem, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Bell, {}), variant: "danger", onSelect: () => onDelete(item), children: "Delete history entry" }) }) })
182
181
  ] });
183
- const HistoryMenu = () => {
184
- const [historyData, setHistoryData] = react.useState([]);
185
- const [isLoading, setIsLoading] = react.useState(true);
186
- const [error, setError] = react.useState(null);
182
+ const HistoryMenu = ({ groupedHistoryData, onRemoveHistoryItem }) => {
187
183
  const [errorMessage, setErrorMessage] = react.useState("");
188
184
  const [succcessMessage, setSucccessMessage] = react.useState("");
189
185
  const [searchTerm, setSearchTerm] = react.useState("");
@@ -192,67 +188,19 @@ const HistoryMenu = () => {
192
188
  field: "time-imported",
193
189
  direction: "desc"
194
190
  });
195
- const { get, post } = admin.getFetchClient();
191
+ const { post } = admin.getFetchClient();
196
192
  const themeColors = index.getThemeColors();
197
193
  const debouncedSearchTerm = useDebounce(searchTerm, DEBOUNCE_DELAY);
198
- react.useEffect(() => {
199
- const fetchHistory = async () => {
200
- setIsLoading(true);
201
- setError(null);
202
- try {
203
- const response = await get("/translationstudio/history");
204
- const result = index.handleHistoryResponse(response.data);
205
- if (result.isError) {
206
- setError(result.errorMessage || "Failed to fetch translation history.");
207
- } else if (result.historyData && Array.isArray(result.historyData) && result.historyData.length > 0) {
208
- const res = result.historyData;
209
- res.sort((a, b) => a["time-updated"] - b["time-updated"]);
210
- setHistoryData(res);
211
- }
212
- } catch (error2) {
213
- console.error("Failed to fetch history:", error2);
214
- setError("Failed to fetch translation history.");
215
- setHistoryData([]);
216
- } finally {
217
- setIsLoading(false);
218
- }
219
- };
220
- fetchHistory();
221
- }, [setIsLoading, setError, setHistoryData]);
222
194
  const processedData = react.useMemo(() => {
223
- const grouped = index.groupHistoryData(historyData);
224
- const filtered = filterBySearchTerm(grouped, debouncedSearchTerm, getSearchableText);
195
+ const filtered = filterBySearchTerm(groupedHistoryData, debouncedSearchTerm, getSearchableText);
225
196
  return sortItems(filtered, sortState);
226
- }, [historyData, debouncedSearchTerm, sortState]);
197
+ }, [groupedHistoryData, debouncedSearchTerm, sortState]);
227
198
  const handleSort = (field) => {
228
199
  setSortState((currentState) => getNextSortState(currentState, field));
229
200
  };
230
201
  const handleSearchChange = (e) => {
231
202
  setSearchTerm(e.target.value);
232
203
  };
233
- if (isLoading) {
234
- return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, {});
235
- }
236
- if (error) {
237
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", textColor: "danger600", children: error }) });
238
- }
239
- const removeHistoryEntry = function(item) {
240
- if (!item || historyData.length === 0)
241
- return;
242
- if (historyData.length === 1) {
243
- setHistoryData([]);
244
- return;
245
- }
246
- let index2 = -1;
247
- for (let i = 0; i < historyData.length && index2 === -1; i++) {
248
- if (historyData[i].id === item.id)
249
- index2 = i;
250
- }
251
- if (index2 === -1)
252
- return;
253
- historyData.splice(index2, 1);
254
- setHistoryData([...historyData]);
255
- };
256
204
  const onDeleteHistoyInfo = function(elem) {
257
205
  if (!errorMessage)
258
206
  setErrorMessage("");
@@ -265,14 +213,14 @@ const HistoryMenu = () => {
265
213
  }).then((res) => {
266
214
  if (res.status !== 204)
267
215
  throw new Error("Could not delete entry");
268
- removeHistoryEntry(deleteItem);
216
+ onRemoveHistoryItem(deleteItem.id);
269
217
  setSucccessMessage("Successfully removed history entry.");
270
218
  }).catch(() => setErrorMessage("Could not delete history entry " + deleteItem["element-name"])).finally(() => setDeleteItem(null));
271
219
  };
272
220
  const hasData = processedData.length > 0;
273
221
  const hasSearchTerm = Boolean(debouncedSearchTerm.trim());
274
222
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, paddingBottom: 4, style: { width: "100%" }, children: !hasData && !hasSearchTerm ? /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { hasSearchTerm: false }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
275
- historyData.length > 10 && /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { value: searchTerm, onChange: handleSearchChange }),
223
+ groupedHistoryData.length > 10 && /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { value: searchTerm, onChange: handleSearchChange }),
276
224
  errorMessage && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { title: errorMessage, variant: "danger", style: { marginBottom: "2em" }, children: errorMessage }),
277
225
  succcessMessage && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { title: succcessMessage, variant: "success", style: { marginBottom: "2em" }, children: succcessMessage }),
278
226
  hasData ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -317,16 +265,7 @@ const LanguageSelector = ({
317
265
  name: "languages",
318
266
  "aria-label": "translationstudio settings",
319
267
  children: [
320
- /* @__PURE__ */ jsxRuntime.jsx(
321
- designSystem.Typography,
322
- {
323
- variant: "omega",
324
- tag: "label",
325
- paddingBottom: 2,
326
- style: { color: themeColors.primaryText },
327
- children: "Translation Options"
328
- }
329
- ),
268
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", tag: "label", paddingBottom: 2, style: { color: themeColors.primaryText }, children: "Available translation options:" }),
330
269
  languages.map((lang) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { value: lang.name, children: lang.name }, lang.id))
331
270
  ]
332
271
  }
@@ -404,6 +343,7 @@ const AlertMessage = ({
404
343
  const BulkTranslationPanel = ({
405
344
  contentType,
406
345
  selectedEntries,
346
+ onClose,
407
347
  onTranslationComplete
408
348
  }) => {
409
349
  const [languages, setLanguages] = react.useState([]);
@@ -420,20 +360,16 @@ const BulkTranslationPanel = ({
420
360
  const [tsIsAvailable, setTsIsAvailable] = react.useState(true);
421
361
  const [isProcessing, setIsProcessing] = react.useState(false);
422
362
  const [progress, setProgress] = react.useState(0);
423
- const [isLoadingMappings, setIsLoadingMappings] = react.useState(false);
363
+ const [isLoadingMappings, setIsLoadingMappings] = react.useState(true);
424
364
  const [mappingsError, setMappingsError] = react.useState(false);
425
365
  const themeColors = index.getThemeColors();
426
- index.useThemeMode();
427
366
  const { get, post } = admin.getFetchClient();
428
367
  const selectedLang = languages.find((lang) => lang.name === selectedOption);
429
368
  const isMachineTranslation = selectedLang?.machine ?? false;
430
369
  react.useEffect(() => {
431
370
  if (!contentType) return;
432
- setIsLoadingMappings(true);
433
- setMappingsError(false);
434
371
  get("/translationstudio/getLicense").then((response) => {
435
- if (typeof response.data.license !== "string") return false;
436
- return response.data.license !== "";
372
+ return response.status === 204;
437
373
  }).then((hasLicense) => {
438
374
  setLicenseValid(hasLicense);
439
375
  if (!hasLicense) throw new Error("No license set");
@@ -454,14 +390,15 @@ const BulkTranslationPanel = ({
454
390
  }).finally(() => {
455
391
  setIsLoadingMappings(false);
456
392
  });
457
- }, [contentType]);
393
+ }, [contentType, setLicenseValid, setTsIsAvailable, setLanguages, setMappingsError, setIsLoadingMappings]);
458
394
  react.useEffect(() => {
459
- if (!contentType || !isEmail) return;
395
+ if (!contentType || !isEmail)
396
+ return;
460
397
  const savedEmail = index.getStoredEmail();
461
398
  if (savedEmail) {
462
399
  setEmail(savedEmail);
463
400
  }
464
- }, [isEmail]);
401
+ }, [contentType, isEmail, setEmail]);
465
402
  const handleUrgentChange = () => {
466
403
  if (isUrgent) {
467
404
  setIsUrgent(false);
@@ -588,76 +525,70 @@ const BulkTranslationPanel = ({
588
525
  const pluginIsAvailable = () => {
589
526
  return !isLoadingMappings && licenseValid !== null && tsIsAvailable === true && !mappingsError;
590
527
  };
591
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
592
- /* @__PURE__ */ jsxRuntime.jsx(
593
- AlertMessage,
594
- {
595
- show: showAlert,
596
- type: alertType,
597
- message: alertMessage,
598
- onClose: () => setShowAlert(false)
599
- }
600
- ),
601
- /* @__PURE__ */ jsxRuntime.jsx(
602
- designSystem.Box,
603
- {
604
- padding: 4,
605
- style: {
606
- width: "100%",
607
- backgroundColor: themeColors.cardBackground,
608
- borderRadius: "4px"
609
- },
610
- children: !pluginIsAvailable() ? renderNotAvailable() : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
611
- /* @__PURE__ */ jsxRuntime.jsx(
612
- LanguageSelector,
613
- {
614
- languages,
615
- selectedOption,
616
- onSelectionChange: handleRadioSelection,
617
- themeColors
618
- }
619
- ),
620
- !isMachineTranslation && selectedOption !== "" && /* @__PURE__ */ jsxRuntime.jsx(
621
- AdditionalSettings,
622
- {
623
- isUrgent,
624
- isEmail,
625
- email,
626
- dueDate,
627
- onUrgentChange: handleUrgentChange,
628
- onEmailChange: handleEmailChange,
629
- onEmailInputChange: handleEmailInputChange,
630
- onDueDateChange: handleDueDateChange,
631
- themeColors
632
- }
633
- ),
634
- isProcessing && /* @__PURE__ */ jsxRuntime.jsx(ProgressIndicator, { progress }),
635
- /* @__PURE__ */ jsxRuntime.jsx(
636
- designSystem.Button,
637
- {
638
- fullWidth: true,
639
- onClick: handleBulkTranslationRequest,
640
- disabled: !selectedOption || isProcessing,
641
- loading: isProcessing,
642
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(
643
- "svg",
644
- {
645
- width: "20",
646
- height: "20",
647
- viewBox: "0 0 24 24",
648
- fill: "none",
649
- xmlns: "http://www.w3.org/2000/svg",
650
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2,21L23,12L2,3V10L17,12L2,14V21Z", fill: "currentColor" })
651
- }
652
- ),
653
- style: { height: "auto" },
654
- children: index.getSubmitLabel(selectedEntries.length, isUrgent, isMachineTranslation)
655
- }
656
- )
657
- ] })
658
- }
659
- )
660
- ] });
528
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: true, onOpenChange: () => onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
529
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: "Translation request" }) }),
530
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
531
+ /* @__PURE__ */ jsxRuntime.jsx(
532
+ AlertMessage,
533
+ {
534
+ show: showAlert,
535
+ type: alertType,
536
+ message: alertMessage,
537
+ onClose: () => setShowAlert(false)
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsxRuntime.jsx(
541
+ designSystem.Box,
542
+ {
543
+ padding: 4,
544
+ style: {
545
+ width: "100%",
546
+ backgroundColor: themeColors.cardBackground,
547
+ borderRadius: "4px"
548
+ },
549
+ children: !pluginIsAvailable() ? renderNotAvailable() : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
550
+ /* @__PURE__ */ jsxRuntime.jsx(
551
+ LanguageSelector,
552
+ {
553
+ languages,
554
+ selectedOption,
555
+ onSelectionChange: handleRadioSelection,
556
+ themeColors
557
+ }
558
+ ),
559
+ !isMachineTranslation && selectedOption !== "" && /* @__PURE__ */ jsxRuntime.jsx(
560
+ AdditionalSettings,
561
+ {
562
+ isUrgent,
563
+ isEmail,
564
+ email,
565
+ dueDate,
566
+ onUrgentChange: handleUrgentChange,
567
+ onEmailChange: handleEmailChange,
568
+ onEmailInputChange: handleEmailInputChange,
569
+ onDueDateChange: handleDueDateChange,
570
+ themeColors
571
+ }
572
+ ),
573
+ isProcessing && /* @__PURE__ */ jsxRuntime.jsx(ProgressIndicator, { progress })
574
+ ] })
575
+ }
576
+ )
577
+ ] }),
578
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
579
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: "Cancel" }),
580
+ /* @__PURE__ */ jsxRuntime.jsx(
581
+ designSystem.Button,
582
+ {
583
+ onClick: handleBulkTranslationRequest,
584
+ disabled: !selectedOption || isProcessing,
585
+ loading: isProcessing,
586
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.PaperPlane, {}),
587
+ children: index.getSubmitLabel(selectedEntries.length, isUrgent, isMachineTranslation)
588
+ }
589
+ )
590
+ ] })
591
+ ] }) });
661
592
  };
662
593
  const filterAndTransformContentTypes = (data) => {
663
594
  return data.filter((type) => type.kind === "collectionType" || type.kind === "singleType").filter((type) => !type.uid.startsWith("admin::") && !type.uid.startsWith("plugin::")).map((type) => ({
@@ -817,14 +748,19 @@ const EntriesTable = ({
817
748
  }
818
749
  );
819
750
  };
820
- const getLangQueued = function(list) {
821
- return list.filter((e) => index.getElementStatus(e) === "queued").map((e) => e["target-language"]).sort().join(", ");
751
+ const getLangQueued = function(e) {
752
+ return e.queued.sort().join(", ");
822
753
  };
823
- const getLangInTranslation = function(list) {
824
- return list.filter((e) => index.getElementStatus(e) === "intranslation").map((e) => e["target-language"]).sort().join(", ");
754
+ const getLangInTranslation = function(e) {
755
+ return e.intranslation.sort().join(", ");
825
756
  };
826
- const getLangTranslated = function(list) {
827
- return list.filter((e) => index.getElementStatus(e) === "translated").map((e) => e["target-language"]).sort().join(", ");
757
+ const getLangTranslated = function(e) {
758
+ return e.translated.sort().join(", ");
759
+ };
760
+ const EmptyHistoryDataLanguageMapItem = {
761
+ queued: [],
762
+ intranslation: [],
763
+ translated: []
828
764
  };
829
765
  const EntryRow = ({
830
766
  entry,
@@ -835,7 +771,7 @@ const EntryRow = ({
835
771
  themeColors
836
772
  }) => {
837
773
  const entryId = getEntryId(entry);
838
- const history = historyData[entryId] ?? [];
774
+ const history = historyData[entryId] ?? EmptyHistoryDataLanguageMapItem;
839
775
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
840
776
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
841
777
  designSystem.Checkbox,
@@ -868,8 +804,45 @@ const fetchContentEntriesData = async function(selectedContentType) {
868
804
  }
869
805
  return [];
870
806
  };
807
+ const extractDocumentId = function(id) {
808
+ const pos = id.lastIndexOf("#");
809
+ return pos === -1 ? id : id.substring(pos + 1);
810
+ };
811
+ const groupHistory = function(history) {
812
+ const map = {};
813
+ for (const e of history) {
814
+ const id = extractDocumentId(e["element-uid"]);
815
+ if (!map[id]) {
816
+ const data = {
817
+ queued: [],
818
+ intranslation: [],
819
+ translated: []
820
+ };
821
+ map[id] = data;
822
+ }
823
+ const entry = map[id];
824
+ switch (e.status) {
825
+ case "intranslation":
826
+ entry.intranslation.push(e.targetLanguage);
827
+ break;
828
+ case "translated":
829
+ entry.translated.push(e.targetLanguage);
830
+ break;
831
+ default:
832
+ entry.queued.push(e.targetLanguage);
833
+ break;
834
+ }
835
+ }
836
+ for (const key in map) {
837
+ const data = map[key];
838
+ data.intranslation.sort();
839
+ data.queued.sort();
840
+ data.translated.sort();
841
+ }
842
+ return map;
843
+ };
871
844
  const BulkTranslationMenu = ({
872
- historyData,
845
+ groupedHistoryData,
873
846
  isLoadingHistory,
874
847
  onTranslationComplete
875
848
  }) => {
@@ -879,7 +852,9 @@ const BulkTranslationMenu = ({
879
852
  const [selectedEntries, setSelectedEntries] = react.useState(/* @__PURE__ */ new Set());
880
853
  const [isLoadingContentTypes, setIsLoadingContentTypes] = react.useState(false);
881
854
  const [isLoadingEntries, setIsLoadingEntries] = react.useState(false);
855
+ const [showTranslation, setShowTranslation] = react.useState(false);
882
856
  const themeColors = index.getThemeColors();
857
+ const historyItemMap = react.useMemo(() => groupHistory(groupedHistoryData), [groupedHistoryData]);
883
858
  const { get } = admin.getFetchClient();
884
859
  react.useEffect(() => {
885
860
  const fetchContentTypes = async () => {
@@ -945,7 +920,7 @@ const BulkTranslationMenu = ({
945
920
  if (contentTypes.length === 0) {
946
921
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, paddingBottom: 4, style: { overflow: "hidden", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", paddingBottom: 3, style: { fontWeight: "bold" }, children: "No content types available." }) });
947
922
  }
948
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, paddingBottom: 4, style: { overflow: "hidden", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1, display: "flex", gap: "16px", overflow: "hidden" }, children: [
923
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, paddingBottom: 4, style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1, display: "flex", gap: "16px" }, children: [
949
924
  /* @__PURE__ */ jsxRuntime.jsx(
950
925
  ContentTypesList,
951
926
  {
@@ -962,9 +937,7 @@ const BulkTranslationMenu = ({
962
937
  style: {
963
938
  flex: 1,
964
939
  display: "flex",
965
- flexDirection: "column",
966
- minWidth: "400px",
967
- width: "60vw"
940
+ flexDirection: "column"
968
941
  },
969
942
  children: [
970
943
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", paddingBottom: 3, style: { fontWeight: "bold" }, children: "Entries" }),
@@ -974,7 +947,7 @@ const BulkTranslationMenu = ({
974
947
  entries,
975
948
  isLoading: isLoadingEntries,
976
949
  selectedEntries,
977
- historyData,
950
+ historyData: historyItemMap,
978
951
  isLoadingHistory,
979
952
  onEntrySelection: handleEntrySelection,
980
953
  onSelectAll: handleSelectAll,
@@ -984,34 +957,55 @@ const BulkTranslationMenu = ({
984
957
  ]
985
958
  }
986
959
  ),
987
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "400px", flexShrink: 0, display: "flex", flexDirection: "column" }, children: selectedEntries.size > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
988
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", paddingBottom: 3, style: { fontWeight: "bold" }, children: "Translation Options" }),
989
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { borderRadius: "4px", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
990
- BulkTranslationPanel,
991
- {
992
- contentType: selectedContentTypeData,
993
- selectedEntries: Array.from(selectedEntries),
994
- onTranslationComplete: handleTranslationComplete
960
+ /* @__PURE__ */ jsxRuntime.jsxs(
961
+ designSystem.Button,
962
+ {
963
+ disabled: selectedEntries.size === 0,
964
+ style: { position: "fixed", right: "5%", bottom: "5%" },
965
+ size: "L",
966
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Earth, {}),
967
+ onClick: () => {
968
+ setShowTranslation(true);
969
+ },
970
+ children: [
971
+ "Translate ",
972
+ selectedEntries.size,
973
+ " ",
974
+ selectedEntries.size === 1 ? "entry" : "entries"
975
+ ]
976
+ }
977
+ ),
978
+ showTranslation && /* @__PURE__ */ jsxRuntime.jsx(
979
+ BulkTranslationPanel,
980
+ {
981
+ contentType: selectedContentTypeData,
982
+ selectedEntries: Array.from(selectedEntries),
983
+ onClose: () => setShowTranslation(false),
984
+ onTranslationComplete: () => {
985
+ handleTranslationComplete();
986
+ setShowTranslation(false);
995
987
  }
996
- ) })
997
- ] }) })
988
+ }
989
+ )
998
990
  ] }) });
999
991
  };
1000
- const processHistoryData = function(list) {
1001
- const map = {};
1002
- for (const elem of list) {
1003
- const id = elem["element-uid"].split("#");
1004
- if (id.length !== 2)
1005
- continue;
1006
- if (!map[id[0]])
1007
- map[id[0]] = [elem];
1008
- else
1009
- map[id[0]].push(elem);
1010
- }
1011
- return map;
992
+ const TranslationstudioLogoBox = function() {
993
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { textAlign: "right", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
994
+ "picture",
995
+ {
996
+ style: {
997
+ width: "150px",
998
+ height: "auto",
999
+ display: "inline-block"
1000
+ },
1001
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.TranslationstudioLogo, {})
1002
+ }
1003
+ ) });
1012
1004
  };
1013
1005
  const HistoryPage = () => {
1014
- const [historyData, setHistoryData] = react.useState({});
1006
+ const [historyData, setHistoryData] = react.useState([]);
1007
+ const [groupedHistoryData, setGroupedHistoryData] = react.useState([]);
1008
+ const [lastUpdated, setLastUpdated] = react.useState(0);
1015
1009
  const [isLoadingHistory, setIsLoadingHistory] = react.useState(true);
1016
1010
  const [activeTab, setActiveTab] = react.useState("history");
1017
1011
  const { get } = admin.getFetchClient();
@@ -1022,50 +1016,77 @@ const HistoryPage = () => {
1022
1016
  const result = index.handleHistoryResponse(response.data);
1023
1017
  if (result.isError)
1024
1018
  throw new Error("Cold not fetch data");
1025
- if (result.historyData && Array.isArray(result.historyData))
1026
- setHistoryData(processHistoryData(result.historyData));
1019
+ if (result.historyData && Array.isArray(result.historyData)) {
1020
+ setHistoryData(result.historyData);
1021
+ setGroupedHistoryData(index.groupHistoryData(result.historyData));
1022
+ setLastUpdated(Date.now());
1023
+ }
1027
1024
  } catch (error) {
1028
1025
  console.error("Failed to fetch history:", error);
1029
- setHistoryData({});
1026
+ setHistoryData([]);
1027
+ setGroupedHistoryData([]);
1030
1028
  } finally {
1031
1029
  setIsLoadingHistory(false);
1032
1030
  }
1033
1031
  };
1034
1032
  fetchHistory();
1035
- }, [setHistoryData, setIsLoadingHistory]);
1033
+ }, [setHistoryData, setIsLoadingHistory, setGroupedHistoryData, setLastUpdated]);
1036
1034
  const refreshHistoryData = async () => {
1037
1035
  setIsLoadingHistory(true);
1038
1036
  try {
1039
1037
  const response = await get("/translationstudio/history");
1040
1038
  const result = index.handleHistoryResponse(response.data);
1041
- if (result.isError)
1042
- setHistoryData({});
1043
- else if (result.historyData && Array.isArray(result.historyData))
1044
- setHistoryData(processHistoryData(result.historyData));
1039
+ if (result.isError) {
1040
+ setGroupedHistoryData([]);
1041
+ setHistoryData([]);
1042
+ } else if (result.historyData && Array.isArray(result.historyData)) {
1043
+ setHistoryData(result.historyData);
1044
+ setGroupedHistoryData(index.groupHistoryData(result.historyData));
1045
+ setLastUpdated(Date.now());
1046
+ }
1045
1047
  } catch (error) {
1046
- setHistoryData({});
1048
+ setHistoryData([]);
1049
+ setGroupedHistoryData([]);
1047
1050
  } finally {
1048
1051
  setIsLoadingHistory(false);
1049
1052
  }
1050
1053
  };
1054
+ const onRefreshHistoryClick = function() {
1055
+ if (Date.now() - lastUpdated > 1e3 * 60 * 5)
1056
+ refreshHistoryData();
1057
+ };
1058
+ const onChangePage = function(input) {
1059
+ onRefreshHistoryClick();
1060
+ setActiveTab(input);
1061
+ };
1062
+ const removeHistoryEntry = function(id) {
1063
+ if (!id || historyData.length === 0)
1064
+ return;
1065
+ if (historyData.length === 1) {
1066
+ setHistoryData([]);
1067
+ return;
1068
+ }
1069
+ let index$1 = -1;
1070
+ for (let i = 0; i < historyData.length && index$1 === -1; i++) {
1071
+ if (historyData[i].id === id)
1072
+ index$1 = i;
1073
+ }
1074
+ if (index$1 === -1)
1075
+ return;
1076
+ historyData.splice(index$1, 1);
1077
+ const res = [...historyData];
1078
+ setHistoryData(res);
1079
+ setGroupedHistoryData(index.groupHistoryData(res));
1080
+ };
1051
1081
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 10, style: { minHeight: "90vh", marginTop: "5vh" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { children: [
1052
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { textAlign: "right", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1053
- "picture",
1054
- {
1055
- style: {
1056
- width: "150px",
1057
- height: "auto",
1058
- display: "inline-block"
1059
- },
1060
- children: /* @__PURE__ */ jsxRuntime.jsx(index.TranslationstudioLogo, {})
1061
- }
1062
- ) }) }),
1082
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(TranslationstudioLogoBox, {}) }),
1063
1083
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1064
1084
  /* @__PURE__ */ jsxRuntime.jsx(
1065
1085
  designSystem.Button,
1066
1086
  {
1067
1087
  variant: activeTab === "history" ? "default" : "tertiary",
1068
- onClick: () => setActiveTab("history"),
1088
+ disabled: isLoadingHistory,
1089
+ onClick: () => onChangePage("history"),
1069
1090
  children: "Translation History"
1070
1091
  }
1071
1092
  ),
@@ -1073,7 +1094,8 @@ const HistoryPage = () => {
1073
1094
  designSystem.Button,
1074
1095
  {
1075
1096
  variant: activeTab === "bulk" ? "default" : "tertiary",
1076
- onClick: () => setActiveTab("bulk"),
1097
+ disabled: isLoadingHistory,
1098
+ onClick: () => onChangePage("bulk"),
1077
1099
  children: "Translate multiple entries"
1078
1100
  }
1079
1101
  )
@@ -1082,7 +1104,7 @@ const HistoryPage = () => {
1082
1104
  activeTab === "bulk" && /* @__PURE__ */ jsxRuntime.jsx(
1083
1105
  BulkTranslationMenu,
1084
1106
  {
1085
- historyData,
1107
+ groupedHistoryData,
1086
1108
  isLoadingHistory,
1087
1109
  onTranslationComplete: refreshHistoryData
1088
1110
  }
@@ -1090,9 +1112,8 @@ const HistoryPage = () => {
1090
1112
  activeTab === "history" && /* @__PURE__ */ jsxRuntime.jsx(
1091
1113
  HistoryMenu,
1092
1114
  {
1093
- historyData,
1094
- isLoadingHistory,
1095
- onRefresh: refreshHistoryData
1115
+ groupedHistoryData,
1116
+ onRemoveHistoryItem: (id) => removeHistoryEntry(id)
1096
1117
  }
1097
1118
  )
1098
1119
  ] })