@byline/host-tanstack-start 3.10.0 → 3.11.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.
Files changed (103) hide show
  1. package/dist/admin-shell/admin-activity/list.d.ts +10 -0
  2. package/dist/admin-shell/admin-activity/list.js +228 -0
  3. package/dist/admin-shell/admin-activity/list.module.js +12 -0
  4. package/dist/admin-shell/admin-activity/list_module.css +45 -0
  5. package/dist/admin-shell/chrome/menu-drawer.js +23 -6
  6. package/dist/admin-shell/chrome/preview-toggle.js +11 -6
  7. package/dist/routes/create-admin-activity-route.d.ts +8 -0
  8. package/dist/routes/create-admin-activity-route.js +71 -0
  9. package/dist/routes/index.d.ts +1 -0
  10. package/dist/routes/index.js +1 -0
  11. package/dist/server-fns/admin-account/change-password.js +1 -1
  12. package/dist/server-fns/admin-account/get.js +1 -1
  13. package/dist/server-fns/admin-account/update.js +1 -1
  14. package/dist/server-fns/admin-activity/get.d.ts +50 -0
  15. package/dist/server-fns/admin-activity/get.js +71 -0
  16. package/dist/server-fns/admin-activity/index.d.ts +15 -0
  17. package/dist/server-fns/admin-activity/index.js +1 -0
  18. package/dist/server-fns/admin-permissions/get-role-abilities.js +1 -1
  19. package/dist/server-fns/admin-permissions/list-registered.d.ts +2 -2
  20. package/dist/server-fns/admin-permissions/set-role-abilities.js +1 -1
  21. package/dist/server-fns/admin-permissions/who-has.js +1 -1
  22. package/dist/server-fns/admin-roles/create.js +1 -1
  23. package/dist/server-fns/admin-roles/delete.js +1 -1
  24. package/dist/server-fns/admin-roles/get.js +1 -1
  25. package/dist/server-fns/admin-roles/reorder.js +1 -1
  26. package/dist/server-fns/admin-roles/update.js +1 -1
  27. package/dist/server-fns/admin-users/create.js +1 -1
  28. package/dist/server-fns/admin-users/delete.js +1 -1
  29. package/dist/server-fns/admin-users/disable.js +1 -1
  30. package/dist/server-fns/admin-users/enable.js +1 -1
  31. package/dist/server-fns/admin-users/get-user-roles.js +1 -1
  32. package/dist/server-fns/admin-users/get.js +1 -1
  33. package/dist/server-fns/admin-users/list.d.ts +1 -1
  34. package/dist/server-fns/admin-users/list.js +1 -1
  35. package/dist/server-fns/admin-users/set-password.js +1 -1
  36. package/dist/server-fns/admin-users/set-user-roles.js +1 -1
  37. package/dist/server-fns/admin-users/update.js +1 -1
  38. package/dist/server-fns/ai/execute.js +1 -1
  39. package/dist/server-fns/auth/sign-in.js +1 -1
  40. package/dist/server-fns/collections/audit.js +1 -1
  41. package/dist/server-fns/collections/copy-to-locale.js +1 -1
  42. package/dist/server-fns/collections/create.js +1 -1
  43. package/dist/server-fns/collections/delete-locale.js +1 -1
  44. package/dist/server-fns/collections/delete.js +1 -1
  45. package/dist/server-fns/collections/duplicate.js +1 -1
  46. package/dist/server-fns/collections/get.js +2 -2
  47. package/dist/server-fns/collections/history.js +1 -1
  48. package/dist/server-fns/collections/list.js +1 -1
  49. package/dist/server-fns/collections/reorder.js +1 -1
  50. package/dist/server-fns/collections/restore-version.js +1 -1
  51. package/dist/server-fns/collections/stats.js +1 -1
  52. package/dist/server-fns/collections/status.js +2 -2
  53. package/dist/server-fns/collections/update.js +2 -2
  54. package/dist/server-fns/collections/upload.js +1 -1
  55. package/dist/server-fns/i18n/set-locale.js +1 -1
  56. package/package.json +14 -10
  57. package/src/admin-shell/admin-activity/list.module.css +69 -0
  58. package/src/admin-shell/admin-activity/list.tsx +242 -0
  59. package/src/admin-shell/chrome/menu-drawer.tsx +34 -3
  60. package/src/admin-shell/chrome/preview-toggle.tsx +14 -3
  61. package/src/routes/create-admin-activity-route.tsx +76 -0
  62. package/src/routes/index.ts +1 -0
  63. package/src/server-fns/admin-account/change-password.ts +1 -1
  64. package/src/server-fns/admin-account/get.ts +1 -1
  65. package/src/server-fns/admin-account/update.ts +1 -1
  66. package/src/server-fns/admin-activity/get.ts +139 -0
  67. package/src/server-fns/admin-activity/index.ts +23 -0
  68. package/src/server-fns/admin-permissions/get-role-abilities.ts +1 -1
  69. package/src/server-fns/admin-permissions/set-role-abilities.ts +1 -1
  70. package/src/server-fns/admin-permissions/who-has.ts +1 -1
  71. package/src/server-fns/admin-roles/create.ts +1 -1
  72. package/src/server-fns/admin-roles/delete.ts +1 -1
  73. package/src/server-fns/admin-roles/get.ts +1 -1
  74. package/src/server-fns/admin-roles/reorder.ts +1 -1
  75. package/src/server-fns/admin-roles/update.ts +1 -1
  76. package/src/server-fns/admin-users/create.ts +1 -1
  77. package/src/server-fns/admin-users/delete.ts +1 -1
  78. package/src/server-fns/admin-users/disable.ts +1 -1
  79. package/src/server-fns/admin-users/enable.ts +1 -1
  80. package/src/server-fns/admin-users/get-user-roles.ts +1 -1
  81. package/src/server-fns/admin-users/get.ts +1 -1
  82. package/src/server-fns/admin-users/list.ts +1 -1
  83. package/src/server-fns/admin-users/set-password.ts +1 -1
  84. package/src/server-fns/admin-users/set-user-roles.ts +1 -1
  85. package/src/server-fns/admin-users/update.ts +1 -1
  86. package/src/server-fns/ai/execute.ts +1 -1
  87. package/src/server-fns/auth/sign-in.ts +1 -1
  88. package/src/server-fns/collections/audit.ts +1 -3
  89. package/src/server-fns/collections/copy-to-locale.ts +1 -1
  90. package/src/server-fns/collections/create.ts +1 -1
  91. package/src/server-fns/collections/delete-locale.ts +1 -1
  92. package/src/server-fns/collections/delete.ts +1 -1
  93. package/src/server-fns/collections/duplicate.ts +1 -1
  94. package/src/server-fns/collections/get.ts +2 -2
  95. package/src/server-fns/collections/history.ts +1 -1
  96. package/src/server-fns/collections/list.ts +1 -1
  97. package/src/server-fns/collections/reorder.ts +1 -1
  98. package/src/server-fns/collections/restore-version.ts +1 -1
  99. package/src/server-fns/collections/stats.ts +1 -1
  100. package/src/server-fns/collections/status.ts +2 -2
  101. package/src/server-fns/collections/update.ts +2 -2
  102. package/src/server-fns/collections/upload.ts +1 -1
  103. package/src/server-fns/i18n/set-locale.ts +1 -1
