@byline/host-tanstack-start 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
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
+ * One serialised audit-log entry as it reaches the admin UI. Mirrors
10
+ * `@byline/client`'s `AuditLogEntry` but with `occurredAt` as an ISO string —
11
+ * the value crosses the TanStack server-fn boundary through `serialise()`,
12
+ * which turns Dates into strings (docs/AUDIT.md — Workstream 3).
13
+ */
14
+ export interface AuditLogEntryView {
15
+ id: string;
16
+ documentId: string | null;
17
+ collectionId: string | null;
18
+ actorId: string | null;
19
+ actorRealm: string;
20
+ action: string;
21
+ field: string | null;
22
+ before: unknown;
23
+ after: unknown;
24
+ occurredAt: string;
25
+ }
26
+ /**
27
+ * The document-history payload attached to the history loader: the page of
28
+ * audit entries plus the admin-side resolved actor labels (`actors`, keyed by
29
+ * actor id — ids absent from the map belong to deleted users; system/tooling
30
+ * rows carry a NULL actorId).
31
+ */
32
+ export interface DocumentHistoryData {
33
+ entries: AuditLogEntryView[];
34
+ meta: {
35
+ total: number;
36
+ page: number;
37
+ pageSize: number;
38
+ totalPages: number;
39
+ };
40
+ actors?: Record<string, {
41
+ label: string;
42
+ }>;
43
+ }
44
+ /**
45
+ * Document-grain audit log for a single document (docs/AUDIT.md — Workstream
46
+ * 3, "Document history" tab): a chronological, newest-first list of the
47
+ * non-versioned changes the version stream does not record — path /
48
+ * available-locales writes, in-place status transitions, and the deletion
49
+ * event. No diff viewer; before/after render inline.
50
+ */
51
+ export declare const DocumentHistoryView: ({ data }: {
52
+ data: DocumentHistoryData;
53
+ }) => import("react").JSX.Element;
@@ -0,0 +1,103 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from "@byline/i18n/react";
3
+ import { Container, Section, Table } from "@byline/ui/react";
4
+ import classnames from "classnames";
5
+ import document_history_module from "./document-history.module.js";
6
+ const ACTION_KEYS = {
7
+ 'document.path.changed': 'collections.documentHistory.actionPathChanged',
8
+ 'document.locales.changed': 'collections.documentHistory.actionLocalesChanged',
9
+ 'document.status.changed': 'collections.documentHistory.actionStatusChanged',
10
+ 'document.deleted': 'collections.documentHistory.actionDeleted'
11
+ };
12
+ function formatAuditValue(value) {
13
+ if (null == value) return '—';
14
+ if (Array.isArray(value)) return value.length > 0 ? value.join(', ') : '—';
15
+ return String(value);
16
+ }
17
+ const DocumentHistoryView = ({ data })=>{
18
+ const { t } = useTranslation('byline-admin');
19
+ const entries = data?.entries ?? [];
20
+ if (0 === entries.length) return /*#__PURE__*/ jsx(Section, {
21
+ children: /*#__PURE__*/ jsx(Container, {
22
+ children: /*#__PURE__*/ jsx("p", {
23
+ className: classnames('byline-coll-dochistory-empty', document_history_module.empty),
24
+ children: t('collections.documentHistory.empty')
25
+ })
26
+ })
27
+ });
28
+ return /*#__PURE__*/ jsx(Section, {
29
+ children: /*#__PURE__*/ jsx(Container, {
30
+ children: /*#__PURE__*/ jsx(Table.Container, {
31
+ className: classnames('byline-coll-dochistory-table-wrap', document_history_module.tableWrap),
32
+ children: /*#__PURE__*/ jsxs(Table, {
33
+ children: [
34
+ /*#__PURE__*/ jsx(Table.Header, {
35
+ children: /*#__PURE__*/ jsxs(Table.Row, {
36
+ children: [
37
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
38
+ scope: "col",
39
+ children: t('collections.documentHistory.colWhen')
40
+ }),
41
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
42
+ scope: "col",
43
+ children: t('collections.documentHistory.colAction')
44
+ }),
45
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
46
+ scope: "col",
47
+ children: t('collections.documentHistory.colActor')
48
+ }),
49
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
50
+ scope: "col",
51
+ children: t('collections.documentHistory.colChange')
52
+ })
53
+ ]
54
+ })
55
+ }),
56
+ /*#__PURE__*/ jsx(Table.Body, {
57
+ children: entries.map((entry)=>{
58
+ const actionKey = ACTION_KEYS[entry.action];
59
+ const actionLabel = actionKey ? t(actionKey) : entry.action;
60
+ const actorLabel = null == entry.actorId || 'system' === entry.actorRealm ? t('collections.documentHistory.systemActor') : data.actors?.[entry.actorId]?.label ?? t('collections.history.audit.formerUser');
61
+ const hasChange = null != entry.before || null != entry.after;
62
+ return /*#__PURE__*/ jsxs(Table.Row, {
63
+ children: [
64
+ /*#__PURE__*/ jsx(Table.Cell, {
65
+ className: classnames('byline-coll-dochistory-when', document_history_module.when),
66
+ children: new Date(entry.occurredAt).toLocaleString()
67
+ }),
68
+ /*#__PURE__*/ jsx(Table.Cell, {
69
+ children: actionLabel
70
+ }),
71
+ /*#__PURE__*/ jsx(Table.Cell, {
72
+ children: actorLabel
73
+ }),
74
+ /*#__PURE__*/ jsx(Table.Cell, {
75
+ className: classnames('byline-coll-dochistory-change', document_history_module.change),
76
+ children: hasChange ? /*#__PURE__*/ jsxs(Fragment, {
77
+ children: [
78
+ /*#__PURE__*/ jsx("span", {
79
+ className: classnames('byline-coll-dochistory-before', document_history_module.before),
80
+ children: formatAuditValue(entry.before)
81
+ }),
82
+ /*#__PURE__*/ jsx("span", {
83
+ className: classnames('byline-coll-dochistory-arrow', document_history_module.arrow),
84
+ children: ' → '
85
+ }),
86
+ /*#__PURE__*/ jsx("span", {
87
+ className: classnames('byline-coll-dochistory-after', document_history_module.after),
88
+ children: formatAuditValue(entry.after)
89
+ })
90
+ ]
91
+ }) : '—'
92
+ })
93
+ ]
94
+ }, entry.id);
95
+ })
96
+ })
97
+ ]
98
+ })
99
+ })
100
+ })
101
+ });
102
+ };
103
+ export { DocumentHistoryView };
@@ -0,0 +1,9 @@
1
+ import "./document-history_module.css";
2
+ const document_history_module = {
3
+ empty: "empty-qolSIa",
4
+ tableWrap: "tableWrap-Cil1ih",
5
+ when: "when-biWgEw",
6
+ change: "change-xCTn7o",
7
+ arrow: "arrow-e1mpOz"
8
+ };
9
+ export default document_history_module;
@@ -0,0 +1,28 @@
1
+ :is(.empty-qolSIa, .byline-coll-dochistory-empty) {
2
+ color: var(--gray-500);
3
+ margin: 1.5rem 0;
4
+ font-size: .875rem;
5
+ }
6
+
7
+ :is(:is([data-theme="dark"], .dark) .empty-qolSIa, :is([data-theme="dark"], .dark) .byline-coll-dochistory-empty) {
8
+ color: var(--gray-400);
9
+ }
10
+
11
+ :is(.tableWrap-Cil1ih, .byline-coll-dochistory-table-wrap) {
12
+ margin-top: .5rem;
13
+ margin-bottom: .75rem;
14
+ }
15
+
16
+ :is(.when-biWgEw, .byline-coll-dochistory-when) {
17
+ white-space: nowrap;
18
+ font-variant-numeric: tabular-nums;
19
+ }
20
+
21
+ :is(.change-xCTn7o, .byline-coll-dochistory-change) {
22
+ font-variant-numeric: tabular-nums;
23
+ }
24
+
25
+ :is(.arrow-e1mpOz, .byline-coll-dochistory-arrow) {
26
+ color: var(--gray-400);
27
+ }
28
+
@@ -7,8 +7,9 @@
7
7
  */
