@parhelia/localization 0.1.12912 → 0.1.12917
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"AA4UA,wBAAgB,kBAAkB,4CAklCjC;AAED,eAAO,MAAM,oCAAoC,8BAA8B,CAAC;AAEhF,wBAAgB,8BAA8B,4CA0Y7C"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState, useMemo, useSyncExternalStore, } from "react";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import { Button, Badge, Select, Input, UserPicker, Popover, PopoverContent, PopoverTrigger, Tooltip, TooltipContent, TooltipTrigger, ContentTree, PageHeader, fetchItemStubs, searchUsers as searchBackendUsers, useEditContext, getLanguages, SimpleIconButton,
|
|
5
|
-
import { listBatches, getTranslationProviders, listBatchTranslationJobs, retryBatchTranslation,
|
|
4
|
+
import { Button, Badge, Select, Input, UserPicker, Popover, PopoverContent, PopoverTrigger, Tooltip, TooltipContent, TooltipTrigger, ContentTree, PageHeader, fetchItemStubs, searchUsers as searchBackendUsers, useEditContext, getLanguages, SimpleIconButton, formatDateTime, } from "@parhelia/core";
|
|
5
|
+
import { listBatches, getTranslationProviders, listBatchTranslationJobs, retryBatchTranslation, abortJob, abortBatch, } from "../services/translationService";
|
|
6
6
|
import { TRANSLATION_BATCH_STARTED_EVENT, } from "../translationEvents";
|
|
7
7
|
import { useDebouncedCallback } from "use-debounce";
|
|
8
8
|
import { X, ChevronDown, Languages, Loader2, Globe, FolderTree, ExternalLink, Check, AlertCircle, CircleStop, Hourglass, Copy, Sparkles, RotateCcw, Plus, } from "lucide-react";
|
|
@@ -187,7 +187,7 @@ export function TranslationBatches() {
|
|
|
187
187
|
const [batchJobs, setBatchJobs] = useState({});
|
|
188
188
|
const [loadingJobs, setLoadingJobs] = useState(new Set());
|
|
189
189
|
const [abortingBatchIds, setAbortingBatchIds] = useState(new Set());
|
|
190
|
-
const [
|
|
190
|
+
const [abortingJobIds, setAbortingJobIds] = useState(new Set());
|
|
191
191
|
const [retryingKeys, setRetryingKeys] = useState(new Set());
|
|
192
192
|
// Cache of userName → displayName so we can render full names on batch rows
|
|
193
193
|
// without re-querying the user service every render.
|
|
@@ -603,41 +603,54 @@ export function TranslationBatches() {
|
|
|
603
603
|
loadBatchJobs,
|
|
604
604
|
loadRecentBatches,
|
|
605
605
|
]);
|
|
606
|
-
const
|
|
606
|
+
const handleAbortJob = useCallback((job) => {
|
|
607
|
+
if (!job.id || !job.batchId) {
|
|
608
|
+
toast.error("This translation job cannot be aborted.");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const jobId = job.id;
|
|
612
|
+
const batchId = job.batchId;
|
|
613
|
+
const targetLabel = job.targetLanguage
|
|
614
|
+
? `${job.targetLanguage} translation`
|
|
615
|
+
: "translation";
|
|
607
616
|
editContext?.confirm({
|
|
608
|
-
header: "
|
|
609
|
-
message:
|
|
610
|
-
acceptLabel: "
|
|
617
|
+
header: "Abort Translation Job",
|
|
618
|
+
message: `Abort this ${targetLabel} job? This stops the remaining translation work for this language, but it does not roll back content that was already written.`,
|
|
619
|
+
acceptLabel: "Abort",
|
|
611
620
|
showCancel: true,
|
|
612
621
|
accept: async () => {
|
|
613
|
-
|
|
622
|
+
setAbortingJobIds((prev) => new Set(prev).add(jobId));
|
|
614
623
|
try {
|
|
615
|
-
const result = await
|
|
624
|
+
const result = await abortJob(jobId);
|
|
616
625
|
if (result.type !== "success") {
|
|
617
626
|
toast.error(result.details ||
|
|
618
627
|
result.summary ||
|
|
619
|
-
"Failed to
|
|
628
|
+
"Failed to abort translation job.");
|
|
620
629
|
return;
|
|
621
630
|
}
|
|
622
|
-
toast.success("Translation
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return next;
|
|
628
|
-
});
|
|
629
|
-
setExpandedBatchId((prev) => (prev === batchId ? null : prev));
|
|
631
|
+
toast.success("Translation job aborted.");
|
|
632
|
+
await loadRecentBatches(0, false, effectiveFilters);
|
|
633
|
+
if (expandedBatchId === batchId || batchJobs[batchId]) {
|
|
634
|
+
await loadBatchJobs(batchId);
|
|
635
|
+
}
|
|
630
636
|
}
|
|
631
637
|
finally {
|
|
632
|
-
|
|
638
|
+
setAbortingJobIds((prev) => {
|
|
633
639
|
const next = new Set(prev);
|
|
634
|
-
next.delete(
|
|
640
|
+
next.delete(jobId);
|
|
635
641
|
return next;
|
|
636
642
|
});
|
|
637
643
|
}
|
|
638
644
|
},
|
|
639
645
|
});
|
|
640
|
-
}, [
|
|
646
|
+
}, [
|
|
647
|
+
batchJobs,
|
|
648
|
+
editContext,
|
|
649
|
+
effectiveFilters,
|
|
650
|
+
expandedBatchId,
|
|
651
|
+
loadBatchJobs,
|
|
652
|
+
loadRecentBatches,
|
|
653
|
+
]);
|
|
641
654
|
const handleRetryJobs = useCallback(async (batchId, provider, jobs, retryKey) => {
|
|
642
655
|
const sessionId = editContext?.sessionId;
|
|
643
656
|
if (!sessionId) {
|
|
@@ -806,11 +819,9 @@ export function TranslationBatches() {
|
|
|
806
819
|
const isLoadingThis = loadingJobs.has(b.id);
|
|
807
820
|
const canRetry = anyError;
|
|
808
821
|
const canAbort = isActiveBatchStatus(info?.status);
|
|
809
|
-
const canDelete = isTerminalBatchStatus(info?.status);
|
|
810
822
|
const isRetryingBatch = retryingKeys.has(`batch:${b.id}`);
|
|
811
823
|
const isAborting = abortingBatchIds.has(b.id);
|
|
812
|
-
const
|
|
813
|
-
const hasBatchActions = canRetry || canAbort || canDelete;
|
|
824
|
+
const hasBatchActions = canRetry || canAbort;
|
|
814
825
|
// Stable test-id status used by Playwright assertions.
|
|
815
826
|
// Matches the visual status dot/spinner logic above.
|
|
816
827
|
const testStatus = anyError
|
|
@@ -828,18 +839,12 @@ export function TranslationBatches() {
|
|
|
828
839
|
}, icon: _jsx(RetryIcon, { className: `h-3.5 w-3.5 ${isRetryingBatch ? "animate-spin" : ""}`, strokeWidth: 1.5 }), label: "Retry failed translations", disabled: !canRetry ||
|
|
829
840
|
isRetryingBatch ||
|
|
830
841
|
isLoadingThis ||
|
|
831
|
-
isAborting
|
|
832
|
-
isDeleting })), canAbort && (_jsx(SimpleIconButton, { onClick: (event) => {
|
|
842
|
+
isAborting })), canAbort && (_jsx(SimpleIconButton, { onClick: (event) => {
|
|
833
843
|
event.stopPropagation();
|
|
834
844
|
if (!canAbort)
|
|
835
845
|
return;
|
|
836
846
|
handleAbortBatch(b.id);
|
|
837
|
-
}, icon: _jsx(AbortIcon, { className: `h-3.5 w-3.5 ${isAborting ? "animate-spin" : ""}`, strokeWidth: 1.5 }), label: "Abort translation batch", disabled: !canAbort || isAborting
|
|
838
|
-
event.stopPropagation();
|
|
839
|
-
if (!canDelete)
|
|
840
|
-
return;
|
|
841
|
-
handleDeleteBatch(b.id);
|
|
842
|
-
}, icon: _jsx(DeleteIcon, { size: "md" }), label: "Delete translation batch from history", disabled: !canDelete || isDeleting || isAborting }))] }));
|
|
847
|
+
}, icon: _jsx(AbortIcon, { className: `h-3.5 w-3.5 ${isAborting ? "animate-spin" : ""}`, strokeWidth: 1.5 }), label: "Abort translation batch", disabled: !canAbort || isAborting }))] }));
|
|
843
848
|
return (_jsxs("div", { "data-testid": `batch-row-${b.id}`, "data-batch-status": testStatus, children: [_jsx("div", { role: "button", tabIndex: 0, className: `group w-full cursor-pointer py-3 text-left transition-colors hover:bg-neutral-grey-5 ${isCompactBatches ? "px-2.5" : "px-4"}`, onClick: () => toggleBatch(b.id), onKeyDown: (e) => {
|
|
844
849
|
if (e.key === "Enter" || e.key === " ") {
|
|
845
850
|
e.preventDefault();
|
|
@@ -851,7 +856,7 @@ export function TranslationBatches() {
|
|
|
851
856
|
? "whitespace-normal break-words leading-5"
|
|
852
857
|
: "truncate"}`, style: isCompactBatches
|
|
853
858
|
? { overflowWrap: "anywhere" }
|
|
854
|
-
: undefined, title: formattedBatchDate, children: batchDisplayName }), _jsx("div", { className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[12px] text-neutral-grey-50", children: desktopMetaParts.map((part, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx("span", { className: "text-neutral-grey-15", children: "\u00B7" })), part] }, i))) }), mobileFooterMetaParts.length > 0 && (_jsx("div", { className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[12px] text-neutral-grey-50", children: mobileFooterMetaParts.map((part, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx("span", { className: "text-neutral-grey-15", children: "\u00B7" })), part] }, `footer-${i}`))) }))] }), isCompactBatches && (_jsxs("div", { className: "mt-2 flex min-w-0 items-center gap-2", children: [totalJobs > 0 && (_jsx("div", { className: "min-w-0 flex-1", children: _jsx(BatchProgressIndicator, { value: progressPct, completedJobs: completedJobs, totalJobs: totalJobs, errorJobs: errorJobs, tone: progressTone, testId: `batch-progress-${b.id}`, variant: "bar" }) })), hasBatchActions && (_jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: batchActions }))] })), !isCompactBatches && hasBatchActions && (_jsx("div", { className: "flex shrink-0 items-center gap-2 self-center", children: batchActions }))] })] }) }), isExpanded && (_jsxs("div", { className: "border-t border-border-default/50", children: [_jsx(CustomPromptPanel, { prompts: parseCustomPrompts(info?.metadata) }), _jsx(BatchItemList, { batchId: b.id, batchProvider: info?.provider, jobs: jobs, isLoading: isLoadingThis, expandedItems: expandedItems, languages: sitecoreLanguages, currentLanguage: editContext?.item?.language, itemFilter: filters.itemIdOrPath.trim(), itemIncludeSubitems: filters.itemIncludeSubitems, statusFilter: filters.status, batchIsTerminal: isTerminalBatchStatus(info?.status), batchStartedAtUtc: info?.startedAtUtc ?? info?.createdAtUtc, retryingKeys: retryingKeys, isMobile: isMobile, onToggleItem: toggleItem, onOpenItem: openItemInEditor, onRetryJobs: handleRetryJobs })] }))] }, b.id));
|
|
859
|
+
: undefined, title: formattedBatchDate, children: batchDisplayName }), _jsx("div", { className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[12px] text-neutral-grey-50", children: desktopMetaParts.map((part, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx("span", { className: "text-neutral-grey-15", children: "\u00B7" })), part] }, i))) }), mobileFooterMetaParts.length > 0 && (_jsx("div", { className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[12px] text-neutral-grey-50", children: mobileFooterMetaParts.map((part, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx("span", { className: "text-neutral-grey-15", children: "\u00B7" })), part] }, `footer-${i}`))) }))] }), isCompactBatches && (_jsxs("div", { className: "mt-2 flex min-w-0 items-center gap-2", children: [totalJobs > 0 && (_jsx("div", { className: "min-w-0 flex-1", children: _jsx(BatchProgressIndicator, { value: progressPct, completedJobs: completedJobs, totalJobs: totalJobs, errorJobs: errorJobs, tone: progressTone, testId: `batch-progress-${b.id}`, variant: "bar" }) })), hasBatchActions && (_jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: batchActions }))] })), !isCompactBatches && hasBatchActions && (_jsx("div", { className: "flex shrink-0 items-center gap-2 self-center", children: batchActions }))] })] }) }), isExpanded && (_jsxs("div", { className: "border-t border-border-default/50", children: [_jsx(CustomPromptPanel, { prompts: parseCustomPrompts(info?.metadata) }), _jsx(BatchItemList, { batchId: b.id, batchProvider: info?.provider, jobs: jobs, isLoading: isLoadingThis, expandedItems: expandedItems, languages: sitecoreLanguages, currentLanguage: editContext?.item?.language, itemFilter: filters.itemIdOrPath.trim(), itemIncludeSubitems: filters.itemIncludeSubitems, statusFilter: filters.status, batchIsTerminal: isTerminalBatchStatus(info?.status), batchStartedAtUtc: info?.startedAtUtc ?? info?.createdAtUtc, abortingJobIds: abortingJobIds, retryingKeys: retryingKeys, isMobile: isMobile, onToggleItem: toggleItem, onOpenItem: openItemInEditor, onAbortJob: handleAbortJob, onRetryJobs: handleRetryJobs })] }))] }, b.id));
|
|
855
860
|
}) })] }, dateGroup.label))), hasMore && (_jsx("div", { className: "flex justify-center pt-6", children: _jsx(Button, { variant: "outline", size: "sm", onClick: loadMore, disabled: isLoadingMore, children: isLoadingMore ? (_jsxs(_Fragment, { children: [_jsx(LoaderIcon, { className: "h-4 w-4 animate-spin text-highlight-100" }), "Loading more..."] })) : (_jsxs(_Fragment, { children: [_jsx(ChevronDownIcon, { className: "h-4 w-4" }), "Load More Batches"] })) }) }))] })) }) })] }));
|
|
856
861
|
}
|
|
857
862
|
export const TRANSLATION_BATCH_FILTERS_SIDEBAR_ID = "translation-batch-filters";
|
|
@@ -965,7 +970,7 @@ export function TranslationBatchFiltersSidebar() {
|
|
|
965
970
|
: undefined, onChange: (user) => setTranslationBatchFilters((prev) => ({
|
|
966
971
|
...prev,
|
|
967
972
|
user: user?.userName ?? "all",
|
|
968
|
-
})), disabled: isLimitedPreviewUser, placeholder: "All Users", buttonClassName: "h-
|
|
973
|
+
})), disabled: isLimitedPreviewUser, placeholder: "All Users", buttonClassName: "h-8 min-h-0 w-full justify-start rounded-[4px] border bg-white px-2 py-0 text-xs font-medium", allowClear: true, clearLabel: "All Users", showIcon: isLimitedPreviewUser || filters.user !== "all", searchUsers: searchTranslationUsers })] }), _jsxs("div", { className: filterFieldClass, children: [_jsx("label", { className: filterLabelClass, children: "Provider" }), _jsx(Select, { className: "w-full bg-white", options: [
|
|
969
974
|
{ value: "all", label: "All Providers" },
|
|
970
975
|
...providers.map((p) => ({
|
|
971
976
|
value: p.name,
|
|
@@ -1170,7 +1175,7 @@ function itemStatusDot(jobs) {
|
|
|
1170
1175
|
return { cls: "bg-feedback-orange", label: "Aborted" };
|
|
1171
1176
|
return { cls: "bg-feedback-green/70", label: "Completed" };
|
|
1172
1177
|
}
|
|
1173
|
-
function InlineLanguageJobChips({ batchId, batchProvider, jobs, languageMap, isExpanded, batchStartedAtUtc, retryingKeys, singleLine = false, onToggle, onOpenItem, onRetryJobs, }) {
|
|
1178
|
+
function InlineLanguageJobChips({ batchId, batchProvider, jobs, languageMap, isExpanded, batchStartedAtUtc, abortingJobIds, retryingKeys, singleLine = false, onToggle, onOpenItem, onAbortJob, onRetryJobs, }) {
|
|
1174
1179
|
if (jobs.length === 0)
|
|
1175
1180
|
return null;
|
|
1176
1181
|
const handleCellClick = (event) => {
|
|
@@ -1191,7 +1196,7 @@ function InlineLanguageJobChips({ batchId, batchProvider, jobs, languageMap, isE
|
|
|
1191
1196
|
: "max-h-7 flex-wrap overflow-hidden"}`, "aria-label": `${jobs.length} target language${jobs.length !== 1 ? "s" : ""}`, "aria-expanded": isExpanded, onClick: handleCellClick, onKeyDown: handleCellKeyDown, children: jobs.map((job, idx) => {
|
|
1192
1197
|
const lang = languageMap.get((job.targetLanguage || "").toLowerCase());
|
|
1193
1198
|
const retryKey = `job:${job.id ?? `${job.itemId}:${job.sourceLanguage}:${job.targetLanguage}`}`;
|
|
1194
|
-
return (_jsx("span", { className: "shrink-0", onClick: (event) => event.stopPropagation(), children: _jsx(LanguageJobChip, { job: job, language: lang, batchStartedAtUtc: batchStartedAtUtc, onOpen: () => onOpenItem(job.itemId, job.targetLanguage), isRetrying: retryingKeys.has(retryKey), onRetry: () => onRetryJobs(batchId, batchProvider, [job], retryKey) }) }, `${job.id ?? idx}-${job.targetLanguage}`));
|
|
1199
|
+
return (_jsx("span", { className: "shrink-0", onClick: (event) => event.stopPropagation(), children: _jsx(LanguageJobChip, { job: job, language: lang, batchStartedAtUtc: batchStartedAtUtc, onOpen: () => onOpenItem(job.itemId, job.targetLanguage), isAborting: !!job.id && abortingJobIds.has(job.id), onAbort: () => onAbortJob(job), isRetrying: retryingKeys.has(retryKey), onRetry: () => onRetryJobs(batchId, batchProvider, [job], retryKey) }) }, `${job.id ?? idx}-${job.targetLanguage}`));
|
|
1195
1200
|
}) }));
|
|
1196
1201
|
}
|
|
1197
1202
|
function CustomPromptPanel({ prompts }) {
|
|
@@ -1232,7 +1237,7 @@ function CustomPromptPanel({ prompts }) {
|
|
|
1232
1237
|
}, icon: justCopied ? (_jsx(CheckIcon, { className: "h-3.5 w-3.5", strokeWidth: 1.5 })) : (_jsx(CopyIcon, { className: "h-3.5 w-3.5", strokeWidth: 1.5 })), label: justCopied ? "Copied" : "Copy prompt" })), _jsx(ChevronDownIcon, { className: `h-3.5 w-3.5 text-neutral-grey-100 transition-transform ${isOpen ? "rotate-180" : ""}` })] })] }), isOpen && (_jsx("div", { style: { paddingLeft: "2.5rem" }, className: "pr-3 md:pr-4 pb-3", children: _jsx("pre", { className: "max-h-64 overflow-y-auto whitespace-pre-wrap break-words rounded border border-border-default/50 bg-white p-2 font-mono text-[12px] text-neutral-grey-100", children: p.prompt }) }))] }, p.provider));
|
|
1233
1238
|
}) }));
|
|
1234
1239
|
}
|
|
1235
|
-
function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems, languages, currentLanguage, itemFilter, itemIncludeSubitems, statusFilter, batchIsTerminal, batchStartedAtUtc, retryingKeys, isMobile = false, onToggleItem, onOpenItem, onRetryJobs, }) {
|
|
1240
|
+
function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems, languages, currentLanguage, itemFilter, itemIncludeSubitems, statusFilter, batchIsTerminal, batchStartedAtUtc, abortingJobIds, retryingKeys, isMobile = false, onToggleItem, onOpenItem, onAbortJob, onRetryJobs, }) {
|
|
1236
1241
|
const languageMap = React.useMemo(() => {
|
|
1237
1242
|
const map = new Map();
|
|
1238
1243
|
for (const l of languages) {
|
|
@@ -1359,11 +1364,7 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1359
1364
|
observer?.disconnect();
|
|
1360
1365
|
window.removeEventListener("resize", updateLayout);
|
|
1361
1366
|
};
|
|
1362
|
-
}, [
|
|
1363
|
-
isMobile,
|
|
1364
|
-
stackLanguageChips,
|
|
1365
|
-
statusFilteredJobs,
|
|
1366
|
-
]);
|
|
1367
|
+
}, [isMobile, stackLanguageChips, statusFilteredJobs]);
|
|
1367
1368
|
if (isLoading && !jobs) {
|
|
1368
1369
|
return (_jsxs("div", { className: "px-3 md:px-4 py-4 pl-8 text-[12px] text-neutral-grey-50", children: [_jsx(LoaderIcon, { className: "inline h-3.5 w-3.5 mr-1.5 animate-spin text-highlight-100" }), "Loading items\u2026"] }));
|
|
1369
1370
|
}
|
|
@@ -1401,7 +1402,7 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1401
1402
|
.filter((language) => Boolean(language));
|
|
1402
1403
|
const normalizedCurrentLanguage = normalizeLanguageCode(currentLanguage);
|
|
1403
1404
|
const rowOpenLanguage = rowLanguages.find((language) => normalizeLanguageCode(language) === normalizedCurrentLanguage) ?? rowLanguages[0];
|
|
1404
|
-
const languageChips = (_jsx(InlineLanguageJobChips, { batchId: batchId, batchProvider: batchProvider, jobs: itemJobs, languageMap: languageMap, isExpanded: isItemExpanded, batchStartedAtUtc: batchStartedAtUtc, retryingKeys: retryingKeys, singleLine: !stackLanguageChips, onToggle: () => onToggleItem(itemId), onOpenItem: onOpenItem, onRetryJobs: onRetryJobs }));
|
|
1405
|
+
const languageChips = (_jsx(InlineLanguageJobChips, { batchId: batchId, batchProvider: batchProvider, jobs: itemJobs, languageMap: languageMap, isExpanded: isItemExpanded, batchStartedAtUtc: batchStartedAtUtc, abortingJobIds: abortingJobIds, retryingKeys: retryingKeys, singleLine: !stackLanguageChips, onToggle: () => onToggleItem(itemId), onOpenItem: onOpenItem, onAbortJob: onAbortJob, onRetryJobs: onRetryJobs }));
|
|
1405
1406
|
const showRetryAction = itemErrorJobs.length > 0;
|
|
1406
1407
|
const retryAction = showRetryAction ? (_jsx("div", { className: "flex shrink-0 items-center justify-end", children: _jsx(SimpleIconButton, { onClick: (event) => {
|
|
1407
1408
|
event.stopPropagation();
|
|
@@ -1422,13 +1423,14 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1422
1423
|
}, children: [!stackLanguageChips && (_jsx("td", { "aria-hidden": "true", className: "px-3 py-2 md:px-4" })), _jsx("td", { className: `py-2 align-top ${stackLanguageChips ? "pr-2 pl-2.5" : "pr-4"}`, children: itemContent }), !stackLanguageChips && (_jsx("td", { className: "py-2 pr-2 align-top", children: languageChips })), !stackLanguageChips && (_jsx("td", { className: "py-2 pr-3 align-top md:pr-4", children: retryAction }))] }, itemId));
|
|
1423
1424
|
}) })] }) }));
|
|
1424
1425
|
}
|
|
1425
|
-
function LanguageJobChip({ job, language, batchStartedAtUtc, onOpen, isRetrying = false, onRetry, }) {
|
|
1426
|
+
function LanguageJobChip({ job, language, batchStartedAtUtc, onOpen, isAborting = false, onAbort, isRetrying = false, onRetry, }) {
|
|
1426
1427
|
const [open, setOpen] = React.useState(false);
|
|
1427
1428
|
const jobStatus = job.status;
|
|
1428
1429
|
const jobInProgress = jobStatus === "In Progress";
|
|
1429
1430
|
const jobPending = jobStatus === "Pending";
|
|
1430
1431
|
const jobError = jobStatus === "Error";
|
|
1431
1432
|
const jobAborted = jobStatus === "Aborted";
|
|
1433
|
+
const canAbort = !!job.id && !!job.batchId && !!onAbort && (jobInProgress || jobPending);
|
|
1432
1434
|
const langName = language?.name || job.targetLanguage;
|
|
1433
1435
|
const chipCls = jobError
|
|
1434
1436
|
? "border-feedback-red bg-feedback-red-light text-feedback-red hover:bg-feedback-red-light"
|
|
@@ -1513,7 +1515,11 @@ function LanguageJobChip({ job, language, batchStartedAtUtc, onOpen, isRetrying
|
|
|
1513
1515
|
// ITranslationService stack trace). Render them in a bounded,
|
|
1514
1516
|
// scrollable monospace block so the popover stays a sensible size and
|
|
1515
1517
|
// the original formatting is preserved.
|
|
1516
|
-
_jsxs("div", { className: "flex flex-col gap-1 px-3 py-2", children: [_jsx("dt", { className: "text-[10px] tracking-wide text-neutral-grey-50", children: jobError ? "Error" : "Aborted" }), _jsx("dd", { className: `max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-sm bg-neutral-grey-5 p-2 font-mono text-[11px] leading-snug ${jobError ? "text-feedback-red" : "text-feedback-orange"}`, children: job.message })] })) : (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Progress" }), _jsx("dd", { className: "text-[12px] break-words text-neutral-grey-100", children: job.message })] }))), job.batchId && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Batch" }), _jsx("dd", { className: "text-[11px] font-mono text-neutral-grey-50 break-all", children: job.batchId })] }))] }), _jsxs("div", { className: "flex justify-end gap-2 border-t border-border-default px-3 py-2", children: [
|
|
1518
|
+
_jsxs("div", { className: "flex flex-col gap-1 px-3 py-2", children: [_jsx("dt", { className: "text-[10px] tracking-wide text-neutral-grey-50", children: jobError ? "Error" : "Aborted" }), _jsx("dd", { className: `max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-sm bg-neutral-grey-5 p-2 font-mono text-[11px] leading-snug ${jobError ? "text-feedback-red" : "text-feedback-orange"}`, children: job.message })] })) : (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Progress" }), _jsx("dd", { className: "text-[12px] break-words text-neutral-grey-100", children: job.message })] }))), job.batchId && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Batch" }), _jsx("dd", { className: "text-[11px] font-mono text-neutral-grey-50 break-all", children: job.batchId })] }))] }), _jsxs("div", { className: "flex justify-end gap-2 border-t border-border-default px-3 py-2", children: [canAbort && (_jsxs(Button, { size: "sm", variant: "outline", disabled: isAborting, onClick: (e) => {
|
|
1519
|
+
e.stopPropagation();
|
|
1520
|
+
void onAbort?.();
|
|
1521
|
+
setOpen(false);
|
|
1522
|
+
}, className: "gap-1.5 text-feedback-orange hover:text-feedback-orange", children: [_jsx(AbortIcon, { className: `h-3.5 w-3.5 ${isAborting ? "animate-spin" : ""}` }), "Abort"] })), jobError && onRetry && (_jsxs(Button, { size: "sm", variant: "outline", disabled: isRetrying, onClick: (e) => {
|
|
1517
1523
|
e.stopPropagation();
|
|
1518
1524
|
void onRetry();
|
|
1519
1525
|
setOpen(false);
|