@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
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
} from '../server-fns/admin-users/index.js'
|
|
36
36
|
import { adminSignIn } from '../server-fns/auth/index.js'
|
|
37
37
|
import { getCollectionDocumentVersion as serverGetCollectionDocumentVersion } from '../server-fns/collections/get.js'
|
|
38
|
+
import { setInterfaceLocaleFn } from '../server-fns/i18n/index.js'
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Diff helper adapter — the contract uses positional args; the underlying
|
|
@@ -66,6 +67,7 @@ export const bylineAdminServices: BylineAdminServices = {
|
|
|
66
67
|
// Account self-service
|
|
67
68
|
updateAccount,
|
|
68
69
|
changeAccountPassword,
|
|
70
|
+
setInterfaceLocale: setInterfaceLocaleFn,
|
|
69
71
|
|
|
70
72
|
// Admin user writes
|
|
71
73
|
createAdminUser,
|
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Host-side adapters that bind the webapp's TanStack Start server functions
|
|
11
11
|
* to the framework-neutral `BylineFieldServices` contract consumed by
|
|
12
|
-
* `@byline/
|
|
12
|
+
* `@byline/admin` field/form components.
|
|
13
13
|
*
|
|
14
14
|
* Wired into the admin route once via `<BylineFieldServicesProvider>`. A
|
|
15
15
|
* future Next.js host would ship its own adapter file and Provider; the
|
|
16
|
-
* @byline/
|
|
16
|
+
* @byline/admin surface is unchanged.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
BylineFieldServices,
|
|
21
|
+
GetCollectionDocumentsFn,
|
|
22
|
+
UploadFieldFn,
|
|
23
|
+
} from '@byline/admin/react'
|
|
20
24
|
|
|
21
25
|
import { getCollectionDocuments as serverGetCollectionDocuments } from '../server-fns/collections/list.js'
|
|
22
26
|
import { uploadField as serverUploadField } from '../server-fns/collections/upload.js'
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { createFileRoute } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
11
|
import { AccountSelfContainer } from '@byline/admin/admin-account/components/container'
|
|
12
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
12
13
|
import { Container, Section } from '@byline/ui/react'
|
|
13
14
|
|
|
14
15
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
@@ -37,18 +38,19 @@ export function createAdminAccountRoute(path: string) {
|
|
|
37
38
|
},
|
|
38
39
|
component: function AdminAccountComponent() {
|
|
39
40
|
const { account } = Route.useLoaderData() as { account: AccountResponse }
|
|
41
|
+
const { t } = useTranslation('byline-admin')
|
|
40
42
|
return (
|
|
41
43
|
<>
|
|
42
44
|
<BreadcrumbsClient
|
|
43
45
|
breadcrumbs={[
|
|
44
|
-
{ label: '
|
|
45
|
-
{ label: '
|
|
46
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
47
|
+
{ label: t('chrome.account'), href: '/admin/account' },
|
|
46
48
|
]}
|
|
47
49
|
/>
|
|
48
50
|
<Section className="pb-2">
|
|
49
|
-
<Container>
|
|
51
|
+
<Container className="mb-2">
|
|
50
52
|
<h1 className="mb-2">{displayNameFor(account)}</h1>
|
|
51
|
-
<p className="muted">
|
|
53
|
+
<p className="muted">{t('account.intro')}</p>
|
|
52
54
|
</Container>
|
|
53
55
|
</Section>
|
|
54
56
|
<Section>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { createFileRoute } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
11
|
import { getClientConfig } from '@byline/core'
|
|
12
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
12
13
|
|
|
13
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
14
15
|
import { AdminDashboard } from '../admin-shell/chrome/dashboard.js'
|
|
@@ -39,9 +40,12 @@ export function createAdminDashboardRoute(path: string) {
|
|
|
39
40
|
const { statsMap } = Route.useLoaderData() as {
|
|
40
41
|
statsMap: Record<string, CollectionStatusCount[]>
|
|
41
42
|
}
|
|
43
|
+
const { t } = useTranslation('byline-admin')
|
|
42
44
|
return (
|
|
43
45
|
<>
|
|
44
|
-
<BreadcrumbsClient
|
|
46
|
+
<BreadcrumbsClient
|
|
47
|
+
breadcrumbs={[{ label: t('chrome.menu.dashboard'), href: '/admin' }]}
|
|
48
|
+
/>
|
|
45
49
|
<AdminDashboard statsMap={statsMap} />
|
|
46
50
|
</>
|
|
47
51
|
)
|
|
@@ -21,8 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'
|
|
23
23
|
|
|
24
|
+
import { BylineFieldServicesProvider } from '@byline/admin/react'
|
|
24
25
|
import { BylineAdminServicesProvider } from '@byline/admin/services'
|
|
25
|
-
import {
|
|
26
|
+
import { getClientConfig } from '@byline/core'
|
|
27
|
+
import type { LocaleCode } from '@byline/i18n'
|
|
28
|
+
import { I18nProvider } from '@byline/i18n/react'
|
|
26
29
|
import cx from 'classnames'
|
|
27
30
|
|
|
28
31
|
import { AdminAppBar } from '../admin-shell/chrome/admin-app-bar.js'
|
|
@@ -33,10 +36,12 @@ import { AdminMenuDrawer } from '../admin-shell/chrome/menu-drawer.js'
|
|
|
33
36
|
import { AdminMenuProvider } from '../admin-shell/chrome/menu-provider.js'
|
|
34
37
|
import { RouteError, RouteNotFound } from '../admin-shell/chrome/route-error.js'
|
|
35
38
|
import { RouteProgressBar } from '../admin-shell/chrome/route-progress-bar.js'
|
|
39
|
+
import { buildLocaleDefinitions } from '../i18n/locale-definitions.js'
|
|
36
40
|
import { bylineAdminServices } from '../integrations/byline-admin-services.js'
|
|
37
41
|
import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
|
|
38
42
|
import { bylineFieldServices } from '../integrations/byline-field-services.js'
|
|
39
43
|
import { getCurrentAdminUser } from '../server-fns/auth/index.js'
|
|
44
|
+
import { getActiveLocaleFn, setInterfaceLocaleFn } from '../server-fns/i18n/index.js'
|
|
40
45
|
|
|
41
46
|
interface AdminLayoutOpts {
|
|
42
47
|
/** Path users are redirected to when unauthenticated. Defaults to `/sign-in`. */
|
|
@@ -51,7 +56,14 @@ export function createAdminLayoutRoute(path: string, opts: AdminLayoutOpts = {})
|
|
|
51
56
|
beforeLoad: async ({ location }: { location: { href: string } }) => {
|
|
52
57
|
try {
|
|
53
58
|
const user = await getCurrentAdminUser()
|
|
54
|
-
|
|
59
|
+
// Resolve the active interface locale once on the server so SSR
|
|
60
|
+
// and the hydrated client render the same translations and
|
|
61
|
+
// there's no locale flicker. Going through the server fn (rather
|
|
62
|
+
// than calling `resolveRequestLocale` directly) keeps
|
|
63
|
+
// `@tanstack/react-start/server` out of the client bundle — the
|
|
64
|
+
// same pattern `getCurrentAdminUser` uses for `getAdminRequestContext`.
|
|
65
|
+
const activeLocale = await getActiveLocaleFn()
|
|
66
|
+
return { user, activeLocale }
|
|
55
67
|
} catch {
|
|
56
68
|
// `getCurrentAdminUser` (via `getAdminRequestContext`) throws
|
|
57
69
|
// `ERR_UNAUTHENTICATED` or a related auth error when no valid
|
|
@@ -64,27 +76,48 @@ export function createAdminLayoutRoute(path: string, opts: AdminLayoutOpts = {})
|
|
|
64
76
|
}
|
|
65
77
|
},
|
|
66
78
|
component: function AdminLayoutComponent() {
|
|
67
|
-
const { user } = Route.useRouteContext() as {
|
|
79
|
+
const { user, activeLocale } = Route.useRouteContext() as {
|
|
68
80
|
user: Awaited<ReturnType<typeof getCurrentAdminUser>>
|
|
81
|
+
activeLocale: LocaleCode
|
|
82
|
+
}
|
|
83
|
+
const { i18n } = getClientConfig()
|
|
84
|
+
const localeDefinitions = buildLocaleDefinitions(
|
|
85
|
+
i18n.interface.locales,
|
|
86
|
+
i18n.interface.localeDefinitions
|
|
87
|
+
)
|
|
88
|
+
// Cookie + DB write happen in setInterfaceLocaleFn; full reload
|
|
89
|
+
// re-runs beforeLoad so the provider re-renders with the new
|
|
90
|
+
// bundle/locale (no in-place bundle swap needed for PR 1's scope).
|
|
91
|
+
const handleSetLocale = async (next: LocaleCode) => {
|
|
92
|
+
await setInterfaceLocaleFn({ data: { locale: next } })
|
|
93
|
+
window.location.reload()
|
|
69
94
|
}
|
|
70
95
|
return (
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
<I18nProvider
|
|
97
|
+
bundle={i18n.translations ?? {}}
|
|
98
|
+
activeLocale={activeLocale}
|
|
99
|
+
defaultLocale={i18n.interface.defaultLocale}
|
|
100
|
+
localeDefinitions={localeDefinitions}
|
|
101
|
+
setLocale={handleSetLocale}
|
|
102
|
+
>
|
|
103
|
+
<BylineAdminServicesProvider services={bylineAdminServices}>
|
|
104
|
+
<BylineFieldServicesProvider services={bylineFieldServices}>
|
|
105
|
+
<BylineAiAdminProvider>
|
|
106
|
+
<AdminMenuProvider>
|
|
107
|
+
<RouteProgressBar />
|
|
108
|
+
<AdminAppBar user={user} />
|
|
109
|
+
<main className={cx('byline-admin-layout-main', layoutStyles.main)}>
|
|
110
|
+
<DrawerToggle />
|
|
111
|
+
<AdminMenuDrawer />
|
|
112
|
+
<Content>
|
|
113
|
+
<Outlet />
|
|
114
|
+
</Content>
|
|
115
|
+
</main>
|
|
116
|
+
</AdminMenuProvider>
|
|
117
|
+
</BylineAiAdminProvider>
|
|
118
|
+
</BylineFieldServicesProvider>
|
|
119
|
+
</BylineAdminServicesProvider>
|
|
120
|
+
</I18nProvider>
|
|
88
121
|
)
|
|
89
122
|
},
|
|
90
123
|
errorComponent: RouteError,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { createFileRoute } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
11
|
import { AbilitiesInspector } from '@byline/admin/admin-permissions/components/inspector'
|
|
12
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
12
13
|
|
|
13
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
14
15
|
import {
|
|
@@ -25,12 +26,13 @@ export function createAdminPermissionsRoute(path: string) {
|
|
|
25
26
|
},
|
|
26
27
|
component: function AdminPermissionsComponent() {
|
|
27
28
|
const { data } = Route.useLoaderData() as { data: ListRegisteredAbilitiesResponse }
|
|
29
|
+
const { t } = useTranslation('byline-admin')
|
|
28
30
|
return (
|
|
29
31
|
<>
|
|
30
32
|
<BreadcrumbsClient
|
|
31
33
|
breadcrumbs={[
|
|
32
|
-
{ label: '
|
|
33
|
-
{ label: '
|
|
34
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
35
|
+
{ label: t('chrome.menu.permissions'), href: '/admin/permissions' },
|
|
34
36
|
]}
|
|
35
37
|
/>
|
|
36
38
|
<AbilitiesInspector data={data} />
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
11
12
|
import { Container, Section } from '@byline/ui/react'
|
|
12
13
|
|
|
13
14
|
import { RoleContainer } from '../admin-shell/admin-roles/container.js'
|
|
@@ -56,13 +57,14 @@ export function createAdminRoleEditRoute(path: string) {
|
|
|
56
57
|
},
|
|
57
58
|
component: function AdminRoleEditComponent() {
|
|
58
59
|
const { role, registered, initialAbilities } = Route.useLoaderData() as LoaderData
|
|
60
|
+
const { t } = useTranslation('byline-admin')
|
|
59
61
|
return (
|
|
60
62
|
<>
|
|
61
63
|
<BreadcrumbsClient
|
|
62
64
|
breadcrumbs={[
|
|
63
|
-
{ label: '
|
|
64
|
-
{ label: '
|
|
65
|
-
{ label: '
|
|
65
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
66
|
+
{ label: t('chrome.menu.adminRoles'), href: '/admin/roles' },
|
|
67
|
+
{ label: t('adminRoles.breadcrumbDetail'), href: `/admin/roles/${role.id}` },
|
|
66
68
|
]}
|
|
67
69
|
/>
|
|
68
70
|
<Section className="py-5 pb-2">
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
12
|
+
|
|
11
13
|
import { AdminRolesListView } from '../admin-shell/admin-roles/list.js'
|
|
12
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
13
15
|
import { type AdminRoleListResponse, listAdminRoles } from '../server-fns/admin-roles/index.js'
|
|
@@ -21,12 +23,13 @@ export function createAdminRolesListRoute(path: string) {
|
|
|
21
23
|
},
|
|
22
24
|
component: function AdminRolesListComponent() {
|
|
23
25
|
const { data } = Route.useLoaderData() as { data: AdminRoleListResponse }
|
|
26
|
+
const { t } = useTranslation('byline-admin')
|
|
24
27
|
return (
|
|
25
28
|
<>
|
|
26
29
|
<BreadcrumbsClient
|
|
27
30
|
breadcrumbs={[
|
|
28
|
-
{ label: '
|
|
29
|
-
{ label: '
|
|
31
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
32
|
+
{ label: t('chrome.menu.adminRoles'), href: '/admin/roles' },
|
|
30
33
|
]}
|
|
31
34
|
/>
|
|
32
35
|
<AdminRolesListView data={data} />
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
11
12
|
import { Container, Section } from '@byline/ui/react'
|
|
12
13
|
|
|
13
14
|
import { AccountContainer } from '../admin-shell/admin-users/container.js'
|
|
@@ -73,13 +74,14 @@ export function createAdminUserEditRoute(path: string) {
|
|
|
73
74
|
},
|
|
74
75
|
component: function AdminUserEditComponent() {
|
|
75
76
|
const { user, allRoles, initialUserRoles } = Route.useLoaderData() as LoaderData
|
|
77
|
+
const { t } = useTranslation('byline-admin')
|
|
76
78
|
return (
|
|
77
79
|
<>
|
|
78
80
|
<BreadcrumbsClient
|
|
79
81
|
breadcrumbs={[
|
|
80
|
-
{ label: '
|
|
81
|
-
{ label: '
|
|
82
|
-
{ label: '
|
|
82
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
83
|
+
{ label: t('chrome.menu.adminUsers'), href: '/admin/users' },
|
|
84
|
+
{ label: t('adminUsers.breadcrumbDetail'), href: `/admin/users/${user.id}` },
|
|
83
85
|
]}
|
|
84
86
|
/>
|
|
85
87
|
<Section className="py-5 pb-2">
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
11
12
|
import { z } from 'zod'
|
|
12
13
|
|
|
13
14
|
import { AdminUsersListView } from '../admin-shell/admin-users/list.js'
|
|
@@ -66,12 +67,13 @@ export function createAdminUsersListRoute(path: string) {
|
|
|
66
67
|
},
|
|
67
68
|
component: function AdminUsersListComponent() {
|
|
68
69
|
const { data } = Route.useLoaderData() as { data: AdminUserListResponse }
|
|
70
|
+
const { t } = useTranslation('byline-admin')
|
|
69
71
|
return (
|
|
70
72
|
<>
|
|
71
73
|
<BreadcrumbsClient
|
|
72
74
|
breadcrumbs={[
|
|
73
|
-
{ label: '
|
|
74
|
-
{ label: '
|
|
75
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
76
|
+
{ label: t('chrome.menu.adminUsers'), href: '/admin/users' },
|
|
75
77
|
]}
|
|
76
78
|
/>
|
|
77
79
|
<AdminUsersListView data={data} />
|
|
@@ -10,6 +10,7 @@ import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
|
10
10
|
|
|
11
11
|
import type { CollectionDefinition } from '@byline/core'
|
|
12
12
|
import { getCollectionDefinition } from '@byline/core'
|
|
13
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
13
14
|
import { z } from 'zod'
|
|
14
15
|
|
|
15
16
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
@@ -77,19 +78,20 @@ export function createCollectionApiRoute(path: string, opts: CollectionApiOpts)
|
|
|
77
78
|
const { collection, id } = Route.useParams() as { collection: string; id: string }
|
|
78
79
|
const { locale, depth } = Route.useSearch() as z.infer<typeof searchSchema>
|
|
79
80
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
81
|
+
const { t } = useTranslation('byline-admin')
|
|
80
82
|
|
|
81
83
|
return (
|
|
82
84
|
<>
|
|
83
85
|
<BreadcrumbsClient
|
|
84
86
|
breadcrumbs={[
|
|
85
|
-
{ label: '
|
|
87
|
+
{ label: t('chrome.menu.dashboard'), href: `/admin` },
|
|
86
88
|
{ label: collectionDef.labels.plural, href: `/admin/collections/${collection}` },
|
|
87
89
|
{
|
|
88
|
-
label: '
|
|
90
|
+
label: t('common.actions.edit'),
|
|
89
91
|
href: `/admin/collections/${collection}/${id}`,
|
|
90
92
|
},
|
|
91
93
|
{
|
|
92
|
-
label: '
|
|
94
|
+
label: t('collections.viewMenu.apiButton'),
|
|
93
95
|
href: `/admin/collections/${collection}/${id}/api`,
|
|
94
96
|
},
|
|
95
97
|
]}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getCollectionAdminConfig,
|
|
15
15
|
getCollectionDefinition,
|
|
16
16
|
} from '@byline/core'
|
|
17
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
17
18
|
|
|
18
19
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
19
20
|
import { CreateView } from '../admin-shell/collections/create.js'
|
|
@@ -43,13 +44,17 @@ export function createCollectionCreateRoute(path: string) {
|
|
|
43
44
|
const { initialData } = Route.useLoaderData() as { initialData: any }
|
|
44
45
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
45
46
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
47
|
+
const { t } = useTranslation('byline-admin')
|
|
46
48
|
return (
|
|
47
49
|
<>
|
|
48
50
|
<BreadcrumbsClient
|
|
49
51
|
breadcrumbs={[
|
|
50
|
-
{ label: '
|
|
52
|
+
{ label: t('chrome.menu.dashboard'), href: '/admin' },
|
|
51
53
|
{ label: collectionDef.labels.plural, href: `/admin/collections/${collection}` },
|
|
52
|
-
{
|
|
54
|
+
{
|
|
55
|
+
label: t('collections.breadcrumbs.create'),
|
|
56
|
+
href: `/admin/collections/${collection}/create`,
|
|
57
|
+
},
|
|
53
58
|
]}
|
|
54
59
|
/>
|
|
55
60
|
<CreateView
|
|
@@ -10,6 +10,7 @@ import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
|
10
10
|
|
|
11
11
|
import type { CollectionDefinition } from '@byline/core'
|
|
12
12
|
import { getCollectionAdminConfig, getCollectionDefinition } from '@byline/core'
|
|
13
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
13
14
|
import { z } from 'zod'
|
|
14
15
|
|
|
15
16
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
@@ -72,15 +73,16 @@ export function createCollectionEditRoute(path: string, opts: CollectionEditOpts
|
|
|
72
73
|
const { locale } = Route.useSearch() as z.infer<typeof searchSchema>
|
|
73
74
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
74
75
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
76
|
+
const { t } = useTranslation('byline-admin')
|
|
75
77
|
|
|
76
78
|
return (
|
|
77
79
|
<>
|
|
78
80
|
<BreadcrumbsClient
|
|
79
81
|
breadcrumbs={[
|
|
80
|
-
{ label: '
|
|
82
|
+
{ label: t('chrome.menu.dashboard'), href: `/admin` },
|
|
81
83
|
{ label: collectionDef.labels.plural, href: `/admin/collections/${collection}` },
|
|
82
84
|
{
|
|
83
|
-
label: '
|
|
85
|
+
label: t('common.actions.edit'),
|
|
84
86
|
href: `/admin/collections/${collection}/${id}`,
|
|
85
87
|
},
|
|
86
88
|
]}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getCollectionDefinition,
|
|
15
15
|
getWorkflowStatuses,
|
|
16
16
|
} from '@byline/core'
|
|
17
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
17
18
|
import { z } from 'zod'
|
|
18
19
|
|
|
19
20
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
@@ -95,19 +96,20 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
95
96
|
const { collection } = Route.useParams() as { collection: string; id: string }
|
|
96
97
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
97
98
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
99
|
+
const { t } = useTranslation('byline-admin')
|
|
98
100
|
|
|
99
101
|
return (
|
|
100
102
|
<>
|
|
101
103
|
<BreadcrumbsClient
|
|
102
104
|
breadcrumbs={[
|
|
103
|
-
{ label: '
|
|
105
|
+
{ label: t('chrome.menu.dashboard'), href: `/admin` },
|
|
104
106
|
{ label: collectionDef.labels.plural, href: `/admin/collections/${collection}` },
|
|
105
107
|
{
|
|
106
|
-
label: '
|
|
108
|
+
label: t('common.actions.edit'),
|
|
107
109
|
href: `/admin/collections/${collection}/${Route.useParams().id}`,
|
|
108
110
|
},
|
|
109
111
|
{
|
|
110
|
-
label: '
|
|
112
|
+
label: t('collections.breadcrumbs.history'),
|
|
111
113
|
href: `/admin/collections/${collection}/${Route.useParams().id}/history`,
|
|
112
114
|
},
|
|
113
115
|
]}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getCollectionDefinition,
|
|
16
16
|
getWorkflowStatuses,
|
|
17
17
|
} from '@byline/core'
|
|
18
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
18
19
|
import { useToastManager } from '@byline/ui/react'
|
|
19
20
|
import { z } from 'zod'
|
|
20
21
|
|
|
@@ -97,6 +98,7 @@ export function createCollectionListRoute(path: string) {
|
|
|
97
98
|
},
|
|
98
99
|
component: function CollectionListComponent() {
|
|
99
100
|
const toastManager = useToastManager()
|
|
101
|
+
const { t } = useTranslation('byline-admin')
|
|
100
102
|
const data = Route.useLoaderData()
|
|
101
103
|
const { collection } = Route.useParams() as { collection: string }
|
|
102
104
|
const search = Route.useSearch() as z.infer<typeof searchSchema>
|
|
@@ -124,8 +126,10 @@ export function createCollectionListRoute(path: string) {
|
|
|
124
126
|
createdToastFiredRef.current = true
|
|
125
127
|
|
|
126
128
|
toastManager.add({
|
|
127
|
-
title:
|
|
128
|
-
description:
|
|
129
|
+
title: t('collections.list.createdToastTitle', { label: collectionDef.labels.singular }),
|
|
130
|
+
description: t('collections.list.createdToastDescription', {
|
|
131
|
+
label: collectionDef.labels.singular.toLowerCase(),
|
|
132
|
+
}),
|
|
129
133
|
data: {
|
|
130
134
|
intent: 'success',
|
|
131
135
|
iconType: 'success',
|
|
@@ -138,13 +142,7 @@ export function createCollectionListRoute(path: string) {
|
|
|
138
142
|
search: (prev: Record<string, unknown>) => ({ ...prev, action: undefined }),
|
|
139
143
|
replace: true,
|
|
140
144
|
})
|
|
141
|
-
}, [
|
|
142
|
-
search.action,
|
|
143
|
-
navigate,
|
|
144
|
-
toastManager.add,
|
|
145
|
-
collectionDef.labels.singular.toLowerCase,
|
|
146
|
-
collectionDef.labels.singular,
|
|
147
|
-
])
|
|
145
|
+
}, [search.action, navigate, toastManager.add, collectionDef.labels.singular, t])
|
|
148
146
|
|
|
149
147
|
const CustomListView = adminConfig?.listView
|
|
150
148
|
|
|
@@ -152,7 +150,7 @@ export function createCollectionListRoute(path: string) {
|
|
|
152
150
|
<>
|
|
153
151
|
<BreadcrumbsClient
|
|
154
152
|
breadcrumbs={[
|
|
155
|
-
{ label: '
|
|
153
|
+
{ label: t('chrome.menu.dashboard'), href: `/admin` },
|
|
156
154
|
{
|
|
157
155
|
label: data.included.collection.labels.plural,
|
|
158
156
|
href: `/admin/collections/${collection}`,
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
import { createFileRoute } from '@tanstack/react-router'
|
|
19
19
|
|
|
20
|
+
import type { LocaleCode } from '@byline/i18n'
|
|
21
|
+
|
|
20
22
|
import { SignInPage } from '../admin-shell/chrome/sign-in-page.js'
|
|
23
|
+
import { getActiveLocaleFn } from '../server-fns/i18n/index.js'
|
|
21
24
|
|
|
22
25
|
interface SignInSearch {
|
|
23
26
|
callbackUrl?: string
|
|
@@ -30,9 +33,19 @@ export function createSignInRoute(path: string) {
|
|
|
30
33
|
const callbackUrl = typeof search.callbackUrl === 'string' ? search.callbackUrl : undefined
|
|
31
34
|
return { callbackUrl }
|
|
32
35
|
},
|
|
36
|
+
beforeLoad: async () => {
|
|
37
|
+
// Resolve the active locale on the server before render so the
|
|
38
|
+
// sign-in page hydrates in the user's chosen language. The
|
|
39
|
+
// resolver works pre-auth — `readPreferredLocaleFromActor()`
|
|
40
|
+
// returns null when there's no admin session and the cascade
|
|
41
|
+
// falls through to cookie → Accept-Language → defaultLocale.
|
|
42
|
+
const activeLocale = await getActiveLocaleFn()
|
|
43
|
+
return { activeLocale }
|
|
44
|
+
},
|
|
33
45
|
component: function SignInRouteComponent() {
|
|
34
46
|
const { callbackUrl } = Route.useSearch() as SignInSearch
|
|
35
|
-
|
|
47
|
+
const { activeLocale } = Route.useRouteContext() as { activeLocale: LocaleCode }
|
|
48
|
+
return <SignInPage callbackUrl={callbackUrl} activeLocale={activeLocale} />
|
|
36
49
|
},
|
|
37
50
|
})
|
|
38
51
|
return Route
|
|
@@ -23,6 +23,8 @@ import { getRequestHeader } from '@tanstack/react-start/server'
|
|
|
23
23
|
import { getServerConfig } from '@byline/core'
|
|
24
24
|
|
|
25
25
|
import { setSessionCookies } from '../../auth/auth-cookies.js'
|
|
26
|
+
import { readAdminLocaleCookie } from '../../i18n/locale-cookie.js'
|
|
27
|
+
import { bylineCore } from '../../integrations/byline-core.js'
|
|
26
28
|
|
|
27
29
|
export interface SignInInput {
|
|
28
30
|
email: string
|
|
@@ -67,5 +69,48 @@ export const adminSignIn = createServerFn({ method: 'POST' })
|
|
|
67
69
|
|
|
68
70
|
setSessionCookies(result)
|
|
69
71
|
|
|
72
|
+
// Reconcile the byline_admin_lng cookie against the freshly-signed-in
|
|
73
|
+
// user's stored preferred_locale. If the cookie carries a permitted
|
|
74
|
+
// locale and it differs from what the user has stored (including the
|
|
75
|
+
// "null preferred_locale" case for brand-new users), update the
|
|
76
|
+
// column so the pre-auth locale choice becomes sticky across devices
|
|
77
|
+
// from day one. Pure best-effort — any error short-circuits and the
|
|
78
|
+
// sign-in still succeeds.
|
|
79
|
+
try {
|
|
80
|
+
await reconcileLocaleAfterSignIn(result.actor.id)
|
|
81
|
+
} catch {
|
|
82
|
+
// Swallow — locale sync is not load-bearing for the sign-in flow.
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
return { userId: result.actor.id }
|
|
71
86
|
})
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Apply the `byline_admin_lng` cookie to `admin_users.preferred_locale`
|
|
90
|
+
* when the two diverge after sign-in. No-op when:
|
|
91
|
+
*
|
|
92
|
+
* - The cookie is unset (the cascade falls through to the existing
|
|
93
|
+
* column / Accept-Language / default anyway).
|
|
94
|
+
* - The cookie carries a locale outside `i18n.interface.locales`
|
|
95
|
+
* (stale value pointing at a removed locale — let the resolver
|
|
96
|
+
* fall through cleanly).
|
|
97
|
+
* - No admin store is configured (headless tooling paths).
|
|
98
|
+
* - The stored value already matches the cookie.
|
|
99
|
+
*/
|
|
100
|
+
async function reconcileLocaleAfterSignIn(adminUserId: string): Promise<void> {
|
|
101
|
+
const cookieLocale = readAdminLocaleCookie()
|
|
102
|
+
if (cookieLocale == null) return
|
|
103
|
+
|
|
104
|
+
const core = bylineCore()
|
|
105
|
+
const locales = core.config.i18n.interface.locales
|
|
106
|
+
if (!locales.includes(cookieLocale)) return
|
|
107
|
+
|
|
108
|
+
const adminStore = core.adminStore
|
|
109
|
+
if (adminStore == null) return
|
|
110
|
+
|
|
111
|
+
const row = await adminStore.adminUsers.getById(adminUserId)
|
|
112
|
+
if (!row) return
|
|
113
|
+
if (row.preferred_locale === cookieLocale) return
|
|
114
|
+
|
|
115
|
+
await adminStore.adminUsers.setPreferredLocale(adminUserId, cookieLocale)
|
|
116
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the active interface locale for the current request as a
|
|
11
|
+
* server function. Wraps `resolveRequestLocale` so route `beforeLoad`
|
|
12
|
+
* callers in client-touching files don't transitively pull the
|
|
13
|
+
* `@tanstack/react-start/server` import (and the
|
|
14
|
+
* `@tanstack/start-server-core` graph behind it) into the client
|
|
15
|
+
* bundle. Same pattern as `getCurrentAdminUser`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
19
|
+
|
|
20
|
+
import { resolveRequestLocale } from '../../i18n/resolve-locale.js'
|
|
21
|
+
|
|
22
|
+
export const getActiveLocaleFn = createServerFn({ method: 'GET' }).handler(
|
|
23
|
+
async (): Promise<string> => {
|
|
24
|
+
return resolveRequestLocale()
|
|
25
|
+
}
|
|
26
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { getActiveLocaleFn } from './get-active-locale.js'
|
|
10
|
+
export { setInterfaceLocaleFn } from './set-locale.js'
|
|
11
|
+
export type { SetInterfaceLocaleInput, SetInterfaceLocaleResult } from './set-locale.js'
|