@byline/host-tanstack-start 1.1.0 → 1.2.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 (71) hide show
  1. package/dist/admin-shell/admin-roles/container.js +1 -2
  2. package/dist/admin-shell/admin-roles/delete.js +1 -1
  3. package/dist/admin-shell/admin-roles/list.js +1 -2
  4. package/dist/admin-shell/admin-users/container.js +1 -2
  5. package/dist/admin-shell/admin-users/delete.js +1 -1
  6. package/dist/admin-shell/admin-users/list.js +1 -2
  7. package/dist/admin-shell/chrome/admin-app-bar.js +1 -1
  8. package/dist/admin-shell/chrome/dashboard.js +1 -1
  9. package/dist/admin-shell/chrome/drawer-toggle.js +1 -1
  10. package/dist/admin-shell/chrome/menu-drawer.js +1 -1
  11. package/dist/admin-shell/chrome/preview-toggle.js +1 -1
  12. package/dist/admin-shell/chrome/route-error.js +1 -1
  13. package/dist/admin-shell/chrome/router-pager.d.ts +1 -1
  14. package/dist/admin-shell/chrome/router-pager.js +1 -1
  15. package/dist/admin-shell/chrome/th-sortable.js +1 -1
  16. package/dist/admin-shell/collections/api.js +1 -1
  17. package/dist/admin-shell/collections/create.js +1 -2
  18. package/dist/admin-shell/collections/edit.js +1 -2
  19. package/dist/admin-shell/collections/history.js +82 -6
  20. package/dist/admin-shell/collections/history.module.js +5 -0
  21. package/dist/admin-shell/collections/history_module.css +46 -0
  22. package/dist/admin-shell/collections/list.js +1 -2
  23. package/dist/admin-shell/collections/preview-link.js +1 -1
  24. package/dist/admin-shell/collections/restore-version-modal.d.ts +10 -0
  25. package/dist/admin-shell/collections/restore-version-modal.js +118 -0
  26. package/dist/admin-shell/collections/restore-version-modal.module.js +10 -0
  27. package/dist/admin-shell/collections/restore-version-modal_module.css +31 -0
  28. package/dist/admin-shell/collections/view-menu.js +1 -1
  29. package/dist/routes/create-admin-account-route.js +1 -2
  30. package/dist/routes/create-admin-role-edit-route.js +1 -1
  31. package/dist/routes/create-admin-user-edit-route.js +1 -1
  32. package/dist/routes/create-collection-list-route.js +1 -1
  33. package/dist/server-fns/auth/current-user.js +3 -8
  34. package/dist/server-fns/collections/index.d.ts +1 -0
  35. package/dist/server-fns/collections/index.js +1 -0
  36. package/dist/server-fns/collections/restore-version.d.ts +21 -0
  37. package/dist/server-fns/collections/restore-version.js +41 -0
  38. package/dist/server-fns/collections/upload.js +1 -1
  39. package/package.json +8 -20
  40. package/src/admin-shell/admin-roles/container.tsx +1 -1
  41. package/src/admin-shell/admin-roles/delete.tsx +1 -1
  42. package/src/admin-shell/admin-roles/list.tsx +1 -1
  43. package/src/admin-shell/admin-users/container.tsx +1 -1
  44. package/src/admin-shell/admin-users/delete.tsx +1 -1
  45. package/src/admin-shell/admin-users/list.tsx +1 -1
  46. package/src/admin-shell/chrome/admin-app-bar.tsx +1 -1
  47. package/src/admin-shell/chrome/dashboard.tsx +1 -1
  48. package/src/admin-shell/chrome/drawer-toggle.tsx +1 -1
  49. package/src/admin-shell/chrome/menu-drawer.tsx +1 -1
  50. package/src/admin-shell/chrome/preview-toggle.tsx +1 -1
  51. package/src/admin-shell/chrome/route-error.tsx +1 -1
  52. package/src/admin-shell/chrome/router-pager.tsx +2 -2
  53. package/src/admin-shell/chrome/th-sortable.tsx +1 -1
  54. package/src/admin-shell/collections/api.tsx +1 -1
  55. package/src/admin-shell/collections/create.tsx +1 -1
  56. package/src/admin-shell/collections/edit.tsx +1 -1
  57. package/src/admin-shell/collections/history.module.css +50 -1
  58. package/src/admin-shell/collections/history.tsx +149 -67
  59. package/src/admin-shell/collections/list.tsx +1 -1
  60. package/src/admin-shell/collections/preview-link.tsx +1 -1
  61. package/src/admin-shell/collections/restore-version-modal.module.css +48 -0
  62. package/src/admin-shell/collections/restore-version-modal.tsx +131 -0
  63. package/src/admin-shell/collections/view-menu.tsx +1 -1
  64. package/src/routes/create-admin-account-route.tsx +1 -1
  65. package/src/routes/create-admin-role-edit-route.tsx +1 -1
  66. package/src/routes/create-admin-user-edit-route.tsx +1 -1
  67. package/src/routes/create-collection-list-route.tsx +1 -1
  68. package/src/server-fns/auth/current-user.ts +3 -9
  69. package/src/server-fns/collections/index.ts +1 -0
  70. package/src/server-fns/collections/restore-version.ts +59 -0
  71. package/src/server-fns/collections/upload.ts +1 -1
