@trieb.work/payload-plugin-backup-mongodb 0.1.1

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.
Files changed (165) hide show
  1. package/README.md +319 -0
  2. package/dist/backup-dashboard.css +1132 -0
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.js +3 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/collections/BackupSettings.d.ts +6 -0
  7. package/dist/collections/BackupSettings.js +91 -0
  8. package/dist/collections/BackupSettings.js.map +1 -0
  9. package/dist/collections/BackupTasks.d.ts +2 -0
  10. package/dist/collections/BackupTasks.js +56 -0
  11. package/dist/collections/BackupTasks.js.map +1 -0
  12. package/dist/components/BackupDashboard/BackupItemActions.client.d.ts +7 -0
  13. package/dist/components/BackupDashboard/BackupItemActions.client.js +90 -0
  14. package/dist/components/BackupDashboard/BackupItemActions.client.js.map +1 -0
  15. package/dist/components/BackupDashboard/BackupListCollapsible.client.d.ts +16 -0
  16. package/dist/components/BackupDashboard/BackupListCollapsible.client.js +597 -0
  17. package/dist/components/BackupDashboard/BackupListCollapsible.client.js.map +1 -0
  18. package/dist/components/BackupDashboard/BackupSettingsModal.client.d.ts +2 -0
  19. package/dist/components/BackupDashboard/BackupSettingsModal.client.js +939 -0
  20. package/dist/components/BackupDashboard/BackupSettingsModal.client.js.map +1 -0
  21. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.d.ts +18 -0
  22. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js +267 -0
  23. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js.map +1 -0
  24. package/dist/components/BackupDashboard/ManualBackupDialog.client.d.ts +1 -0
  25. package/dist/components/BackupDashboard/ManualBackupDialog.client.js +276 -0
  26. package/dist/components/BackupDashboard/ManualBackupDialog.client.js.map +1 -0
  27. package/dist/components/BackupDashboard/RestoreBackupDialog.client.d.ts +6 -0
  28. package/dist/components/BackupDashboard/RestoreBackupDialog.client.js +551 -0
  29. package/dist/components/BackupDashboard/RestoreBackupDialog.client.js.map +1 -0
  30. package/dist/components/BackupDashboard/TaskActionButton.client.d.ts +29 -0
  31. package/dist/components/BackupDashboard/TaskActionButton.client.js +240 -0
  32. package/dist/components/BackupDashboard/TaskActionButton.client.js.map +1 -0
  33. package/dist/components/BackupDashboard/backupDashboardInlineCss.d.ts +1 -0
  34. package/dist/components/BackupDashboard/backupDashboardInlineCss.js +3 -0
  35. package/dist/components/BackupDashboard/backupDashboardInlineCss.js.map +1 -0
  36. package/dist/components/BackupDashboard/index.client.d.ts +8 -0
  37. package/dist/components/BackupDashboard/index.client.js +9 -0
  38. package/dist/components/BackupDashboard/index.client.js.map +1 -0
  39. package/dist/components/BackupDashboard/index.d.ts +8 -0
  40. package/dist/components/BackupDashboard/index.js +196 -0
  41. package/dist/components/BackupDashboard/index.js.map +1 -0
  42. package/dist/core/archive.d.ts +8 -0
  43. package/dist/core/archive.js +47 -0
  44. package/dist/core/archive.js.map +1 -0
  45. package/dist/core/backup.d.ts +41 -0
  46. package/dist/core/backup.js +188 -0
  47. package/dist/core/backup.js.map +1 -0
  48. package/dist/core/backupBlobIO.d.ts +54 -0
  49. package/dist/core/backupBlobIO.js +179 -0
  50. package/dist/core/backupBlobIO.js.map +1 -0
  51. package/dist/core/backupBlobTransfer.d.ts +29 -0
  52. package/dist/core/backupBlobTransfer.js +85 -0
  53. package/dist/core/backupBlobTransfer.js.map +1 -0
  54. package/dist/core/backupSettings.d.ts +35 -0
  55. package/dist/core/backupSettings.js +95 -0
  56. package/dist/core/backupSettings.js.map +1 -0
  57. package/dist/core/backupSourcePreview.d.ts +10 -0
  58. package/dist/core/backupSourcePreview.js +33 -0
  59. package/dist/core/backupSourcePreview.js.map +1 -0
  60. package/dist/core/blobTokenValidate.d.ts +14 -0
  61. package/dist/core/blobTokenValidate.js +75 -0
  62. package/dist/core/blobTokenValidate.js.map +1 -0
  63. package/dist/core/db.d.ts +24 -0
  64. package/dist/core/db.js +12 -0
  65. package/dist/core/db.js.map +1 -0
  66. package/dist/core/restore.d.ts +27 -0
  67. package/dist/core/restore.js +199 -0
  68. package/dist/core/restore.js.map +1 -0
  69. package/dist/core/restorePreview.d.ts +64 -0
  70. package/dist/core/restorePreview.js +266 -0
  71. package/dist/core/restorePreview.js.map +1 -0
  72. package/dist/core/taskProgress.d.ts +26 -0
  73. package/dist/core/taskProgress.js +73 -0
  74. package/dist/core/taskProgress.js.map +1 -0
  75. package/dist/core/vercelBackupCron.d.ts +8 -0
  76. package/dist/core/vercelBackupCron.js +38 -0
  77. package/dist/core/vercelBackupCron.js.map +1 -0
  78. package/dist/endpoints/createBackupEndpoints.d.ts +1 -0
  79. package/dist/endpoints/createBackupEndpoints.js +3 -0
  80. package/dist/endpoints/createBackupEndpoints.js.map +1 -0
  81. package/dist/endpoints/index.d.ts +3 -0
  82. package/dist/endpoints/index.js +34 -0
  83. package/dist/endpoints/index.js.map +1 -0
  84. package/dist/endpoints/paths/admin-backup-download.d.ts +8 -0
  85. package/dist/endpoints/paths/admin-backup-download.js +72 -0
  86. package/dist/endpoints/paths/admin-backup-download.js.map +1 -0
  87. package/dist/endpoints/paths/admin-delete.d.ts +3 -0
  88. package/dist/endpoints/paths/admin-delete.js +55 -0
  89. package/dist/endpoints/paths/admin-delete.js.map +1 -0
  90. package/dist/endpoints/paths/admin-manual.d.ts +3 -0
  91. package/dist/endpoints/paths/admin-manual.js +61 -0
  92. package/dist/endpoints/paths/admin-manual.js.map +1 -0
  93. package/dist/endpoints/paths/admin-preview.d.ts +6 -0
  94. package/dist/endpoints/paths/admin-preview.js +102 -0
  95. package/dist/endpoints/paths/admin-preview.js.map +1 -0
  96. package/dist/endpoints/paths/admin-restore.d.ts +3 -0
  97. package/dist/endpoints/paths/admin-restore.js +74 -0
  98. package/dist/endpoints/paths/admin-restore.js.map +1 -0
  99. package/dist/endpoints/paths/admin-seed.d.ts +3 -0
  100. package/dist/endpoints/paths/admin-seed.js +47 -0
  101. package/dist/endpoints/paths/admin-seed.js.map +1 -0
  102. package/dist/endpoints/paths/admin-settings.d.ts +3 -0
  103. package/dist/endpoints/paths/admin-settings.js +230 -0
  104. package/dist/endpoints/paths/admin-settings.js.map +1 -0
  105. package/dist/endpoints/paths/admin-task.d.ts +3 -0
  106. package/dist/endpoints/paths/admin-task.js +34 -0
  107. package/dist/endpoints/paths/admin-task.js.map +1 -0
  108. package/dist/endpoints/paths/admin-validate-blob-token.d.ts +8 -0
  109. package/dist/endpoints/paths/admin-validate-blob-token.js +26 -0
  110. package/dist/endpoints/paths/admin-validate-blob-token.js.map +1 -0
  111. package/dist/endpoints/paths/cron-list.d.ts +2 -0
  112. package/dist/endpoints/paths/cron-list.js +33 -0
  113. package/dist/endpoints/paths/cron-list.js.map +1 -0
  114. package/dist/endpoints/paths/cron-restore.d.ts +2 -0
  115. package/dist/endpoints/paths/cron-restore.js +61 -0
  116. package/dist/endpoints/paths/cron-restore.js.map +1 -0
  117. package/dist/endpoints/paths/cron-run.d.ts +3 -0
  118. package/dist/endpoints/paths/cron-run.js +45 -0
  119. package/dist/endpoints/paths/cron-run.js.map +1 -0
  120. package/dist/endpoints/shared.d.ts +13 -0
  121. package/dist/endpoints/shared.js +58 -0
  122. package/dist/endpoints/shared.js.map +1 -0
  123. package/dist/exports/client.d.ts +1 -0
  124. package/dist/exports/client.js +3 -0
  125. package/dist/exports/client.js.map +1 -0
  126. package/dist/exports/rsc.d.ts +1 -0
  127. package/dist/exports/rsc.js +3 -0
  128. package/dist/exports/rsc.js.map +1 -0
  129. package/dist/index.d.ts +18 -0
  130. package/dist/index.js +14 -0
  131. package/dist/index.js.map +1 -0
  132. package/dist/plugin.d.ts +3 -0
  133. package/dist/plugin.js +82 -0
  134. package/dist/plugin.js.map +1 -0
  135. package/dist/publicApiPaths.d.ts +19 -0
  136. package/dist/publicApiPaths.js +20 -0
  137. package/dist/publicApiPaths.js.map +1 -0
  138. package/dist/types.d.ts +21 -0
  139. package/dist/types.js +3 -0
  140. package/dist/types.js.map +1 -0
  141. package/dist/utils/backupSelection.d.ts +6 -0
  142. package/dist/utils/backupSelection.js +16 -0
  143. package/dist/utils/backupSelection.js.map +1 -0
  144. package/dist/utils/blobName.d.ts +32 -0
  145. package/dist/utils/blobName.js +99 -0
  146. package/dist/utils/blobName.js.map +1 -0
  147. package/dist/utils/dashboardRoleAccess.d.ts +23 -0
  148. package/dist/utils/dashboardRoleAccess.js +64 -0
  149. package/dist/utils/dashboardRoleAccess.js.map +1 -0
  150. package/dist/utils/dialogBackdrop.d.ts +9 -0
  151. package/dist/utils/dialogBackdrop.js +17 -0
  152. package/dist/utils/dialogBackdrop.js.map +1 -0
  153. package/dist/utils/formatBytes.d.ts +2 -0
  154. package/dist/utils/formatBytes.js +20 -0
  155. package/dist/utils/formatBytes.js.map +1 -0
  156. package/dist/utils/hostname.d.ts +2 -0
  157. package/dist/utils/hostname.js +17 -0
  158. package/dist/utils/hostname.js.map +1 -0
  159. package/dist/utils/index.d.ts +5 -0
  160. package/dist/utils/index.js +7 -0
  161. package/dist/utils/index.js.map +1 -0
  162. package/dist/utils/maskBlobToken.d.ts +11 -0
  163. package/dist/utils/maskBlobToken.js +42 -0
  164. package/dist/utils/maskBlobToken.js.map +1 -0
  165. package/package.json +140 -0
