@byline/host-tanstack-start 3.9.0 → 3.10.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 (97) hide show
  1. package/dist/admin-shell/collections/document-history.d.ts +53 -0
  2. package/dist/admin-shell/collections/document-history.js +103 -0
  3. package/dist/admin-shell/collections/document-history.module.js +9 -0
  4. package/dist/admin-shell/collections/document-history_module.css +28 -0
  5. package/dist/admin-shell/collections/history.d.ts +8 -1
  6. package/dist/admin-shell/collections/history.js +73 -29
  7. package/dist/admin-shell/collections/history.module.js +1 -0
  8. package/dist/admin-shell/collections/history_module.css +4 -0
  9. package/dist/routes/create-collection-history-route.js +23 -6
  10. package/dist/server-fns/admin-account/change-password.js +1 -1
  11. package/dist/server-fns/admin-account/get.js +1 -1
  12. package/dist/server-fns/admin-account/update.js +1 -1
  13. package/dist/server-fns/admin-permissions/get-role-abilities.js +1 -1
  14. package/dist/server-fns/admin-permissions/set-role-abilities.js +1 -1
  15. package/dist/server-fns/admin-permissions/who-has.js +1 -1
  16. package/dist/server-fns/admin-roles/create.js +1 -1
  17. package/dist/server-fns/admin-roles/delete.js +1 -1
  18. package/dist/server-fns/admin-roles/get.js +1 -1
  19. package/dist/server-fns/admin-roles/reorder.js +1 -1
  20. package/dist/server-fns/admin-roles/update.js +1 -1
  21. package/dist/server-fns/admin-users/create.js +1 -1
  22. package/dist/server-fns/admin-users/delete.js +1 -1
  23. package/dist/server-fns/admin-users/disable.js +1 -1
  24. package/dist/server-fns/admin-users/enable.js +1 -1
  25. package/dist/server-fns/admin-users/get-user-roles.js +1 -1
  26. package/dist/server-fns/admin-users/get.js +1 -1
  27. package/dist/server-fns/admin-users/list.js +1 -1
  28. package/dist/server-fns/admin-users/set-password.js +1 -1
  29. package/dist/server-fns/admin-users/set-user-roles.js +1 -1
  30. package/dist/server-fns/admin-users/update.js +1 -1
  31. package/dist/server-fns/ai/execute.js +1 -1
  32. package/dist/server-fns/auth/sign-in.js +1 -1
  33. package/dist/server-fns/collections/audit.d.ts +50 -0
  34. package/dist/server-fns/collections/audit.js +40 -0
  35. package/dist/server-fns/collections/copy-to-locale.js +1 -1
  36. package/dist/server-fns/collections/create.js +1 -1
  37. package/dist/server-fns/collections/delete-locale.js +1 -1
  38. package/dist/server-fns/collections/delete.js +1 -1
  39. package/dist/server-fns/collections/duplicate.js +1 -1
  40. package/dist/server-fns/collections/get.js +2 -2
  41. package/dist/server-fns/collections/history.js +1 -1
  42. package/dist/server-fns/collections/index.d.ts +1 -0
  43. package/dist/server-fns/collections/index.js +1 -0
  44. package/dist/server-fns/collections/list.js +1 -1
  45. package/dist/server-fns/collections/reorder.js +1 -1
  46. package/dist/server-fns/collections/restore-version.js +1 -1
  47. package/dist/server-fns/collections/stats.js +1 -1
  48. package/dist/server-fns/collections/status.js +2 -2
  49. package/dist/server-fns/collections/update.js +2 -2
  50. package/dist/server-fns/collections/upload.js +1 -1
  51. package/dist/server-fns/i18n/set-locale.js +1 -1
  52. package/package.json +10 -10
  53. package/src/admin-shell/collections/document-history.module.css +46 -0
  54. package/src/admin-shell/collections/document-history.tsx +156 -0
  55. package/src/admin-shell/collections/history.module.css +6 -0
  56. package/src/admin-shell/collections/history.tsx +331 -281
  57. package/src/routes/create-collection-history-route.tsx +25 -3
  58. package/src/server-fns/admin-account/change-password.ts +1 -1
  59. package/src/server-fns/admin-account/get.ts +1 -1
  60. package/src/server-fns/admin-account/update.ts +1 -1
  61. package/src/server-fns/admin-permissions/get-role-abilities.ts +1 -1
  62. package/src/server-fns/admin-permissions/set-role-abilities.ts +1 -1
  63. package/src/server-fns/admin-permissions/who-has.ts +1 -1
  64. package/src/server-fns/admin-roles/create.ts +1 -1
  65. package/src/server-fns/admin-roles/delete.ts +1 -1
  66. package/src/server-fns/admin-roles/get.ts +1 -1
  67. package/src/server-fns/admin-roles/reorder.ts +1 -1
  68. package/src/server-fns/admin-roles/update.ts +1 -1
  69. package/src/server-fns/admin-users/create.ts +1 -1
  70. package/src/server-fns/admin-users/delete.ts +1 -1
  71. package/src/server-fns/admin-users/disable.ts +1 -1
  72. package/src/server-fns/admin-users/enable.ts +1 -1
  73. package/src/server-fns/admin-users/get-user-roles.ts +1 -1
  74. package/src/server-fns/admin-users/get.ts +1 -1
  75. package/src/server-fns/admin-users/list.ts +1 -1
  76. package/src/server-fns/admin-users/set-password.ts +1 -1
  77. package/src/server-fns/admin-users/set-user-roles.ts +1 -1
  78. package/src/server-fns/admin-users/update.ts +1 -1
  79. package/src/server-fns/ai/execute.ts +1 -1
  80. package/src/server-fns/auth/sign-in.ts +1 -1
  81. package/src/server-fns/collections/audit.ts +95 -0
  82. package/src/server-fns/collections/copy-to-locale.ts +1 -1
  83. package/src/server-fns/collections/create.ts +1 -1
  84. package/src/server-fns/collections/delete-locale.ts +1 -1
  85. package/src/server-fns/collections/delete.ts +1 -1
  86. package/src/server-fns/collections/duplicate.ts +1 -1
  87. package/src/server-fns/collections/get.ts +2 -2
  88. package/src/server-fns/collections/history.ts +1 -1
  89. package/src/server-fns/collections/index.ts +1 -0
  90. package/src/server-fns/collections/list.ts +1 -1
  91. package/src/server-fns/collections/reorder.ts +1 -1
  92. package/src/server-fns/collections/restore-version.ts +1 -1
  93. package/src/server-fns/collections/stats.ts +1 -1
  94. package/src/server-fns/collections/status.ts +2 -2
  95. package/src/server-fns/collections/update.ts +2 -2
  96. package/src/server-fns/collections/upload.ts +1 -1
  97. package/src/server-fns/i18n/set-locale.ts +1 -1