@@ -11,7 +11,7 @@ import { useParams, useRouterState } from '@tanstack/react-router'
11
11
 
12
12
  import type { CollectionAdminConfig, CollectionDefinition, WorkflowStatus } from '@byline/core'
13
13
  import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
14
- import { Container, IconButton, Section, Select, Table } from '@byline/ui'
14
+ import { Button, CloseIcon, Container, IconButton, Modal, Section, Select, Table } from '@byline/ui/react'
15
15
  import { renderFormatted, StatusBadge } from '@byline/ui/react'
16
16
  import cx from 'classnames'
17
17
 
@@ -20,6 +20,7 @@ import { RouterPager } from '../chrome/router-pager.js'
20
20
  import { TableHeadingCellSortable } from '../chrome/th-sortable.js'
21
21
  import { formatNumber } from '../chrome/utils.js'
22
22
  import styles from './history.module.css'
23
+ import { RestoreVersionModal } from './restore-version-modal.js'
23
24
  import { ViewMenu } from './view-menu.js'
24
25
  import type { ContentLocaleOption } from './view-menu.js'
25
26
 
@@ -27,7 +28,6 @@ import type { ContentLocaleOption } from './view-menu.js'
27
28
  * Resolve a column value from a document, checking `fields` first (user-defined
28
29
  * collection fields) then the root (metadata like status, updated_at).
29
30
  */
