@parhelia/localization 0.4.12796 → 0.4.12800
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":"AAQA,OAAO,KAAK,MAAM,OAAO,CAAC;AAyV1B,wBAAgB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,MAAM,OAAO,CAAC;AAyV1B,wBAAgB,kBAAkB,sBAspCjC;AAED,eAAO,MAAM,oCAAoC,8BAA8B,CAAC;AAEhF,wBAAgB,8BAA8B,sBA0Y7C"}
|
|
@@ -798,8 +798,13 @@ export function TranslationBatches() {
|
|
|
798
798
|
const anyError = errorJobs > 0;
|
|
799
799
|
const anyInProgress = isActiveBatchStatus(info?.status);
|
|
800
800
|
const anyAborted = info?.status === "Aborted";
|
|
801
|
+
const jobs = batchJobs[b.id];
|
|
802
|
+
const statusCounts = computeBatchStatusCounts(info, jobs);
|
|
803
|
+
const processedJobs = statusCounts.succeeded +
|
|
804
|
+
statusCounts.failed +
|
|
805
|
+
statusCounts.aborted;
|
|
801
806
|
const metaParts = [];
|
|
802
|
-
metaParts.push(
|
|
807
|
+
metaParts.push(_jsx("span", { className: "text-neutral-grey-100", children: info?.status === "Pending" ? (_jsxs(_Fragment, { children: [totalJobs, " translation", totalJobs !== 1 ? "s" : "", " queued"] })) : anyInProgress ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "tabular-nums", children: [processedJobs, " of ", totalJobs] }), " ", "translation", totalJobs !== 1 ? "s" : "", " done"] })) : (_jsxs(_Fragment, { children: [totalJobs, " translation", totalJobs !== 1 ? "s" : ""] })) }, "trans"));
|
|
803
808
|
if (typeof itemsCount === "number") {
|
|
804
809
|
metaParts.push(_jsxs("span", { children: [itemsCount, " item", itemsCount !== 1 ? "s" : ""] }, "items"));
|
|
805
810
|
}
|
|
@@ -850,8 +855,6 @@ export function TranslationBatches() {
|
|
|
850
855
|
`Translation Batch · ${formattedBatchDate}`);
|
|
851
856
|
})();
|
|
852
857
|
const isExpanded = expandedBatchId === b.id;
|
|
853
|
-
const jobs = batchJobs[b.id];
|
|
854
|
-
const statusCounts = computeBatchStatusCounts(info, jobs);
|
|
855
858
|
const isLoadingThis = loadingJobs.has(b.id);
|
|
856
859
|
const canRetry = anyError;
|
|
857
860
|
const canAbort = isActiveBatchStatus(info?.status);
|
|
@@ -880,19 +883,19 @@ export function TranslationBatches() {
|
|
|
880
883
|
if (!canAbort)
|
|
881
884
|
return;
|
|
882
885
|
handleAbortBatch(b.id);
|
|
883
|
-
}, icon: _jsx(
|
|
886
|
+
}, icon: isAborting ? (_jsx(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin", strokeWidth: 1 })) : (_jsx(AbortIcon, { className: "h-3.5 w-3.5", strokeWidth: 1 })), label: "Abort translation batch", disabled: !canAbort || isAborting }))] }));
|
|
884
887
|
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) => {
|
|
885
888
|
if (e.key === "Enter" || e.key === " ") {
|
|
886
889
|
e.preventDefault();
|
|
887
890
|
toggleBatch(b.id);
|
|
888
891
|
}
|
|
889
|
-
}, children: _jsxs("div", { className: `flex items-start ${isCompactBatches ? "gap-2" : "gap-4"}`, children: [_jsx("div", { className: `shrink-0 text-neutral-grey-100 transition-transform ${isCompactBatches ? "mt-0.5" : "mt-3"} ${isExpanded ? "rotate-180" : ""}`, children: _jsx(ChevronDownIcon, { className: "h-4 w-4" }) }), !isCompactBatches && totalJobs > 0 && (_jsx(BatchProgressIndicator, { counts: statusCounts, testId: `batch-progress-${b.id}` })), _jsxs("div", { className: `flex min-w-0 flex-1 ${isCompactBatches
|
|
892
|
+
}, children: _jsxs("div", { className: `flex items-start ${isCompactBatches ? "gap-2" : "gap-4"}`, children: [_jsx("div", { className: `shrink-0 text-neutral-grey-100 transition-transform ${isCompactBatches ? "mt-0.5" : "mt-3"} ${isExpanded ? "rotate-180" : ""}`, children: _jsx(ChevronDownIcon, { className: "h-4 w-4" }) }), !isCompactBatches && totalJobs > 0 && (_jsx(BatchProgressIndicator, { counts: statusCounts, status: info?.status, testId: `batch-progress-${b.id}` })), _jsxs("div", { className: `flex min-w-0 flex-1 ${isCompactBatches
|
|
890
893
|
? "flex-col gap-2"
|
|
891
894
|
: "items-center justify-between gap-3"}`, children: [_jsxs("div", { className: "grid min-w-0 flex-1 grid-cols-[minmax(0,1fr)]", children: [_jsx("span", { className: `min-w-0 text-[14px] font-medium text-neutral-grey-100 ${isCompactBatches
|
|
892
895
|
? "whitespace-normal break-words leading-5"
|
|
893
896
|
: "truncate"}`, style: isCompactBatches
|
|
894
897
|
? { overflowWrap: "anywhere" }
|
|
895
|
-
: 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, { counts: statusCounts, 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));
|
|
898
|
+
: 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, { counts: statusCounts, status: info?.status, 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));
|
|
896
899
|
}) })] }, 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"] })) }) }))] })) }) })] }));
|
|
897
900
|
}
|
|
898
901
|
export const TRANSLATION_BATCH_FILTERS_SIDEBAR_ID = "translation-batch-filters";
|
|
@@ -1206,22 +1209,66 @@ function computeBatchStatusCounts(info, jobs) {
|
|
|
1206
1209
|
const pending = info?.status === "Aborted" ? 0 : remaining;
|
|
1207
1210
|
return { total, succeeded, failed, aborted, running: 0, pending };
|
|
1208
1211
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1212
|
+
// One outcome per batch, shared by the ring, the bar and the status badge so
|
|
1213
|
+
// the three never disagree about what colour/state a batch is in.
|
|
1214
|
+
function resolveBatchOutcome(status, counts) {
|
|
1215
|
+
const { total, succeeded, failed } = counts;
|
|
1216
|
+
if (isActiveBatchStatus(status))
|
|
1217
|
+
return "active";
|
|
1218
|
+
if (total > 0 && succeeded === total)
|
|
1219
|
+
return "succeeded";
|
|
1220
|
+
if (total > 0 && failed === total)
|
|
1221
|
+
return "failed";
|
|
1222
|
+
if (status === "Aborted")
|
|
1223
|
+
return "aborted";
|
|
1224
|
+
return "mixed";
|
|
1225
|
+
}
|
|
1226
|
+
function BatchProgressIndicator({ counts, status, testId, variant = "circle", }) {
|
|
1227
|
+
const { total, succeeded, failed, aborted, running, pending } = counts;
|
|
1228
|
+
const outcome = resolveBatchOutcome(status, counts);
|
|
1229
|
+
const isActive = outcome === "active";
|
|
1230
|
+
const processed = succeeded + failed + aborted;
|
|
1231
|
+
// SVG filter ids are document-global; derive a safe one per instance.
|
|
1232
|
+
const glowId = `batch-glow-${React.useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
1233
|
+
// While the batch is running the number is "how far along are we", so it
|
|
1234
|
+
// counts every finished job. Once terminal it is the success rate.
|
|
1235
|
+
const percentBase = isActive ? processed : succeeded;
|
|
1211
1236
|
const percent = total > 0
|
|
1212
|
-
? Math.max(0, Math.min(100, Math.round((
|
|
1237
|
+
? Math.max(0, Math.min(100, Math.round((percentBase / total) * 100)))
|
|
1213
1238
|
: 0;
|
|
1214
|
-
|
|
1215
|
-
//
|
|
1239
|
+
// Colour-carrying segments in tooltip order; queued work stays in the
|
|
1240
|
+
// track. "Still running" is conveyed by the soft halo around the ring (and
|
|
1241
|
+
// bar) rather than by a segment, so the ring stays calm and readable.
|
|
1216
1242
|
const segments = [
|
|
1217
1243
|
{ key: "succeeded", count: succeeded, color: BATCH_SEGMENT_COLORS.succeeded },
|
|
1218
1244
|
{ key: "failed", count: failed, color: BATCH_SEGMENT_COLORS.failed },
|
|
1219
1245
|
{ key: "aborted", count: aborted, color: BATCH_SEGMENT_COLORS.aborted },
|
|
1220
1246
|
{ key: "running", count: running, color: BATCH_SEGMENT_COLORS.running },
|
|
1221
1247
|
].filter((seg) => seg.count > 0);
|
|
1222
|
-
//
|
|
1223
|
-
|
|
1224
|
-
|
|
1248
|
+
// Breathing halo shown only while the batch is active.
|
|
1249
|
+
const glowShadow = "0 0 6px 1px color-mix(in srgb, var(--color-feedback-green) 45%, transparent)";
|
|
1250
|
+
// The centre glyph/number follows the batch outcome so it never contradicts
|
|
1251
|
+
// the ring: green only when everything succeeded, red only when everything
|
|
1252
|
+
// failed, orange when aborted, neutral for running and mixed results.
|
|
1253
|
+
const centreFill = outcome === "succeeded"
|
|
1254
|
+
? "var(--color-feedback-green-light)"
|
|
1255
|
+
: outcome === "failed"
|
|
1256
|
+
? "var(--color-feedback-red-light)"
|
|
1257
|
+
: outcome === "aborted"
|
|
1258
|
+
? "var(--color-feedback-orange-light)"
|
|
1259
|
+
: null;
|
|
1260
|
+
const renderCentreGlyph = (sizeClass) => {
|
|
1261
|
+
if (outcome === "succeeded") {
|
|
1262
|
+
return (_jsx(CheckIcon, { className: `text-feedback-green ${sizeClass}`, style: { strokeWidth: 3 }, "aria-hidden": "true" }));
|
|
1263
|
+
}
|
|
1264
|
+
if (outcome === "failed") {
|
|
1265
|
+
return (_jsx(AlertCircleIcon, { className: `text-feedback-red ${sizeClass}`, style: { strokeWidth: 2.5 }, "aria-hidden": "true" }));
|
|
1266
|
+
}
|
|
1267
|
+
if (outcome === "aborted") {
|
|
1268
|
+
return (_jsx(AbortIcon, { className: `text-feedback-orange ${sizeClass}`, style: { strokeWidth: 2.5 }, "aria-hidden": "true" }));
|
|
1269
|
+
}
|
|
1270
|
+
return null;
|
|
1271
|
+
};
|
|
1225
1272
|
const parts = [`${succeeded}/${total} succeeded`];
|
|
1226
1273
|
if (failed > 0)
|
|
1227
1274
|
parts.push(`${failed} failed`);
|
|
@@ -1229,20 +1276,24 @@ function BatchProgressIndicator({ counts, testId, variant = "circle", }) {
|
|
|
1229
1276
|
parts.push(`${aborted} aborted`);
|
|
1230
1277
|
if (running > 0)
|
|
1231
1278
|
parts.push(`${running} running`);
|
|
1279
|
+
if (isActive && pending > 0)
|
|
1280
|
+
parts.push(`${pending} queued`);
|
|
1232
1281
|
const label = parts.join(", ");
|
|
1233
1282
|
if (variant === "bar") {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1283
|
+
const glyph = renderCentreGlyph("h-3 w-3 shrink-0");
|
|
1284
|
+
return (_jsxs(Tooltip, { delayDuration: 250, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex min-w-0 items-center gap-2", "aria-label": label, "data-testid": testId, "data-outcome": outcome, children: [_jsxs("div", { className: "relative min-w-0 flex-1", children: [isActive && (_jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute -inset-px rounded-full animate-pulse", style: { boxShadow: glowShadow } })), _jsx("div", { className: "bg-neutral-grey-15 flex h-1.5 min-w-0 overflow-hidden rounded-full", children: total > 0 &&
|
|
1285
|
+
segments.map((seg) => (_jsx("div", { className: "h-full", style: {
|
|
1286
|
+
width: `${(seg.count / total) * 100}%`,
|
|
1287
|
+
backgroundColor: seg.color,
|
|
1288
|
+
} }, seg.key))) })] }), glyph ?? (_jsxs("span", { className: "shrink-0 text-[11px] leading-none font-medium tabular-nums text-neutral-grey-100", children: [percent, "%"] }))] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: label })] }));
|
|
1239
1289
|
}
|
|
1240
1290
|
const radius = 16;
|
|
1241
1291
|
const strokeWidth = 3;
|
|
1242
1292
|
const circumference = 2 * Math.PI * radius;
|
|
1243
1293
|
const gap = segments.length > 1 ? circumference * 0.02 : 0;
|
|
1244
1294
|
let segStart = 0;
|
|
1245
|
-
|
|
1295
|
+
const glyph = renderCentreGlyph("h-4 w-4");
|
|
1296
|
+
return (_jsxs(Tooltip, { delayDuration: 250, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "relative h-10 w-10 shrink-0 rounded-full", "aria-label": label, "data-testid": testId, "data-outcome": outcome, children: [_jsxs("svg", { className: "h-10 w-10 -rotate-90 overflow-visible", viewBox: "0 0 40 40", children: [isActive && (_jsxs(_Fragment, { children: [_jsx("defs", { children: _jsx("filter", { id: glowId, x: "-50%", y: "-50%", width: "200%", height: "200%", children: _jsx("feGaussianBlur", { stdDeviation: "2" }) }) }), _jsx("circle", { cx: "20", cy: "20", r: radius, fill: "none", stroke: "var(--color-feedback-green)", strokeWidth: strokeWidth + 4, filter: `url(#${glowId})`, opacity: 0.35, children: _jsx("animate", { attributeName: "opacity", values: "0.15;0.5;0.15", dur: "1.8s", repeatCount: "indefinite" }) })] })), centreFill && (_jsx("circle", { cx: "20", cy: "20", r: radius - strokeWidth / 2, fill: centreFill })), _jsx("circle", { cx: "20", cy: "20", r: radius, fill: "none", stroke: "var(--color-neutral-grey-15)", strokeWidth: strokeWidth }), total > 0 &&
|
|
1246
1297
|
segments.map((seg) => {
|
|
1247
1298
|
const segLen = (seg.count / total) * circumference;
|
|
1248
1299
|
const dash = Math.max(0, segLen - gap);
|
|
@@ -1252,7 +1303,7 @@ function BatchProgressIndicator({ counts, testId, variant = "circle", }) {
|
|
|
1252
1303
|
strokeDasharray: `${dash} ${circumference}`,
|
|
1253
1304
|
strokeDashoffset: dashOffset,
|
|
1254
1305
|
} }, seg.key));
|
|
1255
|
-
})] }), _jsx("span", { className: "absolute inset-0 flex items-center justify-center", children:
|
|
1306
|
+
})] }), _jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: glyph ?? (_jsxs("span", { className: "text-[10px] leading-none font-medium tabular-nums text-neutral-grey-100", children: [percent, "%"] })) })] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: label })] }));
|
|
1256
1307
|
}
|
|
1257
1308
|
function itemStatusDot(jobs) {
|
|
1258
1309
|
const hasError = jobs.some((j) => j.status === "Error");
|
|
@@ -1269,7 +1320,44 @@ function itemStatusDot(jobs) {
|
|
|
1269
1320
|
return { cls: "bg-feedback-orange", label: "Aborted" };
|
|
1270
1321
|
return { cls: "bg-feedback-green/70", label: "Completed" };
|
|
1271
1322
|
}
|
|
1272
|
-
|
|
1323
|
+
// Fallback one-row cap used until the first chip row has been measured.
|
|
1324
|
+
const COLLAPSED_CHIP_ROW_HEIGHT_PX = 28;
|
|
1325
|
+
function InlineLanguageJobChips({ batchId, batchProvider, jobs, languageMap, isExpanded, batchStartedAtUtc, abortingJobIds, retryingKeys, onToggle, onOpenItem, onAbortJob, onRetryJobs, }) {
|
|
1326
|
+
const chipAreaRef = React.useRef(null);
|
|
1327
|
+
const [hiddenCount, setHiddenCount] = React.useState(0);
|
|
1328
|
+
const [firstRowHeight, setFirstRowHeight] = React.useState(null);
|
|
1329
|
+
// Chips always wrap so they can never run into the neighbouring column.
|
|
1330
|
+
// When the item is collapsed only the first line is shown; count the chips
|
|
1331
|
+
// that wrapped onto later lines so a "+N" chip can stand in for them.
|
|
1332
|
+
React.useLayoutEffect(() => {
|
|
1333
|
+
const el = chipAreaRef.current;
|
|
1334
|
+
if (!el || isExpanded) {
|
|
1335
|
+
setHiddenCount(0);
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
const measure = () => {
|
|
1339
|
+
const children = Array.from(el.children);
|
|
1340
|
+
if (children.length === 0) {
|
|
1341
|
+
setHiddenCount(0);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
const firstTop = children[0]?.offsetTop ?? 0;
|
|
1345
|
+
let hidden = 0;
|
|
1346
|
+
for (const child of children) {
|
|
1347
|
+
if (child.offsetTop > firstTop)
|
|
1348
|
+
hidden += 1;
|
|
1349
|
+
}
|
|
1350
|
+
setHiddenCount(hidden);
|
|
1351
|
+
setFirstRowHeight(children[0]?.offsetHeight ?? null);
|
|
1352
|
+
};
|
|
1353
|
+
measure();
|
|
1354
|
+
let observer;
|
|
1355
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
1356
|
+
observer = new ResizeObserver(measure);
|
|
1357
|
+
observer.observe(el);
|
|
1358
|
+
}
|
|
1359
|
+
return () => observer?.disconnect();
|
|
1360
|
+
}, [isExpanded, jobs.length]);
|
|
1273
1361
|
if (jobs.length === 0)
|
|
1274
1362
|
return null;
|
|
1275
1363
|
const handleCellClick = (event) => {
|
|
@@ -1283,15 +1371,20 @@ function InlineLanguageJobChips({ batchId, batchProvider, jobs, languageMap, isE
|
|
|
1283
1371
|
onToggle();
|
|
1284
1372
|
}
|
|
1285
1373
|
};
|
|
1286
|
-
return (
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
:
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1374
|
+
return (_jsxs("div", { role: "button", tabIndex: 0, className: "flex cursor-pointer items-start gap-1.5", "aria-label": `${jobs.length} target language${jobs.length !== 1 ? "s" : ""}`, "aria-expanded": isExpanded, onClick: handleCellClick, onKeyDown: handleCellKeyDown, children: [_jsx("div", { ref: chipAreaRef, className: `flex flex-1 flex-wrap items-start gap-1.5 ${isExpanded ? "" : "overflow-hidden"}`,
|
|
1375
|
+
// The cap is the measured height of one chip row, applied inline so
|
|
1376
|
+
// the clip does not depend on a Tailwind max-height utility being
|
|
1377
|
+
// emitted for this file.
|
|
1378
|
+
style: isExpanded
|
|
1379
|
+
? undefined
|
|
1380
|
+
: { maxHeight: firstRowHeight ?? COLLAPSED_CHIP_ROW_HEIGHT_PX }, children: jobs.map((job, idx) => {
|
|
1381
|
+
const lang = languageMap.get((job.targetLanguage || "").toLowerCase());
|
|
1382
|
+
const retryKey = `job:${job.id ?? `${job.itemId}:${job.sourceLanguage}:${job.targetLanguage}`}`;
|
|
1383
|
+
return (_jsx("span", { className: "shrink-0", onClick: (event) => event.stopPropagation(), children: _jsx(LanguageJobChip, { job: job, language: lang, provider: batchProvider, 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}`));
|
|
1384
|
+
}) }), !isExpanded && hiddenCount > 0 && (_jsxs("button", { type: "button", className: "mt-px shrink-0 rounded border border-border-default bg-neutral-grey-5 badge-pad-sm text-[12px] leading-none tabular-nums text-neutral-grey-50 transition-colors hover:bg-neutral-grey-15 hover:text-neutral-grey-100", "aria-label": `Show ${hiddenCount} more language${hiddenCount !== 1 ? "s" : ""}`, title: `${hiddenCount} more language${hiddenCount !== 1 ? "s" : ""}`, onClick: (event) => {
|
|
1385
|
+
event.stopPropagation();
|
|
1386
|
+
onToggle();
|
|
1387
|
+
}, children: ["+", hiddenCount] }))] }));
|
|
1295
1388
|
}
|
|
1296
1389
|
function CustomPromptPanel({ prompts }) {
|
|
1297
1390
|
const [openProvider, setOpenProvider] = React.useState(null);
|
|
@@ -1431,7 +1524,10 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1431
1524
|
}
|
|
1432
1525
|
if (stackLanguageChips) {
|
|
1433
1526
|
const normalMinWidth = normalTableMinWidthRef.current;
|
|
1434
|
-
|
|
1527
|
+
// Unstack once the columns layout fits again. Before that layout has
|
|
1528
|
+
// ever been measured, fall back to the threshold that forced stacking
|
|
1529
|
+
// so a narrow-then-wide resize does not stay stacked forever.
|
|
1530
|
+
if (availableWidth >= (normalMinWidth ?? 640)) {
|
|
1435
1531
|
setStackLanguageChips(false);
|
|
1436
1532
|
}
|
|
1437
1533
|
return;
|
|
@@ -1476,7 +1572,7 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1476
1572
|
byItem.set(key, []);
|
|
1477
1573
|
byItem.get(key).push(job);
|
|
1478
1574
|
}
|
|
1479
|
-
return (_jsx("div", { ref: itemTableContainerRef, className: "w-full overflow-hidden", "data-language-layout": stackLanguageChips ? "stacked" : "columns", children: _jsxs("table", { ref: itemTableRef, className: "w-full border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { children: [!stackLanguageChips && (_jsx("th", { "aria-hidden": "true", className: "px-3 pt-3 pb-1 md:px-4" })), _jsx("th", { scope: "col", className: `pt-3 pb-1 text-left align-top text-[11px] font-medium text-neutral-grey-50 ${stackLanguageChips ? "pr-2 pl-2.5" : "pr-4"}`, children: _jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [_jsx("span", { className: "h-1.5 w-1.5 shrink-0", "aria-hidden": "true" }), _jsx("span", { children: "Pages / Items" })] }) }), !stackLanguageChips && (_jsx("th", { scope: "col", className: "pt-3 pb-1 pr-2 text-left align-top text-[11px] font-medium text-neutral-grey-50", children: "Languages" })), !stackLanguageChips && (_jsx("th", { "aria-hidden": "true", className: "pt-3 pr-3 pb-1 md:pr-4" }))] }) }), _jsx("tbody", { children: Array.from(byItem.entries()).map(([itemId, itemJobs], itemIndex) => {
|
|
1575
|
+
return (_jsx("div", { ref: itemTableContainerRef, className: "w-full overflow-hidden", "data-language-layout": stackLanguageChips ? "stacked" : "columns", children: _jsxs("table", { ref: itemTableRef, className: "w-full border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { children: [!stackLanguageChips && (_jsx("th", { "aria-hidden": "true", className: "px-3 pt-3 pb-1 md:px-4" })), _jsx("th", { scope: "col", className: `pt-3 pb-1 text-left align-top text-[11px] font-medium text-neutral-grey-50 ${stackLanguageChips ? "pr-2 pl-2.5" : "pr-4"}`, children: _jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [_jsx("span", { className: "h-1.5 w-1.5 shrink-0", "aria-hidden": "true" }), _jsx("span", { children: "Pages / Items" })] }) }), !stackLanguageChips && (_jsx("th", { scope: "col", className: "pt-3 pb-1 pr-2 text-left align-top text-[11px] font-medium text-neutral-grey-50", children: "Languages" })), !stackLanguageChips && (_jsx("th", { "aria-hidden": "true", className: "w-12 pt-3 pr-3 pb-1 md:pr-4" }))] }) }), _jsx("tbody", { children: Array.from(byItem.entries()).map(([itemId, itemJobs], itemIndex) => {
|
|
1480
1576
|
const isItemExpanded = expandedItems.has(itemId);
|
|
1481
1577
|
const itemAnyInProgress = itemJobs.some((j) => j.status === "In Progress");
|
|
1482
1578
|
const itemAnyPending = itemJobs.some((j) => j.status === "Pending");
|
|
@@ -1496,7 +1592,7 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1496
1592
|
.filter((language) => Boolean(language));
|
|
1497
1593
|
const normalizedCurrentLanguage = normalizeLanguageCode(currentLanguage);
|
|
1498
1594
|
const rowOpenLanguage = rowLanguages.find((language) => normalizeLanguageCode(language) === normalizedCurrentLanguage) ?? rowLanguages[0];
|
|
1499
|
-
const languageChips = (_jsx(InlineLanguageJobChips, { batchId: batchId, batchProvider: batchProvider, jobs: itemJobs, languageMap: languageMap, isExpanded: isItemExpanded, batchStartedAtUtc: batchStartedAtUtc, abortingJobIds: abortingJobIds, retryingKeys: retryingKeys,
|
|
1595
|
+
const languageChips = (_jsx(InlineLanguageJobChips, { batchId: batchId, batchProvider: batchProvider, jobs: itemJobs, languageMap: languageMap, isExpanded: isItemExpanded, batchStartedAtUtc: batchStartedAtUtc, abortingJobIds: abortingJobIds, retryingKeys: retryingKeys, onToggle: () => onToggleItem(itemId), onOpenItem: onOpenItem, onAbortJob: onAbortJob, onRetryJobs: onRetryJobs }));
|
|
1500
1596
|
const showRetryAction = itemErrorJobs.length > 0;
|
|
1501
1597
|
const retryAction = showRetryAction ? (_jsx("div", { className: "flex shrink-0 items-center justify-end", children: _jsx(SimpleIconButton, { onClick: (event) => {
|
|
1502
1598
|
event.stopPropagation();
|
|
@@ -1514,7 +1610,7 @@ function BatchItemList({ batchId, batchProvider, jobs, isLoading, expandedItems,
|
|
|
1514
1610
|
e.preventDefault();
|
|
1515
1611
|
void onOpenItem(itemId, rowOpenLanguage);
|
|
1516
1612
|
}
|
|
1517
|
-
}, 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));
|
|
1613
|
+
}, 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: "w-12 py-2 pr-3 align-top whitespace-nowrap md:pr-4", children: retryAction }))] }, itemId));
|
|
1518
1614
|
}) })] }) }));
|
|
1519
1615
|
}
|
|
1520
1616
|
function LanguageJobChip({ job, language, provider, batchStartedAtUtc, onOpen, isAborting = false, onAbort, isRetrying = false, onRetry, }) {
|
|
@@ -1627,7 +1723,7 @@ function LanguageJobChip({ job, language, provider, batchStartedAtUtc, onOpen, i
|
|
|
1627
1723
|
e.stopPropagation();
|
|
1628
1724
|
void onAbort?.();
|
|
1629
1725
|
setOpen(false);
|
|
1630
|
-
}, className: "gap-1.5 text-feedback-orange hover:text-feedback-orange", children: [_jsx(
|
|
1726
|
+
}, className: "gap-1.5 text-feedback-orange hover:text-feedback-orange", children: [isAborting ? (_jsx(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(AbortIcon, { className: "h-3.5 w-3.5" })), "Abort"] })), jobError && onRetry && (_jsxs(Button, { size: "sm", variant: "outline", disabled: isRetrying, onClick: (e) => {
|
|
1631
1727
|
e.stopPropagation();
|
|
1632
1728
|
void onRetry();
|
|
1633
1729
|
setOpen(false);
|