@agent-native/core 0.101.7 → 0.101.10
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +22 -0
- package/corpus/core/docs/content/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
- package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
- package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
- package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
- package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
- package/corpus/core/src/server/auth.ts +25 -1
- package/corpus/core/src/server/builder-browser.ts +353 -1
- package/corpus/core/src/server/core-routes-plugin.ts +601 -223
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
- package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
- package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
- package/corpus/templates/content/AGENTS.md +3 -2
- package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
- package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
- package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
- package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
- package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +1041 -154
- package/corpus/templates/content/actions/_database-utils.ts +89 -12
- package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
- package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
- package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
- package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
- package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +27 -15
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
- package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2353 -576
- package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
- package/corpus/templates/content/app/global.css +18 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
- package/corpus/templates/content/app/i18n/zh-TW.ts +25 -0
- package/corpus/templates/content/app/i18n-data.ts +287 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
- package/corpus/templates/content/parity/matrix.md +27 -25
- package/corpus/templates/content/parity/matrix.ts +50 -2
- package/corpus/templates/content/server/db/schema.ts +47 -0
- package/corpus/templates/content/server/plugins/auth.ts +1 -0
- package/corpus/templates/content/server/plugins/db.ts +53 -0
- package/corpus/templates/content/shared/api.ts +70 -1
- package/corpus/templates/content/shared/builder-mdx.ts +705 -15
- package/corpus/templates/content/shared/nfm.ts +3 -3
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
- package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
- package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/design/app/i18n/en-US.ts +1 -0
- package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
- package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
- package/corpus/templates/design/shared/review-anchor.ts +48 -0
- package/corpus/templates/plan/server/plan-mdx.ts +53 -7
- package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
- package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
- package/dist/client/review/ReviewCommentComposer.js +3 -3
- package/dist/client/review/ReviewCommentComposer.js.map +1 -1
- package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
- package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
- package/dist/client/review/ReviewThreadPanel.js +6 -4
- package/dist/client/review/ReviewThreadPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +3 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +11 -4
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +8 -4
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +19 -6
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
- package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +19 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts +78 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +246 -1
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +41 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +260 -14
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/docs/content/deployment.mdx +9 -7
- package/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/package.json +2 -2
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AlertDialog,
|
|
4
|
+
AlertDialogAction,
|
|
5
|
+
AlertDialogCancel,
|
|
6
|
+
AlertDialogContent,
|
|
7
|
+
AlertDialogDescription,
|
|
8
|
+
AlertDialogFooter,
|
|
9
|
+
AlertDialogHeader,
|
|
10
|
+
AlertDialogTitle,
|
|
11
|
+
} from "@agent-native/toolkit/ui/alert-dialog";
|
|
12
|
+
import { Checkbox } from "@agent-native/toolkit/ui/checkbox";
|
|
3
13
|
import type {
|
|
4
14
|
BuilderCmsPublicationTransitionIntent,
|
|
5
15
|
BuilderCmsWriteEffect,
|
|
@@ -15,7 +25,7 @@ import {
|
|
|
15
25
|
IconCloudUpload,
|
|
16
26
|
IconX,
|
|
17
27
|
} from "@tabler/icons-react";
|
|
18
|
-
import { useEffect, useMemo, useState } from "react";
|
|
28
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
19
29
|
|
|
20
30
|
import { Button } from "@/components/ui/button";
|
|
21
31
|
import {
|
|
@@ -41,6 +51,11 @@ export type BuilderReviewPublicationTransitions = Record<
|
|
|
41
51
|
ExecuteBuilderSourceBatchTransition
|
|
42
52
|
>;
|
|
43
53
|
|
|
54
|
+
export type BuilderReviewSelection = {
|
|
55
|
+
changeSetIds: string[];
|
|
56
|
+
transitions: BuilderReviewPublicationTransitions;
|
|
57
|
+
};
|
|
58
|
+
|
|
44
59
|
const EFFECT_LABELS: Record<
|
|
45
60
|
BuilderCmsWriteEffect,
|
|
46
61
|
{ tag: string; sentence: string }
|
|
@@ -152,6 +167,9 @@ export function builderReviewResultStatus(status?: string): {
|
|
|
152
167
|
return { labelKey: "needsAttention", tone: "warn" };
|
|
153
168
|
case "failed":
|
|
154
169
|
return { labelKey: "failedYouCanRetry", tone: "danger" };
|
|
170
|
+
case "response_received":
|
|
171
|
+
case "reconciliation_required":
|
|
172
|
+
return { labelKey: "reconciliationRequired", tone: "warn" };
|
|
155
173
|
case "stale":
|
|
156
174
|
return { labelKey: "needsAFreshReview", tone: "warn" };
|
|
157
175
|
case "running":
|
|
@@ -195,6 +213,59 @@ export function builderReviewPublicationTransitionsMap(
|
|
|
195
213
|
return transitions;
|
|
196
214
|
}
|
|
197
215
|
|
|
216
|
+
export function builderReviewScopedTransitionSelections(
|
|
217
|
+
changeSetIds: string[],
|
|
218
|
+
selections: BuilderReviewPublicationTransitionSelections,
|
|
219
|
+
) {
|
|
220
|
+
const selectedIds = new Set(changeSetIds);
|
|
221
|
+
return Object.fromEntries(
|
|
222
|
+
Object.entries(selections).filter(([changeSetId]) =>
|
|
223
|
+
selectedIds.has(changeSetId),
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function builderReviewHasUnconfirmedUnpublish(
|
|
229
|
+
changeSetIds: string[],
|
|
230
|
+
selections: BuilderReviewPublicationTransitionSelections,
|
|
231
|
+
) {
|
|
232
|
+
return changeSetIds.some((changeSetId) => {
|
|
233
|
+
const selection = selections[changeSetId];
|
|
234
|
+
return (
|
|
235
|
+
selection?.publicationTransition === "unpublish" &&
|
|
236
|
+
selection.confirmUnpublish !== true
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function builderReviewRowCanCancelPrepared(
|
|
242
|
+
row: ContentDatabaseSourceReviewPayload["rows"][number],
|
|
243
|
+
) {
|
|
244
|
+
const execution = row.execution;
|
|
245
|
+
if (!execution) return false;
|
|
246
|
+
if (
|
|
247
|
+
execution.state !== "ready" &&
|
|
248
|
+
execution.state !== "write_disabled" &&
|
|
249
|
+
execution.state !== "blocked"
|
|
250
|
+
) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
if (
|
|
254
|
+
Object.prototype.hasOwnProperty.call(execution.payload, "response") ||
|
|
255
|
+
Object.prototype.hasOwnProperty.call(execution.payload, "dispatch")
|
|
256
|
+
) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
if (execution.state !== "blocked") return true;
|
|
260
|
+
const dryRun = execution.payload.dryRun;
|
|
261
|
+
return (
|
|
262
|
+
!!dryRun &&
|
|
263
|
+
typeof dryRun === "object" &&
|
|
264
|
+
!Array.isArray(dryRun) &&
|
|
265
|
+
(dryRun as Record<string, unknown>).status === "blocked"
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
198
269
|
const ATTENTION_TAG_EFFECTS: ReadonlySet<BuilderCmsWriteEffect> = new Set([
|
|
199
270
|
"unpublish",
|
|
200
271
|
]);
|
|
@@ -221,9 +292,15 @@ export function BuilderSourceReviewDialog({
|
|
|
221
292
|
canEdit,
|
|
222
293
|
pending,
|
|
223
294
|
batchResult = null,
|
|
295
|
+
error = null,
|
|
224
296
|
checkedAt,
|
|
297
|
+
preparedForExecution = false,
|
|
298
|
+
autoSelectReviewRows = false,
|
|
299
|
+
selectionChangeSetIdMap = null,
|
|
225
300
|
onClose,
|
|
226
301
|
onValidate,
|
|
302
|
+
onCancelPrepared,
|
|
303
|
+
onSelectionChange,
|
|
227
304
|
}: {
|
|
228
305
|
open: boolean;
|
|
229
306
|
review: ContentDatabaseSourceReviewPayload | null;
|
|
@@ -233,18 +310,20 @@ export function BuilderSourceReviewDialog({
|
|
|
233
310
|
// Optional so the inline-database caller (DatabaseView) that doesn't surface
|
|
234
311
|
// batch results can still mount the dialog.
|
|
235
312
|
batchResult?: ExecuteBuilderSourceBatchResponse | null;
|
|
313
|
+
error?: string | null;
|
|
236
314
|
checkedAt: string | null;
|
|
315
|
+
preparedForExecution?: boolean;
|
|
316
|
+
autoSelectReviewRows?: boolean;
|
|
317
|
+
selectionChangeSetIdMap?: Record<string, string> | null;
|
|
237
318
|
onClose: () => void;
|
|
238
|
-
onValidate: (
|
|
319
|
+
onValidate: (selection: BuilderReviewSelection) => void;
|
|
320
|
+
onCancelPrepared?: (changeSetId: string) => void;
|
|
321
|
+
onSelectionChange?: () => void;
|
|
239
322
|
}) {
|
|
240
323
|
const t = useT();
|
|
241
324
|
const checked = !!checkedAt;
|
|
242
|
-
const safeModel =
|
|
243
|
-
source?.sourceType === "builder-cms" &&
|
|
244
|
-
source.sourceTable === BUILDER_CMS_SAFE_WRITE_MODEL;
|
|
245
325
|
const writeMode = source?.metadata.writeMode;
|
|
246
326
|
const allowPublicationTransitionControls =
|
|
247
|
-
safeModel &&
|
|
248
327
|
writeMode === "publish_updates" &&
|
|
249
328
|
source?.metadata.allowPublicationTransitions === true;
|
|
250
329
|
const reviewRows = useMemo(() => review?.rows ?? [], [review]);
|
|
@@ -264,8 +343,43 @@ export function BuilderSourceReviewDialog({
|
|
|
264
343
|
[reviewRows],
|
|
265
344
|
);
|
|
266
345
|
const reviewRowIdsKey = reviewRowIds.join("\u0000");
|
|
346
|
+
const [selectedChangeSetIds, setSelectedChangeSetIds] = useState<string[]>(
|
|
347
|
+
[],
|
|
348
|
+
);
|
|
267
349
|
const [transitionSelections, setTransitionSelections] =
|
|
268
350
|
useState<BuilderReviewPublicationTransitionSelections>({});
|
|
351
|
+
const [cancelChangeSetId, setCancelChangeSetId] = useState<string | null>(
|
|
352
|
+
null,
|
|
353
|
+
);
|
|
354
|
+
const wasOpenRef = useRef(false);
|
|
355
|
+
useEffect(() => {
|
|
356
|
+
const wasOpen = wasOpenRef.current;
|
|
357
|
+
wasOpenRef.current = open;
|
|
358
|
+
|
|
359
|
+
if (!open || (!wasOpen && !autoSelectReviewRows)) {
|
|
360
|
+
setSelectedChangeSetIds([]);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const reviewRowIdSet = new Set(
|
|
365
|
+
reviewRowIdsKey ? reviewRowIdsKey.split("\u0000") : [],
|
|
366
|
+
);
|
|
367
|
+
setSelectedChangeSetIds((current) => {
|
|
368
|
+
const next = current
|
|
369
|
+
.map(
|
|
370
|
+
(changeSetId) =>
|
|
371
|
+
selectionChangeSetIdMap?.[changeSetId] ?? changeSetId,
|
|
372
|
+
)
|
|
373
|
+
.filter((changeSetId) => reviewRowIdSet.has(changeSetId));
|
|
374
|
+
if (autoSelectReviewRows && next.length === 0) {
|
|
375
|
+
return [...reviewRowIdSet];
|
|
376
|
+
}
|
|
377
|
+
return next.length === current.length &&
|
|
378
|
+
next.every((changeSetId, index) => changeSetId === current[index])
|
|
379
|
+
? current
|
|
380
|
+
: next;
|
|
381
|
+
});
|
|
382
|
+
}, [autoSelectReviewRows, open, reviewRowIdsKey, selectionChangeSetIdMap]);
|
|
269
383
|
useEffect(() => {
|
|
270
384
|
if (!open || !allowPublicationTransitionControls) {
|
|
271
385
|
setTransitionSelections({});
|
|
@@ -278,75 +392,114 @@ export function BuilderSourceReviewDialog({
|
|
|
278
392
|
setTransitionSelections((current) => {
|
|
279
393
|
const next: BuilderReviewPublicationTransitionSelections = {};
|
|
280
394
|
for (const [changeSetId, selection] of Object.entries(current)) {
|
|
281
|
-
|
|
395
|
+
const mappedChangeSetId =
|
|
396
|
+
selectionChangeSetIdMap?.[changeSetId] ?? changeSetId;
|
|
397
|
+
if (reviewRowIdSet.has(mappedChangeSetId)) {
|
|
398
|
+
next[mappedChangeSetId] = selection;
|
|
399
|
+
}
|
|
282
400
|
}
|
|
283
|
-
return Object.keys(next).length === Object.keys(current).length
|
|
401
|
+
return Object.keys(next).length === Object.keys(current).length &&
|
|
402
|
+
Object.entries(next).every(
|
|
403
|
+
([changeSetId, selection]) => current[changeSetId] === selection,
|
|
404
|
+
)
|
|
284
405
|
? current
|
|
285
406
|
: next;
|
|
286
407
|
});
|
|
287
|
-
}, [
|
|
408
|
+
}, [
|
|
409
|
+
allowPublicationTransitionControls,
|
|
410
|
+
open,
|
|
411
|
+
reviewRowIdsKey,
|
|
412
|
+
selectionChangeSetIdMap,
|
|
413
|
+
]);
|
|
414
|
+
const selectedChangeSetIdSet = useMemo(
|
|
415
|
+
() => new Set(selectedChangeSetIds),
|
|
416
|
+
[selectedChangeSetIds],
|
|
417
|
+
);
|
|
418
|
+
const selectedReviewRows = useMemo(
|
|
419
|
+
() =>
|
|
420
|
+
reviewRows.filter((row) => selectedChangeSetIdSet.has(row.changeSetId)),
|
|
421
|
+
[reviewRows, selectedChangeSetIdSet],
|
|
422
|
+
);
|
|
423
|
+
const scopedTransitionSelections = useMemo(
|
|
424
|
+
() =>
|
|
425
|
+
builderReviewScopedTransitionSelections(
|
|
426
|
+
selectedChangeSetIds,
|
|
427
|
+
transitionSelections,
|
|
428
|
+
),
|
|
429
|
+
[selectedChangeSetIds, transitionSelections],
|
|
430
|
+
);
|
|
288
431
|
const transitionMap = useMemo(
|
|
289
|
-
() => builderReviewPublicationTransitionsMap(
|
|
290
|
-
[
|
|
432
|
+
() => builderReviewPublicationTransitionsMap(scopedTransitionSelections),
|
|
433
|
+
[scopedTransitionSelections],
|
|
291
434
|
);
|
|
292
435
|
const intentSummary = builderReviewIntentSummary(
|
|
293
|
-
|
|
294
|
-
|
|
436
|
+
selectedReviewRows,
|
|
437
|
+
scopedTransitionSelections,
|
|
295
438
|
);
|
|
296
439
|
const destinationLine = builderReviewDestinationLine({
|
|
297
|
-
rows:
|
|
298
|
-
selections:
|
|
440
|
+
rows: selectedReviewRows,
|
|
441
|
+
selections: scopedTransitionSelections,
|
|
299
442
|
liveWritesEnabled: review?.liveWritesEnabled === true,
|
|
300
443
|
});
|
|
301
|
-
const hasUnconfirmedUnpublish =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
444
|
+
const hasUnconfirmedUnpublish = builderReviewHasUnconfirmedUnpublish(
|
|
445
|
+
selectedChangeSetIds,
|
|
446
|
+
scopedTransitionSelections,
|
|
447
|
+
);
|
|
448
|
+
const selectedBatchResults =
|
|
449
|
+
batchResult?.results.filter((result) =>
|
|
450
|
+
selectedChangeSetIdSet.has(result.changeSetId),
|
|
451
|
+
) ?? [];
|
|
452
|
+
const batchHasIssues = selectedBatchResults.some(
|
|
453
|
+
(result) => result.status === "blocked" || result.status === "failed",
|
|
454
|
+
);
|
|
455
|
+
const batchNeedsReconciliation = selectedBatchResults.some(
|
|
456
|
+
(result) => result.status === "reconciliation_required",
|
|
305
457
|
);
|
|
306
|
-
const batchHasIssues =
|
|
307
|
-
!!batchResult &&
|
|
308
|
-
(batchResult.summary.blocked > 0 || batchResult.summary.failed > 0);
|
|
309
458
|
const retryable =
|
|
310
|
-
|
|
311
|
-
review?.result.status
|
|
312
|
-
review?.result.status === "
|
|
313
|
-
|
|
314
|
-
|
|
459
|
+
!batchNeedsReconciliation &&
|
|
460
|
+
review?.result.status !== "reconciliation_required" &&
|
|
461
|
+
(review?.result.status === "failed" ||
|
|
462
|
+
review?.result.status === "blocked" ||
|
|
463
|
+
review?.result.status === "stale" ||
|
|
464
|
+
batchHasIssues);
|
|
315
465
|
const disabled =
|
|
316
466
|
!canEdit ||
|
|
317
467
|
pending ||
|
|
318
468
|
(!retryable && checked) ||
|
|
319
469
|
!review ||
|
|
320
|
-
|
|
321
|
-
unsafeLiveTarget ||
|
|
470
|
+
selectedReviewRows.length === 0 ||
|
|
322
471
|
hasUnconfirmedUnpublish;
|
|
323
472
|
const rowTitleById = new Map(
|
|
324
473
|
reviewRows.map((row) => [row.changeSetId, row.title]),
|
|
325
474
|
);
|
|
326
|
-
const batchIssueResults =
|
|
327
|
-
|
|
328
|
-
|
|
475
|
+
const batchIssueResults = selectedBatchResults.filter(
|
|
476
|
+
(result) => result.status !== "succeeded",
|
|
477
|
+
);
|
|
329
478
|
const resultStatus = builderReviewResultStatus(
|
|
330
479
|
batchResult
|
|
331
|
-
?
|
|
332
|
-
? "
|
|
333
|
-
:
|
|
480
|
+
? batchNeedsReconciliation
|
|
481
|
+
? "reconciliation_required"
|
|
482
|
+
: batchHasIssues
|
|
483
|
+
? "partial"
|
|
484
|
+
: "succeeded"
|
|
334
485
|
: review?.result.status,
|
|
335
486
|
);
|
|
336
487
|
const footerHint = pending
|
|
337
|
-
?
|
|
488
|
+
? preparedForExecution
|
|
338
489
|
? "Sending to Builder…"
|
|
339
|
-
: "
|
|
490
|
+
: "Preparing full review…"
|
|
340
491
|
: hasUnconfirmedUnpublish
|
|
341
492
|
? "Confirm unpublish on the selected rows first."
|
|
342
|
-
:
|
|
343
|
-
?
|
|
344
|
-
:
|
|
493
|
+
: preparedForExecution
|
|
494
|
+
? "Review the full payload above, then confirm this Builder write."
|
|
495
|
+
: review?.liveWritesEnabled
|
|
496
|
+
? "Prepare the full Builder payload before anything is sent."
|
|
497
|
+
: null;
|
|
345
498
|
const effectiveEffects = new Set(
|
|
346
|
-
|
|
499
|
+
selectedReviewRows.map((row) =>
|
|
347
500
|
builderReviewEffectiveRowEffect(
|
|
348
501
|
row.effect,
|
|
349
|
-
|
|
502
|
+
scopedTransitionSelections[row.changeSetId],
|
|
350
503
|
),
|
|
351
504
|
),
|
|
352
505
|
);
|
|
@@ -357,30 +510,41 @@ export function BuilderSourceReviewDialog({
|
|
|
357
510
|
: effectiveEffects.has("publish")
|
|
358
511
|
? "Publish"
|
|
359
512
|
: effectiveEffects.size === 1 && effectiveEffects.has("create_draft")
|
|
360
|
-
?
|
|
361
|
-
? `Create ${
|
|
513
|
+
? selectedReviewRows.length > 1
|
|
514
|
+
? `Create ${selectedReviewRows.length} drafts`
|
|
362
515
|
: "Create draft"
|
|
363
|
-
:
|
|
364
|
-
? `Push ${
|
|
516
|
+
: selectedReviewRows.length > 1
|
|
517
|
+
? `Push ${selectedReviewRows.length} updates`
|
|
365
518
|
: "Push update";
|
|
366
519
|
const buttonLabel = pending
|
|
367
|
-
?
|
|
368
|
-
? "
|
|
369
|
-
: "
|
|
370
|
-
: checked &&
|
|
371
|
-
?
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
520
|
+
? preparedForExecution
|
|
521
|
+
? "Sending…"
|
|
522
|
+
: "Preparing…"
|
|
523
|
+
: checked && review?.result.status === "running"
|
|
524
|
+
? t("database.working")
|
|
525
|
+
: checked && review?.result.status === "reconciliation_required"
|
|
526
|
+
? t("database.reconciliationRequired")
|
|
527
|
+
: checked && batchResult
|
|
528
|
+
? batchNeedsReconciliation
|
|
529
|
+
? t("database.reconciliationRequired")
|
|
530
|
+
: batchHasIssues
|
|
531
|
+
? "Retry"
|
|
532
|
+
: "Pushed"
|
|
533
|
+
: checked && review?.result.status === "succeeded"
|
|
534
|
+
? "Pushed"
|
|
535
|
+
: checked && !retryable
|
|
536
|
+
? "Checked"
|
|
537
|
+
: preparedForExecution
|
|
538
|
+
? pushVerb
|
|
539
|
+
: review?.liveWritesEnabled
|
|
540
|
+
? "Review details"
|
|
541
|
+
: pushVerb;
|
|
379
542
|
|
|
380
543
|
function setRowPublicationTransition(
|
|
381
544
|
changeSetId: string,
|
|
382
545
|
publicationTransition: BuilderCmsPublicationTransitionIntent,
|
|
383
546
|
) {
|
|
547
|
+
onSelectionChange?.();
|
|
384
548
|
setTransitionSelections((current) => {
|
|
385
549
|
const currentSelection = current[changeSetId];
|
|
386
550
|
const next = { ...current };
|
|
@@ -399,7 +563,28 @@ export function BuilderSourceReviewDialog({
|
|
|
399
563
|
});
|
|
400
564
|
}
|
|
401
565
|
|
|
566
|
+
function setRowSelected(changeSetId: string, selected: boolean) {
|
|
567
|
+
onSelectionChange?.();
|
|
568
|
+
setSelectedChangeSetIds((current) => {
|
|
569
|
+
if (selected) {
|
|
570
|
+
return current.includes(changeSetId)
|
|
571
|
+
? current
|
|
572
|
+
: [...current, changeSetId];
|
|
573
|
+
}
|
|
574
|
+
return current.filter((id) => id !== changeSetId);
|
|
575
|
+
});
|
|
576
|
+
if (!selected) {
|
|
577
|
+
setTransitionSelections((current) => {
|
|
578
|
+
if (!current[changeSetId]) return current;
|
|
579
|
+
const next = { ...current };
|
|
580
|
+
delete next[changeSetId];
|
|
581
|
+
return next;
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
402
586
|
function setRowConfirmUnpublish(changeSetId: string, confirmed: boolean) {
|
|
587
|
+
onSelectionChange?.();
|
|
403
588
|
setTransitionSelections((current) => {
|
|
404
589
|
const currentSelection = current[changeSetId];
|
|
405
590
|
if (currentSelection?.publicationTransition !== "unpublish") {
|
|
@@ -435,7 +620,7 @@ export function BuilderSourceReviewDialog({
|
|
|
435
620
|
{t("database.reviewBuilderUpdate")}
|
|
436
621
|
</DialogTitle>
|
|
437
622
|
<DialogDescription className="truncate text-xs text-muted-foreground">
|
|
438
|
-
{review
|
|
623
|
+
{pending && !review ? "Loading complete diff…" : intentSummary}
|
|
439
624
|
</DialogDescription>
|
|
440
625
|
</div>
|
|
441
626
|
<button
|
|
@@ -465,7 +650,11 @@ export function BuilderSourceReviewDialog({
|
|
|
465
650
|
</div>
|
|
466
651
|
) : null}
|
|
467
652
|
{visibleReviewRows.map((row) => {
|
|
468
|
-
const
|
|
653
|
+
const selected = selectedChangeSetIdSet.has(
|
|
654
|
+
row.changeSetId,
|
|
655
|
+
);
|
|
656
|
+
const selection =
|
|
657
|
+
scopedTransitionSelections[row.changeSetId];
|
|
469
658
|
const effect = builderReviewEffectiveRowEffect(
|
|
470
659
|
row.effect,
|
|
471
660
|
selection,
|
|
@@ -480,18 +669,32 @@ export function BuilderSourceReviewDialog({
|
|
|
480
669
|
key={row.changeSetId}
|
|
481
670
|
className="rounded-md border border-border p-3"
|
|
482
671
|
>
|
|
483
|
-
<div className="flex min-w-0 items-start
|
|
672
|
+
<div className="flex min-w-0 items-start gap-3">
|
|
673
|
+
<Checkbox
|
|
674
|
+
className="mt-0.5"
|
|
675
|
+
checked={selected}
|
|
676
|
+
disabled={pending}
|
|
677
|
+
aria-label={row.title}
|
|
678
|
+
onCheckedChange={(checked) =>
|
|
679
|
+
setRowSelected(row.changeSetId, checked === true)
|
|
680
|
+
}
|
|
681
|
+
/>
|
|
484
682
|
<div className="min-w-0">
|
|
485
683
|
<div className="truncate text-sm font-medium">
|
|
486
684
|
{row.title}
|
|
487
685
|
</div>
|
|
686
|
+
{row.targetEntryId ? (
|
|
687
|
+
<div className="mt-0.5 break-all font-mono text-[11px] text-muted-foreground">
|
|
688
|
+
Builder entry {row.targetEntryId}
|
|
689
|
+
</div>
|
|
690
|
+
) : null}
|
|
488
691
|
<div className="mt-1 text-xs text-muted-foreground">
|
|
489
692
|
{sentence}
|
|
490
693
|
</div>
|
|
491
694
|
</div>
|
|
492
695
|
<span
|
|
493
696
|
className={
|
|
494
|
-
"shrink-0 text-[11px] " +
|
|
697
|
+
"ms-auto shrink-0 text-[11px] " +
|
|
495
698
|
rowEffectTagClass(effect)
|
|
496
699
|
}
|
|
497
700
|
>
|
|
@@ -500,6 +703,7 @@ export function BuilderSourceReviewDialog({
|
|
|
500
703
|
</div>
|
|
501
704
|
<div className="mt-3 grid gap-2">
|
|
502
705
|
{allowPublicationTransitionControls &&
|
|
706
|
+
selected &&
|
|
503
707
|
row.effect !== "create_draft" ? (
|
|
504
708
|
<div className="flex flex-wrap items-center gap-1.5 text-xs">
|
|
505
709
|
<Button
|
|
@@ -553,18 +757,17 @@ export function BuilderSourceReviewDialog({
|
|
|
553
757
|
{transitionSelections[row.changeSetId]
|
|
554
758
|
?.publicationTransition === "unpublish" ? (
|
|
555
759
|
<label className="flex items-center gap-1 rounded border border-destructive/30 bg-destructive/10 px-1.5 py-1 text-[11px] text-destructive">
|
|
556
|
-
<
|
|
557
|
-
|
|
558
|
-
className="size-3 accent-destructive"
|
|
760
|
+
<Checkbox
|
|
761
|
+
aria-label={t("database.confirmUnpublish")}
|
|
559
762
|
checked={
|
|
560
763
|
transitionSelections[row.changeSetId]
|
|
561
764
|
?.confirmUnpublish === true
|
|
562
765
|
}
|
|
563
766
|
disabled={pending}
|
|
564
|
-
|
|
767
|
+
onCheckedChange={(checked) =>
|
|
565
768
|
setRowConfirmUnpublish(
|
|
566
769
|
row.changeSetId,
|
|
567
|
-
|
|
770
|
+
checked === true,
|
|
568
771
|
)
|
|
569
772
|
}
|
|
570
773
|
/>
|
|
@@ -624,6 +827,22 @@ export function BuilderSourceReviewDialog({
|
|
|
624
827
|
{row.execution.lastError}
|
|
625
828
|
</div>
|
|
626
829
|
) : null}
|
|
830
|
+
{canEdit &&
|
|
831
|
+
onCancelPrepared &&
|
|
832
|
+
builderReviewRowCanCancelPrepared(row) ? (
|
|
833
|
+
<Button
|
|
834
|
+
type="button"
|
|
835
|
+
size="sm"
|
|
836
|
+
variant="ghost"
|
|
837
|
+
className="h-7 justify-self-start px-2 text-xs text-destructive hover:text-destructive"
|
|
838
|
+
disabled={pending}
|
|
839
|
+
onClick={() =>
|
|
840
|
+
setCancelChangeSetId(row.changeSetId)
|
|
841
|
+
}
|
|
842
|
+
>
|
|
843
|
+
{t("database.cancelPreparedUpdate")}
|
|
844
|
+
</Button>
|
|
845
|
+
) : null}
|
|
627
846
|
</div>
|
|
628
847
|
</div>
|
|
629
848
|
);
|
|
@@ -650,10 +869,12 @@ export function BuilderSourceReviewDialog({
|
|
|
650
869
|
</div>
|
|
651
870
|
</section>
|
|
652
871
|
|
|
653
|
-
|
|
654
|
-
<
|
|
655
|
-
|
|
656
|
-
|
|
872
|
+
{selectedReviewRows.length > 0 ? (
|
|
873
|
+
<div className="flex items-start gap-1.5 text-xs text-muted-foreground">
|
|
874
|
+
<IconCloudUpload className="mt-0.5 size-3.5 shrink-0" />
|
|
875
|
+
<span>{destinationLine}</span>
|
|
876
|
+
</div>
|
|
877
|
+
) : null}
|
|
657
878
|
|
|
658
879
|
{batchResult && batchIssueResults.length > 0 ? (
|
|
659
880
|
<section className="grid gap-1.5 rounded-md border border-amber-300 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-200">
|
|
@@ -672,6 +893,28 @@ export function BuilderSourceReviewDialog({
|
|
|
672
893
|
))}
|
|
673
894
|
</section>
|
|
674
895
|
) : null}
|
|
896
|
+
{error ? (
|
|
897
|
+
<div
|
|
898
|
+
role="alert"
|
|
899
|
+
aria-live="assertive"
|
|
900
|
+
className="rounded-md border border-destructive/30 bg-destructive/10 p-3 text-xs text-destructive"
|
|
901
|
+
>
|
|
902
|
+
{error}
|
|
903
|
+
</div>
|
|
904
|
+
) : null}
|
|
905
|
+
</div>
|
|
906
|
+
) : pending ? (
|
|
907
|
+
<div className="flex items-center gap-2 rounded-md border border-border p-4 text-sm text-muted-foreground">
|
|
908
|
+
<Spinner className="size-4" />
|
|
909
|
+
{t("database.loadingCompleteBuilderDiff")}
|
|
910
|
+
</div>
|
|
911
|
+
) : error ? (
|
|
912
|
+
<div
|
|
913
|
+
role="alert"
|
|
914
|
+
aria-live="assertive"
|
|
915
|
+
className="rounded-md border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive"
|
|
916
|
+
>
|
|
917
|
+
{error}
|
|
675
918
|
</div>
|
|
676
919
|
) : (
|
|
677
920
|
<div className="rounded-md border border-border p-4 text-sm text-muted-foreground">
|
|
@@ -689,7 +932,7 @@ export function BuilderSourceReviewDialog({
|
|
|
689
932
|
resultToneClass(resultStatus.tone)
|
|
690
933
|
}
|
|
691
934
|
>
|
|
692
|
-
{checked ? (
|
|
935
|
+
{checked || preparedForExecution ? (
|
|
693
936
|
resultStatus.tone === "ok" ? (
|
|
694
937
|
<IconCheck className="size-3.5 shrink-0" />
|
|
695
938
|
) : (
|
|
@@ -697,7 +940,7 @@ export function BuilderSourceReviewDialog({
|
|
|
697
940
|
)
|
|
698
941
|
) : null}
|
|
699
942
|
<span>
|
|
700
|
-
{checked
|
|
943
|
+
{checked || preparedForExecution
|
|
701
944
|
? `${t(`database.${resultStatus.labelKey}`)} · ${intentSummary}`
|
|
702
945
|
: intentSummary}
|
|
703
946
|
</span>
|
|
@@ -713,7 +956,12 @@ export function BuilderSourceReviewDialog({
|
|
|
713
956
|
type="button"
|
|
714
957
|
size="sm"
|
|
715
958
|
disabled={disabled}
|
|
716
|
-
onClick={() =>
|
|
959
|
+
onClick={() =>
|
|
960
|
+
onValidate({
|
|
961
|
+
changeSetIds: selectedChangeSetIds,
|
|
962
|
+
transitions: transitionMap,
|
|
963
|
+
})
|
|
964
|
+
}
|
|
717
965
|
>
|
|
718
966
|
{pending ? (
|
|
719
967
|
<Spinner className="mr-1.5 size-3.5" />
|
|
@@ -725,6 +973,41 @@ export function BuilderSourceReviewDialog({
|
|
|
725
973
|
</div>
|
|
726
974
|
</div>
|
|
727
975
|
</DialogContent>
|
|
976
|
+
<AlertDialog
|
|
977
|
+
open={cancelChangeSetId !== null}
|
|
978
|
+
onOpenChange={(nextOpen) => {
|
|
979
|
+
if (!nextOpen && !pending) setCancelChangeSetId(null);
|
|
980
|
+
}}
|
|
981
|
+
>
|
|
982
|
+
<AlertDialogContent>
|
|
983
|
+
<AlertDialogHeader>
|
|
984
|
+
<AlertDialogTitle>
|
|
985
|
+
{t("database.cancelPreparedUpdateQuestion")}
|
|
986
|
+
</AlertDialogTitle>
|
|
987
|
+
<AlertDialogDescription>
|
|
988
|
+
{t("database.cancelPreparedUpdateDescription")}
|
|
989
|
+
</AlertDialogDescription>
|
|
990
|
+
</AlertDialogHeader>
|
|
991
|
+
<AlertDialogFooter>
|
|
992
|
+
<AlertDialogCancel disabled={pending}>
|
|
993
|
+
{t("database.keepPreparedUpdate")}
|
|
994
|
+
</AlertDialogCancel>
|
|
995
|
+
<AlertDialogAction
|
|
996
|
+
disabled={pending}
|
|
997
|
+
onClick={(event) => {
|
|
998
|
+
event.preventDefault();
|
|
999
|
+
if (!cancelChangeSetId) return;
|
|
1000
|
+
onCancelPrepared?.(cancelChangeSetId);
|
|
1001
|
+
setCancelChangeSetId(null);
|
|
1002
|
+
}}
|
|
1003
|
+
>
|
|
1004
|
+
{pending
|
|
1005
|
+
? t("database.cancellingPreparedUpdate")
|
|
1006
|
+
: t("database.cancelPreparedUpdate")}
|
|
1007
|
+
</AlertDialogAction>
|
|
1008
|
+
</AlertDialogFooter>
|
|
1009
|
+
</AlertDialogContent>
|
|
1010
|
+
</AlertDialog>
|
|
728
1011
|
</Dialog>
|
|
729
1012
|
);
|
|
730
1013
|
}
|