@byline/host-tanstack-start 2.5.1 → 2.6.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-roles/container.js +38 -24
- package/dist/admin-shell/admin-roles/delete.js +9 -7
- package/dist/admin-shell/admin-roles/list.js +20 -16
- package/dist/admin-shell/admin-users/container.js +79 -56
- package/dist/admin-shell/admin-users/delete.js +10 -8
- package/dist/admin-shell/admin-users/list.js +27 -18
- package/dist/admin-shell/chrome/admin-app-bar.js +5 -2
- package/dist/admin-shell/chrome/breadcrumbs/breadcrumbs.js +3 -1
- package/dist/admin-shell/chrome/dashboard.js +13 -11
- package/dist/admin-shell/chrome/hamburger.js +3 -1
- package/dist/admin-shell/chrome/menu-drawer.js +7 -5
- package/dist/admin-shell/chrome/preview-toggle.js +5 -3
- package/dist/admin-shell/chrome/route-error.d.ts +3 -2
- package/dist/admin-shell/chrome/route-error.js +29 -22
- package/dist/admin-shell/chrome/sign-in-page.d.ts +16 -4
- package/dist/admin-shell/chrome/sign-in-page.js +38 -13
- package/dist/admin-shell/chrome/sign-in-page.module.js +1 -0
- package/dist/admin-shell/chrome/sign-in-page_module.css +8 -1
- package/dist/admin-shell/collections/api.js +6 -5
- package/dist/admin-shell/collections/create.js +12 -4
- package/dist/admin-shell/collections/edit.js +112 -37
- package/dist/admin-shell/collections/history.js +17 -12
- package/dist/admin-shell/collections/list.js +18 -13
- package/dist/admin-shell/collections/preview-link.d.ts +1 -10
- package/dist/admin-shell/collections/preview-link.js +9 -11
- package/dist/admin-shell/collections/resolve-preview-url.d.ts +34 -0
- package/dist/admin-shell/collections/resolve-preview-url.js +17 -0
- package/dist/admin-shell/collections/restore-version-modal.js +13 -14
- package/dist/admin-shell/collections/tanstack-navigation-guard.d.ts +1 -1
- package/dist/admin-shell/collections/view-menu.js +7 -5
- package/dist/i18n/index.d.ts +19 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/locale-cookie.d.ts +17 -0
- package/dist/i18n/locale-cookie.js +26 -0
- package/dist/i18n/locale-definitions.d.ts +29 -0
- package/dist/i18n/locale-definitions.js +27 -0
- package/dist/i18n/resolve-locale.d.ts +20 -0
- package/dist/i18n/resolve-locale.js +43 -0
- package/dist/i18n/server-translator.d.ts +33 -0
- package/dist/i18n/server-translator.js +19 -0
- package/dist/integrations/byline-admin-services.js +2 -0
- package/dist/integrations/byline-field-services.d.ts +3 -3
- package/dist/routes/create-admin-account-route.js +6 -3
- package/dist/routes/create-admin-dashboard-route.js +3 -1
- package/dist/routes/create-admin-layout-route.js +48 -25
- package/dist/routes/create-admin-permissions-route.js +4 -2
- package/dist/routes/create-admin-role-edit-route.js +5 -3
- package/dist/routes/create-admin-roles-list-route.js +4 -2
- package/dist/routes/create-admin-user-edit-route.js +5 -3
- package/dist/routes/create-admin-users-list-route.js +4 -2
- package/dist/routes/create-collection-api-route.js +5 -3
- package/dist/routes/create-collection-create-route.js +4 -2
- package/dist/routes/create-collection-edit-route.js +4 -2
- package/dist/routes/create-collection-history-route.js +5 -3
- package/dist/routes/create-collection-list-route.js +11 -5
- package/dist/routes/create-sign-in-route.js +10 -1
- package/dist/server-fns/admin-account/change-password.d.ts +1 -0
- package/dist/server-fns/admin-account/get.d.ts +1 -0
- package/dist/server-fns/admin-account/update.d.ts +1 -0
- package/dist/server-fns/admin-users/create.d.ts +1 -0
- package/dist/server-fns/admin-users/get.d.ts +1 -0
- package/dist/server-fns/admin-users/list.d.ts +1 -0
- package/dist/server-fns/admin-users/set-password.d.ts +1 -0
- package/dist/server-fns/admin-users/update.d.ts +1 -0
- package/dist/server-fns/auth/sign-in.js +18 -0
- package/dist/server-fns/i18n/get-active-locale.d.ts +8 -0
- package/dist/server-fns/i18n/get-active-locale.js +6 -0
- package/dist/server-fns/i18n/index.d.ts +10 -0
- package/dist/server-fns/i18n/index.js +2 -0
- package/dist/server-fns/i18n/set-locale.d.ts +25 -0
- package/dist/server-fns/i18n/set-locale.js +42 -0
- package/package.json +16 -7
- package/src/admin-shell/admin-roles/container.tsx +41 -31
- package/src/admin-shell/admin-roles/delete.tsx +10 -11
- package/src/admin-shell/admin-roles/list.tsx +29 -16
- package/src/admin-shell/admin-users/container.tsx +77 -50
- package/src/admin-shell/admin-users/delete.tsx +11 -12
- package/src/admin-shell/admin-users/list.tsx +39 -18
- package/src/admin-shell/chrome/admin-app-bar.tsx +5 -2
- package/src/admin-shell/chrome/breadcrumbs/breadcrumbs.tsx +3 -1
- package/src/admin-shell/chrome/dashboard.tsx +9 -3
- package/src/admin-shell/chrome/hamburger.tsx +3 -1
- package/src/admin-shell/chrome/menu-drawer.tsx +7 -5
- package/src/admin-shell/chrome/preview-toggle.tsx +6 -4
- package/src/admin-shell/chrome/route-error.tsx +39 -26
- package/src/admin-shell/chrome/sign-in-page.module.css +10 -1
- package/src/admin-shell/chrome/sign-in-page.tsx +46 -12
- package/src/admin-shell/collections/api.tsx +5 -1
- package/src/admin-shell/collections/create.tsx +10 -4
- package/src/admin-shell/collections/edit.tsx +79 -72
- package/src/admin-shell/collections/history.tsx +18 -12
- package/src/admin-shell/collections/list.tsx +25 -14
- package/src/admin-shell/collections/preview-link.tsx +20 -33
- package/src/admin-shell/collections/resolve-preview-url.test.node.ts +167 -0
- package/src/admin-shell/collections/resolve-preview-url.ts +67 -0
- package/src/admin-shell/collections/restore-version-modal.tsx +11 -12
- package/src/admin-shell/collections/tanstack-navigation-guard.ts +1 -1
- package/src/admin-shell/collections/view-menu.tsx +9 -5
- package/src/i18n/index.ts +26 -0
- package/src/i18n/locale-cookie.ts +68 -0
- package/src/i18n/locale-definitions.ts +48 -0
- package/src/i18n/resolve-locale.ts +96 -0
- package/src/i18n/server-translator.ts +60 -0
- package/src/integrations/byline-admin-services.ts +2 -0
- package/src/integrations/byline-field-services.ts +7 -3
- package/src/routes/create-admin-account-route.tsx +6 -4
- package/src/routes/create-admin-dashboard-route.tsx +5 -1
- package/src/routes/create-admin-layout-route.tsx +53 -20
- package/src/routes/create-admin-permissions-route.tsx +4 -2
- package/src/routes/create-admin-role-edit-route.tsx +5 -3
- package/src/routes/create-admin-roles-list-route.tsx +5 -2
- package/src/routes/create-admin-user-edit-route.tsx +5 -3
- package/src/routes/create-admin-users-list-route.tsx +4 -2
- package/src/routes/create-collection-api-route.tsx +5 -3
- package/src/routes/create-collection-create-route.tsx +7 -2
- package/src/routes/create-collection-edit-route.tsx +4 -2
- package/src/routes/create-collection-history-route.tsx +5 -3
- package/src/routes/create-collection-list-route.tsx +8 -10
- package/src/routes/create-sign-in-route.tsx +14 -1
- package/src/server-fns/auth/sign-in.ts +45 -0
- package/src/server-fns/i18n/get-active-locale.ts +26 -0
- package/src/server-fns/i18n/index.ts +11 -0
- package/src/server-fns/i18n/set-locale.ts +103 -0
|
@@ -15,13 +15,14 @@ import { useRouter } from '@tanstack/react-router'
|
|
|
15
15
|
import { UserRoles } from '@byline/admin/admin-users/components/roles'
|
|
16
16
|
import { SetPassword } from '@byline/admin/admin-users/components/set-password'
|
|
17
17
|
import { UpdateUser } from '@byline/admin/admin-users/components/update'
|
|
18
|
+
import { LocalDateTime } from '@byline/admin/react'
|
|
19
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
18
20
|
import {
|
|
19
21
|
Button,
|
|
20
22
|
CloseIcon,
|
|
21
23
|
Drawer,
|
|
22
24
|
EditIcon,
|
|
23
25
|
IconButton,
|
|
24
|
-
LocalDateTime,
|
|
25
26
|
Modal,
|
|
26
27
|
useToastManager,
|
|
27
28
|
} from '@byline/ui/react'
|
|
@@ -61,45 +62,50 @@ interface PanelProps {
|
|
|
61
62
|
onSuccess?: (user: AdminUserResponse) => void
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
type PanelMeta = {
|
|
66
|
+
drawerWidth: 'medium' | 'large'
|
|
67
|
+
component: React.ComponentType<PanelProps>
|
|
68
|
+
titleKey: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const panelMeta: Record<ComponentKey, PanelMeta> = {
|
|
68
72
|
update: {
|
|
69
|
-
title: 'Account Details',
|
|
70
73
|
drawerWidth: 'medium',
|
|
71
74
|
component: UpdateUser,
|
|
75
|
+
titleKey: 'adminUsers.detail.panels.update',
|
|
72
76
|
},
|
|
73
77
|
set_password: {
|
|
74
|
-
title: 'Set Password',
|
|
75
78
|
drawerWidth: 'medium',
|
|
76
79
|
component: SetPassword,
|
|
80
|
+
titleKey: 'adminUsers.detail.panels.setPassword',
|
|
77
81
|
},
|
|
78
82
|
delete_user: {
|
|
79
|
-
title: 'Delete Admin User',
|
|
80
83
|
drawerWidth: 'medium',
|
|
81
84
|
component: DeleteUser,
|
|
85
|
+
titleKey: 'adminUsers.detail.panels.delete',
|
|
82
86
|
},
|
|
83
87
|
roles: {
|
|
84
|
-
title: 'User Roles',
|
|
85
88
|
drawerWidth: 'medium',
|
|
86
89
|
// See container header docstring — rendered inline, this is a stub.
|
|
87
90
|
component: () => null,
|
|
91
|
+
titleKey: 'adminUsers.detail.panels.roles',
|
|
88
92
|
},
|
|
89
93
|
empty: {
|
|
90
|
-
title: '',
|
|
91
94
|
drawerWidth: 'medium',
|
|
92
95
|
component: () => null,
|
|
96
|
+
titleKey: '',
|
|
93
97
|
},
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
function ContainerSection({
|
|
97
101
|
title,
|
|
98
102
|
onEdit,
|
|
103
|
+
editAriaLabel,
|
|
99
104
|
children,
|
|
100
105
|
}: {
|
|
101
106
|
title: string
|
|
102
107
|
onEdit?: () => void
|
|
108
|
+
editAriaLabel?: string
|
|
103
109
|
children: React.ReactNode
|
|
104
110
|
}) {
|
|
105
111
|
return (
|
|
@@ -107,7 +113,7 @@ function ContainerSection({
|
|
|
107
113
|
<div className={cx('byline-admin-user-section-head', styles.sectionHead)}>
|
|
108
114
|
<h2>{title}</h2>
|
|
109
115
|
{onEdit ? (
|
|
110
|
-
<IconButton variant="text" onClick={onEdit} aria-label={
|
|
116
|
+
<IconButton variant="text" onClick={onEdit} aria-label={editAriaLabel ?? title}>
|
|
111
117
|
<EditIcon width="20px" height="20px" />
|
|
112
118
|
</IconButton>
|
|
113
119
|
) : null}
|
|
@@ -137,6 +143,7 @@ interface AccountContainerProps {
|
|
|
137
143
|
export function AccountContainer({ user, allRoles, initialUserRoles }: AccountContainerProps) {
|
|
138
144
|
const router = useRouter()
|
|
139
145
|
const toastManager = useToastManager()
|
|
146
|
+
const { t } = useTranslation('byline-admin')
|
|
140
147
|
const [currentUser, setCurrentUser] = useState<AdminUserResponse>(user)
|
|
141
148
|
const [currentUserRoles, setCurrentUserRoles] = useState<AdminRoleResponse[]>(initialUserRoles)
|
|
142
149
|
const [current, setCurrent] = useState<ComponentKey>('empty')
|
|
@@ -168,56 +175,66 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
168
175
|
setCurrentUserRoles(response.roles)
|
|
169
176
|
void router.invalidate()
|
|
170
177
|
toastManager.add({
|
|
171
|
-
title: '
|
|
172
|
-
description:
|
|
178
|
+
title: t('adminUsers.detail.rolesSavedToast'),
|
|
179
|
+
description: t('adminUsers.detail.rolesAssignedDescription', {
|
|
180
|
+
count: response.roles.length,
|
|
181
|
+
email: currentUser.email,
|
|
182
|
+
}),
|
|
173
183
|
data: { intent: 'success' },
|
|
174
184
|
})
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
const
|
|
187
|
+
const currentMeta = panelMeta[current]
|
|
188
|
+
const Panel = currentMeta.component
|
|
189
|
+
const currentTitle = currentMeta.titleKey ? t(currentMeta.titleKey) : ''
|
|
190
|
+
const editAriaFor = (section: string) => t('account.editAriaLabel', { section })
|
|
178
191
|
|
|
179
192
|
return (
|
|
180
193
|
<>
|
|
181
194
|
<div className={cx('byline-admin-user-grid', styles.grid)}>
|
|
182
195
|
<div className={cx('byline-admin-user-column', styles.column)}>
|
|
183
|
-
<ContainerSection
|
|
196
|
+
<ContainerSection
|
|
197
|
+
title={t('adminUsers.detail.sections.account')}
|
|
198
|
+
onEdit={openDrawer('update')}
|
|
199
|
+
editAriaLabel={editAriaFor(t('adminUsers.detail.sections.account'))}
|
|
200
|
+
>
|
|
184
201
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
185
|
-
<span className="muted">
|
|
202
|
+
<span className="muted">{t('account.profile.emailColon')}</span> {currentUser.email}
|
|
186
203
|
</p>
|
|
187
204
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
188
|
-
<span className="muted">
|
|
205
|
+
<span className="muted">{t('account.profile.givenName')}</span>{' '}
|
|
189
206
|
{currentUser.given_name ?? (
|
|
190
207
|
<span className={cx('muted byline-admin-user-not-set', styles.notSet)}>
|
|
191
|
-
|
|
208
|
+
{t('common.notSet')}
|
|
192
209
|
</span>
|
|
193
210
|
)}
|
|
194
211
|
</p>
|
|
195
212
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
196
|
-
<span className="muted">
|
|
213
|
+
<span className="muted">{t('account.profile.familyName')}</span>{' '}
|
|
197
214
|
{currentUser.family_name ?? (
|
|
198
215
|
<span className={cx('muted byline-admin-user-not-set', styles.notSet)}>
|
|
199
|
-
|
|
216
|
+
{t('common.notSet')}
|
|
200
217
|
</span>
|
|
201
218
|
)}
|
|
202
219
|
</p>
|
|
203
220
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
204
|
-
<span className="muted">
|
|
221
|
+
<span className="muted">{t('account.profile.username')}</span>{' '}
|
|
205
222
|
{currentUser.username ?? (
|
|
206
223
|
<span className={cx('muted byline-admin-user-not-set', styles.notSet)}>
|
|
207
|
-
|
|
224
|
+
{t('common.notSet')}
|
|
208
225
|
</span>
|
|
209
226
|
)}
|
|
210
227
|
</p>
|
|
211
228
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
212
|
-
<span className="muted">
|
|
213
|
-
{currentUser.is_super_admin ? '
|
|
229
|
+
<span className="muted">{t('account.status.superAdmin')}</span>{' '}
|
|
230
|
+
{currentUser.is_super_admin ? t('common.boolean.yes') : t('common.boolean.no')}
|
|
214
231
|
</p>
|
|
215
232
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
216
|
-
<span className="muted">
|
|
217
|
-
{currentUser.is_email_verified ? '
|
|
233
|
+
<span className="muted">{t('account.status.emailVerified')}</span>{' '}
|
|
234
|
+
{currentUser.is_email_verified ? t('common.boolean.yes') : t('common.boolean.no')}
|
|
218
235
|
</p>
|
|
219
236
|
<p className={cx('byline-admin-user-line-spaced', styles.lineSpaced)}>
|
|
220
|
-
<span className="muted">
|
|
237
|
+
<span className="muted">{t('account.status.status')}</span>{' '}
|
|
221
238
|
<span
|
|
222
239
|
className={cx({
|
|
223
240
|
'byline-admin-user-status-on': currentUser.is_enabled,
|
|
@@ -226,34 +243,40 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
226
243
|
[styles.statusOff]: !currentUser.is_enabled,
|
|
227
244
|
})}
|
|
228
245
|
>
|
|
229
|
-
{currentUser.is_enabled
|
|
246
|
+
{currentUser.is_enabled
|
|
247
|
+
? t('account.status.enabled')
|
|
248
|
+
: t('account.status.disabled')}
|
|
230
249
|
</span>
|
|
231
250
|
</p>
|
|
232
251
|
<Button size="sm" onClick={openDrawer('update')}>
|
|
233
|
-
|
|
252
|
+
{t('adminUsers.detail.updateButton')}
|
|
234
253
|
</Button>
|
|
235
254
|
<div className={cx('muted byline-admin-user-meta', styles.meta)}>
|
|
236
255
|
<p>
|
|
237
|
-
<span className="font-bold">
|
|
256
|
+
<span className="font-bold">{t('account.profile.created')} </span>
|
|
238
257
|
<LocalDateTime value={currentUser.created_at} />
|
|
239
258
|
</p>
|
|
240
259
|
<p>
|
|
241
|
-
<span className="font-bold">
|
|
260
|
+
<span className="font-bold">{t('account.profile.updated')} </span>
|
|
242
261
|
<LocalDateTime value={currentUser.updated_at} />
|
|
243
262
|
</p>
|
|
244
263
|
<p className={cx('byline-admin-user-line', styles.line)}>
|
|
245
|
-
<span className="font-bold">
|
|
246
|
-
<LocalDateTime value={currentUser.last_login} fallback=
|
|
264
|
+
<span className="font-bold">{t('account.profile.lastLogin')} </span>
|
|
265
|
+
<LocalDateTime value={currentUser.last_login} fallback={t('common.never')} />
|
|
247
266
|
</p>
|
|
248
267
|
</div>
|
|
249
268
|
</ContainerSection>
|
|
250
269
|
</div>
|
|
251
270
|
|
|
252
271
|
<div className={cx('byline-admin-user-column', styles.column)}>
|
|
253
|
-
<ContainerSection
|
|
272
|
+
<ContainerSection
|
|
273
|
+
title={t('adminUsers.detail.sections.roles')}
|
|
274
|
+
onEdit={openDrawer('roles')}
|
|
275
|
+
editAriaLabel={editAriaFor(t('adminUsers.detail.sections.roles'))}
|
|
276
|
+
>
|
|
254
277
|
{currentUserRoles.length === 0 ? (
|
|
255
278
|
<p className={cx('muted byline-admin-user-role-empty', styles.roleEmpty)}>
|
|
256
|
-
|
|
279
|
+
{t('adminUsers.detail.rolesEmpty')}
|
|
257
280
|
</p>
|
|
258
281
|
) : (
|
|
259
282
|
<div className={cx('byline-admin-user-role-list', styles.roleList)}>
|
|
@@ -263,25 +286,29 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
263
286
|
</div>
|
|
264
287
|
)}
|
|
265
288
|
<Button size="sm" onClick={openDrawer('roles')}>
|
|
266
|
-
|
|
289
|
+
{t('adminUsers.detail.editRolesButton')}
|
|
267
290
|
</Button>
|
|
268
291
|
</ContainerSection>
|
|
269
292
|
|
|
270
|
-
<ContainerSection
|
|
293
|
+
<ContainerSection
|
|
294
|
+
title={t('adminUsers.detail.sections.password')}
|
|
295
|
+
onEdit={openDrawer('set_password')}
|
|
296
|
+
editAriaLabel={editAriaFor(t('adminUsers.detail.sections.password'))}
|
|
297
|
+
>
|
|
271
298
|
<p className={cx('byline-admin-user-line-spaced', styles.lineSpaced)}>
|
|
272
|
-
|
|
299
|
+
{t('adminUsers.detail.password.intro')}
|
|
273
300
|
</p>
|
|
274
301
|
<Button size="sm" onClick={openDrawer('set_password')}>
|
|
275
|
-
|
|
302
|
+
{t('adminUsers.detail.password.setButton')}
|
|
276
303
|
</Button>
|
|
277
304
|
</ContainerSection>
|
|
278
305
|
|
|
279
|
-
<ContainerSection title=
|
|
306
|
+
<ContainerSection title={t('adminUsers.detail.sections.delete')}>
|
|
280
307
|
<p className={cx('byline-admin-user-line-spaced', styles.lineSpaced)}>
|
|
281
|
-
|
|
308
|
+
{t('adminUsers.detail.delete.intro')}
|
|
282
309
|
</p>
|
|
283
310
|
<Button size="sm" intent="danger" onClick={openModal('delete_user')}>
|
|
284
|
-
|
|
311
|
+
{t('adminUsers.detail.delete.button')}
|
|
285
312
|
</Button>
|
|
286
313
|
</ContainerSection>
|
|
287
314
|
</div>
|
|
@@ -295,10 +322,10 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
295
322
|
isOpen={isDrawerOpen}
|
|
296
323
|
onDismiss={closeDrawer}
|
|
297
324
|
className={cx({
|
|
298
|
-
'byline-admin-user-drawer-large':
|
|
299
|
-
[styles.drawerLarge]:
|
|
300
|
-
'byline-admin-user-drawer':
|
|
301
|
-
[styles.drawer]:
|
|
325
|
+
'byline-admin-user-drawer-large': currentMeta.drawerWidth === 'large',
|
|
326
|
+
[styles.drawerLarge]: currentMeta.drawerWidth === 'large',
|
|
327
|
+
'byline-admin-user-drawer': currentMeta.drawerWidth !== 'large',
|
|
328
|
+
[styles.drawer]: currentMeta.drawerWidth !== 'large',
|
|
302
329
|
})}
|
|
303
330
|
>
|
|
304
331
|
<Drawer.Container aria-hidden={!isDrawerOpen} className="p-2">
|
|
@@ -306,12 +333,12 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
306
333
|
<button type="button" tabIndex={0} className="sr-only">
|
|
307
334
|
no action
|
|
308
335
|
</button>
|
|
309
|
-
<IconButton aria-label=
|
|
336
|
+
<IconButton aria-label={t('common.actions.close')} size="sm" onClick={closeDrawer}>
|
|
310
337
|
<CloseIcon width="14px" height="14px" svgClassName="white-icon stroke-white" />
|
|
311
338
|
</IconButton>
|
|
312
339
|
</Drawer.TopActions>
|
|
313
340
|
<Drawer.Header>
|
|
314
|
-
<h2>{
|
|
341
|
+
<h2>{currentTitle}</h2>
|
|
315
342
|
</Drawer.Header>
|
|
316
343
|
<Drawer.Content>
|
|
317
344
|
<div className={cx('byline-admin-user-drawer-scroll', styles.drawerScroll)}>
|
|
@@ -334,8 +361,8 @@ export function AccountContainer({ user, allRoles, initialUserRoles }: AccountCo
|
|
|
334
361
|
<Modal isOpen={isModalOpen} onDismiss={closeModal} closeOnOverlayClick={false}>
|
|
335
362
|
<Modal.Container className={cx('byline-admin-user-modal', styles.modal)}>
|
|
336
363
|
<Modal.Header className={cx('byline-admin-user-modal-head', styles.modalHead)}>
|
|
337
|
-
<h3 className="m-0">{
|
|
338
|
-
<IconButton aria-label=
|
|
364
|
+
<h3 className="m-0">{currentTitle}</h3>
|
|
365
|
+
<IconButton aria-label={t('common.actions.close')} size="sm" onClick={closeModal}>
|
|
339
366
|
<CloseIcon width="14px" height="14px" svgClassName="white-icon" />
|
|
340
367
|
</IconButton>
|
|
341
368
|
</Modal.Header>
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { useState } from 'react'
|
|
24
24
|
import { useRouter } from '@tanstack/react-router'
|
|
25
25
|
|
|
26
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
26
27
|
import { Alert, Button, LoaderEllipsis, Modal } from '@byline/ui/react'
|
|
27
28
|
import cx from 'classnames'
|
|
28
29
|
|
|
@@ -46,6 +47,7 @@ function displayNameFor(user: AdminUserResponse): string {
|
|
|
46
47
|
export function DeleteUser({ user, onClose }: DeleteUserProps) {
|
|
47
48
|
const navigate = useNavigate()
|
|
48
49
|
const router = useRouter()
|
|
50
|
+
const { t } = useTranslation('byline-admin')
|
|
49
51
|
const [error, setError] = useState<string | null>(null)
|
|
50
52
|
const [pending, setPending] = useState(false)
|
|
51
53
|
|
|
@@ -64,15 +66,13 @@ export function DeleteUser({ user, onClose }: DeleteUserProps) {
|
|
|
64
66
|
} catch (err) {
|
|
65
67
|
const code = getErrorCode(err)
|
|
66
68
|
if (code === 'admin.users.selfDeleteForbidden') {
|
|
67
|
-
setError('
|
|
69
|
+
setError(t('adminUsers.delete.errors.selfDelete'))
|
|
68
70
|
} else if (code === 'admin.users.versionConflict') {
|
|
69
|
-
setError(
|
|
70
|
-
'This user has been modified elsewhere since you opened this dialog. Close and reload before trying again.'
|
|
71
|
-
)
|
|
71
|
+
setError(t('adminUsers.delete.errors.versionConflict'))
|
|
72
72
|
} else if (code === 'admin.users.notFound') {
|
|
73
|
-
setError('
|
|
73
|
+
setError(t('adminUsers.delete.errors.notFound'))
|
|
74
74
|
} else {
|
|
75
|
-
setError('
|
|
75
|
+
setError(t('adminUsers.delete.errors.fallback'))
|
|
76
76
|
}
|
|
77
77
|
setPending(false)
|
|
78
78
|
}
|
|
@@ -87,14 +87,13 @@ export function DeleteUser({ user, onClose }: DeleteUserProps) {
|
|
|
87
87
|
</Alert>
|
|
88
88
|
) : null}
|
|
89
89
|
<p className={cx('byline-admin-user-delete-row', styles.row)}>
|
|
90
|
-
<span className="muted">
|
|
90
|
+
<span className="muted">{t('adminUsers.delete.userLabel')}</span> {displayNameFor(user)}
|
|
91
91
|
</p>
|
|
92
92
|
<p className={cx('byline-admin-user-delete-row', styles.row)}>
|
|
93
|
-
<span className="muted">
|
|
93
|
+
<span className="muted">{t('adminUsers.delete.emailLabel')}</span> {user.email}
|
|
94
94
|
</p>
|
|
95
95
|
<p className={cx('byline-admin-user-delete-warning', styles.warning)}>
|
|
96
|
-
|
|
97
|
-
sessions will be invalidated at the next refresh.
|
|
96
|
+
{t('adminUsers.delete.warning')}
|
|
98
97
|
</p>
|
|
99
98
|
</div>
|
|
100
99
|
<div className={cx('byline-admin-user-delete-actions', styles.actions)}>
|
|
@@ -106,7 +105,7 @@ export function DeleteUser({ user, onClose }: DeleteUserProps) {
|
|
|
106
105
|
disabled={pending}
|
|
107
106
|
className={cx('byline-admin-user-delete-button', styles.button)}
|
|
108
107
|
>
|
|
109
|
-
|
|
108
|
+
{t('common.actions.cancel')}
|
|
110
109
|
</Button>
|
|
111
110
|
<Button
|
|
112
111
|
size="sm"
|
|
@@ -115,7 +114,7 @@ export function DeleteUser({ user, onClose }: DeleteUserProps) {
|
|
|
115
114
|
disabled={pending}
|
|
116
115
|
className={cx('byline-admin-user-delete-button', styles.button)}
|
|
117
116
|
>
|
|
118
|
-
{pending === true ? <LoaderEllipsis size={42} /> : '
|
|
117
|
+
{pending === true ? <LoaderEllipsis size={42} /> : t('adminUsers.delete.confirmButton')}
|
|
119
118
|
</Button>
|
|
120
119
|
</div>
|
|
121
120
|
</Modal.Content>
|
|
@@ -8,16 +8,17 @@
|
|
|
8
8
|
* Copyright (c) Infonomic Company Limited
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { useState } from 'react'
|
|
11
|
+
import { useMemo, useState } from 'react'
|
|
12
12
|
import { useRouter, useRouterState } from '@tanstack/react-router'
|
|
13
13
|
|
|
14
14
|
import { CreateAdminUser } from '@byline/admin/admin-users/components/create'
|
|
15
|
+
import { LocalDateTime } from '@byline/admin/react'
|
|
16
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
15
17
|
import {
|
|
16
18
|
CloseIcon,
|
|
17
19
|
Container,
|
|
18
20
|
Drawer,
|
|
19
21
|
IconButton,
|
|
20
|
-
LocalDateTime,
|
|
21
22
|
PlusIcon,
|
|
22
23
|
Search,
|
|
23
24
|
Section,
|
|
@@ -40,10 +41,18 @@ import type {
|
|
|
40
41
|
AdminUserResponse,
|
|
41
42
|
} from '../../server-fns/admin-users/index.js'
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
// Structural column-def template: position / sort-key / alignment / class
|
|
45
|
+
// are fixed at module scope; the `label` field is filled in per-render
|
|
46
|
+
// inside the component so it can flow through `t(...)`. Same pattern the
|
|
47
|
+
// account container's `panels` map uses.
|
|
48
|
+
type ColumnTemplate = Omit<TableHeadingCellSortableProps, 'lng' | 'label'> & {
|
|
49
|
+
labelKey: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const columnTemplates: ColumnTemplate[] = [
|
|
44
53
|
{
|
|
45
54
|
fieldName: 'given_name',
|
|
46
|
-
|
|
55
|
+
labelKey: 'adminUsers.list.columns.givenName',
|
|
47
56
|
path: '/admin/users',
|
|
48
57
|
sortable: true,
|
|
49
58
|
scope: 'col',
|
|
@@ -52,7 +61,7 @@ const tableColumnDefs: Omit<TableHeadingCellSortableProps, 'lng'>[] = [
|
|
|
52
61
|
},
|
|
53
62
|
{
|
|
54
63
|
fieldName: 'family_name',
|
|
55
|
-
|
|
64
|
+
labelKey: 'adminUsers.list.columns.familyName',
|
|
56
65
|
path: '/admin/users',
|
|
57
66
|
sortable: true,
|
|
58
67
|
scope: 'col',
|
|
@@ -61,7 +70,7 @@ const tableColumnDefs: Omit<TableHeadingCellSortableProps, 'lng'>[] = [
|
|
|
61
70
|
},
|
|
62
71
|
{
|
|
63
72
|
fieldName: 'email',
|
|
64
|
-
|
|
73
|
+
labelKey: 'adminUsers.list.columns.email',
|
|
65
74
|
path: '/admin/users',
|
|
66
75
|
sortable: true,
|
|
67
76
|
scope: 'col',
|
|
@@ -70,7 +79,7 @@ const tableColumnDefs: Omit<TableHeadingCellSortableProps, 'lng'>[] = [
|
|
|
70
79
|
},
|
|
71
80
|
{
|
|
72
81
|
fieldName: 'updated_at',
|
|
73
|
-
|
|
82
|
+
labelKey: 'adminUsers.list.columns.updated',
|
|
74
83
|
path: '/admin/users',
|
|
75
84
|
sortable: true,
|
|
76
85
|
scope: 'col',
|
|
@@ -79,7 +88,7 @@ const tableColumnDefs: Omit<TableHeadingCellSortableProps, 'lng'>[] = [
|
|
|
79
88
|
},
|
|
80
89
|
{
|
|
81
90
|
fieldName: 'created_at',
|
|
82
|
-
|
|
91
|
+
labelKey: 'adminUsers.list.columns.created',
|
|
83
92
|
path: '/admin/users',
|
|
84
93
|
sortable: true,
|
|
85
94
|
scope: 'col',
|
|
@@ -123,8 +132,14 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
123
132
|
const router = useRouter()
|
|
124
133
|
const toastManager = useToastManager()
|
|
125
134
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
135
|
+
const { t } = useTranslation('byline-admin')
|
|
126
136
|
const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false)
|
|
127
137
|
|
|
138
|
+
const tableColumnDefs = useMemo(
|
|
139
|
+
() => columnTemplates.map(({ labelKey, ...rest }) => ({ ...rest, label: t(labelKey) })),
|
|
140
|
+
[t]
|
|
141
|
+
)
|
|
142
|
+
|
|
128
143
|
const openCreateDrawer = () => setIsCreateDrawerOpen(true)
|
|
129
144
|
const closeCreateDrawer = () => setIsCreateDrawerOpen(false)
|
|
130
145
|
|
|
@@ -134,7 +149,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
134
149
|
// current page of the table.
|
|
135
150
|
void router.invalidate()
|
|
136
151
|
toastManager.add({
|
|
137
|
-
title: '
|
|
152
|
+
title: t('adminUsers.list.createdToastTitle'),
|
|
138
153
|
description: created.email,
|
|
139
154
|
data: { intent: 'success' },
|
|
140
155
|
})
|
|
@@ -173,9 +188,11 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
173
188
|
<Section>
|
|
174
189
|
<Container>
|
|
175
190
|
<div className={cx('byline-admin-users-list-head', styles.head)}>
|
|
176
|
-
<h1 className={cx('byline-admin-users-list-title', styles.title)}>
|
|
191
|
+
<h1 className={cx('byline-admin-users-list-title', styles.title)}>
|
|
192
|
+
{t('adminUsers.list.title')}
|
|
193
|
+
</h1>
|
|
177
194
|
<Stats total={data.meta.total} />
|
|
178
|
-
<IconButton aria-label=
|
|
195
|
+
<IconButton aria-label={t('adminUsers.list.createAriaLabel')} onClick={openCreateDrawer}>
|
|
179
196
|
<PlusIcon height="18px" width="18px" svgClassName="stroke-white" />
|
|
180
197
|
</IconButton>
|
|
181
198
|
</div>
|
|
@@ -184,7 +201,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
184
201
|
onSearch={handleOnSearch}
|
|
185
202
|
onClear={handleOnClear}
|
|
186
203
|
inputSize="sm"
|
|
187
|
-
placeholder=
|
|
204
|
+
placeholder={t('adminUsers.list.searchPlaceholder')}
|
|
188
205
|
className={cx('byline-admin-users-list-search', styles.search)}
|
|
189
206
|
/>
|
|
190
207
|
<RouterPager
|
|
@@ -193,7 +210,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
193
210
|
showFirstButton
|
|
194
211
|
showLastButton
|
|
195
212
|
componentName="pagerTop"
|
|
196
|
-
aria-label=
|
|
213
|
+
aria-label={t('adminUsers.list.pagerTopAriaLabel')}
|
|
197
214
|
/>
|
|
198
215
|
</div>
|
|
199
216
|
<Table.Container className={cx('byline-admin-users-list-table-wrap', styles.tableWrap)}>
|
|
@@ -220,7 +237,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
220
237
|
<span
|
|
221
238
|
className={cx('muted byline-admin-users-list-not-set', styles.notSet)}
|
|
222
239
|
>
|
|
223
|
-
|
|
240
|
+
{t('common.notSet')}
|
|
224
241
|
</span>
|
|
225
242
|
)}
|
|
226
243
|
</Link>
|
|
@@ -228,7 +245,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
228
245
|
<Table.Cell>
|
|
229
246
|
{user.family_name ?? (
|
|
230
247
|
<span className={cx('muted byline-admin-users-list-not-set', styles.notSet)}>
|
|
231
|
-
|
|
248
|
+
{t('common.notSet')}
|
|
232
249
|
</span>
|
|
233
250
|
)}
|
|
234
251
|
</Table.Cell>
|
|
@@ -277,7 +294,7 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
277
294
|
showFirstButton
|
|
278
295
|
showLastButton
|
|
279
296
|
componentName="pagerBottom"
|
|
280
|
-
aria-label=
|
|
297
|
+
aria-label={t('adminUsers.list.pagerBottomAriaLabel')}
|
|
281
298
|
/>
|
|
282
299
|
</div>
|
|
283
300
|
</Container>
|
|
@@ -296,12 +313,16 @@ export function AdminUsersListView({ data }: { data: AdminUserListResponse }) {
|
|
|
296
313
|
<button type="button" tabIndex={0} className="sr-only">
|
|
297
314
|
no action
|
|
298
315
|
</button>
|
|
299
|
-
<IconButton
|
|
316
|
+
<IconButton
|
|
317
|
+
aria-label={t('common.actions.close')}
|
|
318
|
+
size="sm"
|
|
319
|
+
onClick={closeCreateDrawer}
|
|
320
|
+
>
|
|
300
321
|
<CloseIcon width="14px" height="14px" svgClassName="white-icon stroke-white" />
|
|
301
322
|
</IconButton>
|
|
302
323
|
</Drawer.TopActions>
|
|
303
324
|
<Drawer.Header>
|
|
304
|
-
<h2>
|
|
325
|
+
<h2>{t('adminUsers.list.newDrawerTitle')}</h2>
|
|
305
326
|
</Drawer.Header>
|
|
306
327
|
<Drawer.Content>
|
|
307
328
|
<div className={cx('byline-admin-users-list-drawer-scroll', styles.drawerScroll)}>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { useState } from 'react'
|
|
12
12
|
|
|
13
|
+
import { LanguageMenu, useTranslation } from '@byline/i18n/react'
|
|
13
14
|
import { Button } from '@byline/ui/react'
|
|
14
15
|
import cx from 'classnames'
|
|
15
16
|
|
|
@@ -32,6 +33,7 @@ function displayNameFor(user: CurrentAdminUser): string {
|
|
|
32
33
|
|
|
33
34
|
export function AdminAppBar({ user }: AdminAppBarProps) {
|
|
34
35
|
const { breadCrumbSettings } = useBreadcrumbs()
|
|
36
|
+
const { t } = useTranslation('byline-admin')
|
|
35
37
|
const [signingOut, setSigningOut] = useState(false)
|
|
36
38
|
|
|
37
39
|
async function handleSignOut() {
|
|
@@ -59,14 +61,15 @@ export function AdminAppBar({ user }: AdminAppBarProps) {
|
|
|
59
61
|
/>
|
|
60
62
|
</div>
|
|
61
63
|
<div className={cx('byline-admin-app-bar-right', styles.right)}>
|
|
64
|
+
<LanguageMenu />
|
|
62
65
|
<span className={cx('byline-admin-app-bar-user', styles.user)}>
|
|
63
|
-
|
|
66
|
+
{t('chrome.appBar.signedInAs')}{' '}
|
|
64
67
|
<span className={cx('byline-admin-app-bar-user-name', styles.userName)}>
|
|
65
68
|
{displayNameFor(user)}
|
|
66
69
|
</span>
|
|
67
70
|
</span>
|
|
68
71
|
<Button size="xs" intent="secondary" onClick={handleSignOut} disabled={signingOut}>
|
|
69
|
-
{signingOut ? '
|
|
72
|
+
{signingOut ? t('common.actions.signingOut') : t('common.actions.signOut')}
|
|
70
73
|
</Button>
|
|
71
74
|
</div>
|
|
72
75
|
</header>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
12
12
|
|
|
13
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
13
14
|
import { Dropdown, EllipsisIcon } from '@byline/ui/react'
|
|
14
15
|
import cx from 'classnames'
|
|
15
16
|
|
|
@@ -228,12 +229,13 @@ function ChevronIcon({ isLeaf = false }: { isLeaf?: boolean }): React.JSX.Elemen
|
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
function OverflowDropdown({ items }: { items: Breadcrumb[] }): React.JSX.Element {
|
|
232
|
+
const { t } = useTranslation('byline-admin')
|
|
231
233
|
return (
|
|
232
234
|
<li className={cx('byline-breadcrumbs-item', styles.item)}>
|
|
233
235
|
<ChevronIcon />
|
|
234
236
|
<Dropdown.Root>
|
|
235
237
|
<Dropdown.Trigger
|
|
236
|
-
aria-label=
|
|
238
|
+
aria-label={t('chrome.breadcrumbs.showHiddenAriaLabel')}
|
|
237
239
|
className={cx('byline-breadcrumbs-overflow-trigger', styles.overflowTrigger)}
|
|
238
240
|
>
|
|
239
241
|
<EllipsisIcon className={cx('byline-breadcrumbs-overflow-icon', styles.overflowIcon)} />
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { WorkflowStatus } from '@byline/core'
|
|
10
10
|
import { getClientConfig, getWorkflowStatuses } from '@byline/core'
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
11
12
|
import { Card, Container, Section } from '@byline/ui/react'
|
|
12
13
|
import cx from 'classnames'
|
|
13
14
|
|
|
@@ -62,6 +63,7 @@ interface AdminDashboardProps {
|
|
|
62
63
|
|
|
63
64
|
export function AdminDashboard({ statsMap }: AdminDashboardProps) {
|
|
64
65
|
const config = getClientConfig()
|
|
66
|
+
const { t } = useTranslation('byline-admin')
|
|
65
67
|
|
|
66
68
|
return (
|
|
67
69
|
<Section>
|
|
@@ -86,10 +88,12 @@ export function AdminDashboard({ statsMap }: AdminDashboardProps) {
|
|
|
86
88
|
{collection.labels.plural}
|
|
87
89
|
</span>
|
|
88
90
|
<span className={cx('muted byline-dashboard-title-meta', styles.titleMeta)}>
|
|
89
|
-
{total}
|
|
91
|
+
{t('dashboard.totalCount', { count: total })}
|
|
90
92
|
</span>
|
|
91
93
|
</Card.Title>
|
|
92
|
-
<Card.Description className="muted">
|
|
94
|
+
<Card.Description className="muted">
|
|
95
|
+
{t('dashboard.collectionDescription', { label: collection.labels.plural })}
|
|
96
|
+
</Card.Description>
|
|
93
97
|
</div>
|
|
94
98
|
</Card.Header>
|
|
95
99
|
</Link>
|
|
@@ -114,7 +118,9 @@ export function AdminDashboard({ statsMap }: AdminDashboardProps) {
|
|
|
114
118
|
params={{ collection: collection.path }}
|
|
115
119
|
className={cx('byline-dashboard-empty-link', styles.emptyLink)}
|
|
116
120
|
>
|
|
117
|
-
<p>
|
|
121
|
+
<p>
|
|
122
|
+
{t('dashboard.collectionDescription', { label: collection.labels.plural })}
|
|
123
|
+
</p>
|
|
118
124
|
</Link>
|
|
119
125
|
)}
|
|
120
126
|
</Card.Content>
|