@braze/sanity-plugin-ai-translation 0.2.78 → 0.2.80
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/index.js +568 -203
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { getPublishedId, pathToString, useClient, useValidationStatus, defineType, defineField, definePlugin } from "sanity";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { WarningOutlineIcon,
|
|
3
|
+
import { WarningOutlineIcon, ErrorOutlineIcon, CheckmarkIcon, CloseIcon, SyncIcon, PanelRightIcon, SearchIcon, CircleIcon, ArrowRightIcon, ResetIcon, TranslateIcon } from "@sanity/icons";
|
|
4
4
|
import { createContext, useReducer, useRef, useEffect, useContext, useMemo, useState, useCallback } from "react";
|
|
5
5
|
import defaults from "lodash.defaults";
|
|
6
|
-
import { Card, Stack, Flex, Text, Box, Heading, Button, Spinner, Inline, Checkbox, Grid,
|
|
6
|
+
import { Card, Stack, Flex, Text, Box, Heading, Button, Spinner, Badge, Inline, Radio, Checkbox, Grid, TextInput, Select, Label, Code, Tooltip } from "@sanity/ui";
|
|
7
|
+
import { IntentLink } from "sanity/router";
|
|
8
|
+
import { useOpenInNewPane, useListeningQuery } from "sanity-plugin-utils";
|
|
7
9
|
import { toPlainText } from "@portabletext/react";
|
|
8
10
|
import { uuid } from "@sanity/uuid";
|
|
9
11
|
import cloneDeep from "lodash.clonedeep";
|
|
10
12
|
import get from "lodash.get";
|
|
11
13
|
import set from "lodash.set";
|
|
12
|
-
import { useListeningQuery, useOpenInNewPane } from "sanity-plugin-utils";
|
|
13
|
-
import { IntentLink } from "sanity/router";
|
|
14
14
|
const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === "true" || process.env.SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === "true", devLog = {
|
|
15
15
|
info: (message, context) => {
|
|
16
16
|
isLoggingEnabled() && console.info("[AI Translation]", message, context ? JSON.stringify(context, null, 2) : "");
|
|
@@ -123,6 +123,9 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
123
123
|
mode: "new",
|
|
124
124
|
newReleaseTitle: ""
|
|
125
125
|
},
|
|
126
|
+
hasDraft: !1,
|
|
127
|
+
hasPublished: !1,
|
|
128
|
+
documentSource: "draft",
|
|
126
129
|
reconcilingStatus: "idle",
|
|
127
130
|
reconciliationProgress: void 0,
|
|
128
131
|
dispatch: () => {
|
|
@@ -229,10 +232,21 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
229
232
|
...context,
|
|
230
233
|
reconciliationProgress: action.payload
|
|
231
234
|
};
|
|
235
|
+
case "SET_DOCUMENT_SOURCE":
|
|
236
|
+
return {
|
|
237
|
+
...context,
|
|
238
|
+
documentSource: action.payload
|
|
239
|
+
};
|
|
232
240
|
default:
|
|
233
241
|
return context;
|
|
234
242
|
}
|
|
235
|
-
}, generateDefaultReleaseTitle = (document) => `[AI Translation] ${document.title || document.name || document._id.split("-").slice(0, 2).join("-")}`, TranslationContextProvider = ({
|
|
243
|
+
}, generateDefaultReleaseTitle = (document) => `[AI Translation] ${document.title || document.name || document._id.split("-").slice(0, 2).join("-")}`, TranslationContextProvider = ({
|
|
244
|
+
document,
|
|
245
|
+
config,
|
|
246
|
+
hasDraft,
|
|
247
|
+
hasPublished,
|
|
248
|
+
children
|
|
249
|
+
}) => {
|
|
236
250
|
devLog.debug("TranslationContextProvider initialized", {
|
|
237
251
|
documentId: document._id,
|
|
238
252
|
documentType: document._type,
|
|
@@ -258,6 +272,9 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
258
272
|
mode: "new",
|
|
259
273
|
newReleaseTitle: defaultReleaseTitle
|
|
260
274
|
},
|
|
275
|
+
hasDraft,
|
|
276
|
+
hasPublished,
|
|
277
|
+
documentSource: "draft",
|
|
261
278
|
// Reconciliation state for existing releases
|
|
262
279
|
reconcilingStatus: "idle",
|
|
263
280
|
reconciliationProgress: void 0
|
|
@@ -344,9 +361,114 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
344
361
|
"useTranslationContext must be used within a TranslationContextProvider with document and config props"
|
|
345
362
|
);
|
|
346
363
|
return context;
|
|
347
|
-
},
|
|
364
|
+
}, checkSourceDrafts = async ({
|
|
365
|
+
client,
|
|
366
|
+
referenceIds
|
|
367
|
+
}) => {
|
|
368
|
+
if (referenceIds.length === 0)
|
|
369
|
+
return [];
|
|
370
|
+
const publishedIds = referenceIds.map((id) => getPublishedId(id)), draftIds = publishedIds.map((id) => `drafts.${id}`);
|
|
371
|
+
devLog.debug("Checking for unpublished changes on source references", {
|
|
372
|
+
referenceCount: publishedIds.length
|
|
373
|
+
});
|
|
374
|
+
const drafts = await client.fetch(
|
|
375
|
+
"*[_id in $draftIds]{ _id, _type, title, name }",
|
|
376
|
+
{ draftIds }
|
|
377
|
+
);
|
|
378
|
+
if (drafts.length === 0)
|
|
379
|
+
return devLog.debug("No source references have unpublished changes"), [];
|
|
380
|
+
const results = drafts.map((d) => ({
|
|
381
|
+
publishedId: getPublishedId(d._id),
|
|
382
|
+
_type: d._type,
|
|
383
|
+
title: d.title || d.name
|
|
384
|
+
}));
|
|
385
|
+
return devLog.debug("Source references with unpublished changes", {
|
|
386
|
+
count: results.length
|
|
387
|
+
}), results;
|
|
388
|
+
}, TRANSLATION_METADATA_TYPE = "translation.metadata", getExistingTranslationId = (translationMetadata, targetLanguage) => translationMetadata?.translations?.length && translationMetadata.translations.find(
|
|
348
389
|
(translation) => translation._key === targetLanguage
|
|
349
|
-
)?.value._ref || null,
|
|
390
|
+
)?.value._ref || null, checkTargetDrafts = async ({
|
|
391
|
+
client,
|
|
392
|
+
translationMetadata,
|
|
393
|
+
selectedTargetLanguages,
|
|
394
|
+
sourceDocumentId,
|
|
395
|
+
references
|
|
396
|
+
}) => {
|
|
397
|
+
const targetEntries = [];
|
|
398
|
+
if (translationMetadata?.translations?.length)
|
|
399
|
+
for (const lang of selectedTargetLanguages) {
|
|
400
|
+
const targetId = getExistingTranslationId(translationMetadata, lang);
|
|
401
|
+
targetId && targetEntries.push({ targetId, targetLanguage: lang, sourceDocumentId, isReference: !1 });
|
|
402
|
+
}
|
|
403
|
+
else
|
|
404
|
+
devLog.debug("No translation metadata \u2014 skipping main document draft check", {
|
|
405
|
+
sourceDocumentId
|
|
406
|
+
});
|
|
407
|
+
if (references && references.length > 0) {
|
|
408
|
+
const refIds = references.map((r) => r._id), refMetadataDocs = await client.fetch(
|
|
409
|
+
"*[_type == $metadataType && array::intersects(translations[].value._ref, $refIds)] { translations }",
|
|
410
|
+
{ metadataType: TRANSLATION_METADATA_TYPE, refIds }
|
|
411
|
+
);
|
|
412
|
+
devLog.debug("Fetched reference metadata for draft check", {
|
|
413
|
+
refCount: refIds.length,
|
|
414
|
+
metadataDocsFound: refMetadataDocs.length
|
|
415
|
+
});
|
|
416
|
+
const refIdSet = new Set(refIds);
|
|
417
|
+
for (const metadataDoc of refMetadataDocs) {
|
|
418
|
+
if (!metadataDoc.translations?.length) continue;
|
|
419
|
+
const matchingRefId = metadataDoc.translations.find(
|
|
420
|
+
(t) => t.value?._ref && refIdSet.has(t.value._ref)
|
|
421
|
+
)?.value._ref;
|
|
422
|
+
if (matchingRefId)
|
|
423
|
+
for (const lang of selectedTargetLanguages) {
|
|
424
|
+
const targetId = getExistingTranslationId(metadataDoc, lang);
|
|
425
|
+
targetId && targetEntries.push({
|
|
426
|
+
targetId,
|
|
427
|
+
targetLanguage: lang,
|
|
428
|
+
sourceDocumentId: matchingRefId,
|
|
429
|
+
isReference: !0
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const seen = /* @__PURE__ */ new Set(), deduped = targetEntries.filter((e) => seen.has(e.targetId) ? !1 : (seen.add(e.targetId), !0));
|
|
435
|
+
if (deduped.length === 0)
|
|
436
|
+
return devLog.debug("No existing translations found for selected languages", {
|
|
437
|
+
sourceDocumentId,
|
|
438
|
+
selectedTargetLanguages
|
|
439
|
+
}), [];
|
|
440
|
+
const draftIds = deduped.map((e) => `drafts.${e.targetId}`);
|
|
441
|
+
devLog.debug("Checking for stale drafts on target documents", {
|
|
442
|
+
sourceDocumentId,
|
|
443
|
+
draftIdsToCheck: draftIds.length,
|
|
444
|
+
referenceTargets: deduped.filter((e) => e.isReference).length
|
|
445
|
+
});
|
|
446
|
+
const drafts = await client.fetch(
|
|
447
|
+
"*[_id in $draftIds]{ _id, _type, title, name }",
|
|
448
|
+
{ draftIds }
|
|
449
|
+
);
|
|
450
|
+
if (drafts.length === 0)
|
|
451
|
+
return devLog.debug("No stale drafts found", { sourceDocumentId }), [];
|
|
452
|
+
const draftMap = new Map(drafts.map((d) => [d._id, d])), results = [];
|
|
453
|
+
for (const entry of deduped) {
|
|
454
|
+
const draftId = `drafts.${entry.targetId}`, draft = draftMap.get(draftId);
|
|
455
|
+
draft && results.push({
|
|
456
|
+
targetId: entry.targetId,
|
|
457
|
+
draftId,
|
|
458
|
+
_type: draft._type,
|
|
459
|
+
title: draft.title || draft.name,
|
|
460
|
+
sourceDocumentId: entry.sourceDocumentId,
|
|
461
|
+
targetLanguage: entry.targetLanguage,
|
|
462
|
+
isReference: entry.isReference || void 0
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
return devLog.info("Stale drafts detected on target documents", {
|
|
466
|
+
sourceDocumentId,
|
|
467
|
+
staleDraftCount: results.length,
|
|
468
|
+
referenceDraftCount: results.filter((r) => r.isReference).length,
|
|
469
|
+
affectedLanguages: results.map((r) => r.targetLanguage)
|
|
470
|
+
}), results;
|
|
471
|
+
}, getTranslationMetadata = async ({
|
|
350
472
|
client,
|
|
351
473
|
sourceDocument,
|
|
352
474
|
sourceLanguage
|
|
@@ -3001,6 +3123,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3001
3123
|
excludeFields,
|
|
3002
3124
|
unsetFields,
|
|
3003
3125
|
releaseConfig,
|
|
3126
|
+
sourcePerspective,
|
|
3004
3127
|
telemetryConfig
|
|
3005
3128
|
}) => {
|
|
3006
3129
|
const batchStartTime = Date.now(), logContext = {
|
|
@@ -3104,7 +3227,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3104
3227
|
try {
|
|
3105
3228
|
await translationSemaphore.withPermit(async () => {
|
|
3106
3229
|
const document = await client.withConfig({
|
|
3107
|
-
perspective: "drafts"
|
|
3230
|
+
perspective: sourcePerspective ?? "drafts"
|
|
3108
3231
|
}).getDocument(documentId);
|
|
3109
3232
|
if (document || devLog.error("\u274C SOURCE DOCUMENT NOT FOUND", { documentId }), document && releaseId) {
|
|
3110
3233
|
const versionedTargetDocumentId = await translateDocument({
|
|
@@ -3275,24 +3398,189 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3275
3398
|
] })
|
|
3276
3399
|
] })
|
|
3277
3400
|
] }) });
|
|
3278
|
-
},
|
|
3401
|
+
}, DocumentOpenerButton = ({ documentId, documentType }) => {
|
|
3402
|
+
const publishedId = getPublishedId(documentId), openInNewPane = useOpenInNewPane(publishedId, documentType);
|
|
3403
|
+
return /* @__PURE__ */ jsx(
|
|
3404
|
+
Button,
|
|
3405
|
+
{
|
|
3406
|
+
mode: "bleed",
|
|
3407
|
+
icon: PanelRightIcon,
|
|
3408
|
+
onClick: () => openInNewPane(),
|
|
3409
|
+
"aria-label": "Open document",
|
|
3410
|
+
fontSize: 1,
|
|
3411
|
+
padding: 2
|
|
3412
|
+
}
|
|
3413
|
+
);
|
|
3414
|
+
}, VALIDATION_TIMEOUT_MS = 2e3, DocumentValidator = ({ documentId, documentType, onResult }) => {
|
|
3415
|
+
const publishedId = getPublishedId(documentId), { isValidating, validation } = useValidationStatus(publishedId, documentType), hasReceivedRealResult = useRef(!1), hasSettled = useRef(!1), [timedOut, setTimedOut] = useState(!1);
|
|
3416
|
+
return useEffect(() => {
|
|
3417
|
+
const timer = setTimeout(() => setTimedOut(!0), VALIDATION_TIMEOUT_MS);
|
|
3418
|
+
return () => clearTimeout(timer);
|
|
3419
|
+
}, []), useEffect(() => {
|
|
3420
|
+
if (hasSettled.current) return;
|
|
3421
|
+
(isValidating || validation.length > 0) && (hasReceivedRealResult.current = !0);
|
|
3422
|
+
const stillWaitingForFirstResult = !hasReceivedRealResult.current && !timedOut, errors = validation.filter((v) => v.level === "error"), computedIsValidating = isValidating || stillWaitingForFirstResult;
|
|
3423
|
+
!computedIsValidating && hasReceivedRealResult.current && (hasSettled.current = !0), onResult(documentId, {
|
|
3424
|
+
isValidating: computedIsValidating,
|
|
3425
|
+
errors
|
|
3426
|
+
});
|
|
3427
|
+
}, [isValidating, validation, documentId, onResult, timedOut]), null;
|
|
3428
|
+
}, PreflightView = () => {
|
|
3279
3429
|
const {
|
|
3280
3430
|
document,
|
|
3281
3431
|
config,
|
|
3282
|
-
overallStatus,
|
|
3283
3432
|
dispatch,
|
|
3284
3433
|
references,
|
|
3285
3434
|
selectedReferenceIds,
|
|
3286
3435
|
selectedTargetLanguages,
|
|
3436
|
+
shouldTranslateReferences,
|
|
3437
|
+
translationMetadata,
|
|
3287
3438
|
translationMode,
|
|
3288
|
-
releaseConfig
|
|
3289
|
-
|
|
3439
|
+
releaseConfig,
|
|
3440
|
+
documentSource
|
|
3441
|
+
} = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [scanKey, setScanKey] = useState(0), [validationResults, setValidationResults] = useState(/* @__PURE__ */ new Map()), [sourceRefsWithDrafts, setSourceRefsWithDrafts] = useState([]), [isSourceDraftCheckLoading, setIsSourceDraftCheckLoading] = useState(!0), [staleDrafts, setStaleDrafts] = useState([]), [isDraftCheckLoading, setIsDraftCheckLoading] = useState(!0), [draftAction, setDraftAction] = useState("none"), [isDraftActionInProgress, setIsDraftActionInProgress] = useState(!1), [conflictContext, setConflictContext] = useState(null), [isCheckingConflicts, setIsCheckingConflicts] = useState(!0), filteredReferences = references.filter((ref) => selectedReferenceIds.has(ref._id)), documentsToValidate = useMemo(() => {
|
|
3442
|
+
const docs = [
|
|
3443
|
+
{
|
|
3444
|
+
_id: document._id,
|
|
3445
|
+
_type: document._type,
|
|
3446
|
+
title: document.title || document.name || document._id
|
|
3447
|
+
}
|
|
3448
|
+
];
|
|
3449
|
+
if (shouldTranslateReferences)
|
|
3450
|
+
for (const ref of references)
|
|
3451
|
+
selectedReferenceIds.has(ref._id) && docs.push(ref);
|
|
3452
|
+
return docs;
|
|
3453
|
+
}, [document, references, selectedReferenceIds, shouldTranslateReferences]), handleValidationResult = useCallback((id, result) => {
|
|
3454
|
+
setValidationResults((prev) => {
|
|
3455
|
+
const next = new Map(prev);
|
|
3456
|
+
return next.set(id, result), next;
|
|
3457
|
+
});
|
|
3458
|
+
}, []), { hasValidationErrors, someStillValidating, documentsWithErrors } = useMemo(() => {
|
|
3459
|
+
const allValidated = documentsToValidate.every((doc) => validationResults.has(doc._id)), validating = [...validationResults.values()].some((r) => r.isValidating) || !allValidated, docsWithErrors = [];
|
|
3460
|
+
for (const doc of documentsToValidate) {
|
|
3461
|
+
const result = validationResults.get(doc._id);
|
|
3462
|
+
result && result.errors.length > 0 && docsWithErrors.push({ ...doc, errors: result.errors });
|
|
3463
|
+
}
|
|
3464
|
+
return {
|
|
3465
|
+
hasValidationErrors: docsWithErrors.length > 0,
|
|
3466
|
+
someStillValidating: validating,
|
|
3467
|
+
documentsWithErrors: docsWithErrors
|
|
3468
|
+
};
|
|
3469
|
+
}, [documentsToValidate, validationResults]), runSourceDraftCheck = useCallback(async () => {
|
|
3470
|
+
if (filteredReferences.length === 0) {
|
|
3471
|
+
setIsSourceDraftCheckLoading(!1);
|
|
3472
|
+
return;
|
|
3473
|
+
}
|
|
3474
|
+
setIsSourceDraftCheckLoading(!0);
|
|
3475
|
+
try {
|
|
3476
|
+
const results = await checkSourceDrafts({
|
|
3477
|
+
client,
|
|
3478
|
+
referenceIds: filteredReferences.map((ref) => ref._id)
|
|
3479
|
+
});
|
|
3480
|
+
setSourceRefsWithDrafts(results);
|
|
3481
|
+
} catch (error) {
|
|
3482
|
+
devLog.error(
|
|
3483
|
+
"Failed to check source reference drafts",
|
|
3484
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
3485
|
+
{ component: "PreflightView" }
|
|
3486
|
+
);
|
|
3487
|
+
} finally {
|
|
3488
|
+
setIsSourceDraftCheckLoading(!1);
|
|
3489
|
+
}
|
|
3490
|
+
}, [client, filteredReferences]), runDraftCheck = useCallback(async () => {
|
|
3491
|
+
setIsDraftCheckLoading(!0);
|
|
3492
|
+
try {
|
|
3493
|
+
const drafts = await checkTargetDrafts({
|
|
3494
|
+
client,
|
|
3495
|
+
translationMetadata,
|
|
3496
|
+
selectedTargetLanguages,
|
|
3497
|
+
sourceDocumentId: document._id,
|
|
3498
|
+
references: shouldTranslateReferences ? filteredReferences : void 0
|
|
3499
|
+
});
|
|
3500
|
+
setStaleDrafts(drafts), drafts.length === 0 && setDraftAction("none");
|
|
3501
|
+
} catch (error) {
|
|
3502
|
+
devLog.error("Failed to check target drafts", error instanceof Error ? error : new Error(String(error)), {
|
|
3503
|
+
sourceDocumentId: document._id,
|
|
3504
|
+
component: "PreflightView"
|
|
3505
|
+
});
|
|
3506
|
+
} finally {
|
|
3507
|
+
setIsDraftCheckLoading(!1);
|
|
3508
|
+
}
|
|
3509
|
+
}, [client, translationMetadata, selectedTargetLanguages, document._id, shouldTranslateReferences, filteredReferences]), runConflictCheck = useCallback(async () => {
|
|
3510
|
+
if (releaseConfig.mode === "new") {
|
|
3511
|
+
setIsCheckingConflicts(!1), setConflictContext(null);
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
if (releaseConfig.mode === "existing" && releaseConfig.existingReleaseId) {
|
|
3515
|
+
setIsCheckingConflicts(!0);
|
|
3516
|
+
const checkedReleaseId = releaseConfig.existingReleaseId, foundConflicts = await checkReleaseConflicts({
|
|
3517
|
+
client,
|
|
3518
|
+
releaseId: checkedReleaseId,
|
|
3519
|
+
sourceDocument: document,
|
|
3520
|
+
targetLanguages: selectedTargetLanguages,
|
|
3521
|
+
languageFieldName: config.languageFieldName,
|
|
3522
|
+
references: filteredReferences
|
|
3523
|
+
});
|
|
3524
|
+
foundConflicts.length > 0 ? setConflictContext({ releaseId: checkedReleaseId, conflicts: foundConflicts }) : setConflictContext(null), setIsCheckingConflicts(!1);
|
|
3525
|
+
}
|
|
3526
|
+
}, [releaseConfig, client, document, selectedTargetLanguages, config.languageFieldName, filteredReferences]);
|
|
3290
3527
|
useEffect(() => {
|
|
3291
|
-
|
|
3292
|
-
}, [
|
|
3293
|
-
const
|
|
3528
|
+
runSourceDraftCheck(), runDraftCheck(), runConflictCheck();
|
|
3529
|
+
}, [scanKey]);
|
|
3530
|
+
const handleDiscardDrafts = useCallback(async () => {
|
|
3531
|
+
setIsDraftActionInProgress(!0);
|
|
3532
|
+
try {
|
|
3533
|
+
const draftIds = staleDrafts.map((d) => d.draftId);
|
|
3534
|
+
devLog.info("Discarding stale drafts", { draftIds, component: "PreflightView" });
|
|
3535
|
+
const transaction = client.transaction();
|
|
3536
|
+
for (const id of draftIds)
|
|
3537
|
+
transaction.delete(id);
|
|
3538
|
+
await transaction.commit(), devLog.info("Successfully discarded stale drafts", {
|
|
3539
|
+
count: draftIds.length,
|
|
3540
|
+
component: "PreflightView"
|
|
3541
|
+
}), await runDraftCheck();
|
|
3542
|
+
} catch (error) {
|
|
3543
|
+
devLog.error("Failed to discard drafts", error instanceof Error ? error : new Error(String(error)), {
|
|
3544
|
+
component: "PreflightView"
|
|
3545
|
+
});
|
|
3546
|
+
} finally {
|
|
3547
|
+
setIsDraftActionInProgress(!1);
|
|
3548
|
+
}
|
|
3549
|
+
}, [staleDrafts, client, runDraftCheck]), handlePublishDrafts = useCallback(async () => {
|
|
3550
|
+
setIsDraftActionInProgress(!0);
|
|
3551
|
+
try {
|
|
3552
|
+
devLog.info("Publishing stale drafts", {
|
|
3553
|
+
count: staleDrafts.length,
|
|
3554
|
+
component: "PreflightView"
|
|
3555
|
+
});
|
|
3556
|
+
for (const draft of staleDrafts)
|
|
3557
|
+
await client.request({
|
|
3558
|
+
method: "POST",
|
|
3559
|
+
uri: "/data/actions/mutate",
|
|
3560
|
+
body: {
|
|
3561
|
+
actions: [
|
|
3562
|
+
{
|
|
3563
|
+
actionType: "sanity.action.document.publish",
|
|
3564
|
+
draftId: draft.draftId,
|
|
3565
|
+
publishedId: draft.targetId
|
|
3566
|
+
}
|
|
3567
|
+
]
|
|
3568
|
+
}
|
|
3569
|
+
});
|
|
3570
|
+
devLog.info("Successfully published stale drafts", {
|
|
3571
|
+
count: staleDrafts.length,
|
|
3572
|
+
component: "PreflightView"
|
|
3573
|
+
}), await runDraftCheck();
|
|
3574
|
+
} catch (error) {
|
|
3575
|
+
devLog.error("Failed to publish drafts", error instanceof Error ? error : new Error(String(error)), {
|
|
3576
|
+
component: "PreflightView"
|
|
3577
|
+
});
|
|
3578
|
+
} finally {
|
|
3579
|
+
setIsDraftActionInProgress(!1);
|
|
3580
|
+
}
|
|
3581
|
+
}, [staleDrafts, client, runDraftCheck]), startTranslation = useCallback(
|
|
3294
3582
|
(replaceExisting, overrideReleaseId, skipConflicts) => {
|
|
3295
|
-
|
|
3583
|
+
translateDocumentSet({
|
|
3296
3584
|
sourceDocument: document,
|
|
3297
3585
|
targetLanguages: selectedTargetLanguages,
|
|
3298
3586
|
client,
|
|
@@ -3310,10 +3598,9 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3310
3598
|
...releaseConfig,
|
|
3311
3599
|
replaceExisting,
|
|
3312
3600
|
skipConflicts,
|
|
3313
|
-
// Use the override release ID if provided (from conflict context),
|
|
3314
|
-
// otherwise use the current releaseConfig value
|
|
3315
3601
|
existingReleaseId: overrideReleaseId ?? releaseConfig.existingReleaseId
|
|
3316
3602
|
},
|
|
3603
|
+
sourcePerspective: documentSource === "published" ? "published" : "drafts",
|
|
3317
3604
|
telemetryConfig: config.telemetry
|
|
3318
3605
|
});
|
|
3319
3606
|
},
|
|
@@ -3322,58 +3609,194 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3322
3609
|
selectedTargetLanguages,
|
|
3323
3610
|
client,
|
|
3324
3611
|
dispatch,
|
|
3325
|
-
config
|
|
3326
|
-
config.schemaId,
|
|
3327
|
-
config.maxConcurrentTranslationJobs,
|
|
3328
|
-
config.maxReferenceDepth,
|
|
3329
|
-
config.openai,
|
|
3330
|
-
config.excludeFields,
|
|
3331
|
-
config.unsetFields,
|
|
3332
|
-
config.telemetry,
|
|
3612
|
+
config,
|
|
3333
3613
|
filteredReferences,
|
|
3334
3614
|
translationMode,
|
|
3335
|
-
releaseConfig
|
|
3615
|
+
releaseConfig,
|
|
3616
|
+
documentSource
|
|
3336
3617
|
]
|
|
3337
|
-
),
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
releaseId: checkedReleaseId,
|
|
3348
|
-
sourceDocument: document,
|
|
3349
|
-
targetLanguages: selectedTargetLanguages,
|
|
3350
|
-
languageFieldName: config.languageFieldName,
|
|
3351
|
-
references: filteredReferences
|
|
3352
|
-
});
|
|
3353
|
-
if (setIsCheckingConflicts(!1), foundConflicts.length > 0) {
|
|
3354
|
-
setConflictContext({
|
|
3355
|
-
releaseId: checkedReleaseId,
|
|
3356
|
-
conflicts: foundConflicts
|
|
3357
|
-
});
|
|
3358
|
-
return;
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
startTranslation(!1);
|
|
3362
|
-
}, [
|
|
3363
|
-
releaseConfig,
|
|
3364
|
-
client,
|
|
3365
|
-
document,
|
|
3366
|
-
selectedTargetLanguages,
|
|
3367
|
-
config.languageFieldName,
|
|
3368
|
-
startTranslation,
|
|
3369
|
-
filteredReferences
|
|
3370
|
-
]), handleCancelConflict = useCallback(() => {
|
|
3618
|
+
), handleStartTranslation = useCallback(() => {
|
|
3619
|
+
conflictContext || startTranslation(!1);
|
|
3620
|
+
}, [conflictContext, startTranslation]), handleBack = useCallback(() => {
|
|
3621
|
+
dispatch({ type: "SET_OVERALL_STATUS", payload: "idle" });
|
|
3622
|
+
}, [dispatch]), handleRescan = useCallback(() => {
|
|
3623
|
+
setValidationResults(/* @__PURE__ */ new Map()), setSourceRefsWithDrafts([]), setIsSourceDraftCheckLoading(!0), setStaleDrafts([]), setIsDraftCheckLoading(!0), setDraftAction("none"), setConflictContext(null), setIsCheckingConflicts(!0), setScanKey((k) => k + 1);
|
|
3624
|
+
}, []), sourceRefDraftIds = useMemo(
|
|
3625
|
+
() => new Set(sourceRefsWithDrafts.map((d) => d.publishedId)),
|
|
3626
|
+
[sourceRefsWithDrafts]
|
|
3627
|
+
), isLoading = someStillValidating || isSourceDraftCheckLoading || isDraftCheckLoading || isCheckingConflicts, hasStaleDrafts = staleDrafts.length > 0, draftsDismissed = draftAction === "continue", canStartTranslation = !isLoading && !hasValidationErrors && !conflictContext && (!hasStaleDrafts || draftsDismissed), handleCancelConflict = useCallback(() => {
|
|
3371
3628
|
setConflictContext(null);
|
|
3372
3629
|
}, []), handleReplaceConflict = useCallback(() => {
|
|
3373
3630
|
conflictContext && startTranslation(!0, conflictContext.releaseId);
|
|
3374
3631
|
}, [startTranslation, conflictContext]), handleSkipConflicts = useCallback(() => {
|
|
3375
3632
|
conflictContext && startTranslation(!1, conflictContext.releaseId, conflictContext.conflicts);
|
|
3376
|
-
}, [startTranslation, conflictContext]),
|
|
3633
|
+
}, [startTranslation, conflictContext]), totalTranslations = useMemo(() => (1 + filteredReferences.length) * selectedTargetLanguages.length, [filteredReferences.length, selectedTargetLanguages.length]);
|
|
3634
|
+
return conflictContext ? /* @__PURE__ */ jsx(Stack, { space: 4, children: /* @__PURE__ */ jsx(
|
|
3635
|
+
ReleaseConflictWarning,
|
|
3636
|
+
{
|
|
3637
|
+
conflicts: conflictContext.conflicts,
|
|
3638
|
+
totalTranslations,
|
|
3639
|
+
onCancel: handleCancelConflict,
|
|
3640
|
+
onReplace: handleReplaceConflict,
|
|
3641
|
+
onSkipConflicts: handleSkipConflicts
|
|
3642
|
+
}
|
|
3643
|
+
) }) : /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
3644
|
+
documentsToValidate.map((doc) => /* @__PURE__ */ jsx(
|
|
3645
|
+
DocumentValidator,
|
|
3646
|
+
{
|
|
3647
|
+
documentId: doc._id,
|
|
3648
|
+
documentType: doc._type,
|
|
3649
|
+
onResult: handleValidationResult
|
|
3650
|
+
},
|
|
3651
|
+
`${doc._id}-${scanKey}`
|
|
3652
|
+
)),
|
|
3653
|
+
/* @__PURE__ */ jsx(Text, { weight: "semibold", size: 2, children: "Pre-flight Checks" }),
|
|
3654
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, children: [
|
|
3655
|
+
"Using ",
|
|
3656
|
+
documentSource,
|
|
3657
|
+
" version of all documents \u2192 ",
|
|
3658
|
+
selectedTargetLanguages.length,
|
|
3659
|
+
" language",
|
|
3660
|
+
selectedTargetLanguages.length !== 1 ? "s" : "",
|
|
3661
|
+
" \xB7 ",
|
|
3662
|
+
1 + filteredReferences.length,
|
|
3663
|
+
" document",
|
|
3664
|
+
filteredReferences.length !== 0 ? "s" : ""
|
|
3665
|
+
] }),
|
|
3666
|
+
/* @__PURE__ */ jsx(Card, { padding: 4, border: !0, radius: 2, tone: hasValidationErrors ? "critical" : void 0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3667
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3668
|
+
someStillValidating ? /* @__PURE__ */ jsx(Spinner, { muted: !0 }) : hasValidationErrors ? /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, {}) }) : /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(CheckmarkIcon, {}) }),
|
|
3669
|
+
/* @__PURE__ */ jsx(Text, { weight: "semibold", children: someStillValidating ? "Validating documents\u2026" : hasValidationErrors ? `Validation errors in ${documentsWithErrors.length} document${documentsWithErrors.length !== 1 ? "s" : ""}` : "All documents pass validation" })
|
|
3670
|
+
] }),
|
|
3671
|
+
documentsWithErrors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3672
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: "Documents must pass validation before they can be translated. Click a document to open it and fix the errors." }),
|
|
3673
|
+
/* @__PURE__ */ jsx(Card, { padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsx(Box, { style: { maxHeight: "300px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 3, children: documentsWithErrors.map((doc) => {
|
|
3674
|
+
const publishedId = getPublishedId(doc._id), displayName = doc._id === document._id ? "This document" : doc.title || doc._id, hasUnpublishedChanges = sourceRefDraftIds.has(publishedId);
|
|
3675
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", gap: 2, children: [
|
|
3676
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, flex: 1, style: { minWidth: 0 }, children: [
|
|
3677
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: /* @__PURE__ */ jsx(
|
|
3678
|
+
IntentLink,
|
|
3679
|
+
{
|
|
3680
|
+
intent: "edit",
|
|
3681
|
+
params: { id: publishedId, type: doc._type },
|
|
3682
|
+
style: { color: "inherit" },
|
|
3683
|
+
children: displayName
|
|
3684
|
+
}
|
|
3685
|
+
) }),
|
|
3686
|
+
/* @__PURE__ */ jsx(Badge, { tone: "primary", fontSize: 0, children: doc._type }),
|
|
3687
|
+
hasUnpublishedChanges && /* @__PURE__ */ jsx(Badge, { tone: "caution", fontSize: 0, children: "unpublished changes" })
|
|
3688
|
+
] }),
|
|
3689
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3690
|
+
/* @__PURE__ */ jsxs(Badge, { tone: "critical", fontSize: 0, children: [
|
|
3691
|
+
doc.errors.length,
|
|
3692
|
+
" error",
|
|
3693
|
+
doc.errors.length !== 1 ? "s" : ""
|
|
3694
|
+
] }),
|
|
3695
|
+
/* @__PURE__ */ jsx(DocumentOpenerButton, { documentId: doc._id, documentType: doc._type })
|
|
3696
|
+
] })
|
|
3697
|
+
] }, doc._id);
|
|
3698
|
+
}) }) }) })
|
|
3699
|
+
] })
|
|
3700
|
+
] }) }),
|
|
3701
|
+
filteredReferences.length > 0 && /* @__PURE__ */ jsx(Card, { padding: 4, border: !0, radius: 2, tone: sourceRefsWithDrafts.length > 0 ? "caution" : void 0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3702
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3703
|
+
isSourceDraftCheckLoading ? /* @__PURE__ */ jsx(Spinner, { muted: !0 }) : sourceRefsWithDrafts.length > 0 ? /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(WarningOutlineIcon, {}) }) : /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(CheckmarkIcon, {}) }),
|
|
3704
|
+
/* @__PURE__ */ jsx(Text, { weight: "semibold", children: isSourceDraftCheckLoading ? "Checking referenced documents\u2026" : sourceRefsWithDrafts.length > 0 ? `${sourceRefsWithDrafts.length} referenced document${sourceRefsWithDrafts.length !== 1 ? "s have" : " has"} unpublished changes` : "All referenced documents are in sync" })
|
|
3705
|
+
] }),
|
|
3706
|
+
sourceRefsWithDrafts.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3707
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: documentSource === "draft" ? 'You are translating from drafts. These unpublished changes will be included in the translation. If this is not intended, go back and switch to "Translate from published".' : 'You are translating from published. Unpublished changes on these documents will be ignored. If you want to include them, go back and switch to "Translate from drafts".' }),
|
|
3708
|
+
/* @__PURE__ */ jsx(Card, { padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsx(Box, { style: { maxHeight: "300px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 3, children: sourceRefsWithDrafts.map((ref) => /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", gap: 2, children: [
|
|
3709
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, flex: 1, style: { minWidth: 0 }, children: [
|
|
3710
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: ref.title || ref.publishedId }),
|
|
3711
|
+
/* @__PURE__ */ jsx(Badge, { tone: "primary", fontSize: 0, children: ref._type })
|
|
3712
|
+
] }),
|
|
3713
|
+
/* @__PURE__ */ jsx(DocumentOpenerButton, { documentId: ref.publishedId, documentType: ref._type })
|
|
3714
|
+
] }, ref.publishedId)) }) }) })
|
|
3715
|
+
] })
|
|
3716
|
+
] }) }),
|
|
3717
|
+
(isDraftCheckLoading || hasStaleDrafts) && /* @__PURE__ */ jsx(Card, { padding: 4, border: !0, radius: 2, tone: hasStaleDrafts && !draftsDismissed ? "caution" : void 0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3718
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3719
|
+
isDraftCheckLoading ? /* @__PURE__ */ jsx(Spinner, { muted: !0 }) : draftsDismissed ? /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(CheckmarkIcon, {}) }) : /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(WarningOutlineIcon, {}) }),
|
|
3720
|
+
/* @__PURE__ */ jsx(Text, { weight: "semibold", children: isDraftCheckLoading ? "Checking for stale drafts on target documents\u2026" : draftsDismissed ? "Stale drafts acknowledged" : `${staleDrafts.length} stale draft${staleDrafts.length !== 1 ? "s" : ""} found on target documents` })
|
|
3721
|
+
] }),
|
|
3722
|
+
hasStaleDrafts && !draftsDismissed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3723
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: "These target documents have unpublished drafts that may contain outdated content. Close and reopen the dialog after resolving, or use the actions below." }),
|
|
3724
|
+
/* @__PURE__ */ jsx(Card, { padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsx(Box, { style: { maxHeight: "300px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 3, children: staleDrafts.map((draft) => /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", gap: 2, children: [
|
|
3725
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, flex: 1, style: { minWidth: 0 }, children: [
|
|
3726
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: draft.title || draft.targetId }),
|
|
3727
|
+
/* @__PURE__ */ jsx(Badge, { tone: "primary", fontSize: 0, children: draft.targetLanguage }),
|
|
3728
|
+
draft.isReference && /* @__PURE__ */ jsx(Badge, { fontSize: 0, children: "reference" })
|
|
3729
|
+
] }),
|
|
3730
|
+
/* @__PURE__ */ jsx(DocumentOpenerButton, { documentId: draft.targetId, documentType: draft._type })
|
|
3731
|
+
] }, draft.draftId)) }) }) }),
|
|
3732
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", children: [
|
|
3733
|
+
/* @__PURE__ */ jsx(
|
|
3734
|
+
Button,
|
|
3735
|
+
{
|
|
3736
|
+
mode: "ghost",
|
|
3737
|
+
tone: "caution",
|
|
3738
|
+
text: "Discard All Drafts",
|
|
3739
|
+
icon: CloseIcon,
|
|
3740
|
+
onClick: handleDiscardDrafts,
|
|
3741
|
+
disabled: isDraftActionInProgress
|
|
3742
|
+
}
|
|
3743
|
+
),
|
|
3744
|
+
/* @__PURE__ */ jsx(
|
|
3745
|
+
Button,
|
|
3746
|
+
{
|
|
3747
|
+
mode: "ghost",
|
|
3748
|
+
tone: "caution",
|
|
3749
|
+
text: "Publish All Drafts",
|
|
3750
|
+
onClick: handlePublishDrafts,
|
|
3751
|
+
disabled: isDraftActionInProgress
|
|
3752
|
+
}
|
|
3753
|
+
),
|
|
3754
|
+
/* @__PURE__ */ jsx(
|
|
3755
|
+
Button,
|
|
3756
|
+
{
|
|
3757
|
+
mode: "ghost",
|
|
3758
|
+
text: "Continue Anyway",
|
|
3759
|
+
onClick: () => setDraftAction("continue"),
|
|
3760
|
+
disabled: isDraftActionInProgress
|
|
3761
|
+
}
|
|
3762
|
+
)
|
|
3763
|
+
] }),
|
|
3764
|
+
isDraftActionInProgress && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3765
|
+
/* @__PURE__ */ jsx(Spinner, { muted: !0 }),
|
|
3766
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Processing\u2026" })
|
|
3767
|
+
] })
|
|
3768
|
+
] })
|
|
3769
|
+
] }) }),
|
|
3770
|
+
/* @__PURE__ */ jsxs(Flex, { justify: "space-between", gap: 2, children: [
|
|
3771
|
+
/* @__PURE__ */ jsx(Button, { mode: "ghost", text: "Back", onClick: handleBack }),
|
|
3772
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
3773
|
+
/* @__PURE__ */ jsx(
|
|
3774
|
+
Button,
|
|
3775
|
+
{
|
|
3776
|
+
mode: "ghost",
|
|
3777
|
+
text: "Rescan",
|
|
3778
|
+
icon: SyncIcon,
|
|
3779
|
+
onClick: handleRescan,
|
|
3780
|
+
disabled: isLoading
|
|
3781
|
+
}
|
|
3782
|
+
),
|
|
3783
|
+
/* @__PURE__ */ jsx(
|
|
3784
|
+
Button,
|
|
3785
|
+
{
|
|
3786
|
+
mode: "ghost",
|
|
3787
|
+
tone: "positive",
|
|
3788
|
+
text: isLoading ? "Checking\u2026" : "Start Translation",
|
|
3789
|
+
onClick: handleStartTranslation,
|
|
3790
|
+
disabled: !canStartTranslation
|
|
3791
|
+
}
|
|
3792
|
+
)
|
|
3793
|
+
] })
|
|
3794
|
+
] })
|
|
3795
|
+
] });
|
|
3796
|
+
}, ActionButtonBar = ({ setIsDialogOpen }) => {
|
|
3797
|
+
const { document, config, overallStatus, dispatch, selectedTargetLanguages, releaseConfig } = useTranslationContext(), handleTranslate = useCallback(() => {
|
|
3798
|
+
dispatch({ type: "SET_OVERALL_STATUS", payload: "preflight" });
|
|
3799
|
+
}, [dispatch]), handleSelectAll = useCallback(() => {
|
|
3377
3800
|
dispatch({
|
|
3378
3801
|
type: "SET_SELECTED_TARGET_LANGUAGES",
|
|
3379
3802
|
payload: config.supportedLanguages.map((lang) => lang.id).filter((id) => id !== document.language)
|
|
@@ -3384,16 +3807,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3384
3807
|
payload: []
|
|
3385
3808
|
});
|
|
3386
3809
|
}, [dispatch]);
|
|
3387
|
-
return
|
|
3388
|
-
ReleaseConflictWarning,
|
|
3389
|
-
{
|
|
3390
|
-
conflicts: conflictContext.conflicts,
|
|
3391
|
-
totalTranslations,
|
|
3392
|
-
onCancel: handleCancelConflict,
|
|
3393
|
-
onReplace: handleReplaceConflict,
|
|
3394
|
-
onSkipConflicts: handleSkipConflicts
|
|
3395
|
-
}
|
|
3396
|
-
) }) : /* @__PURE__ */ jsxs(Flex, { justify: "space-between", gap: 2, children: [
|
|
3810
|
+
return /* @__PURE__ */ jsxs(Flex, { justify: "space-between", gap: 2, children: [
|
|
3397
3811
|
/* @__PURE__ */ jsxs(Flex, { justify: "flex-start", gap: 2, children: [
|
|
3398
3812
|
/* @__PURE__ */ jsx(Button, { mode: "ghost", size: 2, onClick: handleSelectAll, children: "Select All" }),
|
|
3399
3813
|
/* @__PURE__ */ jsx(Button, { mode: "ghost", size: 2, onClick: handleSelectNone, children: "Select None" })
|
|
@@ -3407,12 +3821,61 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3407
3821
|
tone: "positive",
|
|
3408
3822
|
size: 2,
|
|
3409
3823
|
onClick: handleTranslate,
|
|
3410
|
-
disabled: selectedTargetLanguages.length === 0 || !["idle", "completed"].includes(overallStatus) || releaseConfig.mode === "existing" && !releaseConfig.existingReleaseId
|
|
3411
|
-
children:
|
|
3824
|
+
disabled: selectedTargetLanguages.length === 0 || !["idle", "completed"].includes(overallStatus) || releaseConfig.mode === "existing" && !releaseConfig.existingReleaseId,
|
|
3825
|
+
children: "Translate"
|
|
3412
3826
|
}
|
|
3413
3827
|
)
|
|
3414
3828
|
] })
|
|
3415
3829
|
] });
|
|
3830
|
+
}, DocumentSourcePanel = () => {
|
|
3831
|
+
const { hasDraft, hasPublished, documentSource, dispatch } = useTranslationContext();
|
|
3832
|
+
if (!hasDraft)
|
|
3833
|
+
return null;
|
|
3834
|
+
if (!hasPublished)
|
|
3835
|
+
return /* @__PURE__ */ jsx(Card, { tone: "transparent", padding: 4, border: !0, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "This document has not been published yet. The current draft will be used for translation, along with drafts of any referenced documents." }) });
|
|
3836
|
+
const handleSourceChange = (source) => {
|
|
3837
|
+
dispatch({
|
|
3838
|
+
type: "SET_DOCUMENT_SOURCE",
|
|
3839
|
+
payload: source
|
|
3840
|
+
});
|
|
3841
|
+
};
|
|
3842
|
+
return /* @__PURE__ */ jsx(Card, { tone: "caution", padding: 4, border: !0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3843
|
+
/* @__PURE__ */ jsx(Text, { weight: "semibold", children: "This document has unpublished changes" }),
|
|
3844
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
3845
|
+
/* @__PURE__ */ jsx(
|
|
3846
|
+
Button,
|
|
3847
|
+
{
|
|
3848
|
+
type: "button",
|
|
3849
|
+
mode: documentSource === "draft" ? "ghost" : "bleed",
|
|
3850
|
+
tone: documentSource === "draft" ? "primary" : "default",
|
|
3851
|
+
onClick: () => handleSourceChange("draft"),
|
|
3852
|
+
children: /* @__PURE__ */ jsxs(Inline, { space: 2, children: [
|
|
3853
|
+
/* @__PURE__ */ jsx(Radio, { checked: documentSource === "draft", readOnly: !0 }),
|
|
3854
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
3855
|
+
/* @__PURE__ */ jsx(Text, { children: "Translate from drafts" }),
|
|
3856
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Uses the latest draft of this document and all referenced documents" })
|
|
3857
|
+
] })
|
|
3858
|
+
] })
|
|
3859
|
+
}
|
|
3860
|
+
),
|
|
3861
|
+
/* @__PURE__ */ jsx(
|
|
3862
|
+
Button,
|
|
3863
|
+
{
|
|
3864
|
+
type: "button",
|
|
3865
|
+
mode: documentSource === "published" ? "ghost" : "bleed",
|
|
3866
|
+
tone: documentSource === "published" ? "primary" : "default",
|
|
3867
|
+
onClick: () => handleSourceChange("published"),
|
|
3868
|
+
children: /* @__PURE__ */ jsxs(Inline, { space: 2, children: [
|
|
3869
|
+
/* @__PURE__ */ jsx(Radio, { checked: documentSource === "published", readOnly: !0 }),
|
|
3870
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
3871
|
+
/* @__PURE__ */ jsx(Text, { children: "Translate from published" }),
|
|
3872
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Uses only published content for this document and all referenced documents" })
|
|
3873
|
+
] })
|
|
3874
|
+
] })
|
|
3875
|
+
}
|
|
3876
|
+
)
|
|
3877
|
+
] })
|
|
3878
|
+
] }) });
|
|
3416
3879
|
}, TRANSLATION_METADATA_QUERY = "*[_type == $translationMetadataType && references($documentId)][0]", useTranslationMetadata = ({ documentId }) => {
|
|
3417
3880
|
const { dispatch } = useTranslationContext(), { data, loading, error } = useListeningQuery(TRANSLATION_METADATA_QUERY, {
|
|
3418
3881
|
params: {
|
|
@@ -3855,108 +4318,6 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3855
4318
|
)
|
|
3856
4319
|
] })
|
|
3857
4320
|
] }) });
|
|
3858
|
-
}, DocumentValidator = ({ documentId, documentType, onResult }) => {
|
|
3859
|
-
const publishedId = getPublishedId(documentId), { isValidating, validation } = useValidationStatus(publishedId, documentType), hasReceivedRealResult = useRef(!1);
|
|
3860
|
-
return useEffect(() => {
|
|
3861
|
-
(isValidating || validation.length > 0) && (hasReceivedRealResult.current = !0);
|
|
3862
|
-
const stillWaitingForFirstResult = !hasReceivedRealResult.current, errors = validation.filter((v) => v.level === "error");
|
|
3863
|
-
onResult(documentId, {
|
|
3864
|
-
isValidating: isValidating || stillWaitingForFirstResult,
|
|
3865
|
-
errors
|
|
3866
|
-
});
|
|
3867
|
-
}, [isValidating, validation, documentId, onResult]), null;
|
|
3868
|
-
}, ValidationPanel = ({ onValidationChange }) => {
|
|
3869
|
-
const { document, references, selectedReferenceIds, shouldTranslateReferences } = useTranslationContext(), [results, setResults] = useState(/* @__PURE__ */ new Map()), documentsToValidate = useMemo(() => {
|
|
3870
|
-
const docs = [
|
|
3871
|
-
{
|
|
3872
|
-
_id: document._id,
|
|
3873
|
-
_type: document._type,
|
|
3874
|
-
title: document.title || document.name || document._id
|
|
3875
|
-
}
|
|
3876
|
-
];
|
|
3877
|
-
if (shouldTranslateReferences)
|
|
3878
|
-
for (const ref of references)
|
|
3879
|
-
selectedReferenceIds.has(ref._id) && docs.push(ref);
|
|
3880
|
-
return docs;
|
|
3881
|
-
}, [document, references, selectedReferenceIds, shouldTranslateReferences]), handleResult = useCallback((id, result) => {
|
|
3882
|
-
setResults((prev) => {
|
|
3883
|
-
const next = new Map(prev);
|
|
3884
|
-
return next.set(id, result), next;
|
|
3885
|
-
});
|
|
3886
|
-
}, []), { shouldBlock, someStillValidating, documentsWithErrors } = useMemo(() => {
|
|
3887
|
-
const allValidated = documentsToValidate.every((doc) => results.has(doc._id)), validating = [...results.values()].some((r) => r.isValidating) || !allValidated, docsWithErrors = [];
|
|
3888
|
-
for (const doc of documentsToValidate) {
|
|
3889
|
-
const result = results.get(doc._id);
|
|
3890
|
-
result && result.errors.length > 0 && docsWithErrors.push({ ...doc, errors: result.errors });
|
|
3891
|
-
}
|
|
3892
|
-
const hasErrors = docsWithErrors.length > 0;
|
|
3893
|
-
return {
|
|
3894
|
-
shouldBlock: validating || hasErrors,
|
|
3895
|
-
someStillValidating: validating,
|
|
3896
|
-
documentsWithErrors: docsWithErrors
|
|
3897
|
-
};
|
|
3898
|
-
}, [documentsToValidate, results]);
|
|
3899
|
-
useEffect(() => {
|
|
3900
|
-
onValidationChange(shouldBlock);
|
|
3901
|
-
}, [shouldBlock, onValidationChange]), useEffect(() => {
|
|
3902
|
-
documentsWithErrors.length > 0 && devLog.warn("Documents have validation errors that block translation", {
|
|
3903
|
-
documentsWithErrors: documentsWithErrors.map((d) => ({
|
|
3904
|
-
id: d._id,
|
|
3905
|
-
type: d._type,
|
|
3906
|
-
errorCount: d.errors.length
|
|
3907
|
-
})),
|
|
3908
|
-
component: "ValidationPanel"
|
|
3909
|
-
});
|
|
3910
|
-
}, [documentsWithErrors]);
|
|
3911
|
-
const totalErrors = useMemo(
|
|
3912
|
-
() => documentsWithErrors.reduce((sum, doc) => sum + doc.errors.length, 0),
|
|
3913
|
-
[documentsWithErrors]
|
|
3914
|
-
);
|
|
3915
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3916
|
-
documentsToValidate.map((doc) => /* @__PURE__ */ jsx(DocumentValidator, { documentId: doc._id, documentType: doc._type, onResult: handleResult }, doc._id)),
|
|
3917
|
-
someStillValidating && documentsWithErrors.length === 0 && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: 2, children: [
|
|
3918
|
-
/* @__PURE__ */ jsx(Spinner, { muted: !0 }),
|
|
3919
|
-
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Validating documents\u2026" })
|
|
3920
|
-
] }),
|
|
3921
|
-
documentsWithErrors.length > 0 && /* @__PURE__ */ jsx(Card, { tone: "critical", padding: 4, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
3922
|
-
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3923
|
-
/* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, {}) }),
|
|
3924
|
-
/* @__PURE__ */ jsxs(Text, { weight: "semibold", children: [
|
|
3925
|
-
totalErrors,
|
|
3926
|
-
" validation error",
|
|
3927
|
-
totalErrors !== 1 ? "s" : "",
|
|
3928
|
-
" found in ",
|
|
3929
|
-
documentsWithErrors.length,
|
|
3930
|
-
" ",
|
|
3931
|
-
"document",
|
|
3932
|
-
documentsWithErrors.length !== 1 ? "s" : ""
|
|
3933
|
-
] })
|
|
3934
|
-
] }),
|
|
3935
|
-
/* @__PURE__ */ jsx(Text, { size: 1, children: "Documents must pass validation before they can be translated. Click a document to open it and fix the errors." }),
|
|
3936
|
-
/* @__PURE__ */ jsx(Card, { padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsx(Box, { style: { maxHeight: "300px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 3, children: documentsWithErrors.map((doc) => {
|
|
3937
|
-
const publishedId = getPublishedId(doc._id);
|
|
3938
|
-
return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", gap: 2, children: [
|
|
3939
|
-
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, flex: 1, style: { minWidth: 0 }, children: [
|
|
3940
|
-
/* @__PURE__ */ jsx(Text, { size: 1, children: /* @__PURE__ */ jsx(
|
|
3941
|
-
IntentLink,
|
|
3942
|
-
{
|
|
3943
|
-
intent: "edit",
|
|
3944
|
-
params: { id: publishedId, type: doc._type },
|
|
3945
|
-
style: { color: "inherit" },
|
|
3946
|
-
children: doc.title || doc._id
|
|
3947
|
-
}
|
|
3948
|
-
) }),
|
|
3949
|
-
/* @__PURE__ */ jsx(Badge, { tone: "primary", fontSize: 0, children: doc._type })
|
|
3950
|
-
] }),
|
|
3951
|
-
/* @__PURE__ */ jsxs(Badge, { tone: "critical", fontSize: 0, children: [
|
|
3952
|
-
doc.errors.length,
|
|
3953
|
-
" error",
|
|
3954
|
-
doc.errors.length !== 1 ? "s" : ""
|
|
3955
|
-
] })
|
|
3956
|
-
] }, doc._id);
|
|
3957
|
-
}) }) }) })
|
|
3958
|
-
] }) })
|
|
3959
|
-
] });
|
|
3960
4321
|
}, assembleReferences = async ({
|
|
3961
4322
|
client,
|
|
3962
4323
|
sourceDocument,
|
|
@@ -4210,31 +4571,26 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
4210
4571
|
!isLoadingReferences && references.length > 0 && showSelectionPanel && /* @__PURE__ */ jsx(ReferenceSelectionPanel, {})
|
|
4211
4572
|
] }) : null
|
|
4212
4573
|
] });
|
|
4213
|
-
}, WriteSettingsPanel = () => /* @__PURE__ */ jsx(ReferenceConfigPanel, {}), TranslationConfigView = ({ setIsDialogOpen }) => {
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
] }),
|
|
4234
|
-
/* @__PURE__ */ jsx(ValidationPanel, { onValidationChange: handleValidationChange }),
|
|
4235
|
-
/* @__PURE__ */ jsx(ActionButtonBar, { setIsDialogOpen, hasValidationErrors })
|
|
4236
|
-
] });
|
|
4237
|
-
}, TERMINAL_STATUSES = ["completed", "skipped", "error"], TranslationStatusPanel = () => {
|
|
4574
|
+
}, WriteSettingsPanel = () => /* @__PURE__ */ jsx(ReferenceConfigPanel, {}), TranslationConfigView = ({ setIsDialogOpen }) => /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
4575
|
+
/* @__PURE__ */ jsx(DocumentSourcePanel, {}),
|
|
4576
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
4577
|
+
/* @__PURE__ */ jsx(Label, { children: "Languages" }),
|
|
4578
|
+
/* @__PURE__ */ jsx(LanguageSelectionPanel, {})
|
|
4579
|
+
] }),
|
|
4580
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
4581
|
+
/* @__PURE__ */ jsx(Label, { children: "Translation Method" }),
|
|
4582
|
+
/* @__PURE__ */ jsx(TranslationModePanel, {})
|
|
4583
|
+
] }),
|
|
4584
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
4585
|
+
/* @__PURE__ */ jsx(Label, { children: "Content Release" }),
|
|
4586
|
+
/* @__PURE__ */ jsx(ReleaseSelectionPanel, {})
|
|
4587
|
+
] }),
|
|
4588
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
4589
|
+
/* @__PURE__ */ jsx(Label, { children: "Settings" }),
|
|
4590
|
+
/* @__PURE__ */ jsx(Card, { padding: 4, border: !0, muted: !0, children: /* @__PURE__ */ jsx(WriteSettingsPanel, {}) })
|
|
4591
|
+
] }),
|
|
4592
|
+
/* @__PURE__ */ jsx(ActionButtonBar, { setIsDialogOpen })
|
|
4593
|
+
] }), TERMINAL_STATUSES = ["completed", "skipped", "error"], TranslationStatusPanel = () => {
|
|
4238
4594
|
const { overallStatus, translationJobs, dispatch, document } = useTranslationContext(), headline = useMemo(() => overallStatus === "completed" ? "Translation completed" : overallStatus === "error" ? "Translation failed" : "Translation in progress", [overallStatus]), jobs = useMemo(() => Array.from(translationJobs.values()), [translationJobs]), isTerminalState = overallStatus === "completed" || overallStatus === "error", handleReset = useCallback(() => {
|
|
4239
4595
|
forceCleanupActiveTranslation(document._id), dispatch({ type: "RESET_TRANSLATION_STATE" });
|
|
4240
4596
|
}, [dispatch, document._id]);
|
|
@@ -4356,7 +4712,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
4356
4712
|
const { overallStatus, document } = useTranslationContext();
|
|
4357
4713
|
return useEffect(() => {
|
|
4358
4714
|
onStatusChange?.(overallStatus);
|
|
4359
|
-
}, [overallStatus, onStatusChange]), STATES_IN_WHICH_TO_SHOW_CONFIG.includes(overallStatus) ? /* @__PURE__ */ jsx(TranslationConfigView, { setIsDialogOpen }) : STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS.includes(overallStatus) ? /* @__PURE__ */ jsx(TranslationStatusPanel, {}) : STATES_IN_WHICH_TO_SHOW_ERROR.includes(overallStatus) ? /* @__PURE__ */ jsx("div", { children: "Error occurred" }) : (devLog.warn("Unhandled translation dialog state", {
|
|
4715
|
+
}, [overallStatus, onStatusChange]), STATES_IN_WHICH_TO_SHOW_CONFIG.includes(overallStatus) ? /* @__PURE__ */ jsx(TranslationConfigView, { setIsDialogOpen }) : overallStatus === "preflight" ? /* @__PURE__ */ jsx(PreflightView, {}) : STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS.includes(overallStatus) ? /* @__PURE__ */ jsx(TranslationStatusPanel, {}) : STATES_IN_WHICH_TO_SHOW_ERROR.includes(overallStatus) ? /* @__PURE__ */ jsx("div", { children: "Error occurred" }) : (devLog.warn("Unhandled translation dialog state", {
|
|
4360
4716
|
documentId: document._id,
|
|
4361
4717
|
overallStatus
|
|
4362
4718
|
}), /* @__PURE__ */ jsx("div", { children: "Unhandled overall state" }));
|
|
@@ -4396,7 +4752,16 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
4396
4752
|
type: "dialog",
|
|
4397
4753
|
onClose: handleClose,
|
|
4398
4754
|
header: "Translate Document",
|
|
4399
|
-
content: /* @__PURE__ */ jsx(
|
|
4755
|
+
content: /* @__PURE__ */ jsx(
|
|
4756
|
+
TranslationContextProvider,
|
|
4757
|
+
{
|
|
4758
|
+
document,
|
|
4759
|
+
config,
|
|
4760
|
+
hasDraft: !!draft,
|
|
4761
|
+
hasPublished: !!published,
|
|
4762
|
+
children: /* @__PURE__ */ jsx(TranslationDialog, { setIsDialogOpen, onStatusChange: handleStatusChange })
|
|
4763
|
+
}
|
|
4764
|
+
)
|
|
4400
4765
|
}
|
|
4401
4766
|
} : (devLog.error("Missing config in TranslateAction"), null);
|
|
4402
4767
|
}, translationGlossaryTerm = defineType({
|