@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.
- package/dist/admin-shell/admin-activity/list.d.ts +10 -0
- package/dist/admin-shell/admin-activity/list.js +228 -0
- package/dist/admin-shell/admin-activity/list.module.js +12 -0
- package/dist/admin-shell/admin-activity/list_module.css +45 -0
- package/dist/admin-shell/chrome/menu-drawer.js +23 -6
- package/dist/admin-shell/chrome/preview-toggle.js +11 -6
- package/dist/routes/create-admin-activity-route.d.ts +8 -0
- package/dist/routes/create-admin-activity-route.js +71 -0
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/server-fns/admin-account/change-password.js +1 -1
- package/dist/server-fns/admin-account/get.js +1 -1
- package/dist/server-fns/admin-account/update.js +1 -1
- package/dist/server-fns/admin-activity/get.d.ts +50 -0
- package/dist/server-fns/admin-activity/get.js +71 -0
- package/dist/server-fns/admin-activity/index.d.ts +15 -0
- package/dist/server-fns/admin-activity/index.js +1 -0
- package/dist/server-fns/admin-permissions/get-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/list-registered.d.ts +2 -2
- package/dist/server-fns/admin-permissions/set-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/who-has.js +1 -1
- package/dist/server-fns/admin-roles/create.js +1 -1
- package/dist/server-fns/admin-roles/delete.js +1 -1
- package/dist/server-fns/admin-roles/get.js +1 -1
- package/dist/server-fns/admin-roles/reorder.js +1 -1
- package/dist/server-fns/admin-roles/update.js +1 -1
- package/dist/server-fns/admin-users/create.js +1 -1
- package/dist/server-fns/admin-users/delete.js +1 -1
- package/dist/server-fns/admin-users/disable.js +1 -1
- package/dist/server-fns/admin-users/enable.js +1 -1
- package/dist/server-fns/admin-users/get-user-roles.js +1 -1
- package/dist/server-fns/admin-users/get.js +1 -1
- package/dist/server-fns/admin-users/list.d.ts +1 -1
- package/dist/server-fns/admin-users/list.js +1 -1
- package/dist/server-fns/admin-users/set-password.js +1 -1
- package/dist/server-fns/admin-users/set-user-roles.js +1 -1
- package/dist/server-fns/admin-users/update.js +1 -1
- package/dist/server-fns/ai/execute.js +1 -1
- package/dist/server-fns/auth/sign-in.js +1 -1
- package/dist/server-fns/collections/audit.js +1 -1
- package/dist/server-fns/collections/copy-to-locale.js +1 -1
- package/dist/server-fns/collections/create.js +1 -1
- package/dist/server-fns/collections/delete-locale.js +1 -1
- package/dist/server-fns/collections/delete.js +1 -1
- package/dist/server-fns/collections/duplicate.js +1 -1
- package/dist/server-fns/collections/get.js +2 -2
- package/dist/server-fns/collections/history.js +1 -1
- package/dist/server-fns/collections/list.js +1 -1
- package/dist/server-fns/collections/reorder.js +1 -1
- package/dist/server-fns/collections/restore-version.js +1 -1
- package/dist/server-fns/collections/stats.js +1 -1
- package/dist/server-fns/collections/status.js +2 -2
- package/dist/server-fns/collections/update.js +2 -2
- package/dist/server-fns/collections/upload.js +1 -1
- package/dist/server-fns/i18n/set-locale.js +1 -1
- package/package.json +14 -10
- package/src/admin-shell/admin-activity/list.module.css +69 -0
- package/src/admin-shell/admin-activity/list.tsx +242 -0
- package/src/admin-shell/chrome/menu-drawer.tsx +34 -3
- package/src/admin-shell/chrome/preview-toggle.tsx +14 -3
- package/src/routes/create-admin-activity-route.tsx +76 -0
- package/src/routes/index.ts +1 -0
- package/src/server-fns/admin-account/change-password.ts +1 -1
- package/src/server-fns/admin-account/get.ts +1 -1
- package/src/server-fns/admin-account/update.ts +1 -1
- package/src/server-fns/admin-activity/get.ts +139 -0
- package/src/server-fns/admin-activity/index.ts +23 -0
- package/src/server-fns/admin-permissions/get-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/set-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/who-has.ts +1 -1
- package/src/server-fns/admin-roles/create.ts +1 -1
- package/src/server-fns/admin-roles/delete.ts +1 -1
- package/src/server-fns/admin-roles/get.ts +1 -1
- package/src/server-fns/admin-roles/reorder.ts +1 -1
- package/src/server-fns/admin-roles/update.ts +1 -1
- package/src/server-fns/admin-users/create.ts +1 -1
- package/src/server-fns/admin-users/delete.ts +1 -1
- package/src/server-fns/admin-users/disable.ts +1 -1
- package/src/server-fns/admin-users/enable.ts +1 -1
- package/src/server-fns/admin-users/get-user-roles.ts +1 -1
- package/src/server-fns/admin-users/get.ts +1 -1
- package/src/server-fns/admin-users/list.ts +1 -1
- package/src/server-fns/admin-users/set-password.ts +1 -1
- package/src/server-fns/admin-users/set-user-roles.ts +1 -1
- package/src/server-fns/admin-users/update.ts +1 -1
- package/src/server-fns/ai/execute.ts +1 -1
- package/src/server-fns/auth/sign-in.ts +1 -1
- package/src/server-fns/collections/audit.ts +1 -3
- package/src/server-fns/collections/copy-to-locale.ts +1 -1
- package/src/server-fns/collections/create.ts +1 -1
- package/src/server-fns/collections/delete-locale.ts +1 -1
- package/src/server-fns/collections/delete.ts +1 -1
- package/src/server-fns/collections/duplicate.ts +1 -1
- package/src/server-fns/collections/get.ts +2 -2
- package/src/server-fns/collections/history.ts +1 -1
- package/src/server-fns/collections/list.ts +1 -1
- package/src/server-fns/collections/reorder.ts +1 -1
- package/src/server-fns/collections/restore-version.ts +1 -1
- package/src/server-fns/collections/stats.ts +1 -1
- package/src/server-fns/collections/status.ts +2 -2
- package/src/server-fns/collections/update.ts +2 -2
- package/src/server-fns/collections/upload.ts +1 -1
- 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(
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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(
|
|
66
|
-
|
|
67
|
-
|
|
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 };
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/routes/index.js
CHANGED
|
@@ -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
|
-
}).
|
|
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
|
-
}).
|
|
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
|
-
}).
|
|
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 };
|