@byline/host-tanstack-start 2.5.2 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type React from 'react'
|
|
4
4
|
|
|
5
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
5
6
|
import cx from 'classnames'
|
|
6
7
|
|
|
7
8
|
import styles from './hamburger.module.css'
|
|
@@ -21,6 +22,7 @@ export function Hamburger({
|
|
|
21
22
|
...other
|
|
22
23
|
}: HamburgerProps): React.JSX.Element {
|
|
23
24
|
const { toggleDrawer } = useAdminMenu()
|
|
25
|
+
const { t } = useTranslation('byline-admin')
|
|
24
26
|
|
|
25
27
|
// Hard code the drawer hamburger to always closed
|
|
26
28
|
// to prevent the 'X' pattern from being the default
|
|
@@ -43,7 +45,7 @@ export function Hamburger({
|
|
|
43
45
|
className
|
|
44
46
|
)}
|
|
45
47
|
tabIndex={0}
|
|
46
|
-
aria-label=
|
|
48
|
+
aria-label={t('chrome.menu.openAriaLabel')}
|
|
47
49
|
aria-controls="admin-menu"
|
|
48
50
|
aria-haspopup="true"
|
|
49
51
|
{...other}
|
|
@@ -12,6 +12,7 @@ import { useRouterState } from '@tanstack/react-router'
|
|
|
12
12
|
import { ADMIN_PERMISSIONS_ABILITIES } from '@byline/admin/admin-permissions'
|
|
13
13
|
import { ADMIN_ROLES_ABILITIES } from '@byline/admin/admin-roles'
|
|
14
14
|
import { ADMIN_USERS_ABILITIES } from '@byline/admin/admin-users'
|
|
15
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
15
16
|
import { HomeIcon, RolesIcon, SettingsSlidersIcon, UserIcon, UsersIcon } from '@byline/ui/react'
|
|
16
17
|
import cx from 'classnames'
|
|
17
18
|
import { useSwipeable } from 'react-swipeable'
|
|
@@ -51,6 +52,7 @@ function MenuItem({ to, label, icon, pathname, compact }: MenuItemProps) {
|
|
|
51
52
|
export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
52
53
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
53
54
|
const { mobile, drawerOpen, closeDrawer } = useAdminMenu()
|
|
55
|
+
const { t } = useTranslation('byline-admin')
|
|
54
56
|
|
|
55
57
|
// Cosmetic ability cues — hide the admin-management section entirely
|
|
56
58
|
// when the user holds none of its read abilities. Server-side
|
|
@@ -96,7 +98,7 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
96
98
|
<ul>
|
|
97
99
|
<MenuItem
|
|
98
100
|
to="/admin"
|
|
99
|
-
label=
|
|
101
|
+
label={t('chrome.menu.dashboard')}
|
|
100
102
|
icon={<HomeIcon width="20px" height="20px" />}
|
|
101
103
|
pathname={pathname}
|
|
102
104
|
compact={compact}
|
|
@@ -107,7 +109,7 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
107
109
|
{canReadUsers && (
|
|
108
110
|
<MenuItem
|
|
109
111
|
to="/admin/users"
|
|
110
|
-
label=
|
|
112
|
+
label={t('chrome.menu.adminUsers')}
|
|
111
113
|
icon={<UsersIcon width="20px" height="20px" />}
|
|
112
114
|
pathname={pathname}
|
|
113
115
|
compact={compact}
|
|
@@ -116,7 +118,7 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
116
118
|
{canReadRoles && (
|
|
117
119
|
<MenuItem
|
|
118
120
|
to="/admin/roles"
|
|
119
|
-
label=
|
|
121
|
+
label={t('chrome.menu.adminRoles')}
|
|
120
122
|
icon={<RolesIcon width="20px" height="20px" />}
|
|
121
123
|
pathname={pathname}
|
|
122
124
|
compact={compact}
|
|
@@ -125,7 +127,7 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
125
127
|
{canReadPermissions && (
|
|
126
128
|
<MenuItem
|
|
127
129
|
to="/admin/permissions"
|
|
128
|
-
label=
|
|
130
|
+
label={t('chrome.menu.permissions')}
|
|
129
131
|
icon={<SettingsSlidersIcon width="20px" height="20px" />}
|
|
130
132
|
pathname={pathname}
|
|
131
133
|
compact={compact}
|
|
@@ -137,7 +139,7 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
137
139
|
<PreviewToggle compact={compact} />
|
|
138
140
|
<MenuItem
|
|
139
141
|
to="/admin/account"
|
|
140
|
-
label=
|
|
142
|
+
label={t('chrome.account')}
|
|
141
143
|
icon={<UserIcon />}
|
|
142
144
|
pathname={pathname}
|
|
143
145
|
compact={compact}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { useEffect, useState } from 'react'
|
|
25
25
|
|
|
26
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
26
27
|
import { EyeClosedIcon, EyeOpenIcon } from '@byline/ui/react'
|
|
27
28
|
import cx from 'classnames'
|
|
28
29
|
|
|
@@ -37,6 +38,7 @@ interface PreviewToggleProps {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export function PreviewToggle({ compact }: PreviewToggleProps) {
|
|
41
|
+
const { t } = useTranslation('byline-admin')
|
|
40
42
|
const [preview, setPreview] = useState<boolean | null>(null)
|
|
41
43
|
const [busy, setBusy] = useState(false)
|
|
42
44
|
|
|
@@ -81,7 +83,7 @@ export function PreviewToggle({ compact }: PreviewToggleProps) {
|
|
|
81
83
|
// Hide entirely until first read resolves so the label doesn't flicker.
|
|
82
84
|
if (preview == null) return null
|
|
83
85
|
|
|
84
|
-
const label = preview ? '
|
|
86
|
+
const label = preview ? t('chrome.preview.on') : t('chrome.preview.off')
|
|
85
87
|
const icon = preview ? (
|
|
86
88
|
<EyeOpenIcon width="20px" height="20px" />
|
|
87
89
|
) : (
|
|
@@ -94,10 +96,10 @@ export function PreviewToggle({ compact }: PreviewToggleProps) {
|
|
|
94
96
|
type="button"
|
|
95
97
|
onClick={handleToggle}
|
|
96
98
|
disabled={busy}
|
|
97
|
-
aria-label={
|
|
98
|
-
|
|
99
|
-
preview ? 'Drafts are visible on the public site' : 'Public site shows published only'
|
|
99
|
+
aria-label={
|
|
100
|
+
preview ? t('chrome.preview.disableAriaLabel') : t('chrome.preview.enableAriaLabel')
|
|
100
101
|
}
|
|
102
|
+
title={preview ? t('chrome.preview.onTitle') : t('chrome.preview.offTitle')}
|
|
101
103
|
>
|
|
102
104
|
<span className="icon">{icon}</span>
|
|
103
105
|
<span className="label">{label}</span>
|
|
@@ -16,48 +16,56 @@ import type { ErrorComponentProps, NotFoundRouteProps } from '@tanstack/react-ro
|
|
|
16
16
|
import { useRouter } from '@tanstack/react-router'
|
|
17
17
|
|
|
18
18
|
import { BylineError, ErrorCodes } from '@byline/core'
|
|
19
|
+
import type { UseTranslationReturn } from '@byline/i18n/react'
|
|
20
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
19
21
|
import { Alert, Button, Container, Section } from '@byline/ui/react'
|
|
20
22
|
import cx from 'classnames'
|
|
21
23
|
|
|
22
24
|
import styles from './route-error.module.css'
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
[ErrorCodes.
|
|
29
|
-
[ErrorCodes.
|
|
30
|
-
[ErrorCodes.
|
|
31
|
-
[ErrorCodes.
|
|
32
|
-
[ErrorCodes.
|
|
26
|
+
// Static map from ErrorCode → translation key. Resolved to a localised
|
|
27
|
+
// string at render time via the `t` function below — module-scope
|
|
28
|
+
// strings would otherwise freeze the English copy in.
|
|
29
|
+
const ERROR_TITLE_KEYS: Record<string, string> = {
|
|
30
|
+
[ErrorCodes.NOT_FOUND]: 'routeError.titles.notFound',
|
|
31
|
+
[ErrorCodes.VALIDATION]: 'routeError.titles.validation',
|
|
32
|
+
[ErrorCodes.CONFLICT]: 'routeError.titles.conflict',
|
|
33
|
+
[ErrorCodes.INVALID_TRANSITION]: 'routeError.titles.invalidTransition',
|
|
34
|
+
[ErrorCodes.PATCH_FAILED]: 'routeError.titles.patchFailed',
|
|
35
|
+
[ErrorCodes.DATABASE]: 'routeError.titles.database',
|
|
36
|
+
[ErrorCodes.STORAGE]: 'routeError.titles.storage',
|
|
37
|
+
[ErrorCodes.UNHANDLED]: 'routeError.titles.unhandled',
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
type Translate = UseTranslationReturn['t']
|
|
41
|
+
|
|
42
|
+
function getErrorTitle(error: unknown, t: Translate): string {
|
|
36
43
|
if (error instanceof BylineError) {
|
|
37
|
-
return
|
|
44
|
+
return t(ERROR_TITLE_KEYS[error.code] ?? 'routeError.titles.unhandled')
|
|
38
45
|
}
|
|
39
|
-
return '
|
|
46
|
+
return t('routeError.titles.unhandled')
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
function getErrorMessage(error: unknown): string {
|
|
49
|
+
function getErrorMessage(error: unknown, t: Translate): string {
|
|
43
50
|
if (error instanceof BylineError) {
|
|
44
51
|
return error.message
|
|
45
52
|
}
|
|
46
53
|
if (error instanceof Error) {
|
|
47
54
|
return error.message
|
|
48
55
|
}
|
|
49
|
-
return '
|
|
56
|
+
return t('routeError.defaultMessage')
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
export function RouteError({ error, reset }: ErrorComponentProps) {
|
|
53
60
|
const router = useRouter()
|
|
61
|
+
const { t } = useTranslation('byline-admin')
|
|
54
62
|
|
|
55
63
|
return (
|
|
56
64
|
<Section className={cx('byline-route-error', styles.section)}>
|
|
57
65
|
<Container className={cx('byline-route-error-container', styles.container)}>
|
|
58
|
-
<Alert intent="danger" icon close={false} title={getErrorTitle(error)}>
|
|
66
|
+
<Alert intent="danger" icon close={false} title={getErrorTitle(error, t)}>
|
|
59
67
|
<p className={cx('byline-route-error-message', styles.message)}>
|
|
60
|
-
{getErrorMessage(error)}
|
|
68
|
+
{getErrorMessage(error, t)}
|
|
61
69
|
</p>
|
|
62
70
|
<div className={cx('byline-route-error-actions', styles.actions)}>
|
|
63
71
|
<Button
|
|
@@ -69,10 +77,10 @@ export function RouteError({ error, reset }: ErrorComponentProps) {
|
|
|
69
77
|
router.invalidate()
|
|
70
78
|
}}
|
|
71
79
|
>
|
|
72
|
-
|
|
80
|
+
{t('common.actions.tryAgain')}
|
|
73
81
|
</Button>
|
|
74
82
|
<a href="/" className={cx('byline-route-error-link', styles.link)}>
|
|
75
|
-
|
|
83
|
+
{t('common.actions.goToHomepage')}
|
|
76
84
|
</a>
|
|
77
85
|
</div>
|
|
78
86
|
</Alert>
|
|
@@ -82,16 +90,17 @@ export function RouteError({ error, reset }: ErrorComponentProps) {
|
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
export function RouteNotFound(_props: NotFoundRouteProps) {
|
|
93
|
+
const { t } = useTranslation('byline-admin')
|
|
85
94
|
return (
|
|
86
95
|
<Section className={cx('byline-route-error', styles.section)}>
|
|
87
96
|
<Container className={cx('byline-route-error-container', styles.container)}>
|
|
88
|
-
<Alert intent="warning" icon close={false} title=
|
|
97
|
+
<Alert intent="warning" icon close={false} title={t('routeError.notFound.title')}>
|
|
89
98
|
<p className={cx('byline-route-error-message', styles.message)}>
|
|
90
|
-
|
|
99
|
+
{t('routeError.notFound.message')}
|
|
91
100
|
</p>
|
|
92
101
|
<div className={cx('byline-route-error-actions', styles.actions)}>
|
|
93
102
|
<a href="/" className={cx('byline-route-error-link', styles.link)}>
|
|
94
|
-
|
|
103
|
+
{t('common.actions.goToHomepage')}
|
|
95
104
|
</a>
|
|
96
105
|
</div>
|
|
97
106
|
</Alert>
|
|
@@ -101,17 +110,21 @@ export function RouteNotFound(_props: NotFoundRouteProps) {
|
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
/**
|
|
104
|
-
* Minimal fallback for when providers may be broken
|
|
105
|
-
* route
|
|
113
|
+
* Minimal fallback for when providers may be broken — used at the root
|
|
114
|
+
* route where the i18n provider itself may not be mounted. Stays in
|
|
115
|
+
* English on purpose.
|
|
106
116
|
*/
|
|
107
117
|
export function RootError({ error, reset }: ErrorComponentProps) {
|
|
118
|
+
function rootMessage(err: unknown): string {
|
|
119
|
+
if (err instanceof Error) return err.message
|
|
120
|
+
return 'An unexpected error occurred. Please try again.'
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
return (
|
|
109
124
|
<div className={cx('byline-root-error', styles.rootRoot)}>
|
|
110
125
|
<div className={cx('byline-root-error-inner', styles.rootInner)}>
|
|
111
126
|
<h1 className={cx('byline-root-error-title', styles.rootTitle)}>Something went wrong</h1>
|
|
112
|
-
<p className={cx('byline-root-error-detail', styles.rootDetail)}>
|
|
113
|
-
{getErrorMessage(error)}
|
|
114
|
-
</p>
|
|
127
|
+
<p className={cx('byline-root-error-detail', styles.rootDetail)}>{rootMessage(error)}</p>
|
|
115
128
|
<button
|
|
116
129
|
type="button"
|
|
117
130
|
onClick={reset}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Override handles:
|
|
5
5
|
* .byline-sign-in-page — outer <main>
|
|
6
|
+
* .byline-sign-in-page-bar — top bar carrying the LanguageMenu
|
|
6
7
|
* .byline-sign-in-page-inner — vertically-centred card container
|
|
7
8
|
*
|
|
8
9
|
* The Home link itself lives inside `SignInForm`'s action row — see
|
|
@@ -14,10 +15,18 @@
|
|
|
14
15
|
display: flex;
|
|
15
16
|
flex-direction: column;
|
|
16
17
|
flex: 1 1 0%;
|
|
17
|
-
align-items:
|
|
18
|
+
align-items: stretch;
|
|
18
19
|
padding: 1.5rem;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
.bar,
|
|
23
|
+
:global(.byline-sign-in-page-bar) {
|
|
24
|
+
display: flex;
|
|
25
|
+
justify-content: flex-end;
|
|
26
|
+
align-items: center;
|
|
27
|
+
min-height: 1.75rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
.inner,
|
|
22
31
|
:global(.byline-sign-in-page-inner) {
|
|
23
32
|
display: flex;
|
|
@@ -9,35 +9,69 @@
|
|
|
9
9
|
import { SignInForm } from '@byline/admin/auth/components/sign-in-form'
|
|
10
10
|
import { BylineAdminServicesProvider } from '@byline/admin/services'
|
|
11
11
|
import { getClientConfig } from '@byline/core'
|
|
12
|
+
import type { LocaleCode } from '@byline/i18n'
|
|
13
|
+
import { I18nProvider, LanguageMenu } from '@byline/i18n/react'
|
|
12
14
|
import cx from 'classnames'
|
|
13
15
|
|
|
16
|
+
import { buildLocaleDefinitions } from '../../i18n/locale-definitions.js'
|
|
14
17
|
import { bylineAdminServices } from '../../integrations/byline-admin-services.js'
|
|
18
|
+
import { setInterfaceLocaleFn } from '../../server-fns/i18n/index.js'
|
|
15
19
|
import styles from './sign-in-page.module.css'
|
|
16
20
|
|
|
17
21
|
interface SignInPageProps {
|
|
18
22
|
callbackUrl?: string
|
|
23
|
+
activeLocale: LocaleCode
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
27
|
* Sign-in page chrome — rendered outside the authenticated admin
|
|
23
28
|
* layout (no app bar, breadcrumbs, or menu drawer). Wraps the
|
|
24
|
-
* `SignInForm` from `@byline/
|
|
29
|
+
* `SignInForm` from `@byline/admin` in the admin services provider so
|
|
25
30
|
* the form can call `signIn` via the typed contract.
|
|
26
31
|
*
|
|
32
|
+
* Mounts its own `<I18nProvider>` because the layout-level provider
|
|
33
|
+
* (the one that wraps the authenticated admin) doesn't apply here.
|
|
34
|
+
* `<LanguageMenu>` lights up automatically when two or more interface
|
|
35
|
+
* locales are configured. On change, the menu calls
|
|
36
|
+
* `setInterfaceLocaleFn` which writes the cookie unconditionally and
|
|
37
|
+
* skips the DB write on the pre-auth path (the user has no admin
|
|
38
|
+
* session yet). After sign-in succeeds, the `adminSignIn` server fn
|
|
39
|
+
* reconciles the cookie locale into the user's
|
|
40
|
+
* `admin_users.preferred_locale` so the pre-auth choice becomes
|
|
41
|
+
* sticky across devices from day one.
|
|
42
|
+
*
|
|
27
43
|
* Threads the configured `serverURL` into the `SignInForm` as `homeUrl` so
|
|
28
44
|
* the form's action row can render a plain "Home" link beside the submit
|
|
29
|
-
* button.
|
|
30
|
-
* them get back to the public site without typing the URL.
|
|
45
|
+
* button.
|
|
31
46
|
*/
|
|
32
|
-
export function SignInPage({ callbackUrl }: SignInPageProps) {
|
|
33
|
-
const { serverURL } = getClientConfig()
|
|
47
|
+
export function SignInPage({ callbackUrl, activeLocale }: SignInPageProps) {
|
|
48
|
+
const { i18n, serverURL } = getClientConfig()
|
|
49
|
+
const localeDefinitions = buildLocaleDefinitions(
|
|
50
|
+
i18n.interface.locales,
|
|
51
|
+
i18n.interface.localeDefinitions
|
|
52
|
+
)
|
|
53
|
+
const handleSetLocale = async (next: LocaleCode) => {
|
|
54
|
+
await setInterfaceLocaleFn({ data: { locale: next } })
|
|
55
|
+
window.location.reload()
|
|
56
|
+
}
|
|
34
57
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
<I18nProvider
|
|
59
|
+
bundle={i18n.translations ?? {}}
|
|
60
|
+
activeLocale={activeLocale}
|
|
61
|
+
defaultLocale={i18n.interface.defaultLocale}
|
|
62
|
+
localeDefinitions={localeDefinitions}
|
|
63
|
+
setLocale={handleSetLocale}
|
|
64
|
+
>
|
|
65
|
+
<BylineAdminServicesProvider services={bylineAdminServices}>
|
|
66
|
+
<main className={cx('byline-sign-in-page', styles.main)}>
|
|
67
|
+
<div className={cx('byline-sign-in-page-bar', styles.bar)}>
|
|
68
|
+
<LanguageMenu />
|
|
69
|
+
</div>
|
|
70
|
+
<div className={cx('byline-sign-in-page-inner', styles.inner)}>
|
|
71
|
+
<SignInForm callbackUrl={callbackUrl} homeUrl={serverURL} />
|
|
72
|
+
</div>
|
|
73
|
+
</main>
|
|
74
|
+
</BylineAdminServicesProvider>
|
|
75
|
+
</I18nProvider>
|
|
42
76
|
)
|
|
43
77
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { CollectionDefinition } from '@byline/core'
|
|
10
10
|
import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
11
12
|
import { Container, Section } from '@byline/ui/react'
|
|
12
13
|
import cx from 'classnames'
|
|
13
14
|
import { allExpanded, darkStyles, JsonView } from 'react-json-view-lite'
|
|
@@ -41,12 +42,15 @@ export const ApiView = ({
|
|
|
41
42
|
defaultContentLocale: string
|
|
42
43
|
}) => {
|
|
43
44
|
const { labels, path } = collectionDefinition
|
|
45
|
+
const { t } = useTranslation('byline-admin')
|
|
44
46
|
|
|
45
47
|
return (
|
|
46
48
|
<Section className={cx('byline-api-section', styles.section)}>
|
|
47
49
|
<Container className={cx('byline-api-container', styles.container)}>
|
|
48
50
|
<div className={cx('byline-api-head', styles.head)}>
|
|
49
|
-
<h2 className={cx('byline-api-title', styles.title)}>
|
|
51
|
+
<h2 className={cx('byline-api-title', styles.title)}>
|
|
52
|
+
{t('collections.api.title', { label: labels.singular })}
|
|
53
|
+
</h2>
|
|
50
54
|
<ViewMenu
|
|
51
55
|
collection={path}
|
|
52
56
|
documentId={String(initialData.id)}
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import { useState } from 'react'
|
|
10
10
|
|
|
11
|
+
import { FormRenderer } from '@byline/admin/react'
|
|
11
12
|
import type { CollectionAdminConfig, CollectionDefinition } from '@byline/core'
|
|
12
|
-
import {
|
|
13
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
14
|
+
import { Container, Section, useToastManager } from '@byline/ui/react'
|
|
13
15
|
|
|
14
16
|
import { createCollectionDocument } from '../../server-fns/collections/index.js'
|
|
15
17
|
import { useNavigate } from '../chrome/loose-router.js'
|
|
@@ -31,6 +33,7 @@ export const CreateView = ({
|
|
|
31
33
|
initialData?: Record<string, any>
|
|
32
34
|
}) => {
|
|
33
35
|
const toastManager = useToastManager()
|
|
36
|
+
const { t } = useTranslation('byline-admin')
|
|
34
37
|
const [_createState, setCreateState] = useState<CreateState>({
|
|
35
38
|
status: 'idle',
|
|
36
39
|
message: '',
|
|
@@ -62,9 +65,12 @@ export const CreateView = ({
|
|
|
62
65
|
} catch (err) {
|
|
63
66
|
console.error(err)
|
|
64
67
|
|
|
68
|
+
const description = t('collections.create.errorToastDescription', {
|
|
69
|
+
label: labels.singular.toLowerCase(),
|
|
70
|
+
})
|
|
65
71
|
toastManager.add({
|
|
66
|
-
title:
|
|
67
|
-
description
|
|
72
|
+
title: t('collections.create.errorToastTitle', { label: labels.singular }),
|
|
73
|
+
description,
|
|
68
74
|
data: {
|
|
69
75
|
intent: 'danger',
|
|
70
76
|
iconType: 'danger',
|
|
@@ -75,7 +81,7 @@ export const CreateView = ({
|
|
|
75
81
|
|
|
76
82
|
setCreateState({
|
|
77
83
|
status: 'failed',
|
|
78
|
-
message:
|
|
84
|
+
message: description,
|
|
79
85
|
})
|
|
80
86
|
}
|
|
81
87
|
}
|