@@ -0,0 +1,10 @@
1
+ import type { SystemActivityResponse } from '../../server-fns/admin-activity/index.js';
2
+ /**
3
+ * The system-wide activity report (docs/AUDIT.md — Workstream 4): a paged,
4
+ * filterable feed over the union of the version stream (content saves) and the
5
+ * audit log (status / path / locale changes, deletions, and future admin-realm
6
+ * events). Read-only; gated by `admin.activity.read`.
7
+ */
8
+ export declare const ActivitySystemView: ({ data }: {
9
+ data: SystemActivityResponse;
10
+ }) => import("react").JSX.Element;
@@ -0,0 +1,228 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useRouterState } from "@tanstack/react-router";
4
+ import { getClientConfig } from "@byline/core";
5
+ import { useTranslation } from "@byline/i18n/react";
6
+ import { Button, Container, Section, Select, Table } from "@byline/ui/react";
7
+ import classnames from "classnames";
8
+ import { Link, useNavigate } from "../chrome/loose-router.js";
9
+ import { RouterPager } from "../chrome/router-pager.js";
10
+ import list_module from "./list.module.js";
11
+ const ACTION_KEYS = {
12
+ 'document.created': 'activity.actions.created',
13
+ 'document.updated': 'activity.actions.updated',
14
+ 'document.path.changed': 'activity.actions.pathChanged',
15
+ 'document.locales.changed': 'activity.actions.localesChanged',
16
+ 'document.status.changed': 'activity.actions.statusChanged',
17
+ 'document.deleted': 'activity.actions.deleted'
18
+ };
19
+ const ACTION_FILTER_VALUES = [
20
+ 'document.created',
21
+ 'document.updated',
22
+ 'document.status.changed',
23
+ 'document.path.changed',
24
+ 'document.locales.changed',
25
+ 'document.deleted'
26
+ ];
27
+ function formatAuditValue(value) {
28
+ if (null == value) return '—';
29
+ if (Array.isArray(value)) return value.length > 0 ? value.join(', ') : '—';
30
+ return String(value);
31
+ }
32
+ const ActivitySystemView = ({ data })=>{
33
+ const { t } = useTranslation('byline-admin');
34
+ const navigate = useNavigate();
35
+ const location = useRouterState({
36
+ select: (s)=>s.location
37
+ });
38
+ const search = location.search;
39
+ const entries = data?.entries ?? [];
40
+ const collections = getClientConfig().collections;
41
+ const applyFilter = (key, value)=>{
42
+ const params = structuredClone(location.search);
43
+ params.page = void 0;
44
+ if ('_all' === value) params[key] = void 0;
45
+ else params[key] = value;
46
+ navigate({
47
+ to: '/admin/activity',
48
+ search: params
49
+ });
50
+ };
51
+ const collectionItems = [
52
+ {
53
+ value: '_all',
54
+ label: t('activity.filters.allCollections')
55
+ },
56
+ ...collections.map((c)=>({
57
+ value: c.path,
58
+ label: c.labels?.plural ?? c.path
59
+ }))
60
+ ];
61
+ const actionItems = [
62
+ {
63
+ value: '_all',
64
+ label: t('activity.filters.allActions')
65
+ },
66
+ ...ACTION_FILTER_VALUES.map((a)=>({
67
+ value: a,
68
+ label: ACTION_KEYS[a] ? t(ACTION_KEYS[a]) : a
69
+ }))
70
+ ];
71
+ const hasFilters = null != search.collection || null != search.action;
72
+ return /*#__PURE__*/ jsx(Section, {
73
+ children: /*#__PURE__*/ jsxs(Container, {
74
+ children: [
75
+ /*#__PURE__*/ jsx("div", {
76
+ className: classnames('byline-activity-head', list_module.head),
77
+ children: /*#__PURE__*/ jsx("h1", {
78
+ className: classnames('byline-activity-title', list_module.title),
79
+ children: t('activity.title')
80
+ })
81
+ }),
82
+ /*#__PURE__*/ jsxs("div", {
83
+ className: classnames('byline-activity-options', list_module.options),
84
+ children: [
85
+ /*#__PURE__*/ jsx(Select, {
86
+ id: "activity_collection_filter",
87
+ name: "activity_collection_filter",
88
+ size: "sm",
89
+ value: search.collection ?? '_all',
90
+ items: collectionItems,
91
+ onValueChange: (v)=>{
92
+ if ('string' == typeof v) applyFilter('collection', v);
93
+ }
94
+ }),
95
+ /*#__PURE__*/ jsx(Select, {
96
+ id: "activity_action_filter",
97
+ name: "activity_action_filter",
98
+ size: "sm",
99
+ value: search.action ?? '_all',
100
+ items: actionItems,
101
+ onValueChange: (v)=>{
102
+ if ('string' == typeof v) applyFilter('action', v);
103
+ }
104
+ }),
105
+ hasFilters && /*#__PURE__*/ jsx(Button, {
106
+ size: "sm",
107
+ variant: "text",
108
+ onClick: ()=>navigate({
109
+ to: '/admin/activity',
110
+ search: {}
111
+ }),
112
+ children: t('activity.filters.clear')
113
+ }),
114
+ /*#__PURE__*/ jsx(RouterPager, {
115
+ page: data.meta.page,
116
+ count: data.meta.totalPages,
117
+ showFirstButton: true,
118
+ showLastButton: true,
119
+ componentName: "pagerTop",
120
+ "aria-label": t('activity.pagerAriaLabel')
121
+ })
122
+ ]
123
+ }),
124
+ 0 === entries.length ? /*#__PURE__*/ jsx("p", {
125
+ className: classnames('byline-activity-empty', list_module.empty),
126
+ children: t('activity.empty')
127
+ }) : /*#__PURE__*/ jsx(Table.Container, {
128
+ className: classnames('byline-activity-table-wrap', list_module.tableWrap),
129
+ children: /*#__PURE__*/ jsxs(Table, {
130
+ children: [
131
+ /*#__PURE__*/ jsx(Table.Header, {
132
+ children: /*#__PURE__*/ jsxs(Table.Row, {
133
+ children: [
134
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
135
+ scope: "col",
136
+ children: t('activity.columns.when')
137
+ }),
138
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
139
+ scope: "col",
140
+ children: t('activity.columns.collection')
141
+ }),
142
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
143
+ scope: "col",
144
+ children: t('activity.columns.action')
145
+ }),
146
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
147
+ scope: "col",
148
+ children: t('activity.columns.actor')
149
+ }),
150
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
151
+ scope: "col",
152
+ children: t('activity.columns.change')
153
+ })
154
+ ]
155
+ })
156
+ }),
157
+ /*#__PURE__*/ jsx(Table.Body, {
158
+ children: entries.map((entry)=>{
159
+ const actionKey = ACTION_KEYS[entry.action];
160
+ const actionLabel = actionKey ? t(actionKey) : entry.action;
161
+ const actorLabel = null == entry.actorId || 'system' === entry.actorRealm ? t('activity.systemActor') : data.actors?.[entry.actorId]?.label ?? t('activity.formerUser');
162
+ const col = entry.collectionId ? data.collections[entry.collectionId] : void 0;
163
+ const collectionLabel = col?.plural ?? col?.path ?? '—';
164
+ const hasChange = null != entry.before || null != entry.after;
165
+ return /*#__PURE__*/ jsxs(Table.Row, {
166
+ children: [
167
+ /*#__PURE__*/ jsx(Table.Cell, {
168
+ className: classnames('byline-activity-when', list_module.when),
169
+ children: new Date(entry.occurredAt).toLocaleString()
170
+ }),
171
+ /*#__PURE__*/ jsx(Table.Cell, {
172
+ children: null != col && null != entry.documentId ? /*#__PURE__*/ jsx(Link, {
173
+ to: '/admin/collections/$collection/$id',
174
+ params: {
175
+ collection: col.path,
176
+ id: entry.documentId
177
+ },
178
+ children: collectionLabel
179
+ }) : collectionLabel
180
+ }),
181
+ /*#__PURE__*/ jsx(Table.Cell, {
182
+ children: actionLabel
183
+ }),
184
+ /*#__PURE__*/ jsx(Table.Cell, {
185
+ children: actorLabel
186
+ }),
187
+ /*#__PURE__*/ jsx(Table.Cell, {
188
+ className: classnames('byline-activity-change', list_module.change),
189
+ children: hasChange ? /*#__PURE__*/ jsxs(Fragment, {
190
+ children: [
191
+ /*#__PURE__*/ jsx("span", {
192
+ className: classnames('byline-activity-before', list_module.before),
193
+ children: formatAuditValue(entry.before)
194
+ }),
195
+ /*#__PURE__*/ jsx("span", {
196
+ className: classnames('byline-activity-arrow', list_module.arrow),
197
+ children: ' → '
198
+ }),
199
+ /*#__PURE__*/ jsx("span", {
200
+ className: classnames('byline-activity-after', list_module.after),
201
+ children: formatAuditValue(entry.after)
202
+ })
203
+ ]
204
+ }) : '—'
205
+ })
206
+ ]
207
+ }, entry.id);
208
+ })
209
+ })
210
+ ]
211
+ })
212
+ }),
213
+ /*#__PURE__*/ jsx("div", {
214
+ className: classnames('byline-activity-options', list_module.options),
215
+ children: /*#__PURE__*/ jsx(RouterPager, {
216
+ page: data.meta.page,
217
+ count: data.meta.totalPages,
218
+ showFirstButton: true,
219
+ showLastButton: true,
220
+ componentName: "pagerBottom",
221
+ "aria-label": t('activity.pagerAriaLabel')
222
+ })
223
+ })
224
+ ]
225
+ })
226
+ });
227
+ };
228
+ export { ActivitySystemView };
@@ -0,0 +1,12 @@
1
+ import "./list_module.css";
2
+ const list_module = {
3
+ head: "head-kI5Xwr",
4
+ title: "title-ealGXb",
5
+ options: "options-phg69k",
6
+ empty: "empty-abhubg",
7
+ tableWrap: "tableWrap-VncCtZ",
8
+ when: "when-LlN6zo",
9
+ change: "change-jeXuPf",
10
+ arrow: "arrow-W5ERDF"
11
+ };
12
+ export default list_module;
@@ -0,0 +1,45 @@
1
+ :is(.head-kI5Xwr, .byline-activity-head) {
2
+ margin-bottom: .5rem;
3
+ }
4
+
5
+ :is(.title-ealGXb, .byline-activity-title) {
6
+ font-size: 1.25rem;
7
+ font-weight: 600;
8
+ }
9
+
10
+ :is(.options-phg69k, .byline-activity-options) {
11
+ flex-wrap: wrap;
12
+ align-items: center;
13
+ gap: .5rem;
14
+ margin: .75rem 0;
15
+ display: flex;
16
+ }
17
+
18
+ :is(.empty-abhubg, .byline-activity-empty) {
19
+ color: var(--gray-500);
20
+ margin: 1.5rem 0;
21
+ font-size: .875rem;
22
+ }
23
+
24
+ :is(:is([data-theme="dark"], .dark) .empty-abhubg, :is([data-theme="dark"], .dark) .byline-activity-empty) {
25
+ color: var(--gray-400);
26
+ }
27
+
28
+ :is(.tableWrap-VncCtZ, .byline-activity-table-wrap) {
29
+ margin-top: .5rem;
30
+ margin-bottom: .75rem;
31
+ }
32
+
33
+ :is(.when-LlN6zo, .byline-activity-when) {
34
+ white-space: nowrap;
35
+ font-variant-numeric: tabular-nums;
36
+ }
37
+
38
+ :is(.change-jeXuPf, .byline-activity-change) {
39
+ font-variant-numeric: tabular-nums;
40
+ }
41
+
42
+ :is(.arrow-W5ERDF, .byline-activity-arrow) {
43
+ color: var(--gray-400);
44
+ }
45
+
@@ -1,10 +1,11 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useRouterState } from "@tanstack/react-router";
3
+ import { ADMIN_ACTIVITY_ABILITIES } from "@byline/admin/admin-activity";
3
4
  import { ADMIN_PERMISSIONS_ABILITIES } from "@byline/admin/admin-permissions";
