@byline/host-tanstack-start 4.19.0 → 5.0.0
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/admin-shell/collections/edit.d.ts +1 -1
- package/dist/admin-shell/collections/edit.js +151 -31
- package/dist/admin-shell/collections/history.js +4 -2
- package/dist/admin-shell/collections/list.js +30 -6
- package/dist/admin-shell/collections/restore-version-modal.d.ts +3 -1
- package/dist/admin-shell/collections/restore-version-modal.js +9 -4
- package/dist/admin-shell/collections/tree-list.js +16 -6
- package/dist/admin-shell/collections/version-history.d.ts +2 -1
- package/dist/admin-shell/collections/version-history.js +11 -2
- package/dist/admin-shell/document-mutation-notice.d.ts +6 -0
- package/dist/admin-shell/document-mutation-notice.js +53 -0
- package/dist/admin-shell/document-mutation-state.d.ts +22 -0
- package/dist/admin-shell/document-mutation-state.js +71 -0
- package/dist/admin-shell/document-mutation-state.test.d.ts +1 -0
- package/dist/admin-shell/scheduled-publications/list.js +15 -5
- package/dist/admin-shell/singletons/history.js +4 -2
- package/dist/admin-shell/singletons/view.d.ts +1 -1
- package/dist/admin-shell/singletons/view.js +98 -19
- package/dist/integrations/byline-field-services.js +2 -9
- package/dist/routes/create-collection-edit-route.js +1 -1
- package/dist/routes/create-collection-list-route.d.ts +0 -7
- package/dist/routes/create-collection-list-route.js +7 -9
- package/dist/routes/create-singleton-route.d.ts +2 -0
- package/dist/routes/create-singleton-route.js +4 -3
- package/dist/server-fns/collections/copy-to-locale.d.ts +2 -7
- package/dist/server-fns/collections/copy-to-locale.js +4 -2
- package/dist/server-fns/collections/create.d.ts +1 -7
- package/dist/server-fns/collections/create.js +5 -3
- package/dist/server-fns/collections/delete-locale.d.ts +2 -7
- package/dist/server-fns/collections/delete-locale.js +4 -2
- package/dist/server-fns/collections/delete-outcome.d.ts +7 -3
- package/dist/server-fns/collections/delete-outcome.js +4 -0
- package/dist/server-fns/collections/delete.d.ts +2 -7
- package/dist/server-fns/collections/delete.js +5 -3
- package/dist/server-fns/collections/duplicate.d.ts +2 -7
- package/dist/server-fns/collections/duplicate.js +5 -3
- package/dist/server-fns/collections/get.d.ts +1 -7
- package/dist/server-fns/collections/get.js +5 -4
- package/dist/server-fns/collections/list.d.ts +0 -7
- package/dist/server-fns/collections/list.js +11 -3
- package/dist/server-fns/collections/reorder.d.ts +7 -8
- package/dist/server-fns/collections/reorder.js +20 -76
- package/dist/server-fns/collections/restore-version.d.ts +3 -7
- package/dist/server-fns/collections/restore-version.js +5 -2
- package/dist/server-fns/collections/save-outcome.d.ts +1 -0
- package/dist/server-fns/collections/save-outcome.js +1 -0
- package/dist/server-fns/collections/scheduled-publication.d.ts +49 -9
- package/dist/server-fns/collections/scheduled-publication.js +31 -21
- package/dist/server-fns/collections/status.d.ts +8 -7
- package/dist/server-fns/collections/status.js +13 -5
- package/dist/server-fns/collections/tree-mutation.d.ts +7 -6
- package/dist/server-fns/collections/tree-mutation.js +17 -5
- package/dist/server-fns/collections/tree.d.ts +14 -8
- package/dist/server-fns/collections/tree.js +21 -17
- package/dist/server-fns/collections/update.d.ts +12 -11
- package/dist/server-fns/collections/update.js +34 -20
- package/dist/server-fns/collections/upload.d.ts +1 -0
- package/dist/server-fns/collections/upload.js +3 -2
- package/dist/server-fns/document-mutation-errors.d.ts +5 -0
- package/dist/server-fns/document-mutation-errors.js +50 -0
- package/dist/server-fns/editable-revision.d.ts +9 -0
- package/dist/server-fns/editable-revision.js +11 -0
- package/dist/server-fns/singleton-document-read.d.ts +1 -1
- package/dist/server-fns/singleton-document-read.js +7 -3
- package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +6 -8
- package/dist/server-fns/singletons/cancel-scheduled-publish.js +4 -1
- package/dist/server-fns/singletons/change-status.d.ts +2 -7
- package/dist/server-fns/singletons/change-status.js +4 -1
- package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +5 -8
- package/dist/server-fns/singletons/confirm-scheduled-publish.js +4 -2
- package/dist/server-fns/singletons/copy-to-locale.d.ts +2 -7
- package/dist/server-fns/singletons/copy-to-locale.js +4 -2
- package/dist/server-fns/singletons/get.d.ts +4 -4
- package/dist/server-fns/singletons/restore-version.d.ts +2 -7
- package/dist/server-fns/singletons/restore-version.js +6 -3
- package/dist/server-fns/singletons/schedule-publish.d.ts +5 -8
- package/dist/server-fns/singletons/schedule-publish.js +4 -2
- package/dist/server-fns/singletons/unpublish.d.ts +2 -7
- package/dist/server-fns/singletons/unpublish.js +4 -1
- package/dist/server-fns/singletons/update.d.ts +4 -11
- package/dist/server-fns/singletons/update.js +5 -4
- package/dist/server-fns/structural-receipt.d.ts +4 -0
- package/dist/server-fns/structural-receipt.js +31 -0
- package/package.json +11 -11
- package/src/admin-shell/collections/edit.tsx +173 -54
- package/src/admin-shell/collections/history.tsx +11 -2
- package/src/admin-shell/collections/list.tsx +22 -6
- package/src/admin-shell/collections/restore-version-modal.tsx +12 -3
- package/src/admin-shell/collections/save-navigation.test.tsx +156 -8
- package/src/admin-shell/collections/tree-list.tsx +16 -6
- package/src/admin-shell/collections/version-history.tsx +13 -3
- package/src/admin-shell/document-mutation-notice.tsx +45 -0
- package/src/admin-shell/document-mutation-state.test.tsx +146 -0
- package/src/admin-shell/document-mutation-state.ts +95 -0
- package/src/admin-shell/scheduled-publications/list.tsx +16 -6
- package/src/admin-shell/singletons/history.test.tsx +3 -1
- package/src/admin-shell/singletons/history.tsx +10 -2
- package/src/admin-shell/singletons/view.test.tsx +59 -18
- package/src/admin-shell/singletons/view.tsx +110 -29
- package/src/integrations/byline-field-services.ts +2 -3
- package/src/routes/create-collection-edit-route.tsx +1 -0
- package/src/routes/create-collection-list-route.tsx +13 -2
- package/src/routes/create-singleton-route.test.node.ts +10 -2
- package/src/routes/create-singleton-route.tsx +6 -4
- package/src/server-fns/collections/copy-to-locale.ts +41 -36
- package/src/server-fns/collections/create.ts +54 -50
- package/src/server-fns/collections/delete-locale.ts +41 -32
- package/src/server-fns/collections/delete-outcome.test.node.ts +16 -0
- package/src/server-fns/collections/delete-outcome.ts +27 -16
- package/src/server-fns/collections/delete.ts +44 -38
- package/src/server-fns/collections/duplicate.ts +38 -32
- package/src/server-fns/collections/get.ts +8 -4
- package/src/server-fns/collections/list.ts +8 -3
- package/src/server-fns/collections/reorder.ts +32 -124
- package/src/server-fns/collections/restore-version.ts +47 -39
- package/src/server-fns/collections/revision-input.test.node.ts +108 -0
- package/src/server-fns/collections/save-outcome.test.node.ts +3 -0
- package/src/server-fns/collections/save-outcome.ts +2 -0
- package/src/server-fns/collections/scheduled-publication.ts +138 -111
- package/src/server-fns/collections/status.ts +86 -64
- package/src/server-fns/collections/tree-mutation.test.node.ts +87 -29
- package/src/server-fns/collections/tree-mutation.ts +28 -11
- package/src/server-fns/collections/tree.ts +150 -122
- package/src/server-fns/collections/update.ts +110 -86
- package/src/server-fns/collections/upload-singleton.test.node.ts +3 -7
- package/src/server-fns/collections/upload.ts +81 -77
- package/src/server-fns/document-mutation-errors.ts +67 -0
- package/src/server-fns/document-mutation-transport.test.node.ts +211 -0
- package/src/server-fns/editable-revision.test.node.ts +23 -0
- package/src/server-fns/editable-revision.ts +18 -0
- package/src/server-fns/singleton-document-read.ts +7 -3
- package/src/server-fns/singletons/cancel-scheduled-publish.ts +10 -3
- package/src/server-fns/singletons/change-status.ts +10 -3
- package/src/server-fns/singletons/confirm-scheduled-publish.ts +12 -6
- package/src/server-fns/singletons/copy-to-locale.ts +20 -15
- package/src/server-fns/singletons/get.ts +3 -3
- package/src/server-fns/singletons/restore-version.ts +17 -12
- package/src/server-fns/singletons/schedule-publish.ts +18 -7
- package/src/server-fns/singletons/singletons.test.node.ts +63 -25
- package/src/server-fns/singletons/unpublish.ts +10 -3
- package/src/server-fns/singletons/update.ts +21 -16
- package/src/server-fns/structural-receipt.ts +41 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { CollectionAdminConfig, MultiCollectionDefinition } from '@byline/core';
|
|
9
9
|
import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas';
|
|
10
10
|
import type { ContentLocaleOption } from './view-menu.js';
|
|
11
|
-
export declare const EditView: ({ collectionDefinition, adminConfig, initialData, locale, contentLocales, defaultContentLocale, returnSearch, }: {
|
|
11
|
+
export declare const EditView: ({ collectionDefinition, adminConfig, initialData: loadedData, locale, contentLocales, defaultContentLocale, returnSearch, }: {
|
|
12
12
|
collectionDefinition: MultiCollectionDefinition;
|
|
13
13
|
adminConfig?: CollectionAdminConfig;
|
|
14
14
|
initialData: AnyCollectionSchemaTypes['UpdateType'];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { FormRenderer } from "@byline/admin/react";
|
|
4
4
|
import { getDefaultStatus, getWorkflow, getWorkflowStatuses, validateStatusTransition } from "@byline/core";
|
|
@@ -6,11 +6,14 @@ import { useTranslation } from "@byline/i18n/react";
|
|
|
6
6
|
import { Container, Section, useToastManager } from "@byline/ui/react";
|
|
7
7
|
import { getAdminRoutePath } from "../../routes/admin-path.js";
|
|
8
8
|
import { clearListReturnState } from "../../routes/list-return-storage.js";
|
|
9
|
-
import { cancelCollectionDocumentScheduledPublish, confirmCollectionDocumentScheduledPublish, copyDocumentToLocale, deleteDocument, deleteDocumentLocale, duplicateCollectionDocument, hasCommittedDocumentHookFailure, hasDeleteSideEffectFailures, scheduleCollectionDocumentPublish, unpublishDocument,
|
|
9
|
+
import { cancelCollectionDocumentScheduledPublish, confirmCollectionDocumentScheduledPublish, copyDocumentToLocale, deleteDocument, deleteDocumentLocale, duplicateCollectionDocument, hasCommittedDocumentHookFailure, hasDeleteSideEffectFailures, scheduleCollectionDocumentPublish, unpublishDocument, updateCollectionDocumentWithPatches, updateDocumentStatus } from "../../server-fns/collections/index.js";
|
|
10
10
|
import { useNavigate } from "../chrome/loose-router.js";
|
|
11
|
+
import { useDocumentMutationState } from "../document-mutation-state.js";
|
|
11
12
|
import { useTanStackNavigationGuard } from "./tanstack-navigation-guard.js";
|
|
12
13
|
import { ViewMenu } from "./view-menu.js";
|
|
13
|
-
const EditView = ({ collectionDefinition, adminConfig, initialData, locale, contentLocales, defaultContentLocale, returnSearch })=>{
|
|
14
|
+
const EditView = ({ collectionDefinition, adminConfig, initialData: loadedData, locale, contentLocales, defaultContentLocale, returnSearch })=>{
|
|
15
|
+
const mutation = useDocumentMutationState(loadedData);
|
|
16
|
+
const initialData = mutation.document ?? loadedData;
|
|
14
17
|
const toastManager = useToastManager();
|
|
15
18
|
const { t } = useTranslation('byline-admin');
|
|
16
19
|
const [_editState, setEditState] = useState({
|
|
@@ -75,13 +78,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
75
78
|
};
|
|
76
79
|
const handleStatusChange = async (status)=>{
|
|
77
80
|
try {
|
|
78
|
-
|
|
81
|
+
mutation.assertWritable();
|
|
82
|
+
const result = await updateDocumentStatus({
|
|
79
83
|
data: {
|
|
84
|
+
expectedRevision: mutation.expectedRevision(),
|
|
80
85
|
collection: path,
|
|
81
86
|
id: String(initialData.id),
|
|
82
87
|
status
|
|
83
88
|
}
|
|
84
89
|
});
|
|
90
|
+
mutation.adopt(result);
|
|
85
91
|
const description = t("collections.edit.statusChangedDescription", {
|
|
86
92
|
status
|
|
87
93
|
});
|
|
@@ -112,9 +118,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
112
118
|
})
|
|
113
119
|
});
|
|
114
120
|
} catch (err) {
|
|
121
|
+
const handled = mutation.report(err);
|
|
122
|
+
if ('blocked' === handled) throw err;
|
|
123
|
+
if ('committed' === handled) {
|
|
124
|
+
notifyCommittedHookFailure();
|
|
125
|
+
reloadDocument();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
115
128
|
console.error('Status change error:', err);
|
|
116
129
|
const description = t("collections.edit.statusChangeFailedDescription", {
|
|
117
|
-
message:
|
|
130
|
+
message: t('documentConcurrency.failed')
|
|
118
131
|
});
|
|
119
132
|
toastManager.add({
|
|
120
133
|
title: t('collections.edit.statusUpdateTitle', {
|
|
@@ -148,14 +161,17 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
148
161
|
};
|
|
149
162
|
const handleSchedulePublication = async ({ publishAt })=>{
|
|
150
163
|
try {
|
|
151
|
-
|
|
164
|
+
mutation.assertWritable();
|
|
165
|
+
const result = await scheduleCollectionDocumentPublish({
|
|
152
166
|
data: {
|
|
167
|
+
expectedRevision: mutation.expectedRevision(),
|
|
153
168
|
collection: path,
|
|
154
169
|
id: String(initialData.id),
|
|
155
170
|
publishAt,
|
|
156
171
|
expectedVersionId: String(initialData.versionId)
|
|
157
172
|
}
|
|
158
173
|
});
|
|
174
|
+
mutation.adopt(result);
|
|
159
175
|
toastManager.add({
|
|
160
176
|
title: t('scheduledPublication.toast.scheduledTitle'),
|
|
161
177
|
description: t("scheduledPublication.toast.scheduledDescription"),
|
|
@@ -168,9 +184,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
168
184
|
});
|
|
169
185
|
reloadDocument();
|
|
170
186
|
} catch (err) {
|
|
187
|
+
const handled = mutation.report(err);
|
|
188
|
+
if ('blocked' === handled) throw err;
|
|
189
|
+
if ('committed' === handled) {
|
|
190
|
+
notifyCommittedHookFailure();
|
|
191
|
+
reloadDocument();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
171
194
|
toastManager.add({
|
|
172
195
|
title: t('scheduledPublication.toast.failedTitle'),
|
|
173
|
-
description:
|
|
196
|
+
description: t('documentConcurrency.failed'),
|
|
174
197
|
data: {
|
|
175
198
|
intent: 'danger',
|
|
176
199
|
iconType: 'danger',
|
|
@@ -183,13 +206,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
183
206
|
};
|
|
184
207
|
const handleConfirmScheduledPublication = async ()=>{
|
|
185
208
|
try {
|
|
186
|
-
|
|
209
|
+
mutation.assertWritable();
|
|
210
|
+
const result = await confirmCollectionDocumentScheduledPublish({
|
|
187
211
|
data: {
|
|
212
|
+
expectedRevision: mutation.expectedRevision(),
|
|
188
213
|
collection: path,
|
|
189
214
|
id: String(initialData.id),
|
|
190
215
|
expectedVersionId: String(initialData.versionId)
|
|
191
216
|
}
|
|
192
217
|
});
|
|
218
|
+
mutation.adopt(result);
|
|
193
219
|
toastManager.add({
|
|
194
220
|
title: t('scheduledPublication.toast.confirmedTitle'),
|
|
195
221
|
description: t("scheduledPublication.toast.confirmedDescription"),
|
|
@@ -202,9 +228,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
202
228
|
});
|
|
203
229
|
reloadDocument();
|
|
204
230
|
} catch (err) {
|
|
231
|
+
const handled = mutation.report(err);
|
|
232
|
+
if ('blocked' === handled) throw err;
|
|
233
|
+
if ('committed' === handled) {
|
|
234
|
+
notifyCommittedHookFailure();
|
|
235
|
+
reloadDocument();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
205
238
|
toastManager.add({
|
|
206
239
|
title: t('scheduledPublication.toast.failedTitle'),
|
|
207
|
-
description:
|
|
240
|
+
description: t('documentConcurrency.failed'),
|
|
208
241
|
data: {
|
|
209
242
|
intent: 'danger',
|
|
210
243
|
iconType: 'danger',
|
|
@@ -217,12 +250,18 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
217
250
|
};
|
|
218
251
|
const handleCancelScheduledPublication = async ()=>{
|
|
219
252
|
try {
|
|
253
|
+
mutation.assertWritable();
|
|
220
254
|
const result = await cancelCollectionDocumentScheduledPublish({
|
|
221
255
|
data: {
|
|
256
|
+
expectedRevision: mutation.expectedRevision(),
|
|
222
257
|
collection: path,
|
|
223
258
|
id: String(initialData.id)
|
|
224
259
|
}
|
|
225
260
|
});
|
|
261
|
+
mutation.adopt({
|
|
262
|
+
...result,
|
|
263
|
+
documentId: String(initialData.id)
|
|
264
|
+
});
|
|
226
265
|
const cancelled = 'cancelled' === result.status;
|
|
227
266
|
toastManager.add({
|
|
228
267
|
title: cancelled ? t('scheduledPublication.toast.cancelledTitle') : t('scheduledPublication.toast.cancelLostTitle'),
|
|
@@ -236,9 +275,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
236
275
|
});
|
|
237
276
|
reloadDocument();
|
|
238
277
|
} catch (err) {
|
|
278
|
+
const handled = mutation.report(err);
|
|
279
|
+
if ('blocked' === handled) throw err;
|
|
280
|
+
if ('committed' === handled) {
|
|
281
|
+
notifyCommittedHookFailure();
|
|
282
|
+
reloadDocument();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
239
285
|
toastManager.add({
|
|
240
286
|
title: t('scheduledPublication.toast.failedTitle'),
|
|
241
|
-
description:
|
|
287
|
+
description: t('documentConcurrency.failed'),
|
|
242
288
|
data: {
|
|
243
289
|
intent: 'danger',
|
|
244
290
|
iconType: 'danger',
|
|
@@ -253,12 +299,15 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
253
299
|
const restoreWarnings = initialData?._restoreWarnings;
|
|
254
300
|
const handleUnpublish = async ()=>{
|
|
255
301
|
try {
|
|
256
|
-
|
|
302
|
+
mutation.assertWritable();
|
|
303
|
+
const result = await unpublishDocument({
|
|
257
304
|
data: {
|
|
305
|
+
expectedRevision: mutation.expectedRevision(),
|
|
258
306
|
collection: path,
|
|
259
307
|
id: String(initialData.id)
|
|
260
308
|
}
|
|
261
309
|
});
|
|
310
|
+
mutation.adopt(result);
|
|
262
311
|
const description = t("collections.edit.unpublishedDescription");
|
|
263
312
|
toastManager.add({
|
|
264
313
|
title: t('collections.edit.unpublishTitle', {
|
|
@@ -287,9 +336,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
287
336
|
})
|
|
288
337
|
});
|
|
289
338
|
} catch (err) {
|
|
339
|
+
const handled = mutation.report(err);
|
|
340
|
+
if ('blocked' === handled) throw err;
|
|
341
|
+
if ('committed' === handled) {
|
|
342
|
+
notifyCommittedHookFailure();
|
|
343
|
+
reloadDocument();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
290
346
|
console.error('Unpublish error:', err);
|
|
291
347
|
const description = t("collections.edit.unpublishFailedDescription", {
|
|
292
|
-
message:
|
|
348
|
+
message: t('documentConcurrency.failed')
|
|
293
349
|
});
|
|
294
350
|
toastManager.add({
|
|
295
351
|
title: t('collections.edit.unpublishTitle', {
|
|
@@ -311,8 +367,10 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
311
367
|
};
|
|
312
368
|
const handleDuplicate = async ()=>{
|
|
313
369
|
try {
|
|
370
|
+
mutation.assertWritable();
|
|
314
371
|
const result = await duplicateCollectionDocument({
|
|
315
372
|
data: {
|
|
373
|
+
expectedRevision: mutation.expectedRevision(),
|
|
316
374
|
collection: path,
|
|
317
375
|
id: String(initialData.id)
|
|
318
376
|
}
|
|
@@ -365,9 +423,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
365
423
|
})
|
|
366
424
|
});
|
|
367
425
|
} catch (err) {
|
|
426
|
+
const handled = mutation.report(err);
|
|
427
|
+
if ('blocked' === handled) throw err;
|
|
428
|
+
if ('committed' === handled) {
|
|
429
|
+
notifyCommittedHookFailure();
|
|
430
|
+
reloadDocument();
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
368
433
|
console.error('Duplicate error:', err);
|
|
369
434
|
const description = t("collections.edit.duplicateFailedDescription", {
|
|
370
|
-
message:
|
|
435
|
+
message: t('documentConcurrency.failed')
|
|
371
436
|
});
|
|
372
437
|
toastManager.add({
|
|
373
438
|
title: t('collections.edit.duplicateTitle', {
|
|
@@ -389,8 +454,10 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
389
454
|
};
|
|
390
455
|
const handleCopyToLocale = async ({ targetLocale, overwrite })=>{
|
|
391
456
|
try {
|
|
457
|
+
mutation.assertWritable();
|
|
392
458
|
const result = await copyDocumentToLocale({
|
|
393
459
|
data: {
|
|
460
|
+
expectedRevision: mutation.expectedRevision(),
|
|
394
461
|
collection: path,
|
|
395
462
|
id: String(initialData.id),
|
|
396
463
|
sourceLocale: locale ?? defaultContentLocale,
|
|
@@ -398,6 +465,7 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
398
465
|
overwrite
|
|
399
466
|
}
|
|
400
467
|
});
|
|
468
|
+
mutation.adopt(result);
|
|
401
469
|
if (hasCommittedDocumentHookFailure(result)) {
|
|
402
470
|
notifyCommittedHookFailure();
|
|
403
471
|
notifyScheduleSuspended();
|
|
@@ -456,9 +524,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
456
524
|
})
|
|
457
525
|
});
|
|
458
526
|
} catch (err) {
|
|
527
|
+
const handled = mutation.report(err);
|
|
528
|
+
if ('blocked' === handled) throw err;
|
|
529
|
+
if ('committed' === handled) {
|
|
530
|
+
notifyCommittedHookFailure();
|
|
531
|
+
reloadDocument();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
459
534
|
console.error('Copy to locale error:', err);
|
|
460
535
|
const description = t("collections.edit.copyFailedDescription", {
|
|
461
|
-
message:
|
|
536
|
+
message: t('documentConcurrency.failed')
|
|
462
537
|
});
|
|
463
538
|
toastManager.add({
|
|
464
539
|
title: t('collections.edit.copyToLocaleTitle', {
|
|
@@ -480,13 +555,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
480
555
|
};
|
|
481
556
|
const handleDeleteLocale = async ({ targetLocale })=>{
|
|
482
557
|
try {
|
|
558
|
+
mutation.assertWritable();
|
|
483
559
|
const result = await deleteDocumentLocale({
|
|
484
560
|
data: {
|
|
561
|
+
expectedRevision: mutation.expectedRevision(),
|
|
485
562
|
collection: path,
|
|
486
563
|
id: String(initialData.id),
|
|
487
564
|
locale: targetLocale
|
|
488
565
|
}
|
|
489
566
|
});
|
|
567
|
+
mutation.adopt(result);
|
|
490
568
|
if (hasCommittedDocumentHookFailure(result)) {
|
|
491
569
|
notifyCommittedHookFailure();
|
|
492
570
|
notifyScheduleSuspended();
|
|
@@ -537,9 +615,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
537
615
|
})
|
|
538
616
|
});
|
|
539
617
|
} catch (err) {
|
|
618
|
+
const handled = mutation.report(err);
|
|
619
|
+
if ('blocked' === handled) throw err;
|
|
620
|
+
if ('committed' === handled) {
|
|
621
|
+
notifyCommittedHookFailure();
|
|
622
|
+
reloadDocument();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
540
625
|
console.error('Delete locale error:', err);
|
|
541
626
|
const description = t("collections.edit.deleteLocaleFailedDescription", {
|
|
542
|
-
message:
|
|
627
|
+
message: t('documentConcurrency.failed')
|
|
543
628
|
});
|
|
544
629
|
toastManager.add({
|
|
545
630
|
title: t('collections.edit.deleteLocaleTitle', {
|
|
@@ -561,12 +646,34 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
561
646
|
};
|
|
562
647
|
const handleDelete = async ()=>{
|
|
563
648
|
try {
|
|
649
|
+
mutation.assertWritable();
|
|
564
650
|
const result = await deleteDocument({
|
|
565
651
|
data: {
|
|
652
|
+
expectedRevision: mutation.expectedRevision(),
|
|
566
653
|
collection: path,
|
|
567
654
|
id: String(initialData.id)
|
|
568
655
|
}
|
|
569
656
|
});
|
|
657
|
+
if (result.scheduledPublicationsNeedReconfirmation) toastManager.add({
|
|
658
|
+
title: t('documentConcurrency.schedulesTitle'),
|
|
659
|
+
description: /*#__PURE__*/ jsxs(Fragment, {
|
|
660
|
+
children: [
|
|
661
|
+
/*#__PURE__*/ jsx("p", {
|
|
662
|
+
children: t('documentConcurrency.schedules')
|
|
663
|
+
}),
|
|
664
|
+
/*#__PURE__*/ jsx("a", {
|
|
665
|
+
href: getAdminRoutePath('scheduled-publications'),
|
|
666
|
+
children: t('documentConcurrency.reviewSchedules')
|
|
667
|
+
})
|
|
668
|
+
]
|
|
669
|
+
}),
|
|
670
|
+
data: {
|
|
671
|
+
intent: 'warning',
|
|
672
|
+
iconType: 'warning',
|
|
673
|
+
icon: true,
|
|
674
|
+
close: true
|
|
675
|
+
}
|
|
676
|
+
});
|
|
570
677
|
const hasSideEffectFailures = hasDeleteSideEffectFailures(result);
|
|
571
678
|
const description = hasSideEffectFailures ? t("collections.edit.deletedWithWarningsDescription", {
|
|
572
679
|
label: singular
|
|
@@ -598,9 +705,16 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
598
705
|
search: returnSearch
|
|
599
706
|
});
|
|
600
707
|
} catch (err) {
|
|
708
|
+
const handled = mutation.report(err);
|
|
709
|
+
if ('blocked' === handled) throw err;
|
|
710
|
+
if ('committed' === handled) {
|
|
711
|
+
notifyCommittedHookFailure();
|
|
712
|
+
reloadDocument();
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
601
715
|
console.error('Delete error:', err);
|
|
602
716
|
const description = t("collections.edit.deleteFailedDescription", {
|
|
603
|
-
message:
|
|
717
|
+
message: t('documentConcurrency.failed')
|
|
604
718
|
});
|
|
605
719
|
toastManager.add({
|
|
606
720
|
title: t('collections.edit.deleteTitle', {
|
|
@@ -622,11 +736,13 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
622
736
|
};
|
|
623
737
|
const handleSubmit = async ({ data: _data, patches, contentDirty, pathDirty, systemPath, availableLocalesDirty, systemAvailableLocales })=>{
|
|
624
738
|
try {
|
|
625
|
-
|
|
626
|
-
|
|
739
|
+
mutation.assertWritable();
|
|
740
|
+
const result = await updateCollectionDocumentWithPatches({
|
|
627
741
|
data: {
|
|
628
742
|
collection: path,
|
|
629
743
|
id: String(initialData.id),
|
|
744
|
+
expectedRevision: mutation.expectedRevision(),
|
|
745
|
+
patches: contentDirty ? patches : [],
|
|
630
746
|
locale,
|
|
631
747
|
...pathDirty ? {
|
|
632
748
|
path: systemPath ?? null
|
|
@@ -636,19 +752,9 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
636
752
|
} : {}
|
|
637
753
|
}
|
|
638
754
|
});
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
collection: path,
|
|
643
|
-
id: String(initialData.id),
|
|
644
|
-
patches,
|
|
645
|
-
versionId: initialData.versionId,
|
|
646
|
-
locale
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
hookFailed = hasCommittedDocumentHookFailure(result);
|
|
650
|
-
}
|
|
651
|
-
if (contentDirty && scheduledPublication?.state === 'armed') notifyScheduleSuspended();
|
|
755
|
+
mutation.adopt(result);
|
|
756
|
+
const hookFailed = hasCommittedDocumentHookFailure(result);
|
|
757
|
+
if ((contentDirty || pathDirty || availableLocalesDirty) && scheduledPublication?.state === 'armed') notifyScheduleSuspended();
|
|
652
758
|
const description = hookFailed ? t("collections.save.hookFailedDescription") : t("collections.edit.updatedDescription", {
|
|
653
759
|
label: singularLower
|
|
654
760
|
});
|
|
@@ -680,6 +786,13 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
680
786
|
ignoreBlocker: true
|
|
681
787
|
});
|
|
682
788
|
} catch (err) {
|
|
789
|
+
const handled = mutation.report(err);
|
|
790
|
+
if ('blocked' === handled) throw err;
|
|
791
|
+
if ('committed' === handled) {
|
|
792
|
+
notifyCommittedHookFailure();
|
|
793
|
+
reloadDocument();
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
683
796
|
console.error('Network error:', err);
|
|
684
797
|
const description = t("collections.edit.updateFailedDescription", {
|
|
685
798
|
label: singularLower
|
|
@@ -706,6 +819,13 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
706
819
|
return /*#__PURE__*/ jsx(Section, {
|
|
707
820
|
children: /*#__PURE__*/ jsx(Container, {
|
|
708
821
|
children: /*#__PURE__*/ jsx(FormRenderer, {
|
|
822
|
+
mutationIssue: mutation.issue,
|
|
823
|
+
mutationsBlocked: mutation.blocked,
|
|
824
|
+
observedRevision: mutation.revision,
|
|
825
|
+
onMutationError: mutation.report,
|
|
826
|
+
onTreeMutationCommitted: mutation.adopt,
|
|
827
|
+
scheduledPublicationsNeedReconfirmation: mutation.scheduleNotice,
|
|
828
|
+
scheduledPublicationsHref: getAdminRoutePath('scheduled-publications'),
|
|
709
829
|
mode: "edit",
|
|
710
830
|
fields: fields,
|
|
711
831
|
onSubmit: handleSubmit,
|
|
@@ -91,7 +91,7 @@ const HistoryView = ({ collectionDefinition, adminConfig, data, auditLog, workfl
|
|
|
91
91
|
];
|
|
92
92
|
}),
|
|
93
93
|
auditColSpan: columns.length + (hasRestoreColumn ? 1 : 0),
|
|
94
|
-
renderCells: ({ document, versionId, versionNumber, currentVersionId, restoreStatusLabel, openCompare, openRestore })=>columns.flatMap((column)=>{
|
|
94
|
+
renderCells: ({ document, versionId, versionNumber, currentVersionId, restoreStatusLabel, openCompare, mutationsBlocked, openRestore })=>columns.flatMap((column)=>{
|
|
95
95
|
const value = getColumnValue(document, String(column.fieldName));
|
|
96
96
|
const dataCell = /*#__PURE__*/ jsx(Table.Cell, {
|
|
97
97
|
className: clsx({
|
|
@@ -126,6 +126,7 @@ const HistoryView = ({ collectionDefinition, adminConfig, data, auditLog, workfl
|
|
|
126
126
|
variant: "outlined",
|
|
127
127
|
size: "xs",
|
|
128
128
|
intent: "noeffect",
|
|
129
|
+
disabled: mutationsBlocked,
|
|
129
130
|
onClick: openRestore,
|
|
130
131
|
className: clsx('byline-coll-history-restore-button', history_module.restoreButton),
|
|
131
132
|
title: t('collections.history.restoreButtonTitle', {
|
|
@@ -209,8 +210,9 @@ const HistoryView = ({ collectionDefinition, adminConfig, data, auditLog, workfl
|
|
|
209
210
|
rowPresentation: rowPresentation,
|
|
210
211
|
loadHistoricalVersion: getCollectionDocumentVersion,
|
|
211
212
|
onPageSizeChange: handlePageSizeChange,
|
|
212
|
-
restoreVersion: (versionId)=>restoreDocumentVersion({
|
|
213
|
+
restoreVersion: (versionId, expectedRevision)=>restoreDocumentVersion({
|
|
213
214
|
data: {
|
|
215
|
+
expectedRevision,
|
|
214
216
|
collection,
|
|
215
217
|
id,
|
|
216
218
|
versionId
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DocumentMutationNotice } from "../document-mutation-notice.js";
|
|
3
|
+
import { useDocumentMutationState } from "../document-mutation-state.js";
|
|
2
4
|
import { useEffect, useMemo, useState } from "react";
|
|
3
5
|
import { useRouter, useRouterState } from "@tanstack/react-router";
|
|
4
6
|
import { StatusBadge, renderFormatted } from "@byline/admin/react";
|
|
@@ -77,6 +79,7 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
77
79
|
const navigate = useNavigate();
|
|
78
80
|
const router = useRouter();
|
|
79
81
|
const toastManager = useToastManager();
|
|
82
|
+
const mutation = useDocumentMutationState(void 0);
|
|
80
83
|
const { t } = useTranslation('byline-admin');
|
|
81
84
|
const location = useRouterState({
|
|
82
85
|
select: (s)=>s.location
|
|
@@ -84,14 +87,15 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
84
87
|
const [localDocs, setLocalDocs] = useState(data.docs);
|
|
85
88
|
const [isReordering, setIsReordering] = useState(false);
|
|
86
89
|
useEffect(()=>{
|
|
87
|
-
if (!isReordering) setLocalDocs(data.docs);
|
|
90
|
+
if (!isReordering && !mutation.blocked) setLocalDocs(data.docs);
|
|
88
91
|
}, [
|
|
89
92
|
data.docs,
|
|
90
|
-
isReordering
|
|
93
|
+
isReordering,
|
|
94
|
+
mutation.blocked
|
|
91
95
|
]);
|
|
92
96
|
const searchParams = location.search;
|
|
93
97
|
const isCanonicalView = !searchParams.order && !searchParams.desc && !searchParams.query && !searchParams.status;
|
|
94
|
-
const dragEnabled = orderable && isCanonicalView && !!onReorder;
|
|
98
|
+
const dragEnabled = orderable && isCanonicalView && !!onReorder && !mutation.blocked;
|
|
95
99
|
const activeOrder = searchParams.order ?? data?.meta.order;
|
|
96
100
|
const activeDesc = null != searchParams.order ? searchParams.desc : data?.meta.desc;
|
|
97
101
|
const sensors = useSensors(useSensor(PointerSensor, {
|
|
@@ -103,7 +107,7 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
103
107
|
}));
|
|
104
108
|
const handleDragEnd = async (event)=>{
|
|
105
109
|
const { active, over } = event;
|
|
106
|
-
if (!over || active.id === over.id || !onReorder) return;
|
|
110
|
+
if (mutation.blocked || isReordering || !over || active.id === over.id || !onReorder) return;
|
|
107
111
|
const oldIndex = localDocs.findIndex((d)=>d.id === active.id);
|
|
108
112
|
const newIndex = localDocs.findIndex((d)=>d.id === over.id);
|
|
109
113
|
if (oldIndex < 0 || newIndex < 0 || oldIndex === newIndex) return;
|
|
@@ -119,14 +123,30 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
119
123
|
const before = next[newIndex - 1]?.id ?? null;
|
|
120
124
|
const after = next[newIndex + 1]?.id ?? null;
|
|
121
125
|
try {
|
|
122
|
-
|
|
126
|
+
mutation.assertWritable();
|
|
127
|
+
const receipt = await onReorder({
|
|
123
128
|
documentId: String(active.id),
|
|
124
129
|
beforeDocumentId: before,
|
|
125
130
|
afterDocumentId: after
|
|
126
131
|
});
|
|
127
|
-
|
|
132
|
+
mutation.reportStructure(receipt);
|
|
133
|
+
try {
|
|
134
|
+
await router.invalidate();
|
|
135
|
+
} catch {
|
|
136
|
+
toastManager.add({
|
|
137
|
+
title: t('collections.list.treeRefreshFailedToast'),
|
|
138
|
+
description: t("collections.list.treeRefreshFailedDescription"),
|
|
139
|
+
data: {
|
|
140
|
+
intent: 'warning',
|
|
141
|
+
iconType: 'warning',
|
|
142
|
+
icon: true,
|
|
143
|
+
close: true
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
128
147
|
} catch (_err) {
|
|
129
148
|
setLocalDocs(previousDocs);
|
|
149
|
+
if (mutation.report(_err)) return;
|
|
130
150
|
toastManager.add({
|
|
131
151
|
title: t('collections.list.reorderFailedToast'),
|
|
132
152
|
description: t("collections.list.reorderFailedDescription"),
|
|
@@ -241,6 +261,10 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
241
261
|
return /*#__PURE__*/ jsx(Section, {
|
|
242
262
|
children: /*#__PURE__*/ jsxs(Container, {
|
|
243
263
|
children: [
|
|
264
|
+
/*#__PURE__*/ jsx(DocumentMutationNotice, {
|
|
265
|
+
issue: mutation.issue,
|
|
266
|
+
scheduleNotice: mutation.scheduleNotice
|
|
267
|
+
}),
|
|
244
268
|
/*#__PURE__*/ jsxs("div", {
|
|
245
269
|
className: clsx('byline-coll-list-head', list_module.head),
|
|
246
270
|
children: [
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
interface RestoreVersionModalProps {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
onMutationError?: (error: unknown) => unknown;
|
|
2
4
|
versionLabel: string;
|
|
3
5
|
versionNumber: number;
|
|
4
6
|
restoreStatusLabel: string;
|
|
@@ -6,5 +8,5 @@ interface RestoreVersionModalProps {
|
|
|
6
8
|
restoreVersion: () => Promise<unknown>;
|
|
7
9
|
onRestoreComplete: () => void | Promise<void>;
|
|
8
10
|
}
|
|
9
|
-
export declare function RestoreVersionModal({ versionLabel, versionNumber, restoreStatusLabel, onClose, restoreVersion, onRestoreComplete, }: RestoreVersionModalProps): import("react").JSX.Element;
|
|
11
|
+
export declare function RestoreVersionModal({ disabled, onMutationError, versionLabel, versionNumber, restoreStatusLabel, onClose, restoreVersion, onRestoreComplete, }: RestoreVersionModalProps): import("react").JSX.Element;
|
|
10
12
|
export {};
|
|
@@ -6,13 +6,13 @@ import { Alert, Button, LoaderEllipsis, Modal, useToastManager } from "@byline/u
|
|
|
6
6
|
import clsx from "clsx";
|
|
7
7
|
import { hasCommittedDocumentHookFailure } from "../../server-fns/collections/save-outcome.js";
|
|
8
8
|
import restore_version_modal_module from "./restore-version-modal.module.js";
|
|
9
|
-
function RestoreVersionModal({ versionLabel, versionNumber, restoreStatusLabel, onClose, restoreVersion, onRestoreComplete }) {
|
|
9
|
+
function RestoreVersionModal({ disabled = false, onMutationError, versionLabel, versionNumber, restoreStatusLabel, onClose, restoreVersion, onRestoreComplete }) {
|
|
10
10
|
const { t } = useTranslation('byline-admin');
|
|
11
11
|
const toastManager = useToastManager();
|
|
12
12
|
const [error, setError] = useState(null);
|
|
13
13
|
const [pending, setPending] = useState(false);
|
|
14
14
|
async function handleRestore() {
|
|
15
|
-
if (pending) return;
|
|
15
|
+
if (pending || disabled) return;
|
|
16
16
|
setPending(true);
|
|
17
17
|
setError(null);
|
|
18
18
|
try {
|
|
@@ -30,6 +30,11 @@ function RestoreVersionModal({ versionLabel, versionNumber, restoreStatusLabel,
|
|
|
30
30
|
onClose();
|
|
31
31
|
await onRestoreComplete();
|
|
32
32
|
} catch (err) {
|
|
33
|
+
if (onMutationError?.(err)) {
|
|
34
|
+
setPending(false);
|
|
35
|
+
onClose();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
33
38
|
const code = getErrorCode(err);
|
|
34
39
|
if ('ERR_INVALID_TRANSITION' === code) setError(t('collections.restore.errors.alreadyCurrent'));
|
|
35
40
|
else if ('ERR_NOT_FOUND' === code) setError(t('collections.restore.errors.notFound'));
|
|
@@ -97,7 +102,7 @@ function RestoreVersionModal({ versionLabel, versionNumber, restoreStatusLabel,
|
|
|
97
102
|
intent: "noeffect",
|
|
98
103
|
size: "sm",
|
|
99
104
|
onClick: onClose,
|
|
100
|
-
disabled: pending,
|
|
105
|
+
disabled: pending || disabled,
|
|
101
106
|
className: clsx('byline-coll-restore-button', restore_version_modal_module.button),
|
|
102
107
|
children: t('common.actions.cancel')
|
|
103
108
|
}),
|
|
@@ -108,7 +113,7 @@ function RestoreVersionModal({ versionLabel, versionNumber, restoreStatusLabel,
|
|
|
108
113
|
style: {
|
|
109
114
|
minWidth: '80px'
|
|
110
115
|
},
|
|
111
|
-
disabled: pending,
|
|
116
|
+
disabled: pending || disabled,
|
|
112
117
|
className: clsx('byline-coll-restore-button', restore_version_modal_module.button),
|
|
113
118
|
children: true === pending ? /*#__PURE__*/ jsx(LoaderEllipsis, {
|
|
114
119
|
size: 42
|