8
8
  import type { CollectionAdminConfig, CollectionDefinition, WorkflowStatus } from '@byline/core';
9
9
  import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas';
10
+ import { type DocumentHistoryData } from './document-history.js';
10
11
  import type { ContentLocaleOption } from './view-menu.js';
11
- export declare const HistoryView: ({ collectionDefinition, adminConfig, data, workflowStatuses, currentDocument, contentLocales, defaultContentLocale, }: {
12
+ export declare const HistoryView: ({ collectionDefinition, adminConfig, data, auditLog, workflowStatuses, currentDocument, contentLocales, defaultContentLocale, }: {
12
13
  collectionDefinition: CollectionDefinition;
13
14
  adminConfig?: CollectionAdminConfig;
14
15
  data: AnyCollectionSchemaTypes["HistoryType"] & {
@@ -21,6 +22,12 @@ export declare const HistoryView: ({ collectionDefinition, adminConfig, data, wo
21
22
  label: string;
22
23
  }>;
23
24
  };
25
+ /**
26
+ * Document-grain audit log for the "Document history" tab (docs/AUDIT.md —
27
+ * Workstream 3): the non-versioned path / available-locales / status
28
+ * changes and the deletion event, with admin-resolved actor labels.
29
+ */
30
+ auditLog?: DocumentHistoryData;
24
31
  workflowStatuses?: WorkflowStatus[];
25
32
  currentDocument?: Record<string, unknown> | null;
26
33
  contentLocales: ReadonlyArray<ContentLocaleOption>;
@@ -1,7 +1,7 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Fragment as external_react_Fragment, Suspense, lazy, useState } from "react";
3
3
  import { useParams, useRouterState } from "@tanstack/react-router";
4
- import { StatusBadge, renderFormatted } from "@byline/admin/react";
4
+ import { AdminTabs, StatusBadge, renderFormatted } from "@byline/admin/react";
5
5
  import { useBylineAdminServices } from "@byline/admin/services";
6
6
  import { useTranslation } from "@byline/i18n/react";
7
7
  import { Button, CloseIcon, Container, IconButton, Modal, Section, Select, Table } from "@byline/ui/react";
@@ -10,6 +10,7 @@ import { Link, useNavigate } from "../chrome/loose-router.js";
10
10
  import { RouterPager } from "../chrome/router-pager.js";
11
11
  import { TableHeadingCellSortable } from "../chrome/th-sortable.js";
12
12
  import { formatNumber } from "../chrome/utils.js";
13
+ import { DocumentHistoryView } from "./document-history.js";
13
14
  import history_module from "./history.module.js";
14
15
  import { RestoreVersionModal } from "./restore-version-modal.js";
15
16
  import { ViewMenu } from "./view-menu.js";
@@ -50,7 +51,7 @@ const AUDIT_ACTION_KEYS = {
50
51
  copy_to_locale: 'collections.history.audit.actionCopyToLocale',
51
52
  delete_locale: 'collections.history.audit.actionDeleteLocale'
52
53
  };
53
- const HistoryView = ({ collectionDefinition, adminConfig, data, workflowStatuses, currentDocument, contentLocales, defaultContentLocale })=>{
54
+ const HistoryView = ({ collectionDefinition, adminConfig, data, auditLog, workflowStatuses, currentDocument, contentLocales, defaultContentLocale })=>{
54
55
  const { id, collection } = useParams({
55
56
  from: '/_byline/admin/collections/$collection/$id/history'
56
57
  });
@@ -64,9 +65,24 @@ const HistoryView = ({ collectionDefinition, adminConfig, data, workflowStatuses
64
65
  select: (s)=>s.location
65
66
  });
66
67
  const locale = location.search.locale;
68
+ const activeTab = 'document' === location.search.tab ? 'document' : 'versions';
67
69
  const [selectedVersion, setSelectedVersion] = useState(null);
68
70
  const [restoreTarget, setRestoreTarget] = useState(null);
69
71
  const currentVersionId = currentDocument && 'string' == typeof currentDocument.versionId ? currentDocument.versionId : null;
72
+ function handleTabChange(name) {
73
+ const params = structuredClone(location.search);
74
+ delete params.page;
75
+ if ('document' === name) params.tab = 'document';
76
+ else delete params.tab;
77
+ navigate({
78
+ to: '/admin/collections/$collection/$id/history',
79
+ params: {
80
+ collection,
81
+ id
82
+ },
83
+ search: params
84
+ });
85
+ }
70
86
  function handleOnPageSizeChange(value) {
71
87
  if ('string' != typeof value || 0 === value.length) return;
72
88
  const params = structuredClone(location.search);
@@ -84,35 +100,63 @@ const HistoryView = ({ collectionDefinition, adminConfig, data, workflowStatuses
84
100
  return /*#__PURE__*/ jsxs(Fragment, {
85
101
  children: [
86
102
  /*#__PURE__*/ jsx(Section, {
87
- children: /*#__PURE__*/ jsx(Container, {
88
- children: /*#__PURE__*/ jsxs("div", {
89
- className: classnames('byline-coll-history-head', history_module.head),
90
- children: [
91
- /*#__PURE__*/ jsxs("h2", {
92
- className: classnames('byline-coll-history-title', history_module.title),
93
- children: [
94
- t('collections.history.title', {
95
- label: labels.singular
96
- }),
97
- ' ',
98
- /*#__PURE__*/ jsx(Stats, {
99
- total: data?.meta.total
100
- })
101
- ]
102
- }),
103
- /*#__PURE__*/ jsx(ViewMenu, {
104
- collection: collection,
105
- documentId: id,
106
- activeView: "history",
107
- locale: locale,
108
- contentLocales: contentLocales,
109
- defaultContentLocale: defaultContentLocale
110
- })
111
- ]
112
- })
103
+ children: /*#__PURE__*/ jsxs(Container, {
104
+ children: [
105
+ /*#__PURE__*/ jsxs("div", {
106
+ className: classnames('byline-coll-history-head', history_module.head),
107
+ children: [
108
+ /*#__PURE__*/ jsxs("h2", {
109
+ className: classnames('byline-coll-history-title', history_module.title),
110
+ children: [
111
+ t('collections.history.title', {
112
+ label: labels.singular
113
+ }),
114
+ ' ',
115
+ /*#__PURE__*/ jsx(Stats, {
116
+ total: 'document' === activeTab ? auditLog?.meta.total ?? 0 : data?.meta.total
117
+ })
118
+ ]
119
+ }),
120
+ /*#__PURE__*/ jsx(ViewMenu, {
121
+ collection: collection,
122
+ documentId: id,
123
+ activeView: "history",
124
+ locale: locale,
125
+ contentLocales: contentLocales,
126
+ defaultContentLocale: defaultContentLocale
127
+ })
128
+ ]
129
+ }),
130
+ /*#__PURE__*/ jsx(AdminTabs, {
131
+ tabs: [
132
+ {
133
+ name: 'versions',
134
+ label: t('collections.history.tabs.versions')
135
+ },
136
+ {
137
+ name: 'document',
138
+ label: t('collections.history.tabs.document')
139
+ }
140
+ ],
141
+ activeTab: activeTab,
142
+ onChange: handleTabChange,
143
+ className: classnames('byline-coll-history-tabs', history_module.tabs)
144
+ })
145
+ ]
113
146
  })
114
147
  }),
115
- /*#__PURE__*/ jsx(Section, {
148
+ 'document' === activeTab && /*#__PURE__*/ jsx(DocumentHistoryView, {
149
+ data: auditLog ?? {
150
+ entries: [],
151
+ meta: {
152
+ total: 0,
153
+ page: 1,
154
+ pageSize: 0,
155
+ totalPages: 0
156
+ }
157
+ }
158
+ }),
159
+ 'document' !== activeTab && /*#__PURE__*/ jsx(Section, {
116
160
  children: /*#__PURE__*/ jsxs(Container, {
117
161
  children: [
118
162
  /*#__PURE__*/ jsx("div", {
@@ -3,6 +3,7 @@ const history_module = {
3
3
  head: "head-WvAUUN",
4
4
  title: "title-zFZEGp",
5
5
  stats: "stats-e6bP_s",
6
+ tabs: "tabs-TIAJAP",
6
7
  options: "options-oC29XB",
7
8
  optionsBottom: "optionsBottom-ppYTec",
8
9
  tableWrap: "tableWrap-VnRkHp",
@@ -40,6 +40,10 @@
40
40
  background-color: var(--canvas-700);
41
41
  }
42
42
 
43
+ :is(.tabs-TIAJAP, .byline-coll-history-tabs) {
44
+ margin-top: .25rem;
45
+ }
46
+
43
47
  :is(.options-oC29XB, .byline-coll-history-options) {
44
48
  flex-direction: column;
45
49
  align-items: flex-start;
@@ -5,14 +5,19 @@ import { useTranslation } from "@byline/i18n/react";
5
5
  import { z } from "zod";
6
6
  import { BreadcrumbsClient } from "../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js";
7
7
  import { HistoryView } from "../admin-shell/collections/history.js";
8
- import { getCollectionDocument, getCollectionDocumentHistory } from "../server-fns/collections/index.js";
8
+ import { getCollectionDocument, getCollectionDocumentAuditLog, getCollectionDocumentHistory } from "../server-fns/collections/index.js";
9
9
  const searchSchema = z.object({
10
10
  page: z.coerce.number().min(1).optional(),
11
11
  page_size: z.coerce.number().max(100).optional(),
12
12
  order: z.string().optional(),
13
13
  desc: z.coerce.boolean().optional(),
14
- locale: z.string().optional()
14
+ locale: z.string().optional(),
15
+ tab: z["enum"]([
16
+ 'versions',
17
+ 'document'
18
+ ]).optional()
15
19
  });
20
+ const AUDIT_LOG_PAGE_SIZE = 100;
16
21
  function createCollectionHistoryRoute(path, opts) {
17
22
  const Route = createFileRoute(path)({
18
23
  validateSearch: searchSchema,
@@ -26,7 +31,7 @@ function createCollectionHistoryRoute(path, opts) {
26
31
  loader: async ({ params, deps })=>{
27
32
  const collectionDef = getCollectionDefinition(params.collection);
28
33
  if (!collectionDef) throw notFound();
29
- const [history, currentDocument] = await Promise.all([
34
+ const [history, currentDocument, auditLog] = await Promise.all([
30
35
  getCollectionDocumentHistory({
31
36
  data: {
32
37
  collection: params.collection,
@@ -40,18 +45,29 @@ function createCollectionHistoryRoute(path, opts) {
40
45
  }
41
46
  }
42
47
  }),
43
- getCollectionDocument(params.collection, params.id, deps.locale ?? 'all')
48
+ getCollectionDocument(params.collection, params.id, deps.locale ?? 'all'),
49
+ getCollectionDocumentAuditLog({
50
+ data: {
51
+ collection: params.collection,
52
+ id: params.id,
53
+ params: {
54
+ page: 1,
55
+ page_size: AUDIT_LOG_PAGE_SIZE
56
+ }
57
+ }
58
+ })
44
59
  ]);
45
60
  return {
46
61
  history,
47
- currentDocument
62
+ currentDocument,
63
+ auditLog
48
64
  };
49
65
  },
50
66
  staleTime: 0,
51
67
  gcTime: 0,
52
68
  shouldReload: true,
53
69
  component: function() {
54
- const { history, currentDocument } = Route.useLoaderData();
70
+ const { history, currentDocument, auditLog } = Route.useLoaderData();
55
71
  const { collection } = Route.useParams();
56
72
  const collectionDef = getCollectionDefinition(collection);
57
73
  const adminConfig = getCollectionAdminConfig(collection);
@@ -83,6 +99,7 @@ function createCollectionHistoryRoute(path, opts) {
83
99
  workflowStatuses: getWorkflowStatuses(collectionDef),
84
100
  adminConfig: adminConfig ?? void 0,
85
101
  data: history,
102
+ auditLog: auditLog,
86
103
  currentDocument: currentDocument,
87
104
  contentLocales: opts.contentLocales,
88
105
  defaultContentLocale: opts.defaultContentLocale
@@ -0,0 +1,50 @@
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
+ import { type ActorLabelMap } from './actors.js';
9
+ export interface AuditLogSearchParams {
10
+ page?: number;
11
+ page_size?: number;
12
+ }
13
+ /**
14
+ * Audit entry as it crosses the server-fn boundary. `occurredAt` is an ISO
15
+ * string (Date doesn't survive serialization) and `before` / `after` are
16
+ * narrowed from the storage layer's `unknown` jsonb to the concrete shapes the
17
+ * shipped actions actually carry (path/status strings, the available-locales
18
+ * array, or null for the deletion event) — `unknown` is not a serializable
19
+ * type the TanStack server-fn validator accepts.
20
+ */
21
+ export interface AuditLogEntryDto {
22
+ id: string;
23
+ documentId: string | null;
24
+ collectionId: string | null;
25
+ actorId: string | null;
26
+ actorRealm: string;
27
+ action: string;
28
+ field: string | null;
29
+ before: string | string[] | null;
30
+ after: string | string[] | null;
31
+ occurredAt: string;
32
+ }
33
+ export declare const getCollectionDocumentAuditLog: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
34
+ collection: string;
35
+ id: string;
36
+ params?: AuditLogSearchParams;
37
+ }) => {
38
+ collection: string;
39
+ id: string;
40
+ params?: AuditLogSearchParams;
41
+ }, Promise<{
42
+ entries: AuditLogEntryDto[];
43
+ meta: {
44
+ total: number;
45
+ page: number;
46
+ pageSize: number;
47
+ totalPages: number;
48
+ };
49
+ actors: ActorLabelMap;
50
+ }>>;
@@ -0,0 +1,40 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { ERR_NOT_FOUND, getLogger } from "@byline/core";
3
+ import { ensureCollection } from "../../integrations/api-utils.js";
4
+ import { getAdminBylineClient } from "../../integrations/byline-client.js";
5
+ import { resolveActorLabels } from "./actors.js";
6
+ const getCollectionDocumentAuditLog = createServerFn({
7
+ method: 'GET'
8
+ }).inputValidator((input)=>input).handler(async ({ data })=>{
9
+ const { collection: path, id, params } = data;
10
+ const config = await ensureCollection(path);
11
+ if (!config) throw ERR_NOT_FOUND({
12
+ message: 'Collection not found',
13
+ details: {
14
+ collectionPath: path
15
+ }
16
+ }).log(getLogger());
17
+ const result = await getAdminBylineClient().collection(path).auditLog(id, {
18
+ page: params?.page,
19
+ pageSize: params?.page_size
20
+ });
21
+ const actors = await resolveActorLabels(result.entries.map((e)=>e.actorId));
22
+ const entries = result.entries.map((e)=>({
23
+ id: e.id,
24
+ documentId: e.documentId,
25
+ collectionId: e.collectionId,
26
+ actorId: e.actorId,
27
+ actorRealm: e.actorRealm,
28
+ action: e.action,
29
+ field: e.field,
30
+ before: e.before,
31
+ after: e.after,
32
+ occurredAt: e.occurredAt instanceof Date ? e.occurredAt.toISOString() : String(e.occurredAt)
33
+ }));
34
+ return {
35
+ entries,
36
+ meta: result.meta,
37
+ actors
38
+ };
39
+ });
40
+ export { getCollectionDocumentAuditLog };
@@ -4,6 +4,7 @@
4
4
  * Each module is self-contained: it defines the TanStack Start server
5
5
  * function and exports a clean public API.
6
6
  */
7
+ export * from './audit';
7
8
  export * from './copy-to-locale';
8
9
  export * from './create';
9
10
  export * from './delete';
@@ -1,3 +1,4 @@
1
+ export * from "./audit.js";
1
2
  export * from "./copy-to-locale.js";
2
3
  export * from "./create.js";
3
4
  export * from "./delete.js";
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "license": "MPL-2.0",
6
- "version": "3.8.0",
6
+ "version": "3.10.0",
7
7
  "engines": {
8
8
  "node": ">=20.9.0"
9
9
  },
@@ -115,13 +115,13 @@
115
115
  "react-swipeable": "^7.0.2",
116
116
  "uuid": "^14.0.0",
117
117
  "zod": "^4.4.3",
118
- "@byline/admin": "3.8.0",
119
- "@byline/auth": "3.8.0",
120
- "@byline/core": "3.8.0",
121
- "@byline/i18n": "3.8.0",
122
- "@byline/ui": "3.8.0",
123
- "@byline/ai": "3.8.0",
124
- "@byline/client": "3.8.0"
118
+ "@byline/admin": "3.10.0",
119
+ "@byline/core": "3.10.0",
120
+ "@byline/auth": "3.10.0",
121
+ "@byline/client": "3.10.0",
122
+ "@byline/ui": "3.10.0",
123
+ "@byline/i18n": "3.10.0",
124
+ "@byline/ai": "3.10.0"
125
125
  },
126
126
  "peerDependencies": {
127
127
  "@tanstack/react-router": "^1.167.0",
@@ -0,0 +1,46 @@
1
+ /**
2
+ * DocumentHistoryView — the document-grain audit log tab (docs/AUDIT.md — W3).
3
+ *
4
+ * Override handles:
5
+ * .byline-coll-dochistory-empty — empty-state explanation
6
+ * .byline-coll-dochistory-table-wrap — table container
7
+ * .byline-coll-dochistory-when — timestamp cell
8
+ * .byline-coll-dochistory-change — before → after cell
9
+ * .byline-coll-dochistory-before — pre-change value
10
+ * .byline-coll-dochistory-arrow — muted transition arrow
11
+ * .byline-coll-dochistory-after — post-change value
12
+ */
13
+
14
+ .empty,
15
+ :global(.byline-coll-dochistory-empty) {
16
+ margin: 1.5rem 0;
17
+ color: var(--gray-500);
18
+ font-size: 0.875rem;
19
+ }
20
+
21
+ :is([data-theme="dark"], :global(.dark)) .empty,
22
+ :is([data-theme="dark"], :global(.dark)) :global(.byline-coll-dochistory-empty) {
23
+ color: var(--gray-400);
24
+ }
25
+
26
+ .tableWrap,
27
+ :global(.byline-coll-dochistory-table-wrap) {
28
+ margin-top: 0.5rem;
29
+ margin-bottom: 0.75rem;
30
+ }
31
+
32
+ .when,
33
+ :global(.byline-coll-dochistory-when) {
34
+ white-space: nowrap;
35
+ font-variant-numeric: tabular-nums;
36
+ }
37
+
38
+ .change,
39
+ :global(.byline-coll-dochistory-change) {
40
+ font-variant-numeric: tabular-nums;
41
+ }
42
+
43
+ .arrow,
44
+ :global(.byline-coll-dochistory-arrow) {
45
+ color: var(--gray-400);
46
+ }