4
5
  import { ADMIN_ROLES_ABILITIES } from "@byline/admin/admin-roles";
5
6
  import { ADMIN_USERS_ABILITIES } from "@byline/admin/admin-users";
6
7
  import { useTranslation } from "@byline/i18n/react";
7
- import { HomeIcon, RolesIcon, SettingsSlidersIcon, UserIcon, UsersIcon } from "@byline/ui/react";
8
+ import { ActivityIcon, HomeIcon, RolesIcon, SettingsSlidersIcon, Tooltip, UserIcon, UsersIcon } from "@byline/ui/react";
8
9
  import classnames from "classnames";
9
10
  import { useSwipeable } from "react-swipeable";
10
11
  import { useAbilities } from "../../integrations/abilities.js";
@@ -17,6 +18,10 @@ const isActive = (currentPath, linkHref)=>{
17
18
  return currentPath.startsWith(linkHref);
18
19
  };
19
20
  function MenuItem({ to, label, icon, pathname, compact }) {
21
+ const iconSpan = /*#__PURE__*/ jsx("span", {
22
+ className: "icon",
23
+ children: icon
24
+ });
20
25
  return /*#__PURE__*/ jsx("li", {
21
26
  className: classnames('menu-item', {
22
27
  active: isActive(pathname, to),
@@ -25,10 +30,11 @@ function MenuItem({ to, label, icon, pathname, compact }) {
25
30
  children: /*#__PURE__*/ jsxs(Link, {
26
31
  to: to,
27
32
  children: [
28
- /*#__PURE__*/ jsx("span", {
29
- className: "icon",
30
- children: icon
31
- }),
33
+ compact ? /*#__PURE__*/ jsx(Tooltip, {
34
+ text: label,
35
+ side: "right",
36
+ children: iconSpan
37
+ }) : iconSpan,
32
38
  /*#__PURE__*/ jsx("span", {
33
39
  className: "label",
34
40
  children: label
@@ -47,7 +53,8 @@ function AdminMenuDrawer() {
47
53
  const canReadUsers = has(ADMIN_USERS_ABILITIES.read);
48
54
  const canReadRoles = has(ADMIN_ROLES_ABILITIES.read);
49
55
  const canReadPermissions = has(ADMIN_PERMISSIONS_ABILITIES.read);
50
- const showAdminSection = canReadUsers || canReadRoles || canReadPermissions;
56
+ const canReadActivity = has(ADMIN_ACTIVITY_ABILITIES.read);
57
+ const showAdminSection = canReadUsers || canReadRoles || canReadPermissions || canReadActivity;
51
58
  const handlers = useSwipeable({
52
59
  onSwipedLeft: ()=>{
53
60
  closeDrawer();
@@ -119,6 +126,16 @@ function AdminMenuDrawer() {
119
126
  }),
120
127
  pathname: pathname,
121
128
  compact: compact
129
+ }),
130
+ canReadActivity && /*#__PURE__*/ jsx(MenuItem, {
131
+ to: "/admin/activity",
132
+ label: t('chrome.menu.activity'),
133
+ icon: /*#__PURE__*/ jsx(ActivityIcon, {
134
+ width: "20px",
135
+ height: "20px"
136
+ }),
137
+ pathname: pathname,
138
+ compact: compact
122
139
  })
123
140
  ]
124
141
  }),
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { useTranslation } from "@byline/i18n/react";
4
- import { EyeClosedIcon, EyeOpenIcon } from "@byline/ui/react";
4
+ import { EyeClosedIcon, EyeOpenIcon, Tooltip } from "@byline/ui/react";
5
5
  import classnames from "classnames";
6
6
  import { disablePreviewModeFn, enablePreviewModeFn, getPreviewStateFn } from "../../server-fns/preview/index.js";
7
7
  function PreviewToggle({ compact }) {
@@ -50,6 +50,10 @@ function PreviewToggle({ compact }) {
50
50
  width: "20px",
51
51
  height: "20px"
52
52
  });
53
+ const iconSpan = /*#__PURE__*/ jsx("span", {
54
+ className: "icon",
55
+ children: icon
56
+ });
53
57
  return /*#__PURE__*/ jsx("li", {
54
58
  className: classnames('menu-item byline-preview-toggle', {
55
59
  compact,
@@ -60,12 +64,13 @@ function PreviewToggle({ compact }) {
60
64
  onClick: handleToggle,
61
65
  disabled: busy,
62
66
  "aria-label": preview ? t('chrome.preview.disableAriaLabel') : t('chrome.preview.enableAriaLabel'),
63
- title: preview ? t('chrome.preview.onTitle') : t('chrome.preview.offTitle'),
67
+ title: compact ? void 0 : preview ? t('chrome.preview.onTitle') : t('chrome.preview.offTitle'),
64
68
  children: [
65
- /*#__PURE__*/ jsx("span", {
66
- className: "icon",
67
- children: icon
68
- }),
69
+ compact ? /*#__PURE__*/ jsx(Tooltip, {
70
+ text: label,
71
+ side: "right",
72
+ children: iconSpan
73
+ }) : iconSpan,
69
74
  /*#__PURE__*/ jsx("span", {
70
75
  className: "label",
71
76
  children: label
@@ -0,0 +1,8 @@
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
+ export declare function createAdminActivityRoute(path: string): any;
@@ -0,0 +1,71 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { createFileRoute } from "@tanstack/react-router";
3
+ import { useTranslation } from "@byline/i18n/react";
4
+ import { z } from "zod";
5
+ import { ActivitySystemView } from "../admin-shell/admin-activity/list.js";
6
+ import { BreadcrumbsClient } from "../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js";
7
+ import { getSystemActivityLog } from "../server-fns/admin-activity/index.js";
8
+ const searchSchema = z.object({
9
+ page: z.coerce.number().int().min(1).optional(),
10
+ page_size: z.coerce.number().int().min(1).max(100).optional(),
11
+ collection: z.string().optional(),
12
+ action: z.string().optional(),
13
+ actorId: z.string().optional(),
14
+ from: z.string().optional(),
15
+ to: z.string().optional()
16
+ });
17
+ function createAdminActivityRoute(path) {
18
+ const Route = createFileRoute(path)({
19
+ validateSearch: searchSchema,
20
+ loaderDeps: ({ search })=>({
21
+ page: search.page,
22
+ page_size: search.page_size,
23
+ collection: search.collection,
24
+ action: search.action,
25
+ actorId: search.actorId,
26
+ from: search.from,
27
+ to: search.to
28
+ }),
29
+ loader: async ({ deps })=>{
30
+ const data = await getSystemActivityLog({
31
+ data: {
32
+ page: deps.page,
33
+ page_size: deps.page_size,
34
+ collection: deps.collection,
35
+ action: deps.action,
36
+ actorId: deps.actorId,
37
+ from: deps.from,
38
+ to: deps.to
39
+ }
40
+ });
41
+ return {
42
+ data
43
+ };
44
+ },
45
+ component: function() {
46
+ const { data } = Route.useLoaderData();
47
+ const { t } = useTranslation('byline-admin');
48
+ return /*#__PURE__*/ jsxs(Fragment, {
49
+ children: [
50
+ /*#__PURE__*/ jsx(BreadcrumbsClient, {
51
+ breadcrumbs: [
52
+ {
53
+ label: t('chrome.menu.dashboard'),
54
+ href: '/admin'
55
+ },
56
+ {
57
+ label: t('activity.title'),
58
+ href: '/admin/activity'
59
+ }
60
+ ]
61
+ }),
62
+ /*#__PURE__*/ jsx(ActivitySystemView, {
63
+ data: data
64
+ })
65
+ ]
66
+ });
67
+ }
68
+ });
69
+ return Route;
70
+ }
71
+ export { createAdminActivityRoute };
@@ -19,6 +19,7 @@
19
19
  * locales) take a second `opts` argument.
20
20
  */
21
21
  export { createAdminAccountRoute } from './create-admin-account-route.js';
22
+ export { createAdminActivityRoute } from './create-admin-activity-route.js';
22
23
  export { createAdminDashboardRoute } from './create-admin-dashboard-route.js';
23
24
  export { createAdminLayoutRoute } from './create-admin-layout-route.js';
24
25
  export { createAdminPermissionsRoute } from './create-admin-permissions-route.js';
@@ -1,4 +1,5 @@
1
1
  export { createAdminAccountRoute } from "./create-admin-account-route.js";
2
+ export { createAdminActivityRoute } from "./create-admin-activity-route.js";
2
3
  export { createAdminDashboardRoute } from "./create-admin-dashboard-route.js";
3
4
  export { createAdminLayoutRoute } from "./create-admin-layout-route.js";
4
5
  export { createAdminPermissionsRoute } from "./create-admin-permissions-route.js";
@@ -4,7 +4,7 @@ import { getAdminRequestContext } from "../../auth/auth-context.js";
4
4
  import { bylineCore } from "../../integrations/byline-core.js";
5
5
  const changeAccountPassword = createServerFn({
6
6
  method: 'POST'
7
- }).inputValidator((input)=>input).handler(async ({ data })=>{
7
+ }).validator((input)=>input).handler(async ({ data })=>{
8
8
  const context = await getAdminRequestContext();
9
9
  return changeAccountPasswordCommand(context, data, {
10
10
  store: bylineCore().adminStore
@@ -4,7 +4,7 @@ import { getAdminRequestContext } from "../../auth/auth-context.js";
4
4
  import { bylineCore } from "../../integrations/byline-core.js";
5
5
  const getAccount = createServerFn({
6
6
  method: 'GET'
7
- }).inputValidator((input)=>input ?? {}).handler(async ({ data })=>{
7
+ }).validator((input)=>input ?? {}).handler(async ({ data })=>{
8
8
  const context = await getAdminRequestContext();
9
9
  return getAccountCommand(context, data, {
10
10
  store: bylineCore().adminStore
@@ -4,7 +4,7 @@ import { getAdminRequestContext } from "../../auth/auth-context.js";
4
4
  import { bylineCore } from "../../integrations/byline-core.js";
5
5
  const updateAccount = createServerFn({
6
6
  method: 'POST'
7
- }).inputValidator((input)=>input).handler(async ({ data })=>{
7
+ }).validator((input)=>input).handler(async ({ data })=>{
8
8
  const context = await getAdminRequestContext();
9
9
  return updateAccountCommand(context, data, {
10
10
  store: bylineCore().adminStore
@@ -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 '../collections/actors.js';
9
+ import type { AuditLogEntryDto } from '../collections/audit.js';
10
+ /** Filters for the activity feed. `from` / `to` are ISO strings over the wire. */
11
+ export interface SystemActivitySearchParams {
12
+ actorId?: string;
13
+ /** Collection **path** (the admin works in paths; the handler resolves it to the stored collection id). */
14
+ collection?: string;
15
+ action?: string;
16
+ from?: string;
17
+ to?: string;
18
+ page?: number;
19
+ page_size?: number;
20
+ }
21
+ /** Collection display info, captured at query time (a row may name a collection that was later renamed/removed). */
22
+ export interface ActivityCollectionInfo {
23
+ path: string;
24
+ singular: string;
25
+ plural: string;
26
+ }
27
+ export type ActivityCollectionMap = Record<string, ActivityCollectionInfo>;
28
+ export interface SystemActivityResponse {
29
+ entries: AuditLogEntryDto[];
30
+ meta: {
31
+ total: number;
32
+ page: number;
33
+ pageSize: number;
34
+ totalPages: number;
35
+ };
36
+ /** Acting-user id → display label. Absent ids (system rows, deleted users) render a tombstone. */
37
+ actors: ActorLabelMap;
38
+ /** Collection id → display info. Absent ids name a removed collection. */
39
+ collections: ActivityCollectionMap;
40
+ }
41
+ /**
42
+ * The system-wide activity feed: the union of the version stream (content
43
+ * saves) and the audit log (status / path / locale changes, deletions, and
44
+ * future admin-realm events). Unlike the per-document audit fn, this is NOT
45
+ * routed through `CollectionHandle` — it is cross-collection and includes
46
+ * admin-realm rows with no `document_id`, so it reads the adapter's audit
47
+ * queries directly and is gated system-wide by `admin.activity.read` rather
48
+ * than by any document's own read gate.
49
+ */
50
+ export declare const getSystemActivityLog: import("@tanstack/react-start").RequiredFetcher<undefined, (input: SystemActivitySearchParams) => SystemActivitySearchParams, Promise<SystemActivityResponse>>;
@@ -0,0 +1,71 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { assertAdminActor } from "@byline/admin";
3
+ import { ADMIN_ACTIVITY_ABILITIES } from "@byline/admin/admin-activity";
4
+ import { ERR_AUDIT_UNSUPPORTED, getLogger, getServerConfig } from "@byline/core";
5
+ import { getAdminRequestContext } from "../../auth/auth-context.js";
6
+ import { resolveActorLabels } from "../collections/actors.js";
7
+ const getSystemActivityLog = createServerFn({
8
+ method: 'GET'
9
+ }).validator((input)=>input ?? {}).handler(async ({ data })=>{
10
+ const context = await getAdminRequestContext();
11
+ assertAdminActor(context, ADMIN_ACTIVITY_ABILITIES.read);
12
+ const queries = getServerConfig().db.queries;
13
+ if (null == queries.audit) throw ERR_AUDIT_UNSUPPORTED({
14
+ message: 'the configured db adapter does not support audit-log reads (queries.audit)'
15
+ }).log(getLogger());
16
+ let collectionId;
17
+ if (data.collection) {
18
+ const col = await queries.collections.getCollectionByPath(data.collection);
19
+ collectionId = col?.id;
20
+ if (null == collectionId) return {
21
+ entries: [],
22
+ meta: {
23
+ total: 0,
24
+ page: 1,
25
+ pageSize: 0,
26
+ totalPages: 0
27
+ },
28
+ actors: {},
29
+ collections: {}
30
+ };
31
+ }
32
+ const result = await queries.audit.findAuditLog({
33
+ actorId: data.actorId,
34
+ collectionId,
35
+ action: data.action,
36
+ from: data.from ? new Date(data.from) : void 0,
37
+ to: data.to ? new Date(data.to) : void 0,
38
+ page: data.page,
39
+ page_size: data.page_size
40
+ });
41
+ const actors = await resolveActorLabels(result.entries.map((e)=>e.actorId));
42
+ const collections = {};
43
+ const collectionIds = new Set(result.entries.map((e)=>e.collectionId).filter((id)=>null != id));
44
+ if (collectionIds.size > 0) {
45
+ const all = await queries.collections.getAllCollections();
46
+ for (const c of all)if (collectionIds.has(c.id)) collections[c.id] = {
47
+ path: c.path,
48
+ singular: c.singular,
49
+ plural: c.plural
50
+ };
51
+ }
52
+ const entries = result.entries.map((e)=>({
53
+ id: e.id,
54
+ documentId: e.documentId,
55
+ collectionId: e.collectionId,
56
+ actorId: e.actorId,
57
+ actorRealm: e.actorRealm,
58
+ action: e.action,
59
+ field: e.field,
60
+ before: e.before,
61
+ after: e.after,
62
+ occurredAt: e.occurredAt instanceof Date ? e.occurredAt.toISOString() : String(e.occurredAt)
63
+ }));
64
+ return {
65
+ entries,
66
+ meta: result.meta,
67
+ actors,
68
+ collections
69
+ };
70
+ });
71
+ export { getSystemActivityLog };