30
- // biome-ignore lint/suspicious/noExplicitAny: collection rows are heterogeneous
31
31
  function getColumnValue(document: any, fieldName: string): any {
32
32
  if (document.fields && fieldName in document.fields) {
33
33
  return document.fields[fieldName]
@@ -75,7 +75,7 @@ function padRows(value: number) {
75
75
  key={`empty-row-${
76
76
  // biome-ignore lint/suspicious/noArrayIndexKey: we're okay here
77
77
  index
78
- }`}
78
+ }`}
79
79
  className={cx('byline-coll-history-pad-row', styles.padRow)}
80
80
  >
81
81
   
@@ -112,6 +112,15 @@ export const HistoryView = ({
112
112
  versionId: string
113
113
  label: string
114
114
  } | null>(null)
115
+ const [restoreTarget, setRestoreTarget] = useState<{
116
+ versionId: string
117
+ label: string
118
+ versionNumber: number
119
+ } | null>(null)
120
+ const currentVersionId =
121
+ currentDocument && typeof currentDocument.versionId === 'string'
122
+ ? (currentDocument.versionId as string)
123
+ : null
115
124
 
116
125
  function handleOnPageSizeChange(value: string | null): void {
117
126
  if (typeof value !== 'string' || value.length === 0) return
@@ -164,8 +173,8 @@ export const HistoryView = ({
164
173
  scope="col"
165
174
  className={cx('byline-coll-history-col-version', styles.colVersion)}
166
175
  />
167
- {columns.map((column) => {
168
- return (
176
+ {columns.flatMap((column) => {
177
+ const cell = (
169
178
  <TableHeadingCellSortable
170
179
  key={String(column.fieldName)}
171
180
  fieldName={String(column.fieldName)}
@@ -176,6 +185,17 @@ export const HistoryView = ({
176
185
  className={column.className}
177
186
  />
178
187
  )
188
+ if (column.fieldName === 'title') {
189
+ return [
190
+ cell,
191
+ <th
192
+ key="__restore"
193
+ scope="col"
194
+ className={cx('byline-coll-history-col-restore', styles.colRestore)}
195
+ />,
196
+ ]
197
+ }
198
+ return [cell]
179
199
  })}
180
200
  </Table.Row>
181
201
  </Table.Header>
@@ -214,84 +234,119 @@ export const HistoryView = ({
214
234
  </IconButton>
215
235
  ) : null}
216
236
  </Table.Cell>
217
- {columns.map((column) => (
218
- <Table.Cell
219
- key={String(column.fieldName)}
220
- className={cx({
221
- 'byline-coll-history-cell-right': column.align === 'right',
222
- [styles.cellRight]: column.align === 'right',
223
- 'byline-coll-history-cell-center': column.align === 'center',
224
- [styles.cellCenter]: column.align === 'center',
225
- })}
226
- >
227
- {column.fieldName === 'title' ? (
228
- versionId && currentDocument ? (
229
- <button
230
- type="button"
231
- className={cx(
232
- 'byline-coll-history-title-button',
233
- styles.titleButton
234
- )}
235
- onClick={() =>
236
- setSelectedVersion({
237
- versionId,
238
- label: new Date(document.createdAt).toLocaleString(),
239
- })
240
- }
241
- >
242
- {column.formatter
243
- ? renderFormatted(
237
+ {columns.flatMap((column) => {
238
+ const dataCell = (
239
+ <Table.Cell
240
+ key={String(column.fieldName)}
241
+ className={cx({
242
+ 'byline-coll-history-cell-right': column.align === 'right',
243
+ [styles.cellRight]: column.align === 'right',
244
+ 'byline-coll-history-cell-center': column.align === 'center',
245
+ [styles.cellCenter]: column.align === 'center',
246
+ })}
247
+ >
248
+ {column.fieldName === 'title' ? (
249
+ versionId && currentDocument ? (
250
+ <button
251
+ type="button"
252
+ className={cx(
253
+ 'byline-coll-history-title-button',
254
+ styles.titleButton
255
+ )}
256
+ onClick={() =>
257
+ setSelectedVersion({
258
+ versionId,
259
+ label: new Date(document.createdAt).toLocaleString(),
260
+ })
261
+ }
262
+ >
263
+ {column.formatter
264
+ ? renderFormatted(
244
265
  getColumnValue(document, column.fieldName as string),
245
266
  document,
246
267
  column.formatter
247
268
  )
248
- : resolveDisplayValue(
269
+ : resolveDisplayValue(
249
270
  getColumnValue(document, column.fieldName as string),
250
271
  locale,
251
272
  defaultContentLocale
252
273
  ) || '------'}
253
- </button>
254
- ) : (
255
- <Link
256
- to={'/admin/collections/$collection/$id' as never}
257
- params={{
258
- collection,
259
- id: document.id,
260
- }}
261
- >
262
- {column.formatter
263
- ? renderFormatted(
274
+ </button>
275
+ ) : (
276
+ <Link
277
+ to={'/admin/collections/$collection/$id' as never}
278
+ params={{
279
+ collection,
280
+ id: document.id,
281
+ }}
282
+ >
283
+ {column.formatter
284
+ ? renderFormatted(
264
285
  getColumnValue(document, column.fieldName as string),
265
286
  document,
266
287
  column.formatter
267
288
  )
268
- : resolveDisplayValue(
289
+ : resolveDisplayValue(
269
290
  getColumnValue(document, column.fieldName as string),
270
291
  locale,
271
292
  defaultContentLocale
272
293
  ) || '------'}
273
- </Link>
274
- )
275
- ) : column.formatter ? (
276
- renderFormatted(
277
- getColumnValue(document, column.fieldName as string),
278
- document,
279
- column.formatter
280
- )
281
- ) : column.fieldName === 'status' && workflowStatuses ? (
282
- <StatusBadge
283
- status={document.status}
284
- workflowStatuses={workflowStatuses}
285
- />
286
- ) : (
287
- resolveDisplayValue(
288
- getColumnValue(document, column.fieldName as string),
289
- locale,
290
- defaultContentLocale
291
- ) || ''
292
- )}
293
- </Table.Cell>
294
- ))}
294
+ </Link>
295
+ )
296
+ ) : column.formatter ? (
297
+ renderFormatted(
298
+ getColumnValue(document, column.fieldName as string),
299
+ document,
300
+ column.formatter
301
+ )
302
+ ) : column.fieldName === 'status' && workflowStatuses ? (
303
+ <StatusBadge
304
+ status={document.status}
305
+ workflowStatuses={workflowStatuses}
306
+ />
307
+ ) : (
308
+ resolveDisplayValue(
309
+ getColumnValue(document, column.fieldName as string),
310
+ locale,
311
+ defaultContentLocale
312
+ ) || ''
313
+ )}
314
+ </Table.Cell>
315
+ )
316
+ if (column.fieldName === 'title') {
317
+ return [
318
+ dataCell,
319
+ <Table.Cell
320
+ key="__restore"
321
+ className={cx('byline-coll-history-restore-cell', styles.restoreCell)}
322
+ >
323
+ {versionId && versionId !== currentVersionId ? (
324
+ <Button
325
+ type="button"
326
+ variant="outlined"
327
+ size="xs"
328
+ intent="noeffect"
329
+ onClick={() =>
330
+ setRestoreTarget({
331
+ versionId,
332
+ label: new Date(document.createdAt).toLocaleString(),
333
+ versionNumber,
334
+ })
335
+ }
336
+ className={cx(
337
+ 'byline-coll-history-restore-button',
338
+ styles.restoreButton
339
+ )}
340
+ title="Restore this version as the current draft"
341
+ >
342
+ Restore
343
+ </Button>
344
+ ) : null}
345
+ </Table.Cell>,
346
+ ]
347
+ }
348
+ return [dataCell]
349
+ })}
295
350
  </Table.Row>
296
351
  )
297
352
  })}
@@ -347,6 +402,33 @@ export const HistoryView = ({
347
402
  />
348
403
  </Suspense>
349
404
  )}
405
+
406
+ <Modal
407
+ isOpen={restoreTarget != null}
408
+ onDismiss={() => setRestoreTarget(null)}
409
+ closeOnOverlayClick={false}
410
+ >
411
+ <Modal.Container className={cx('byline-coll-history-restore-modal', styles.restoreModal)}>
412
+ <Modal.Header
413
+ className={cx('byline-coll-history-restore-modal-head', styles.restoreModalHead)}
414
+ >
415
+ <h3 className="m-0">Restore version</h3>
416
+ <IconButton aria-label="Close" size="xs" onClick={() => setRestoreTarget(null)}>
417
+ <CloseIcon width="14px" height="14px" svgClassName="white-icon" />
418
+ </IconButton>
419
+ </Modal.Header>
420
+ {restoreTarget ? (
421
+ <RestoreVersionModal
422
+ collection={collection}
423
+ documentId={id}
424
+ versionId={restoreTarget.versionId}
425
+ versionLabel={restoreTarget.label}
426
+ versionNumber={restoreTarget.versionNumber}
427
+ onClose={() => setRestoreTarget(null)}
428
+ />
429
+ ) : null}
430
+ </Modal.Container>
431
+ </Modal>
350
432
  </>
351
433
  )
352
434
  }
@@ -20,7 +20,7 @@ import {
20
20
  Section,
21
21
  Select,
22
22
  Table,
23
- } from '@byline/ui'
23
+ } from '@byline/ui/react'
24
24
  import { renderFormatted, StatusBadge } from '@byline/ui/react'
25
25
  import cx from 'classnames'
26
26
 
@@ -40,7 +40,7 @@
40
40
  import { useState } from 'react'
41
41
 
42
42
  import type { CollectionAdminConfig, PreviewDocument } from '@byline/core'
43
- import { ExternalLinkIcon, IconButton, useToastManager } from '@byline/ui'
43
+ import { ExternalLinkIcon, IconButton, useToastManager } from '@byline/ui/react'
44
44
  import cx from 'classnames'
45
45
 
46
46
  import { enablePreviewModeFn } from '../../server-fns/preview/index.js'
@@ -0,0 +1,48 @@
1
+ /**
2
+ * RestoreVersionModal — modal body for the "make current" action on the
3
+ * collection history view.
4
+ *
5
+ * Override handles:
6
+ * .byline-coll-restore-content — outer Modal.Content (gap)
7
+ * .byline-coll-restore-body — vertical stack of identity rows
8
+ * .byline-coll-restore-warning — warning paragraph
9
+ * .byline-coll-restore-actions — Cancel/Restore row
10
+ * .byline-coll-restore-button — action buttons (min-width)
11
+ */
12
+
13
+ .content,
14
+ :global(.byline-coll-restore-content) {
15
+ gap: 0.25rem;
16
+ }
17
+
18
+ .body,
19
+ :global(.byline-coll-restore-body) {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 0;
23
+ }
24
+
25
+ .row,
26
+ :global(.byline-coll-restore-row) {
27
+ margin: 0;
28
+ }
29
+
30
+ .warning,
31
+ :global(.byline-coll-restore-warning) {
32
+ margin-top: 0.75rem;
33
+ }
34
+
35
+ .actions,
36
+ :global(.byline-coll-restore-actions) {
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: flex-end;
40
+ gap: 0.5rem;
41
+ margin-top: 1.5rem;
42
+ margin-bottom: 1rem;
43
+ }
44
+
45
+ .button,
46
+ :global(.byline-coll-restore-button) {
47
+ min-width: 4rem;
48
+ }
@@ -0,0 +1,131 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * This Source Code is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ *
8
+ * Copyright (c) Infonomic Company Limited
9
+ */
10
+
11
+ /**
12
+ * Restore-version modal body.
13
+ *
14
+ * Confirmation dialog for the "make current" action on the history view.
15
+ * Calls the restore-version server fn, invalidates the router, and
16
+ * navigates to the document's edit view so the user lands on the freshly
17
+ * restored draft.
18
+ */
19
+
20
+ import { useState } from 'react'
21
+ import { useRouter } from '@tanstack/react-router'
22
+
23
+ import { Alert, Button, LoaderEllipsis, Modal } from '@byline/ui/react'
24
+ import cx from 'classnames'
25
+
26
+ import { restoreDocumentVersion } from '../../server-fns/collections/index.js'
27
+ import { useNavigate } from '../chrome/loose-router.js'
28
+ import styles from './restore-version-modal.module.css'
29
+
30
+ interface RestoreVersionModalProps {
31
+ collection: string
32
+ documentId: string
33
+ versionId: string
34
+ versionLabel: string
35
+ versionNumber: number
36
+ onClose: () => void
37
+ }
38
+
39
+ export function RestoreVersionModal({
40
+ collection,
41
+ documentId,
42
+ versionId,
43
+ versionLabel,
44
+ versionNumber,
45
+ onClose,
46
+ }: RestoreVersionModalProps) {
47
+ const navigate = useNavigate()
48
+ const router = useRouter()
49
+ const [error, setError] = useState<string | null>(null)
50
+ const [pending, setPending] = useState(false)
51
+
52
+ async function handleRestore() {
53
+ if (pending) return
54
+ setPending(true)
55
+ setError(null)
56
+ try {
57
+ await restoreDocumentVersion({
58
+ data: { collection, id: documentId, versionId },
59
+ })
60
+ onClose()
61
+ await router.invalidate()
62
+ navigate({
63
+ to: '/admin/collections/$collection/$id' as never,
64
+ params: { collection, id: documentId },
65
+ })
66
+ } catch (err) {
67
+ const code = getErrorCode(err)
68
+ if (code === 'ERR_INVALID_TRANSITION') {
69
+ setError('This version is already the current version of the document.')
70
+ } else if (code === 'ERR_NOT_FOUND') {
71
+ setError('The selected version could not be found. The history may be out of date.')
72
+ } else if (code === 'ERR_FORBIDDEN' || code === 'ERR_UNAUTHENTICATED') {
73
+ setError('You do not have permission to restore versions for this collection.')
74
+ } else {
75
+ setError('Could not restore this version. Please try again.')
76
+ }
77
+ setPending(false)
78
+ }
79
+ }
80
+
81
+ return (
82
+ <Modal.Content className={cx('byline-coll-restore-content', styles.content)}>
83
+ <div className={cx('byline-coll-restore-body', styles.body)}>
84
+ {error ? (
85
+ <Alert intent="danger" close={false}>
86
+ {error}
87
+ </Alert>
88
+ ) : null}
89
+ <p className={cx('byline-coll-restore-row', styles.row)}>
90
+ <span className="muted">Version:</span> {versionNumber}
91
+ </p>
92
+ <p className={cx('byline-coll-restore-row', styles.row)}>
93
+ <span className="muted">Created:</span> {versionLabel}
94
+ </p>
95
+ <p className={cx('byline-coll-restore-warning', styles.warning)}>
96
+ This will create a new draft version of this document with the content from version{' '}
97
+ {versionNumber}, and that draft will become the current version. The existing versions
98
+ (including any published version) are preserved in history. The restored draft will need
99
+ to be published through the normal workflow.
100
+ </p>
101
+ </div>
102
+ <div className={cx('byline-coll-restore-actions', styles.actions)}>
103
+ <Button
104
+ type="button"
105
+ intent="secondary"
106
+ size="sm"
107
+ onClick={onClose}
108
+ disabled={pending}
109
+ className={cx('byline-coll-restore-button', styles.button)}
110
+ >
111
+ Cancel
112
+ </Button>
113
+ <Button
114
+ size="sm"
115
+ intent="primary"
116
+ onClick={handleRestore}
117
+ disabled={pending}
118
+ className={cx('byline-coll-restore-button', styles.button)}
119
+ >
120
+ {pending === true ? <LoaderEllipsis size={42} /> : 'Restore as Draft'}
121
+ </Button>
122
+ </div>
123
+ </Modal.Content>
124
+ )
125
+ }
126
+
127
+ function getErrorCode(err: unknown): string | null {
128
+ return typeof (err as { code?: unknown })?.code === 'string'
129
+ ? (err as { code: string }).code
130
+ : null
131
+ }
@@ -9,7 +9,7 @@
9
9
  import { useEffect } from 'react'
10
10
 
11
11
  import type { CollectionAdminConfig, PreviewDocument } from '@byline/core'
12
- import { Button, HistoryIcon, IconButton, Label, Select } from '@byline/ui'
12
+ import { Button, HistoryIcon, IconButton, Label, Select } from '@byline/ui/react'
13
13
  import cx from 'classnames'
14
14
 
15
15
  import { useNavigate } from '../chrome/loose-router.js'
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { createFileRoute } from '@tanstack/react-router'
10
10
 
11
- import { Container, Section } from '@byline/ui'
11
+ import { Container, Section } from '@byline/ui/react'
12
12
  import { AccountSelfContainer } from '@byline/ui/react'
13
13
 
14
14
  import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { createFileRoute, notFound } from '@tanstack/react-router'
10
10
 
11
- import { Container, Section } from '@byline/ui'
11
+ import { Container, Section } from '@byline/ui/react'
12
12
 
13
13
  import { RoleContainer } from '../admin-shell/admin-roles/container.js'
14
14
  import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { createFileRoute, notFound } from '@tanstack/react-router'
10
10
 
11
- import { Container, Section } from '@byline/ui'
11
+ import { Container, Section } from '@byline/ui/react'
12
12
 
13
13
  import { AccountContainer } from '../admin-shell/admin-users/container.js'
14
14
  import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
@@ -15,7 +15,7 @@ import {
15
15
  getCollectionDefinition,
16
16
  getWorkflowStatuses,
17
17
  } from '@byline/core'
18
- import { useToastManager } from '@byline/ui'
18
+ import { useToastManager } from '@byline/ui/react'
19
19
  import { z } from 'zod'
20
20
 
21
21
  import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
@@ -21,11 +21,9 @@
21
21
  import { createServerFn } from '@tanstack/react-start'
22
22
 
23
23
  import { type Actor, AuthError } from '@byline/auth'
24
- import { getServerConfig } from '@byline/core'
25
- import type { PgAdapter } from '@byline/db-postgres'
26
- import { createAdminUsersRepository } from '@byline/db-postgres/admin'
27
24
 
28
25
  import { getAdminRequestContext } from '../../auth/auth-context.js'
26
+ import { bylineCore } from '../../integrations/byline-core.js'
29
27
 
30
28
  export interface CurrentAdminUser {
31
29
  id: string
@@ -57,9 +55,7 @@ export const getCurrentAdminUser = createServerFn({ method: 'GET' }).handler(
57
55
  throw new Error('unexpected null actor after getAdminRequestContext')
58
56
  }
59
57
 
60
- const db = (getServerConfig().db as PgAdapter).drizzle
61
- const users = createAdminUsersRepository(db)
62
- const row = await users.getById(actor.id)
58
+ const row = await bylineCore().adminStore!.adminUsers.getById(actor.id)
63
59
  if (!row) {
64
60
  // Session resolved to an admin id that no longer exists — force the
65
61
  // caller back through sign-in rather than return partial data.
@@ -97,9 +93,7 @@ export const getCurrentAdminUserSoft = createServerFn({ method: 'GET' }).handler
97
93
  }
98
94
  if (!actor) return null
99
95
 
100
- const db = (getServerConfig().db as PgAdapter).drizzle
101
- const users = createAdminUsersRepository(db)
102
- const row = await users.getById(actor.id)
96
+ const row = await bylineCore().adminStore!.adminUsers.getById(actor.id)
103
97
  if (!row) return null
104
98
 
105
99
  return {
@@ -10,6 +10,7 @@ export * from './delete'
10
10
  export * from './get'
11
11
  export * from './history'
12
12
  export * from './list'
13
+ export * from './restore-version'
13
14
  export * from './stats'
14
15
  export * from './status'
15
16
  export * from './update'
@@ -0,0 +1,59 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { createServerFn } from '@tanstack/react-start'
10
+
11
+ import { ERR_NOT_FOUND, getLogger, getServerConfig } from '@byline/core'
12
+ import type { DocumentLifecycleContext } from '@byline/core/services'
13
+ import { restoreDocumentVersion as restoreDocumentVersionService } from '@byline/core/services'
14
+
15
+ import { getAdminRequestContext } from '../../auth/auth-context.js'
16
+ import { ensureCollection } from '../../integrations/api-utils.js'
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Restore a historical document version as the new current version
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export const restoreDocumentVersion = createServerFn({ method: 'POST' })
23
+ .inputValidator((input: { collection: string; id: string; versionId: string }) => input)
24
+ .handler(async ({ data: input }) => {
25
+ const { collection: path, id, versionId } = input
26
+ const logger = getLogger()
27
+ const config = await ensureCollection(path)
28
+ if (!config) {
29
+ throw ERR_NOT_FOUND({
30
+ message: 'Collection not found',
31
+ details: { collectionPath: path },
32
+ }).log(logger)
33
+ }
34
+
35
+ const serverConfig = getServerConfig()
36
+ const ctx: DocumentLifecycleContext = {
37
+ db: serverConfig.db,
38
+ definition: config.definition,
39
+ collectionId: config.collection.id,
40
+ collectionVersion: config.collection.version,
41
+ collectionPath: path,
42
+ logger,
43
+ defaultLocale: serverConfig.i18n.content.defaultLocale,
44
+ slugifier: serverConfig.slugifier,
45
+ requestContext: await getAdminRequestContext(),
46
+ }
47
+
48
+ const result = await restoreDocumentVersionService(ctx, {
49
+ documentId: id,
50
+ sourceVersionId: versionId,
51
+ })
52
+
53
+ return {
54
+ status: 'ok' as const,
55
+ documentId: result.documentId,
56
+ documentVersionId: result.documentVersionId,
57
+ sourceVersionId: result.sourceVersionId,
58
+ }
59
+ })
@@ -10,7 +10,7 @@ import type {
10
10
  import { ERR_NOT_FOUND, ERR_VALIDATION, getServerConfig, getUploadFields } from '@byline/core'
11
11
  import { getLogger, withLogContext } from '@byline/core/logger'
12
12
  import { uploadField as coreUploadField } from '@byline/core/services'
13
- import { extractImageMeta, generateImageVariants, isBypassMimeType } from '@byline/storage-local'
13
+ import { extractImageMeta, generateImageVariants, isBypassMimeType } from '@byline/core/image'
14
14
 
15
15
  import { getAdminRequestContext } from '../../auth/auth-context.js'
16
16
  import { ensureCollection } from '../../integrations/api-utils.js'