@@ -0,0 +1,551 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button } from '@payloadcms/ui';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { backupPluginPublicApiPaths } from '../../publicApiPaths';
7
+ import { closeNativeDialogOnBackdropPointer } from '../../utils/dialogBackdrop';
8
+ import { TaskActionButton } from './TaskActionButton.client';
9
+ function hiddenPills(reasons) {
10
+ const out = [];
11
+ if (reasons.includes('collection-config')) {
12
+ out.push('Hidden in admin');
13
+ }
14
+ return out;
15
+ }
16
+ function groupDocTotal(g) {
17
+ return (g.main?.docCount ?? 0) + (g.versions?.docCount ?? 0);
18
+ }
19
+ function isRestoreRowVisuallyEmpty(g, preview) {
20
+ const t = groupDocTotal(g);
21
+ const isMedia = g.groupId === 'media';
22
+ const hasBlobs = preview.mediaBlobCount > 0;
23
+ return t === 0 && !(isMedia && hasBlobs);
24
+ }
25
+ function isRestoreCollectionCheckboxDisabled(g, preview) {
26
+ return isRestoreRowVisuallyEmpty(g, preview);
27
+ }
28
+ function isRestoreSignInAgainGroup(groupId) {
29
+ return groupId === 'users' || groupId === 'roles' || groupId === 'payload-preferences';
30
+ }
31
+ export const RestoreBackupDialog = ({ downloadUrl, pathname })=>{
32
+ const router = useRouter();
33
+ const dialogRef = useRef(null);
34
+ const postSuccessCloseTimerRef = useRef(null);
35
+ const [preview, setPreview] = useState(null);
36
+ const [phase, setPhase] = useState('idle');
37
+ const [errorMessage, setErrorMessage] = useState(null);
38
+ const [selected, setSelected] = useState({});
39
+ const [restoreAllCollections, setRestoreAllCollections] = useState(false);
40
+ const [restoreArchiveMedia, setRestoreArchiveMedia] = useState(true);
41
+ const resetState = useCallback(()=>{
42
+ setPreview(null);
43
+ setPhase('idle');
44
+ setErrorMessage(null);
45
+ setSelected({});
46
+ setRestoreAllCollections(false);
47
+ setRestoreArchiveMedia(true);
48
+ }, []);
49
+ useEffect(()=>{
50
+ const el = dialogRef.current;
51
+ if (!el) {
52
+ return;
53
+ }
54
+ const onClose = ()=>{
55
+ if (postSuccessCloseTimerRef.current) {
56
+ clearTimeout(postSuccessCloseTimerRef.current);
57
+ postSuccessCloseTimerRef.current = null;
58
+ }
59
+ resetState();
60
+ };
61
+ el.addEventListener('close', onClose);
62
+ return ()=>el.removeEventListener('close', onClose);
63
+ }, [
64
+ resetState
65
+ ]);
66
+ useEffect(()=>{
67
+ return ()=>{
68
+ if (postSuccessCloseTimerRef.current) {
69
+ clearTimeout(postSuccessCloseTimerRef.current);
70
+ }
71
+ };
72
+ }, []);
73
+ useEffect(()=>{
74
+ if (!preview) {
75
+ return;
76
+ }
77
+ setSelected(Object.fromEntries(preview.groups.map((g)=>{
78
+ const t = groupDocTotal(g);
79
+ const isMedia = g.groupId === 'media';
80
+ const hasBlobs = preview.mediaBlobCount > 0;
81
+ if (t === 0 && !(isMedia && hasBlobs)) {
82
+ return [
83
+ g.groupId,
84
+ false
85
+ ];
86
+ }
87
+ return [
88
+ g.groupId,
89
+ true
90
+ ];
91
+ })));
92
+ setRestoreAllCollections(false);
93
+ setRestoreArchiveMedia(true);
94
+ }, [
95
+ preview
96
+ ]);
97
+ const loadPreview = useCallback(async ()=>{
98
+ setPhase('loading');
99
+ setErrorMessage(null);
100
+ setPreview(null);
101
+ try {
102
+ const locale = typeof document !== 'undefined' ? (document.documentElement.lang || 'en').slice(0, 2) : 'en';
103
+ const response = await fetch(backupPluginPublicApiPaths.adminRestorePreview, {
104
+ body: JSON.stringify({
105
+ locale,
106
+ pathname,
107
+ url: downloadUrl
108
+ }),
109
+ headers: {
110
+ 'Content-Type': 'application/json'
111
+ },
112
+ method: 'POST'
113
+ });
114
+ const data = await response.json();
115
+ if (!response.ok) {
116
+ throw new Error(data.error || 'Could not analyse backup');
117
+ }
118
+ setPreview(data);
119
+ setPhase('ready');
120
+ } catch (e) {
121
+ setPhase('error');
122
+ setErrorMessage(e instanceof Error ? e.message : 'Could not analyse backup');
123
+ }
124
+ }, [
125
+ downloadUrl,
126
+ pathname
127
+ ]);
128
+ const openDialog = ()=>{
129
+ setPhase('loading');
130
+ setErrorMessage(null);
131
+ setPreview(null);
132
+ setSelected({});
133
+ setRestoreAllCollections(false);
134
+ setRestoreArchiveMedia(true);
135
+ dialogRef.current?.showModal();
136
+ void loadPreview();
137
+ };
138
+ const skipCollections = useMemo(()=>{
139
+ if (!preview) {
140
+ return [];
141
+ }
142
+ if (restoreAllCollections) {
143
+ return [];
144
+ }
145
+ return preview.groups.filter((g)=>selected[g.groupId] === false).flatMap((g)=>g.mongoNames);
146
+ }, [
147
+ preview,
148
+ restoreAllCollections,
149
+ selected
150
+ ]);
151
+ const willAutoLogout = useMemo(()=>{
152
+ if (!preview) {
153
+ return false;
154
+ }
155
+ return preview.groups.some((g)=>(restoreAllCollections || selected[g.groupId] !== false) && g.affectsAuthSession);
156
+ }, [
157
+ preview,
158
+ restoreAllCollections,
159
+ selected
160
+ ]);
161
+ const needsAuthLockoutConfirm = useCallback(()=>{
162
+ if (!preview) {
163
+ return false;
164
+ }
165
+ return preview.groups.some((g)=>isRestoreSignInAgainGroup(g.groupId) && (restoreAllCollections || selected[g.groupId] !== false));
166
+ }, [
167
+ preview,
168
+ restoreAllCollections,
169
+ selected
170
+ ]);
171
+ const hasArchiveMedia = preview ? preview.mediaBlobCount > 0 : false;
172
+ const mediaGroupIncluded = preview ? restoreAllCollections || selected['media'] !== false : false;
173
+ const hasMediaBlobOption = Boolean(preview && preview.mediaBlobCount > 0 && mediaGroupIncluded);
174
+ const restoreBody = useMemo(()=>({
175
+ pathname,
176
+ restoreArchiveMedia: hasArchiveMedia ? restoreArchiveMedia : true,
177
+ skipCollections,
178
+ url: downloadUrl
179
+ }), [
180
+ downloadUrl,
181
+ hasArchiveMedia,
182
+ pathname,
183
+ restoreArchiveMedia,
184
+ skipCollections
185
+ ]);
186
+ const closeModalAfterRestoreSuccess = useCallback(()=>{
187
+ if (postSuccessCloseTimerRef.current) {
188
+ clearTimeout(postSuccessCloseTimerRef.current);
189
+ }
190
+ postSuccessCloseTimerRef.current = setTimeout(()=>{
191
+ postSuccessCloseTimerRef.current = null;
192
+ router.refresh();
193
+ dialogRef.current?.close();
194
+ }, 1000);
195
+ }, [
196
+ router
197
+ ]);
198
+ return /*#__PURE__*/ _jsxs(_Fragment, {
199
+ children: [
200
+ /*#__PURE__*/ _jsx(Button, {
201
+ buttonStyle: "secondary",
202
+ onClick: openDialog,
203
+ size: "small",
204
+ children: "Restore"
205
+ }),
206
+ /*#__PURE__*/ _jsxs("dialog", {
207
+ className: "backup-confirm-dialog backup-confirm-dialog--restore",
208
+ onMouseDown: (e)=>closeNativeDialogOnBackdropPointer(e, dialogRef),
209
+ ref: dialogRef,
210
+ children: [
211
+ /*#__PURE__*/ _jsx("p", {
212
+ className: "backup-confirm-dialog__title",
213
+ children: "Restore this backup?"
214
+ }),
215
+ /*#__PURE__*/ _jsxs("div", {
216
+ className: "backup-confirm-dialog__body restore-preview",
217
+ children: [
218
+ phase === 'loading' && /*#__PURE__*/ _jsxs("div", {
219
+ "aria-live": "polite",
220
+ className: "restore-preview__loading-block",
221
+ children: [
222
+ /*#__PURE__*/ _jsx("p", {
223
+ className: "restore-preview__status",
224
+ children: "Downloading and analysing backup…"
225
+ }),
226
+ /*#__PURE__*/ _jsx("div", {
227
+ className: "restore-preview__loading-line"
228
+ }),
229
+ /*#__PURE__*/ _jsx("div", {
230
+ className: "restore-preview__loading-line restore-preview__loading-line--short"
231
+ }),
232
+ /*#__PURE__*/ _jsx("div", {
233
+ className: "restore-preview__loading-line"
234
+ })
235
+ ]
236
+ }),
237
+ phase === 'error' && /*#__PURE__*/ _jsxs("div", {
238
+ className: "restore-preview__error",
239
+ children: [
240
+ /*#__PURE__*/ _jsx("p", {
241
+ children: errorMessage
242
+ }),
243
+ /*#__PURE__*/ _jsx("p", {
244
+ className: "restore-preview__error-hint",
245
+ children: "You can still start restore below. If this backup replaces users or sessions, sign out manually afterwards if you are not redirected automatically."
246
+ }),
247
+ /*#__PURE__*/ _jsx(Button, {
248
+ buttonStyle: "secondary",
249
+ onClick: ()=>void loadPreview(),
250
+ size: "small",
251
+ children: "Retry analysis"
252
+ })
253
+ ]
254
+ }),
255
+ phase === 'ready' && preview && /*#__PURE__*/ _jsxs(_Fragment, {
256
+ children: [
257
+ /*#__PURE__*/ _jsx("p", {
258
+ className: "restore-preview__sticky-heading",
259
+ children: "Collection selection"
260
+ }),
261
+ /*#__PURE__*/ _jsx("p", {
262
+ className: "restore-preview__intro",
263
+ children: "Choose which collections to restore. Version history is grouped with its collection. Collections with no documents in this backup still appear (greyed); you can leave them unchecked."
264
+ }),
265
+ preview.groups.length === 0 && /*#__PURE__*/ _jsx("p", {
266
+ className: "restore-preview__empty",
267
+ children: "No collections found to list."
268
+ }),
269
+ restoreAllCollections ? /*#__PURE__*/ _jsxs("div", {
270
+ className: "restore-preview__all-block",
271
+ children: [
272
+ /*#__PURE__*/ _jsxs("label", {
273
+ className: "restore-preview__all-block-head",
274
+ children: [
275
+ /*#__PURE__*/ _jsx("input", {
276
+ "aria-label": "Restore all collections",
277
+ checked: true,
278
+ className: "checkbox-input__input restore-preview__group-check",
279
+ onChange: ()=>setRestoreAllCollections((prev)=>{
280
+ const next = !prev;
281
+ if (next && preview) {
282
+ setSelected(Object.fromEntries(preview.groups.map((g)=>[
283
+ g.groupId,
284
+ true
285
+ ])));
286
+ }
287
+ return next;
288
+ }),
289
+ type: "checkbox"
290
+ }),
291
+ /*#__PURE__*/ _jsx("span", {
292
+ className: "restore-preview__media-label",
293
+ children: "Restore all collections"
294
+ })
295
+ ]
296
+ }),
297
+ preview.groups.length > 0 ? /*#__PURE__*/ _jsx("ul", {
298
+ "aria-label": "Collections in backup",
299
+ className: "restore-preview__all-list",
300
+ children: preview.groups.map((g)=>{
301
+ const isMedia = g.groupId === 'media';
302
+ const rowEmpty = isRestoreRowVisuallyEmpty(g, preview);
303
+ const versionDocs = g.versions?.docCount ?? 0;
304
+ const docTotal = groupDocTotal(g);
305
+ const signInAgain = isRestoreSignInAgainGroup(g.groupId);
306
+ let countLabel;
307
+ if (rowEmpty) {
308
+ countLabel = 'Nothing to restore';
309
+ } else if (docTotal === 0 && isMedia && preview.mediaBlobCount > 0) {
310
+ countLabel = `0 docs · ${preview.mediaBlobCount} media file${preview.mediaBlobCount === 1 ? '' : 's'} in archive`;
311
+ } else {
312
+ countLabel = `${docTotal} doc${docTotal === 1 ? '' : 's'} to restore`;
313
+ if (versionDocs > 0) {
314
+ countLabel += ` (incl. ${versionDocs} version${versionDocs === 1 ? '' : 's'})`;
315
+ }
316
+ }
317
+ return /*#__PURE__*/ _jsxs("li", {
318
+ className: "restore-preview__all-item",
319
+ children: [
320
+ /*#__PURE__*/ _jsxs("span", {
321
+ className: "restore-preview__all-name",
322
+ children: [
323
+ g.displayTitle,
324
+ signInAgain ? /*#__PURE__*/ _jsxs(_Fragment, {
325
+ children: [
326
+ ' ',
327
+ /*#__PURE__*/ _jsx("span", {
328
+ className: "restore-preview__pill restore-preview__pill--signin",
329
+ children: "Sign-in again"
330
+ })
331
+ ]
332
+ }) : null
333
+ ]
334
+ }),
335
+ /*#__PURE__*/ _jsx("span", {
336
+ className: "restore-preview__all-count",
337
+ children: countLabel
338
+ })
339
+ ]
340
+ }, g.groupId);
341
+ })
342
+ }) : null,
343
+ hasMediaBlobOption ? /*#__PURE__*/ _jsxs("label", {
344
+ className: "restore-preview__media-block restore-preview__media-block--nested",
345
+ children: [
346
+ /*#__PURE__*/ _jsx("input", {
347
+ "aria-label": `Restore media files from backup (${preview.mediaBlobCount} files in archive)`,
348
+ checked: restoreArchiveMedia,
349
+ className: "checkbox-input__input restore-preview__group-check",
350
+ onChange: ()=>setRestoreArchiveMedia((v)=>!v),
351
+ type: "checkbox"
352
+ }),
353
+ /*#__PURE__*/ _jsxs("span", {
354
+ className: "restore-preview__media-label",
355
+ children: [
356
+ "Restore media files from backup? (",
357
+ preview.mediaBlobCount,
358
+ " file",
359
+ preview.mediaBlobCount === 1 ? '' : 's',
360
+ " in archive)"
361
+ ]
362
+ })
363
+ ]
364
+ }) : null
365
+ ]
366
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
367
+ children: [
368
+ /*#__PURE__*/ _jsxs("label", {
369
+ className: "restore-preview__media-block",
370
+ children: [
371
+ /*#__PURE__*/ _jsx("input", {
372
+ "aria-label": "Restore all collections",
373
+ checked: false,
374
+ className: "checkbox-input__input restore-preview__group-check",
375
+ onChange: ()=>setRestoreAllCollections((prev)=>{
376
+ const next = !prev;
377
+ if (next && preview) {
378
+ setSelected(Object.fromEntries(preview.groups.map((g)=>[
379
+ g.groupId,
380
+ true
381
+ ])));
382
+ }
383
+ return next;
384
+ }),
385
+ type: "checkbox"
386
+ }),
387
+ /*#__PURE__*/ _jsx("span", {
388
+ className: "restore-preview__media-label",
389
+ children: "Restore all collections"
390
+ })
391
+ ]
392
+ }),
393
+ /*#__PURE__*/ _jsx("ul", {
394
+ "aria-label": "Collections in backup",
395
+ className: "restore-preview__list",
396
+ children: preview.groups.map((g)=>{
397
+ const adminPills = hiddenPills(g.adminHiddenReasons);
398
+ const isMedia = g.groupId === 'media';
399
+ const rowEmpty = isRestoreRowVisuallyEmpty(g, preview);
400
+ const checkboxDisabled = isRestoreCollectionCheckboxDisabled(g, preview);
401
+ const docTotal = groupDocTotal(g);
402
+ return /*#__PURE__*/ _jsxs("li", {
403
+ className: `restore-preview__group${rowEmpty ? 'restore-preview__group--empty' : ''}`,
404
+ children: [
405
+ /*#__PURE__*/ _jsxs("div", {
406
+ className: "restore-preview__group-head",
407
+ children: [
408
+ /*#__PURE__*/ _jsxs("label", {
409
+ className: "restore-preview__group-label",
410
+ children: [
411
+ /*#__PURE__*/ _jsx("input", {
412
+ "aria-label": `Restore collection ${g.displayTitle}`,
413
+ checked: selected[g.groupId] !== false,
414
+ className: "checkbox-input__input restore-preview__group-check",
415
+ disabled: checkboxDisabled,
416
+ onChange: ()=>{
417
+ setSelected((prev)=>{
418
+ const isOn = prev[g.groupId] !== false;
419
+ return {
420
+ ...prev,
421
+ [g.groupId]: !isOn
422
+ };
423
+ });
424
+ },
425
+ type: "checkbox"
426
+ }),
427
+ /*#__PURE__*/ _jsx("span", {
428
+ className: "restore-preview__group-title",
429
+ children: g.displayTitle
430
+ })
431
+ ]
432
+ }),
433
+ isRestoreSignInAgainGroup(g.groupId) && /*#__PURE__*/ _jsx("span", {
434
+ className: "restore-preview__pill restore-preview__pill--signin",
435
+ children: "Requires sign-in again"
436
+ })
437
+ ]
438
+ }),
439
+ /*#__PURE__*/ _jsxs("div", {
440
+ className: "restore-preview__sublines",
441
+ children: [
442
+ rowEmpty ? /*#__PURE__*/ _jsx("div", {
443
+ className: "restore-preview__subline restore-preview__subline--empty",
444
+ children: "0 docs, nothing to restore"
445
+ }) : docTotal === 0 && isMedia && preview.mediaBlobCount > 0 ? /*#__PURE__*/ _jsx("div", {
446
+ className: "restore-preview__subline",
447
+ children: "0 collection docs in backup"
448
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
449
+ children: [
450
+ g.main ? /*#__PURE__*/ _jsxs("div", {
451
+ className: "restore-preview__subline",
452
+ children: [
453
+ g.main.docCount,
454
+ " doc",
455
+ g.main.docCount === 1 ? '' : 's',
456
+ " to restore"
457
+ ]
458
+ }) : null,
459
+ g.versions ? /*#__PURE__*/ _jsxs("div", {
460
+ className: "restore-preview__subline",
461
+ children: [
462
+ g.versions.docCount,
463
+ " version doc",
464
+ g.versions.docCount === 1 ? '' : 's',
465
+ " to restore"
466
+ ]
467
+ }) : null
468
+ ]
469
+ }),
470
+ isMedia && selected['media'] !== false && preview.mediaBlobCount === 0 && docTotal > 0 && /*#__PURE__*/ _jsx("div", {
471
+ className: "restore-preview__note restore-preview__note--media",
472
+ children: "No media files in this backup archive — only collection data will be restored for media."
473
+ }),
474
+ isMedia && hasMediaBlobOption && /*#__PURE__*/ _jsxs("label", {
475
+ className: "restore-preview__media-block restore-preview__media-block--nested",
476
+ children: [
477
+ /*#__PURE__*/ _jsx("input", {
478
+ "aria-label": `Restore media files from backup (${preview.mediaBlobCount} files in archive)`,
479
+ checked: restoreArchiveMedia,
480
+ className: "checkbox-input__input restore-preview__group-check",
481
+ onChange: ()=>setRestoreArchiveMedia((v)=>!v),
482
+ type: "checkbox"
483
+ }),
484
+ /*#__PURE__*/ _jsxs("span", {
485
+ className: "restore-preview__media-label",
486
+ children: [
487
+ "Restore media files from backup? (",
488
+ preview.mediaBlobCount,
489
+ " file",
490
+ preview.mediaBlobCount === 1 ? '' : 's',
491
+ " in archive)"
492
+ ]
493
+ })
494
+ ]
495
+ }),
496
+ adminPills.length > 0 && /*#__PURE__*/ _jsx("div", {
497
+ className: "restore-preview__pills",
498
+ children: adminPills.map((t)=>/*#__PURE__*/ _jsx("span", {
499
+ className: "restore-preview__pill",
500
+ children: t
501
+ }, t))
502
+ })
503
+ ]
504
+ })
505
+ ]
506
+ }, g.groupId);
507
+ })
508
+ })
509
+ ]
510
+ })
511
+ ]
512
+ })
513
+ ]
514
+ }),
515
+ /*#__PURE__*/ _jsxs("div", {
516
+ className: "backup-confirm-dialog__actions",
517
+ children: [
518
+ /*#__PURE__*/ _jsx(TaskActionButton, {
519
+ body: restoreBody,
520
+ buttonStyle: "error",
521
+ completeLabel: "Restore started",
522
+ dangerConfirm: {
523
+ body: 'You have selected Users, Roles, and/or Payload preferences. Restoring these collections replaces login accounts, role definitions, and/or UI session data. You can be locked out until you sign in again with the restored credentials. After a successful restore you will be signed out automatically.',
524
+ cancelLabel: 'Go back',
525
+ confirmLabel: 'I understand — restore anyway',
526
+ title: 'Danger: lock-out risk',
527
+ when: needsAuthLockoutConfirm
528
+ },
529
+ endpoint: backupPluginPublicApiPaths.adminRestore,
530
+ idleDisabled: phase !== 'ready' && phase !== 'error',
531
+ idleLabel: "Yes, restore",
532
+ kind: "restore",
533
+ onComplete: closeModalAfterRestoreSuccess,
534
+ pendingLabel: "Restoring...",
535
+ redirectOnComplete: willAutoLogout ? '/admin/logout' : undefined
536
+ }),
537
+ /*#__PURE__*/ _jsx(Button, {
538
+ buttonStyle: "secondary",
539
+ onClick: ()=>dialogRef.current?.close(),
540
+ size: "small",
541
+ children: "Cancel"
542
+ })
543
+ ]
544
+ })
545
+ ]
546
+ })
547
+ ]
548
+ });
549
+ };
550
+
551
+ //# sourceMappingURL=RestoreBackupDialog.client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/BackupDashboard/RestoreBackupDialog.client.tsx"],"sourcesContent":["'use client'\n\nimport { Button } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { RestorePreviewResponse } from '../../core/restorePreview'\n\nimport { backupPluginPublicApiPaths } from '../../publicApiPaths'\nimport { closeNativeDialogOnBackdropPointer } from '../../utils/dialogBackdrop'\nimport { TaskActionButton } from './TaskActionButton.client'\n\ntype RestoreBackupDialogProps = {\n downloadUrl: string\n pathname: string\n}\n\nfunction hiddenPills(\n reasons: RestorePreviewResponse['groups'][number]['adminHiddenReasons'],\n): string[] {\n const out: string[] = []\n if (reasons.includes('collection-config')) {\n out.push('Hidden in admin')\n }\n return out\n}\n\nfunction groupDocTotal(g: RestorePreviewResponse['groups'][number]): number {\n return (g.main?.docCount ?? 0) + (g.versions?.docCount ?? 0)\n}\n\nfunction isRestoreRowVisuallyEmpty(\n g: RestorePreviewResponse['groups'][number],\n preview: RestorePreviewResponse,\n): boolean {\n const t = groupDocTotal(g)\n const isMedia = g.groupId === 'media'\n const hasBlobs = preview.mediaBlobCount > 0\n return t === 0 && !(isMedia && hasBlobs)\n}\n\nfunction isRestoreCollectionCheckboxDisabled(\n g: RestorePreviewResponse['groups'][number],\n preview: RestorePreviewResponse,\n): boolean {\n return isRestoreRowVisuallyEmpty(g, preview)\n}\n\nfunction isRestoreSignInAgainGroup(groupId: string): boolean {\n return groupId === 'users' || groupId === 'roles' || groupId === 'payload-preferences'\n}\n\nexport const RestoreBackupDialog: React.FC<RestoreBackupDialogProps> = ({\n downloadUrl,\n pathname,\n}) => {\n const router = useRouter()\n const dialogRef = useRef<HTMLDialogElement>(null)\n const postSuccessCloseTimerRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n const [preview, setPreview] = useState<null | RestorePreviewResponse>(null)\n const [phase, setPhase] = useState<'error' | 'idle' | 'loading' | 'ready'>('idle')\n const [errorMessage, setErrorMessage] = useState<null | string>(null)\n const [selected, setSelected] = useState<Record<string, boolean>>({})\n const [restoreAllCollections, setRestoreAllCollections] = useState(false)\n const [restoreArchiveMedia, setRestoreArchiveMedia] = useState(true)\n\n const resetState = useCallback(() => {\n setPreview(null)\n setPhase('idle')\n setErrorMessage(null)\n setSelected({})\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n }, [])\n\n useEffect(() => {\n const el = dialogRef.current\n if (!el) {\n return\n }\n const onClose = () => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n postSuccessCloseTimerRef.current = null\n }\n resetState()\n }\n el.addEventListener('close', onClose)\n return () => el.removeEventListener('close', onClose)\n }, [resetState])\n\n useEffect(() => {\n return () => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n }\n }\n }, [])\n\n useEffect(() => {\n if (!preview) {\n return\n }\n setSelected(\n Object.fromEntries(\n preview.groups.map((g) => {\n const t = groupDocTotal(g)\n const isMedia = g.groupId === 'media'\n const hasBlobs = preview.mediaBlobCount > 0\n if (t === 0 && !(isMedia && hasBlobs)) {\n return [g.groupId, false]\n }\n return [g.groupId, true]\n }),\n ),\n )\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n }, [preview])\n\n const loadPreview = useCallback(async () => {\n setPhase('loading')\n setErrorMessage(null)\n setPreview(null)\n try {\n const locale =\n typeof document !== 'undefined' ? (document.documentElement.lang || 'en').slice(0, 2) : 'en'\n const response = await fetch(backupPluginPublicApiPaths.adminRestorePreview, {\n body: JSON.stringify({\n locale,\n pathname,\n url: downloadUrl,\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n const data = (await response.json()) as { error?: string } & RestorePreviewResponse\n if (!response.ok) {\n throw new Error(data.error || 'Could not analyse backup')\n }\n setPreview(data)\n setPhase('ready')\n } catch (e) {\n setPhase('error')\n setErrorMessage(e instanceof Error ? e.message : 'Could not analyse backup')\n }\n }, [downloadUrl, pathname])\n\n const openDialog = () => {\n setPhase('loading')\n setErrorMessage(null)\n setPreview(null)\n setSelected({})\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n dialogRef.current?.showModal()\n void loadPreview()\n }\n\n const skipCollections = useMemo(() => {\n if (!preview) {\n return []\n }\n if (restoreAllCollections) {\n return []\n }\n return preview.groups.filter((g) => selected[g.groupId] === false).flatMap((g) => g.mongoNames)\n }, [preview, restoreAllCollections, selected])\n\n const willAutoLogout = useMemo(() => {\n if (!preview) {\n return false\n }\n return preview.groups.some(\n (g) => (restoreAllCollections || selected[g.groupId] !== false) && g.affectsAuthSession,\n )\n }, [preview, restoreAllCollections, selected])\n\n const needsAuthLockoutConfirm = useCallback(() => {\n if (!preview) {\n return false\n }\n return preview.groups.some(\n (g) =>\n isRestoreSignInAgainGroup(g.groupId) &&\n (restoreAllCollections || selected[g.groupId] !== false),\n )\n }, [preview, restoreAllCollections, selected])\n\n const hasArchiveMedia = preview ? preview.mediaBlobCount > 0 : false\n const mediaGroupIncluded = preview ? restoreAllCollections || selected['media'] !== false : false\n const hasMediaBlobOption = Boolean(preview && preview.mediaBlobCount > 0 && mediaGroupIncluded)\n\n const restoreBody = useMemo(\n () => ({\n pathname,\n restoreArchiveMedia: hasArchiveMedia ? restoreArchiveMedia : true,\n skipCollections,\n url: downloadUrl,\n }),\n [downloadUrl, hasArchiveMedia, pathname, restoreArchiveMedia, skipCollections],\n )\n\n const closeModalAfterRestoreSuccess = useCallback(() => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n }\n postSuccessCloseTimerRef.current = setTimeout(() => {\n postSuccessCloseTimerRef.current = null\n router.refresh()\n dialogRef.current?.close()\n }, 1000)\n }, [router])\n\n return (\n <>\n <Button buttonStyle=\"secondary\" onClick={openDialog} size=\"small\">\n Restore\n </Button>\n\n {/* Native <dialog>: backdrop dismiss; element not in jsx-a11y interactive list */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <dialog\n className=\"backup-confirm-dialog backup-confirm-dialog--restore\"\n onMouseDown={(e) => closeNativeDialogOnBackdropPointer(e, dialogRef)}\n ref={dialogRef}\n >\n <p className=\"backup-confirm-dialog__title\">Restore this backup?</p>\n\n <div className=\"backup-confirm-dialog__body restore-preview\">\n {phase === 'loading' && (\n <div aria-live=\"polite\" className=\"restore-preview__loading-block\">\n <p className=\"restore-preview__status\">Downloading and analysing backup…</p>\n <div className=\"restore-preview__loading-line\" />\n <div className=\"restore-preview__loading-line restore-preview__loading-line--short\" />\n <div className=\"restore-preview__loading-line\" />\n </div>\n )}\n\n {phase === 'error' && (\n <div className=\"restore-preview__error\">\n <p>{errorMessage}</p>\n <p className=\"restore-preview__error-hint\">\n You can still start restore below. If this backup replaces users or sessions, sign\n out manually afterwards if you are not redirected automatically.\n </p>\n <Button buttonStyle=\"secondary\" onClick={() => void loadPreview()} size=\"small\">\n Retry analysis\n </Button>\n </div>\n )}\n\n {phase === 'ready' && preview && (\n <>\n <p className=\"restore-preview__sticky-heading\">Collection selection</p>\n <p className=\"restore-preview__intro\">\n Choose which collections to restore. Version history is grouped with its collection.\n Collections with no documents in this backup still appear (greyed); you can leave\n them unchecked.\n </p>\n\n {preview.groups.length === 0 && (\n <p className=\"restore-preview__empty\">No collections found to list.</p>\n )}\n\n {restoreAllCollections ? (\n <div className=\"restore-preview__all-block\">\n <label className=\"restore-preview__all-block-head\">\n <input\n aria-label=\"Restore all collections\"\n checked\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() =>\n setRestoreAllCollections((prev) => {\n const next = !prev\n if (next && preview) {\n setSelected(\n Object.fromEntries(preview.groups.map((g) => [g.groupId, true])),\n )\n }\n return next\n })\n }\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">Restore all collections</span>\n </label>\n {preview.groups.length > 0 ? (\n <ul aria-label=\"Collections in backup\" className=\"restore-preview__all-list\">\n {preview.groups.map((g) => {\n const isMedia = g.groupId === 'media'\n const rowEmpty = isRestoreRowVisuallyEmpty(g, preview)\n const versionDocs = g.versions?.docCount ?? 0\n const docTotal = groupDocTotal(g)\n const signInAgain = isRestoreSignInAgainGroup(g.groupId)\n\n let countLabel: string\n if (rowEmpty) {\n countLabel = 'Nothing to restore'\n } else if (docTotal === 0 && isMedia && preview.mediaBlobCount > 0) {\n countLabel = `0 docs · ${preview.mediaBlobCount} media file${preview.mediaBlobCount === 1 ? '' : 's'} in archive`\n } else {\n countLabel = `${docTotal} doc${docTotal === 1 ? '' : 's'} to restore`\n if (versionDocs > 0) {\n countLabel += ` (incl. ${versionDocs} version${versionDocs === 1 ? '' : 's'})`\n }\n }\n\n return (\n <li className=\"restore-preview__all-item\" key={g.groupId}>\n <span className=\"restore-preview__all-name\">\n {g.displayTitle}\n {signInAgain ? (\n <>\n {' '}\n <span className=\"restore-preview__pill restore-preview__pill--signin\">\n Sign-in again\n </span>\n </>\n ) : null}\n </span>\n <span className=\"restore-preview__all-count\">{countLabel}</span>\n </li>\n )\n })}\n </ul>\n ) : null}\n {hasMediaBlobOption ? (\n <label className=\"restore-preview__media-block restore-preview__media-block--nested\">\n <input\n aria-label={`Restore media files from backup (${preview.mediaBlobCount} files in archive)`}\n checked={restoreArchiveMedia}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() => setRestoreArchiveMedia((v) => !v)}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">\n Restore media files from backup? ({preview.mediaBlobCount} file\n {preview.mediaBlobCount === 1 ? '' : 's'} in archive)\n </span>\n </label>\n ) : null}\n </div>\n ) : (\n <>\n <label className=\"restore-preview__media-block\">\n <input\n aria-label=\"Restore all collections\"\n checked={false}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() =>\n setRestoreAllCollections((prev) => {\n const next = !prev\n if (next && preview) {\n setSelected(\n Object.fromEntries(preview.groups.map((g) => [g.groupId, true])),\n )\n }\n return next\n })\n }\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">Restore all collections</span>\n </label>\n\n <ul aria-label=\"Collections in backup\" className=\"restore-preview__list\">\n {preview.groups.map((g) => {\n const adminPills = hiddenPills(g.adminHiddenReasons)\n const isMedia = g.groupId === 'media'\n const rowEmpty = isRestoreRowVisuallyEmpty(g, preview)\n const checkboxDisabled = isRestoreCollectionCheckboxDisabled(g, preview)\n const docTotal = groupDocTotal(g)\n\n return (\n <li\n className={`restore-preview__group${rowEmpty ? 'restore-preview__group--empty' : ''}`}\n key={g.groupId}\n >\n <div className=\"restore-preview__group-head\">\n <label className=\"restore-preview__group-label\">\n <input\n aria-label={`Restore collection ${g.displayTitle}`}\n checked={selected[g.groupId] !== false}\n className=\"checkbox-input__input restore-preview__group-check\"\n disabled={checkboxDisabled}\n onChange={() => {\n setSelected((prev) => {\n const isOn = prev[g.groupId] !== false\n return { ...prev, [g.groupId]: !isOn }\n })\n }}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__group-title\">{g.displayTitle}</span>\n </label>\n {isRestoreSignInAgainGroup(g.groupId) && (\n <span className=\"restore-preview__pill restore-preview__pill--signin\">\n Requires sign-in again\n </span>\n )}\n </div>\n <div className=\"restore-preview__sublines\">\n {rowEmpty ? (\n <div className=\"restore-preview__subline restore-preview__subline--empty\">\n 0 docs, nothing to restore\n </div>\n ) : docTotal === 0 && isMedia && preview.mediaBlobCount > 0 ? (\n <div className=\"restore-preview__subline\">\n 0 collection docs in backup\n </div>\n ) : (\n <>\n {g.main ? (\n <div className=\"restore-preview__subline\">\n {g.main.docCount} doc{g.main.docCount === 1 ? '' : 's'} to\n restore\n </div>\n ) : null}\n {g.versions ? (\n <div className=\"restore-preview__subline\">\n {g.versions.docCount} version doc\n {g.versions.docCount === 1 ? '' : 's'} to restore\n </div>\n ) : null}\n </>\n )}\n {isMedia &&\n selected['media'] !== false &&\n preview.mediaBlobCount === 0 &&\n docTotal > 0 && (\n <div className=\"restore-preview__note restore-preview__note--media\">\n No media files in this backup archive — only collection data will\n be restored for media.\n </div>\n )}\n {isMedia && hasMediaBlobOption && (\n <label className=\"restore-preview__media-block restore-preview__media-block--nested\">\n <input\n aria-label={`Restore media files from backup (${preview.mediaBlobCount} files in archive)`}\n checked={restoreArchiveMedia}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() => setRestoreArchiveMedia((v) => !v)}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">\n Restore media files from backup? ({preview.mediaBlobCount} file\n {preview.mediaBlobCount === 1 ? '' : 's'} in archive)\n </span>\n </label>\n )}\n {adminPills.length > 0 && (\n <div className=\"restore-preview__pills\">\n {adminPills.map((t) => (\n <span className=\"restore-preview__pill\" key={t}>\n {t}\n </span>\n ))}\n </div>\n )}\n </div>\n </li>\n )\n })}\n </ul>\n </>\n )}\n </>\n )}\n </div>\n\n <div className=\"backup-confirm-dialog__actions\">\n <TaskActionButton\n body={restoreBody}\n buttonStyle=\"error\"\n completeLabel=\"Restore started\"\n dangerConfirm={{\n body: 'You have selected Users, Roles, and/or Payload preferences. Restoring these collections replaces login accounts, role definitions, and/or UI session data. You can be locked out until you sign in again with the restored credentials. After a successful restore you will be signed out automatically.',\n cancelLabel: 'Go back',\n confirmLabel: 'I understand — restore anyway',\n title: 'Danger: lock-out risk',\n when: needsAuthLockoutConfirm,\n }}\n endpoint={backupPluginPublicApiPaths.adminRestore}\n idleDisabled={phase !== 'ready' && phase !== 'error'}\n idleLabel=\"Yes, restore\"\n kind=\"restore\"\n onComplete={closeModalAfterRestoreSuccess}\n pendingLabel=\"Restoring...\"\n redirectOnComplete={willAutoLogout ? '/admin/logout' : undefined}\n />\n <Button buttonStyle=\"secondary\" onClick={() => dialogRef.current?.close()} size=\"small\">\n Cancel\n </Button>\n </div>\n </dialog>\n </>\n )\n}\n"],"names":["Button","useRouter","useCallback","useEffect","useMemo","useRef","useState","backupPluginPublicApiPaths","closeNativeDialogOnBackdropPointer","TaskActionButton","hiddenPills","reasons","out","includes","push","groupDocTotal","g","main","docCount","versions","isRestoreRowVisuallyEmpty","preview","t","isMedia","groupId","hasBlobs","mediaBlobCount","isRestoreCollectionCheckboxDisabled","isRestoreSignInAgainGroup","RestoreBackupDialog","downloadUrl","pathname","router","dialogRef","postSuccessCloseTimerRef","setPreview","phase","setPhase","errorMessage","setErrorMessage","selected","setSelected","restoreAllCollections","setRestoreAllCollections","restoreArchiveMedia","setRestoreArchiveMedia","resetState","el","current","onClose","clearTimeout","addEventListener","removeEventListener","Object","fromEntries","groups","map","loadPreview","locale","document","documentElement","lang","slice","response","fetch","adminRestorePreview","body","JSON","stringify","url","headers","method","data","json","ok","Error","error","e","message","openDialog","showModal","skipCollections","filter","flatMap","mongoNames","willAutoLogout","some","affectsAuthSession","needsAuthLockoutConfirm","hasArchiveMedia","mediaGroupIncluded","hasMediaBlobOption","Boolean","restoreBody","closeModalAfterRestoreSuccess","setTimeout","refresh","close","buttonStyle","onClick","size","dialog","className","onMouseDown","ref","p","div","aria-live","length","label","input","aria-label","checked","onChange","prev","next","type","span","ul","rowEmpty","versionDocs","docTotal","signInAgain","countLabel","li","displayTitle","v","adminPills","adminHiddenReasons","checkboxDisabled","disabled","isOn","completeLabel","dangerConfirm","cancelLabel","confirmLabel","title","when","endpoint","adminRestore","idleDisabled","idleLabel","kind","onComplete","pendingLabel","redirectOnComplete","undefined"],"mappings":"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"}