@@ -9,7 +9,7 @@
9
9
  import { Fragment, lazy, Suspense, useState } from 'react'
10
10
  import { useParams, useRouterState } from '@tanstack/react-router'
11
11
 
12
- import { renderFormatted, StatusBadge } from '@byline/admin/react'
12
+ import { AdminTabs, renderFormatted, StatusBadge } from '@byline/admin/react'
13
13
  import { useBylineAdminServices } from '@byline/admin/services'
14
14
  import type { CollectionAdminConfig, CollectionDefinition, WorkflowStatus } from '@byline/core'
15
15
  import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
@@ -30,6 +30,7 @@ import { Link, useNavigate } from '../chrome/loose-router.js'
30
30
  import { RouterPager } from '../chrome/router-pager.js'
31
31
  import { TableHeadingCellSortable } from '../chrome/th-sortable.js'
32
32
  import { formatNumber } from '../chrome/utils.js'
33
+ import { type DocumentHistoryData, DocumentHistoryView } from './document-history.js'
33
34
  import styles from './history.module.css'
34
35
  import { RestoreVersionModal } from './restore-version-modal.js'
35
36
  import { ViewMenu } from './view-menu.js'
@@ -110,6 +111,7 @@ export const HistoryView = ({
110
111
  collectionDefinition,
111
112
  adminConfig,
112
113
  data,
114
+ auditLog,
113
115
  workflowStatuses,
114
116
  currentDocument,
115
117
  contentLocales,
@@ -125,6 +127,12 @@ export const HistoryView = ({
125
127
  */
126
128
  actors?: Record<string, { label: string }>
127
129
  }
130
+ /**
131
+ * Document-grain audit log for the "Document history" tab (docs/AUDIT.md —
132
+ * Workstream 3): the non-versioned path / available-locales / status
133
+ * changes and the deletion event, with admin-resolved actor labels.
134
+ */
135
+ auditLog?: DocumentHistoryData
128
136
  workflowStatuses?: WorkflowStatus[]
129
137
  currentDocument?: Record<string, unknown> | null
130
138
  contentLocales: ReadonlyArray<ContentLocaleOption>
@@ -144,6 +152,10 @@ export const HistoryView = ({
144
152
  const titleFieldName = collectionDefinition.useAsTitle
145
153
  const location = useRouterState({ select: (s) => s.location })
146
154
  const locale = (location.search as { locale?: string }).locale
155
+ // Which sub-view is active (docs/AUDIT.md — W3). Absent / anything but
156
+ // 'document' → the content version stream.
157
+ const activeTab =
158
+ (location.search as { tab?: string }).tab === 'document' ? 'document' : 'versions'
147
159
  const [selectedVersion, setSelectedVersion] = useState<{
148
160
  versionId: string
149
161
  label: string
@@ -158,6 +170,23 @@ export const HistoryView = ({
158
170
  ? (currentDocument.versionId as string)
159
171
  : null
160
172
 
173
+ function handleTabChange(name: string): void {
174
+ // Switching tabs resets pagination; 'versions' (the default) drops the
175
+ // `tab` param entirely to keep the URL clean.
176
+ const params = structuredClone(location.search)
177
+ delete params.page
178
+ if (name === 'document') {
179
+ params.tab = 'document'
180
+ } else {
181
+ delete params.tab
182
+ }
183
+ navigate({
184
+ to: '/admin/collections/$collection/$id/history' as never,
185
+ params: { collection, id } as never,
186
+ search: params,
187
+ })
188
+ }
189
+
161
190
  function handleOnPageSizeChange(value: string | null): void {
162
191
  if (typeof value !== 'string' || value.length === 0) return
163
192
  const params = structuredClone(location.search)
@@ -177,7 +206,9 @@ export const HistoryView = ({
177
206
  <div className={cx('byline-coll-history-head', styles.head)}>
178
207
  <h2 className={cx('byline-coll-history-title', styles.title)}>
179
208
  {t('collections.history.title', { label: labels.singular })}{' '}
180
- <Stats total={data?.meta.total} />
209
+ <Stats
210
+ total={activeTab === 'document' ? (auditLog?.meta.total ?? 0) : data?.meta.total}
211
+ />
181
212
  </h2>
182
213
  <ViewMenu
183
214
  collection={collection}
@@ -188,293 +219,312 @@ export const HistoryView = ({
188
219
  defaultContentLocale={defaultContentLocale}
189
220
  />
190
221
  </div>
222
+ <AdminTabs
223
+ tabs={[
224
+ { name: 'versions', label: t('collections.history.tabs.versions') },
225
+ { name: 'document', label: t('collections.history.tabs.document') },
226
+ ]}
227
+ activeTab={activeTab}
228
+ onChange={handleTabChange}
229
+ className={cx('byline-coll-history-tabs', styles.tabs)}
230
+ />
191
231
  </Container>
192
232
  </Section>
193
- <Section>
194
- <Container>
195
- <div className={cx('byline-coll-history-options', styles.options)}>
196
- <RouterPager
197
- page={data?.meta.page}
198
- count={data?.meta.totalPages}
199
- showFirstButton
200
- showLastButton
201
- componentName="pagerTop"
202
- aria-label={t('collections.list.pagerTopAriaLabel')}
203
- />
204
- </div>
205
- <Table.Container className={cx('byline-coll-history-table-wrap', styles.tableWrap)}>
206
- <Table>
207
- <Table.Header>
208
- <Table.Row>
209
- <th
210
- scope="col"
211
- className={cx('byline-coll-history-col-version', styles.colVersion)}
212
- />
213
- {columns.flatMap((column) => {
214
- const cell = (
215
- <TableHeadingCellSortable
216
- key={String(column.fieldName)}
217
- fieldName={String(column.fieldName)}
218
- label={column.label}
219
- sortable={column.sortable}
220
- scope="col"
221
- align={column.align}
222
- className={column.className}
223
- />
224
- )
225
- if (titleFieldName != null && column.fieldName === titleFieldName) {
226
- return [
227
- cell,
228
- <th
229
- key="__restore"
233
+ {activeTab === 'document' && (
234
+ <DocumentHistoryView
235
+ data={
236
+ auditLog ?? { entries: [], meta: { total: 0, page: 1, pageSize: 0, totalPages: 0 } }
237
+ }
238
+ />
239
+ )}
240
+
241
+ {activeTab !== 'document' && (
242
+ <Section>
243
+ <Container>
244
+ <div className={cx('byline-coll-history-options', styles.options)}>
245
+ <RouterPager
246
+ page={data?.meta.page}
247
+ count={data?.meta.totalPages}
248
+ showFirstButton
249
+ showLastButton
250
+ componentName="pagerTop"
251
+ aria-label={t('collections.list.pagerTopAriaLabel')}
252
+ />
253
+ </div>
254
+ <Table.Container className={cx('byline-coll-history-table-wrap', styles.tableWrap)}>
255
+ <Table>
256
+ <Table.Header>
257
+ <Table.Row>
258
+ <th
259
+ scope="col"
260
+ className={cx('byline-coll-history-col-version', styles.colVersion)}
261
+ />
262
+ {columns.flatMap((column) => {
263
+ const cell = (
264
+ <TableHeadingCellSortable
265
+ key={String(column.fieldName)}
266
+ fieldName={String(column.fieldName)}
267
+ label={column.label}
268
+ sortable={column.sortable}
230
269
  scope="col"
231
- className={cx('byline-coll-history-col-restore', styles.colRestore)}
232
- />,
233
- ]
234
- }
235
- return [cell]
236
- })}
237
- </Table.Row>
238
- </Table.Header>
270
+ align={column.align}
271
+ className={column.className}
272
+ />
273
+ )
274
+ if (titleFieldName != null && column.fieldName === titleFieldName) {
275
+ return [
276
+ cell,
277
+ <th
278
+ key="__restore"
279
+ scope="col"
280
+ className={cx('byline-coll-history-col-restore', styles.colRestore)}
281
+ />,
282
+ ]
283
+ }
284
+ return [cell]
285
+ })}
286
+ </Table.Row>
287
+ </Table.Header>
239
288
 
240
- <Table.Body>
241
- {data?.docs?.map((document, rowIndex) => {
242
- const versionId = document.versionId
243
- const { total, page, pageSize, desc } = data.meta
244
- const versionNumber = desc
245
- ? total - (page - 1) * pageSize - rowIndex
246
- : (page - 1) * pageSize + rowIndex + 1
247
- // Audit strip (docs/AUDIT.md — W1): who created this
248
- // version, via which action. A present-but-unresolved id
249
- // is a deleted user; an absent id is a row written before
250
- // audit wiring or an internal-tooling write.
251
- const actorLabel = document.createdBy
252
- ? (data.actors?.[document.createdBy]?.label ??
253
- t('collections.history.audit.formerUser'))
254
- : t('collections.history.audit.unknown')
255
- const actionKey = document.eventType
256
- ? AUDIT_ACTION_KEYS[document.eventType]
257
- : undefined
258
- const actionLabel = actionKey ? t(actionKey) : (document.eventType ?? '')
259
- // The strip starts on the second column — an empty spacer
260
- // cell sits under the version-number column, then one cell
261
- // spans the data columns plus the restore cell appended
262
- // after the identity column when present.
263
- const auditColSpan =
264
- columns.length +
265
- (titleFieldName != null && columns.some((c) => c.fieldName === titleFieldName)
266
- ? 1
267
- : 0)
268
- return (
269
- <Fragment key={versionId ?? document.id}>
270
- <Table.Row className={cx('byline-coll-history-row', styles.historyRow)}>
271
- <Table.Cell
272
- className={cx('byline-coll-history-version-cell', styles.versionCell)}
273
- >
274
- {versionId && currentDocument ? (
275
- <IconButton
276
- size="xs"
277
- variant="outlined"
278
- intent="noeffect"
279
- aria-label={t('collections.history.compareAriaLabel')}
280
- title={t('collections.history.compareTitle')}
281
- className={cx(
282
- 'byline-coll-history-version-button',
283
- styles.versionButton
284
- )}
285
- onClick={() =>
286
- setSelectedVersion({
287
- versionId,
288
- label: new Date(document.createdAt).toLocaleString(),
289
- })
290
- }
291
- >
292
- {versionNumber}
293
- </IconButton>
294
- ) : null}
295
- </Table.Cell>
296
- {columns.flatMap((column) => {
297
- const dataCell = (
298
- <Table.Cell
299
- key={String(column.fieldName)}
300
- className={cx({
301
- 'byline-coll-history-cell-right': column.align === 'right',
302
- [styles.cellRight]: column.align === 'right',
303
- 'byline-coll-history-cell-center': column.align === 'center',
304
- [styles.cellCenter]: column.align === 'center',
305
- })}
306
- >
307
- {titleFieldName != null && column.fieldName === titleFieldName ? (
308
- versionId && currentDocument ? (
309
- <button
310
- type="button"
311
- className={cx(
312
- 'byline-coll-history-title-button',
313
- styles.titleButton
314
- )}
315
- onClick={() =>
316
- setSelectedVersion({
317
- versionId,
318
- label: new Date(document.createdAt).toLocaleString(),
319
- })
320
- }
321
- >
322
- {column.formatter
323
- ? renderFormatted(
324
- getColumnValue(document, column.fieldName as string),
325
- document,
326
- column.formatter
327
- )
328
- : resolveDisplayValue(
329
- getColumnValue(document, column.fieldName as string),
330
- locale,
331
- defaultContentLocale
332
- ) || '------'}
333
- </button>
334
- ) : (
335
- <Link
336
- to={'/admin/collections/$collection/$id' as never}
337
- params={{
338
- collection,
339
- id: document.id,
340
- }}
341
- >
342
- {column.formatter
343
- ? renderFormatted(
344
- getColumnValue(document, column.fieldName as string),
345
- document,
346
- column.formatter
347
- )
348
- : resolveDisplayValue(
349
- getColumnValue(document, column.fieldName as string),
350
- locale,
351
- defaultContentLocale
352
- ) || '------'}
353
- </Link>
354
- )
355
- ) : column.formatter ? (
356
- renderFormatted(
357
- getColumnValue(document, column.fieldName as string),
358
- document,
359
- column.formatter
360
- )
361
- ) : column.fieldName === 'status' && workflowStatuses ? (
362
- <StatusBadge
363
- status={document.status}
364
- workflowStatuses={workflowStatuses}
365
- />
366
- ) : (
367
- resolveDisplayValue(
368
- getColumnValue(document, column.fieldName as string),
369
- locale,
370
- defaultContentLocale
371
- ) || ''
372
- )}
373
- </Table.Cell>
374
- )
375
- if (titleFieldName != null && column.fieldName === titleFieldName) {
376
- return [
377
- dataCell,
378
- <Table.Cell
379
- key="__restore"
289
+ <Table.Body>
290
+ {data?.docs?.map((document, rowIndex) => {
291
+ const versionId = document.versionId
292
+ const { total, page, pageSize, desc } = data.meta
293
+ const versionNumber = desc
294
+ ? total - (page - 1) * pageSize - rowIndex
295
+ : (page - 1) * pageSize + rowIndex + 1
296
+ // Audit strip (docs/AUDIT.md — W1): who created this
297
+ // version, via which action. A present-but-unresolved id
298
+ // is a deleted user; an absent id is a row written before
299
+ // audit wiring or an internal-tooling write.
300
+ const actorLabel = document.createdBy
301
+ ? (data.actors?.[document.createdBy]?.label ??
302
+ t('collections.history.audit.formerUser'))
303
+ : t('collections.history.audit.unknown')
304
+ const actionKey = document.eventType
305
+ ? AUDIT_ACTION_KEYS[document.eventType]
306
+ : undefined
307
+ const actionLabel = actionKey ? t(actionKey) : (document.eventType ?? '')
308
+ // The strip starts on the second column — an empty spacer
309
+ // cell sits under the version-number column, then one cell
310
+ // spans the data columns plus the restore cell appended
311
+ // after the identity column when present.
312
+ const auditColSpan =
313
+ columns.length +
314
+ (titleFieldName != null && columns.some((c) => c.fieldName === titleFieldName)
315
+ ? 1
316
+ : 0)
317
+ return (
318
+ <Fragment key={versionId ?? document.id}>
319
+ <Table.Row className={cx('byline-coll-history-row', styles.historyRow)}>
320
+ <Table.Cell
321
+ className={cx('byline-coll-history-version-cell', styles.versionCell)}
322
+ >
323
+ {versionId && currentDocument ? (
324
+ <IconButton
325
+ size="xs"
326
+ variant="outlined"
327
+ intent="noeffect"
328
+ aria-label={t('collections.history.compareAriaLabel')}
329
+ title={t('collections.history.compareTitle')}
380
330
  className={cx(
381
- 'byline-coll-history-restore-cell',
382
- styles.restoreCell
331
+ 'byline-coll-history-version-button',
332
+ styles.versionButton
383
333
  )}
334
+ onClick={() =>
335
+ setSelectedVersion({
336
+ versionId,
337
+ label: new Date(document.createdAt).toLocaleString(),
338
+ })
339
+ }
384
340
  >
385
- {versionId && versionId !== currentVersionId ? (
386
- <Button
387
- type="button"
388
- variant="outlined"
389
- size="xs"
390
- intent="noeffect"
391
- onClick={() =>
392
- setRestoreTarget({
393
- versionId,
394
- label: new Date(document.createdAt).toLocaleString(),
395
- versionNumber,
396
- })
397
- }
398
- className={cx(
399
- 'byline-coll-history-restore-button',
400
- styles.restoreButton
401
- )}
402
- title={t('collections.history.restoreButtonTitle')}
403
- >
404
- {t('collections.history.restoreButton')}
405
- </Button>
406
- ) : null}
407
- </Table.Cell>,
408
- ]
409
- }
410
- return [dataCell]
411
- })}
412
- </Table.Row>
413
- <Table.Row
414
- className={cx('byline-coll-history-audit-row', styles.auditRow)}
415
- aria-label={t('collections.history.audit.createdBy', {
416
- label: actorLabel,
417
- })}
418
- >
419
- <Table.Cell
420
- className={cx(
421
- 'byline-coll-history-audit-spacer-cell',
422
- styles.auditSpacerCell
423
- )}
424
- />
425
- <Table.Cell
426
- colSpan={auditColSpan}
427
- className={cx('byline-coll-history-audit-cell', styles.auditCell)}
341
+ {versionNumber}
342
+ </IconButton>
343
+ ) : null}
344
+ </Table.Cell>
345
+ {columns.flatMap((column) => {
346
+ const dataCell = (
347
+ <Table.Cell
348
+ key={String(column.fieldName)}
349
+ className={cx({
350
+ 'byline-coll-history-cell-right': column.align === 'right',
351
+ [styles.cellRight]: column.align === 'right',
352
+ 'byline-coll-history-cell-center': column.align === 'center',
353
+ [styles.cellCenter]: column.align === 'center',
354
+ })}
355
+ >
356
+ {titleFieldName != null && column.fieldName === titleFieldName ? (
357
+ versionId && currentDocument ? (
358
+ <button
359
+ type="button"
360
+ className={cx(
361
+ 'byline-coll-history-title-button',
362
+ styles.titleButton
363
+ )}
364
+ onClick={() =>
365
+ setSelectedVersion({
366
+ versionId,
367
+ label: new Date(document.createdAt).toLocaleString(),
368
+ })
369
+ }
370
+ >
371
+ {column.formatter
372
+ ? renderFormatted(
373
+ getColumnValue(document, column.fieldName as string),
374
+ document,
375
+ column.formatter
376
+ )
377
+ : resolveDisplayValue(
378
+ getColumnValue(document, column.fieldName as string),
379
+ locale,
380
+ defaultContentLocale
381
+ ) || '------'}
382
+ </button>
383
+ ) : (
384
+ <Link
385
+ to={'/admin/collections/$collection/$id' as never}
386
+ params={{
387
+ collection,
388
+ id: document.id,
389
+ }}
390
+ >
391
+ {column.formatter
392
+ ? renderFormatted(
393
+ getColumnValue(document, column.fieldName as string),
394
+ document,
395
+ column.formatter
396
+ )
397
+ : resolveDisplayValue(
398
+ getColumnValue(document, column.fieldName as string),
399
+ locale,
400
+ defaultContentLocale
401
+ ) || '------'}
402
+ </Link>
403
+ )
404
+ ) : column.formatter ? (
405
+ renderFormatted(
406
+ getColumnValue(document, column.fieldName as string),
407
+ document,
408
+ column.formatter
409
+ )
410
+ ) : column.fieldName === 'status' && workflowStatuses ? (
411
+ <StatusBadge
412
+ status={document.status}
413
+ workflowStatuses={workflowStatuses}
414
+ />
415
+ ) : (
416
+ resolveDisplayValue(
417
+ getColumnValue(document, column.fieldName as string),
418
+ locale,
419
+ defaultContentLocale
420
+ ) || ''
421
+ )}
422
+ </Table.Cell>
423
+ )
424
+ if (titleFieldName != null && column.fieldName === titleFieldName) {
425
+ return [
426
+ dataCell,
427
+ <Table.Cell
428
+ key="__restore"
429
+ className={cx(
430
+ 'byline-coll-history-restore-cell',
431
+ styles.restoreCell
432
+ )}
433
+ >
434
+ {versionId && versionId !== currentVersionId ? (
435
+ <Button
436
+ type="button"
437
+ variant="outlined"
438
+ size="xs"
439
+ intent="noeffect"
440
+ onClick={() =>
441
+ setRestoreTarget({
442
+ versionId,
443
+ label: new Date(document.createdAt).toLocaleString(),
444
+ versionNumber,
445
+ })
446
+ }
447
+ className={cx(
448
+ 'byline-coll-history-restore-button',
449
+ styles.restoreButton
450
+ )}
451
+ title={t('collections.history.restoreButtonTitle')}
452
+ >
453
+ {t('collections.history.restoreButton')}
454
+ </Button>
455
+ ) : null}
456
+ </Table.Cell>,
457
+ ]
458
+ }
459
+ return [dataCell]
460
+ })}
461
+ </Table.Row>
462
+ <Table.Row
463
+ className={cx('byline-coll-history-audit-row', styles.auditRow)}
464
+ aria-label={t('collections.history.audit.createdBy', {
465
+ label: actorLabel,
466
+ })}
428
467
  >
429
- <span className={cx('byline-coll-history-audit', styles.audit)}>
430
- {actionLabel}
431
- {' · '}
432
- {t('collections.history.audit.createdBy', { label: actorLabel })}
433
- {' · '}
434
- {new Date(document.createdAt).toLocaleString()}
435
- </span>
436
- </Table.Cell>
437
- </Table.Row>
438
- </Fragment>
439
- )
440
- })}
441
- </Table.Body>
442
- </Table>
443
- {padRows(6 - (data?.docs?.length ?? 0))}
444
- </Table.Container>
445
- <div
446
- className={cx(
447
- 'byline-coll-history-options byline-coll-history-options-bottom',
448
- styles.options,
449
- styles.optionsBottom
450
- )}
451
- >
452
- <Select<string>
453
- containerClassName={cx('byline-coll-history-page-size', styles.pageSize)}
454
- id="page_size"
455
- name="page_size"
456
- size="sm"
457
- defaultValue="15"
458
- items={[
459
- { value: '15', label: '15' },
460
- { value: '30', label: '30' },
461
- { value: '50', label: '50' },
462
- { value: '100', label: '100' },
463
- ]}
464
- onValueChange={handleOnPageSizeChange}
465
- />
466
- <RouterPager
467
- smoothScrollToTop={true}
468
- page={data?.meta.page}
469
- count={data?.meta.totalPages}
470
- showFirstButton
471
- showLastButton
472
- componentName="pagerBottom"
473
- aria-label={t('collections.list.pagerBottomAriaLabel')}
474
- />
475
- </div>
476
- </Container>
477
- </Section>
468
+ <Table.Cell
469
+ className={cx(
470
+ 'byline-coll-history-audit-spacer-cell',
471
+ styles.auditSpacerCell
472
+ )}
473
+ />
474
+ <Table.Cell
475
+ colSpan={auditColSpan}
476
+ className={cx('byline-coll-history-audit-cell', styles.auditCell)}
477
+ >
478
+ <span className={cx('byline-coll-history-audit', styles.audit)}>
479
+ {actionLabel}
480
+ {' · '}
481
+ {t('collections.history.audit.createdBy', { label: actorLabel })}
482
+ {' · '}
483
+ {new Date(document.createdAt).toLocaleString()}
484
+ </span>
485
+ </Table.Cell>
486
+ </Table.Row>
487
+ </Fragment>
488
+ )
489
+ })}
490
+ </Table.Body>
491
+ </Table>
492
+ {padRows(6 - (data?.docs?.length ?? 0))}
493
+ </Table.Container>
494
+ <div
495
+ className={cx(
496
+ 'byline-coll-history-options byline-coll-history-options-bottom',
497
+ styles.options,
498
+ styles.optionsBottom
499
+ )}
500
+ >
501
+ <Select<string>
502
+ containerClassName={cx('byline-coll-history-page-size', styles.pageSize)}
503
+ id="page_size"
504
+ name="page_size"
505
+ size="sm"
506
+ defaultValue="15"
507
+ items={[
508
+ { value: '15', label: '15' },
509
+ { value: '30', label: '30' },
510
+ { value: '50', label: '50' },
511
+ { value: '100', label: '100' },
512
+ ]}
513
+ onValueChange={handleOnPageSizeChange}
514
+ />
515
+ <RouterPager
516
+ smoothScrollToTop={true}
517
+ page={data?.meta.page}
518
+ count={data?.meta.totalPages}
519
+ showFirstButton
520
+ showLastButton
521
+ componentName="pagerBottom"
522
+ aria-label={t('collections.list.pagerBottomAriaLabel')}
523
+ />
524
+ </div>
525
+ </Container>
526
+ </Section>
527
+ )}
478
528
 
479
529
  {selectedVersion && currentDocument && (
480
530
  <Suspense fallback={null}>