@byline/host-tanstack-start 3.20.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin-shell/admin-activity/list.js +15 -5
- package/dist/admin-shell/admin-roles/delete.js +2 -1
- package/dist/admin-shell/admin-roles/list.js +2 -1
- package/dist/admin-shell/admin-users/delete.js +2 -1
- package/dist/admin-shell/admin-users/list.js +4 -7
- package/dist/admin-shell/chrome/admin-app-bar.js +2 -1
- package/dist/admin-shell/chrome/admin-menu-drawer.js +9 -8
- package/dist/admin-shell/chrome/admin-menu-provider.js +2 -1
- package/dist/admin-shell/chrome/dashboard.js +4 -3
- package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -2
- package/dist/admin-shell/chrome/sign-in-page.js +2 -2
- package/dist/admin-shell/collections/create.js +4 -3
- package/dist/admin-shell/collections/document-history.js +6 -1
- package/dist/admin-shell/collections/edit.js +18 -14
- package/dist/admin-shell/collections/history.js +5 -4
- package/dist/admin-shell/collections/list.js +8 -7
- package/dist/admin-shell/collections/restore-version-modal.js +2 -1
- package/dist/admin-shell/collections/tree-list.js +53 -17
- package/dist/admin-shell/collections/view-menu.js +9 -8
- package/dist/auth/auth-context.d.ts +13 -0
- package/dist/auth/auth-context.js +4 -0
- package/dist/auth/request-scope.d.ts +16 -0
- package/dist/auth/request-scope.js +24 -0
- package/dist/integrations/abilities.js +8 -7
- package/dist/integrations/byline-public-client.d.ts +38 -0
- package/dist/integrations/byline-public-client.js +8 -4
- package/dist/integrations/byline-viewer-client.d.ts +6 -0
- package/dist/integrations/byline-viewer-client.js +22 -17
- package/dist/integrations/start-errors.js +2 -1
- package/dist/routes/admin-path.d.ts +22 -0
- package/dist/routes/admin-path.js +35 -0
- package/dist/routes/create-admin-account-route.js +3 -2
- package/dist/routes/create-admin-activity-route.js +3 -2
- package/dist/routes/create-admin-dashboard-route.js +2 -1
- package/dist/routes/create-admin-layout-route.d.ts +1 -1
- package/dist/routes/create-admin-layout-route.js +3 -1
- package/dist/routes/create-admin-permissions-route.js +3 -2
- package/dist/routes/create-admin-role-edit-route.js +4 -3
- package/dist/routes/create-admin-roles-list-route.js +3 -2
- package/dist/routes/create-admin-user-edit-route.js +4 -3
- package/dist/routes/create-admin-users-list-route.js +3 -2
- package/dist/routes/create-collection-api-route.d.ts +1 -7
- package/dist/routes/create-collection-api-route.js +12 -8
- package/dist/routes/create-collection-create-route.js +4 -3
- package/dist/routes/create-collection-edit-route.d.ts +1 -7
- package/dist/routes/create-collection-edit-route.js +9 -6
- package/dist/routes/create-collection-history-route.d.ts +1 -7
- package/dist/routes/create-collection-history-route.js +10 -7
- package/dist/routes/create-collection-list-route.js +3 -2
- package/dist/routes/create-sign-in-route.js +3 -2
- package/dist/routes/get-content-locale-route-config.d.ts +12 -0
- package/dist/routes/get-content-locale-route-config.js +16 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.js +2 -0
- package/dist/routes/sign-in-path.d.ts +13 -0
- package/dist/routes/sign-in-path.js +19 -0
- package/dist/server-fns/collections/delete-outcome.d.ts +30 -0
- package/dist/server-fns/collections/delete-outcome.js +34 -0
- package/dist/server-fns/collections/delete.d.ts +1 -4
- package/dist/server-fns/collections/delete.js +2 -4
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/tree-mutation.d.ts +46 -0
- package/dist/server-fns/collections/tree-mutation.js +109 -0
- package/dist/server-fns/collections/tree-read.d.ts +6 -0
- package/dist/server-fns/collections/tree-read.js +15 -0
- package/dist/server-fns/collections/tree.d.ts +5 -4
- package/dist/server-fns/collections/tree.js +15 -12
- package/dist/server-fns/collections/update.js +2 -1
- package/dist/vite/client-hook-boundary.d.ts +19 -0
- package/dist/vite/client-hook-boundary.js +20 -0
- package/dist/vite/index.d.ts +8 -0
- package/dist/vite/index.js +1 -0
- package/package.json +15 -9
- package/src/admin-shell/admin-activity/list.tsx +13 -3
- package/src/admin-shell/admin-roles/delete.tsx +2 -1
- package/src/admin-shell/admin-roles/list.tsx +2 -1
- package/src/admin-shell/admin-users/delete.tsx +3 -2
- package/src/admin-shell/admin-users/list.tsx +9 -8
- package/src/admin-shell/chrome/admin-app-bar.tsx +2 -1
- package/src/admin-shell/chrome/admin-menu-drawer.tsx +13 -8
- package/src/admin-shell/chrome/admin-menu-provider.tsx +2 -1
- package/src/admin-shell/chrome/dashboard.tsx +4 -3
- package/src/admin-shell/chrome/sign-in-page.tsx +3 -3
- package/src/admin-shell/collections/create.tsx +4 -3
- package/src/admin-shell/collections/document-history.tsx +5 -0
- package/src/admin-shell/collections/edit.tsx +20 -13
- package/src/admin-shell/collections/history.tsx +6 -5
- package/src/admin-shell/collections/list.tsx +8 -7
- package/src/admin-shell/collections/restore-version-modal.tsx +2 -1
- package/src/admin-shell/collections/tree-list.tsx +78 -22
- package/src/admin-shell/collections/view-menu.tsx +9 -8
- package/src/auth/auth-context.test.node.ts +87 -0
- package/src/auth/auth-context.ts +18 -0
- package/src/auth/request-scope.test.node.ts +93 -0
- package/src/auth/request-scope.ts +62 -0
- package/src/integrations/abilities.tsx +14 -4
- package/src/integrations/byline-client.ts +6 -4
- package/src/integrations/byline-public-client.ts +18 -4
- package/src/integrations/byline-viewer-client.ts +30 -18
- package/src/integrations/request-context-contract.test.node.ts +131 -0
- package/src/integrations/request-context-factories.test.node.ts +140 -0
- package/src/integrations/start-errors.test.node.ts +22 -0
- package/src/integrations/start-errors.ts +1 -0
- package/src/routes/admin-path.test.node.ts +129 -0
- package/src/routes/admin-path.ts +66 -0
- package/src/routes/auth-routes.test.node.ts +39 -0
- package/src/routes/create-admin-account-route.tsx +3 -2
- package/src/routes/create-admin-activity-route.tsx +3 -2
- package/src/routes/create-admin-dashboard-route.tsx +2 -1
- package/src/routes/create-admin-layout-route.tsx +4 -4
- package/src/routes/create-admin-permissions-route.tsx +3 -2
- package/src/routes/create-admin-role-edit-route.tsx +7 -3
- package/src/routes/create-admin-roles-list-route.tsx +3 -2
- package/src/routes/create-admin-user-edit-route.tsx +7 -3
- package/src/routes/create-admin-users-list-route.tsx +3 -2
- package/src/routes/create-collection-api-route.tsx +15 -14
- package/src/routes/create-collection-create-route.tsx +7 -3
- package/src/routes/create-collection-edit-route.tsx +12 -12
- package/src/routes/create-collection-history-route.tsx +13 -13
- package/src/routes/create-collection-list-route.tsx +3 -2
- package/src/routes/create-sign-in-route.tsx +9 -3
- package/src/routes/get-content-locale-route-config.ts +26 -0
- package/src/routes/index.ts +11 -0
- package/src/routes/sign-in-path.test.node.ts +79 -0
- package/src/routes/sign-in-path.ts +36 -0
- package/src/server-fns/collections/delete-outcome.test.node.ts +72 -0
- package/src/server-fns/collections/delete-outcome.ts +80 -0
- package/src/server-fns/collections/delete.ts +2 -1
- package/src/server-fns/collections/get.ts +2 -0
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/tree-mutation.test.node.ts +225 -0
- package/src/server-fns/collections/tree-mutation.ts +165 -0
- package/src/server-fns/collections/tree-read.test.node.ts +46 -0
- package/src/server-fns/collections/tree-read.ts +26 -0
- package/src/server-fns/collections/tree.ts +26 -10
- package/src/server-fns/collections/update.ts +3 -0
- package/src/vite/client-hook-boundary.test.node.ts +50 -0
- package/src/vite/client-hook-boundary.ts +45 -0
- package/src/vite/index.ts +9 -0
|
@@ -43,18 +43,18 @@ import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
|
|
|
43
43
|
import { bylineFieldServices } from '../integrations/byline-field-services.js'
|
|
44
44
|
import { getCurrentAdminUser } from '../server-fns/auth/index.js'
|
|
45
45
|
import { getActiveLocaleFn, setInterfaceLocaleFn } from '../server-fns/i18n/index.js'
|
|
46
|
+
import { createSignInRoutePathResolver } from './sign-in-path.js'
|
|
46
47
|
|
|
47
48
|
interface AdminLayoutOpts {
|
|
48
|
-
/**
|
|
49
|
+
/** @deprecated Configure `routes.signIn`; an override must resolve to the same path. */
|
|
49
50
|
signInPath?: string
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export function createAdminLayoutRoute(path: string, opts: AdminLayoutOpts = {}) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
54
|
+
const resolveSignInPath = createSignInRoutePathResolver(opts.signInPath)
|
|
56
55
|
const Route: any = createFileRoute(path as never)({
|
|
57
56
|
beforeLoad: async ({ location }: { location: { href: string } }) => {
|
|
57
|
+
const signInPath = resolveSignInPath()
|
|
58
58
|
try {
|
|
59
59
|
const user = await getCurrentAdminUser()
|
|
60
60
|
// Resolve the active interface locale once on the server so SSR
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ListRegisteredAbilitiesResponse,
|
|
17
17
|
listRegisteredAbilities,
|
|
18
18
|
} from '../server-fns/admin-permissions/index.js'
|
|
19
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
19
20
|
|
|
20
21
|
export function createAdminPermissionsRoute(path: string) {
|
|
21
22
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -31,8 +32,8 @@ export function createAdminPermissionsRoute(path: string) {
|
|
|
31
32
|
<>
|
|
32
33
|
<BreadcrumbsClient
|
|
33
34
|
breadcrumbs={[
|
|
34
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
35
|
-
{ label: t('chrome.menu.permissions'), href: '
|
|
35
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
36
|
+
{ label: t('chrome.menu.permissions'), href: getAdminRoutePath('permissions') },
|
|
36
37
|
]}
|
|
37
38
|
/>
|
|
38
39
|
<AbilitiesInspector data={data} />
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
listRegisteredAbilities,
|
|
20
20
|
} from '../server-fns/admin-permissions/index.js'
|
|
21
21
|
import { type AdminRoleResponse, getAdminRole } from '../server-fns/admin-roles/index.js'
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
22
23
|
|
|
23
24
|
interface LoaderData {
|
|
24
25
|
role: AdminRoleResponse
|
|
@@ -62,9 +63,12 @@ export function createAdminRoleEditRoute(path: string) {
|
|
|
62
63
|
<>
|
|
63
64
|
<BreadcrumbsClient
|
|
64
65
|
breadcrumbs={[
|
|
65
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
66
|
-
{ label: t('chrome.menu.adminRoles'), href: '
|
|
67
|
-
{
|
|
66
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
67
|
+
{ label: t('chrome.menu.adminRoles'), href: getAdminRoutePath('roles') },
|
|
68
|
+
{
|
|
69
|
+
label: t('adminRoles.breadcrumbDetail'),
|
|
70
|
+
href: getAdminRoutePath('roles', role.id),
|
|
71
|
+
},
|
|
68
72
|
]}
|
|
69
73
|
/>
|
|
70
74
|
<Section className="py-5 pb-2">
|
|
@@ -13,6 +13,7 @@ import { useTranslation } from '@byline/i18n/react'
|
|
|
13
13
|
import { AdminRolesListView } from '../admin-shell/admin-roles/list.js'
|
|
14
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
15
15
|
import { type AdminRoleListResponse, listAdminRoles } from '../server-fns/admin-roles/index.js'
|
|
16
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
16
17
|
|
|
17
18
|
export function createAdminRolesListRoute(path: string) {
|
|
18
19
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -28,8 +29,8 @@ export function createAdminRolesListRoute(path: string) {
|
|
|
28
29
|
<>
|
|
29
30
|
<BreadcrumbsClient
|
|
30
31
|
breadcrumbs={[
|
|
31
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
32
|
-
{ label: t('chrome.menu.adminRoles'), href: '
|
|
32
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
33
|
+
{ label: t('chrome.menu.adminRoles'), href: getAdminRoutePath('roles') },
|
|
33
34
|
]}
|
|
34
35
|
/>
|
|
35
36
|
<AdminRolesListView data={data} />
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getAdminUser,
|
|
20
20
|
getUserRoles,
|
|
21
21
|
} from '../server-fns/admin-users/index.js'
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
22
23
|
|
|
23
24
|
interface LoaderData {
|
|
24
25
|
user: AdminUserResponse
|
|
@@ -79,9 +80,12 @@ export function createAdminUserEditRoute(path: string) {
|
|
|
79
80
|
<>
|
|
80
81
|
<BreadcrumbsClient
|
|
81
82
|
breadcrumbs={[
|
|
82
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
83
|
-
{ label: t('chrome.menu.adminUsers'), href: '
|
|
84
|
-
{
|
|
83
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
84
|
+
{ label: t('chrome.menu.adminUsers'), href: getAdminRoutePath('users') },
|
|
85
|
+
{
|
|
86
|
+
label: t('adminUsers.breadcrumbDetail'),
|
|
87
|
+
href: getAdminRoutePath('users', user.id),
|
|
88
|
+
},
|
|
85
89
|
]}
|
|
86
90
|
/>
|
|
87
91
|
<Section className="py-5 pb-2">
|
|
@@ -14,6 +14,7 @@ import { z } from 'zod'
|
|
|
14
14
|
import { AdminUsersListView } from '../admin-shell/admin-users/list.js'
|
|
15
15
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
16
16
|
import { type AdminUserListResponse, listAdminUsers } from '../server-fns/admin-users/index.js'
|
|
17
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
17
18
|
|
|
18
19
|
const orderSchema = z.enum([
|
|
19
20
|
'given_name',
|
|
@@ -72,8 +73,8 @@ export function createAdminUsersListRoute(path: string) {
|
|
|
72
73
|
<>
|
|
73
74
|
<BreadcrumbsClient
|
|
74
75
|
breadcrumbs={[
|
|
75
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
76
|
-
{ label: t('chrome.menu.adminUsers'), href: '
|
|
76
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
77
|
+
{ label: t('chrome.menu.adminUsers'), href: getAdminRoutePath('users') },
|
|
77
78
|
]}
|
|
78
79
|
/>
|
|
79
80
|
<AdminUsersListView data={data} />
|
|
@@ -16,7 +16,8 @@ import { z } from 'zod'
|
|
|
16
16
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
17
17
|
import { ApiView } from '../admin-shell/collections/api.js'
|
|
18
18
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
19
|
-
import
|
|
19
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
20
|
+
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
20
21
|
|
|
21
22
|
const searchSchema = z.object({
|
|
22
23
|
locale: z.string().optional(),
|
|
@@ -28,12 +29,7 @@ const searchSchema = z.object({
|
|
|
28
29
|
depth: z.coerce.number().int().min(0).max(3).optional(),
|
|
29
30
|
})
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
contentLocales: ReadonlyArray<ContentLocaleOption>
|
|
33
|
-
defaultContentLocale: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function createCollectionApiRoute(path: string, opts: CollectionApiOpts) {
|
|
32
|
+
export function createCollectionApiRoute(path: string) {
|
|
37
33
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
38
34
|
const Route: any = createFileRoute(path as never)({
|
|
39
35
|
validateSearch: searchSchema,
|
|
@@ -56,7 +52,8 @@ export function createCollectionApiRoute(path: string, opts: CollectionApiOpts)
|
|
|
56
52
|
// 'all' is now always explicit in the URL when the user picks it.
|
|
57
53
|
// No locale param means the user hasn't made a selection yet — default
|
|
58
54
|
// to the content default locale (same as History and Edit behaviours).
|
|
59
|
-
const
|
|
55
|
+
const { defaultContentLocale } = getContentLocaleRouteConfig()
|
|
56
|
+
const resolvedLocale = deps.locale ?? defaultContentLocale
|
|
60
57
|
const data = await getCollectionDocument(
|
|
61
58
|
params.collection,
|
|
62
59
|
params.id,
|
|
@@ -79,20 +76,24 @@ export function createCollectionApiRoute(path: string, opts: CollectionApiOpts)
|
|
|
79
76
|
const { locale, depth } = Route.useSearch() as z.infer<typeof searchSchema>
|
|
80
77
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
81
78
|
const { t } = useTranslation('byline-admin')
|
|
79
|
+
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
82
80
|
|
|
83
81
|
return (
|
|
84
82
|
<>
|
|
85
83
|
<BreadcrumbsClient
|
|
86
84
|
breadcrumbs={[
|
|
87
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
88
|
-
{
|
|
85
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
86
|
+
{
|
|
87
|
+
label: collectionDef.labels.plural,
|
|
88
|
+
href: getAdminRoutePath('collections', collection),
|
|
89
|
+
},
|
|
89
90
|
{
|
|
90
91
|
label: t('common.actions.edit'),
|
|
91
|
-
href:
|
|
92
|
+
href: getAdminRoutePath('collections', collection, id),
|
|
92
93
|
},
|
|
93
94
|
{
|
|
94
95
|
label: t('collections.viewMenu.apiButton'),
|
|
95
|
-
href:
|
|
96
|
+
href: getAdminRoutePath('collections', collection, id, 'api'),
|
|
96
97
|
},
|
|
97
98
|
]}
|
|
98
99
|
/>
|
|
@@ -101,8 +102,8 @@ export function createCollectionApiRoute(path: string, opts: CollectionApiOpts)
|
|
|
101
102
|
initialData={data}
|
|
102
103
|
locale={locale}
|
|
103
104
|
depth={depth}
|
|
104
|
-
contentLocales={
|
|
105
|
-
defaultContentLocale={
|
|
105
|
+
contentLocales={contentLocales}
|
|
106
|
+
defaultContentLocale={defaultContentLocale}
|
|
106
107
|
/>
|
|
107
108
|
</>
|
|
108
109
|
)
|
|
@@ -18,6 +18,7 @@ import { useTranslation } from '@byline/i18n/react'
|
|
|
18
18
|
|
|
19
19
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
20
20
|
import { CreateView } from '../admin-shell/collections/create.js'
|
|
21
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
21
22
|
|
|
22
23
|
export function createCollectionCreateRoute(path: string) {
|
|
23
24
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -49,11 +50,14 @@ export function createCollectionCreateRoute(path: string) {
|
|
|
49
50
|
<>
|
|
50
51
|
<BreadcrumbsClient
|
|
51
52
|
breadcrumbs={[
|
|
52
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
53
|
-
{
|
|
53
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
54
|
+
{
|
|
55
|
+
label: collectionDef.labels.plural,
|
|
56
|
+
href: getAdminRoutePath('collections', collection),
|
|
57
|
+
},
|
|
54
58
|
{
|
|
55
59
|
label: t('collections.breadcrumbs.create'),
|
|
56
|
-
href:
|
|
60
|
+
href: getAdminRoutePath('collections', collection, 'create'),
|
|
57
61
|
},
|
|
58
62
|
]}
|
|
59
63
|
/>
|
|
@@ -19,7 +19,8 @@ import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs
|
|
|
19
19
|
import { useNavigate } from '../admin-shell/chrome/loose-router.js'
|
|
20
20
|
import { EditView } from '../admin-shell/collections/edit.js'
|
|
21
21
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
22
|
-
import
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
23
|
+
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
23
24
|
|
|
24
25
|
const searchSchema = z.object({
|
|
25
26
|
locale: z.string().optional(),
|
|
@@ -27,12 +28,7 @@ const searchSchema = z.object({
|
|
|
27
28
|
action: z.enum(['created']).optional(),
|
|
28
29
|
})
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
contentLocales: ReadonlyArray<ContentLocaleOption>
|
|
32
|
-
defaultContentLocale: string
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function createCollectionEditRoute(path: string, opts: CollectionEditOpts) {
|
|
31
|
+
export function createCollectionEditRoute(path: string) {
|
|
36
32
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
37
33
|
const Route: any = createFileRoute(path as never)({
|
|
38
34
|
validateSearch: searchSchema,
|
|
@@ -82,6 +78,7 @@ export function createCollectionEditRoute(path: string, opts: CollectionEditOpts
|
|
|
82
78
|
const { t } = useTranslation('byline-admin')
|
|
83
79
|
const toastManager = useToastManager()
|
|
84
80
|
const navigate = useNavigate()
|
|
81
|
+
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
85
82
|
|
|
86
83
|
// Post-create toast for the create → edit redirect (?action=created).
|
|
87
84
|
// Ref-guarded for the same reason as the list route's created toast:
|
|
@@ -120,11 +117,14 @@ export function createCollectionEditRoute(path: string, opts: CollectionEditOpts
|
|
|
120
117
|
<>
|
|
121
118
|
<BreadcrumbsClient
|
|
122
119
|
breadcrumbs={[
|
|
123
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
124
|
-
{
|
|
120
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
121
|
+
{
|
|
122
|
+
label: collectionDef.labels.plural,
|
|
123
|
+
href: getAdminRoutePath('collections', collection),
|
|
124
|
+
},
|
|
125
125
|
{
|
|
126
126
|
label: t('common.actions.edit'),
|
|
127
|
-
href:
|
|
127
|
+
href: getAdminRoutePath('collections', collection, id),
|
|
128
128
|
},
|
|
129
129
|
]}
|
|
130
130
|
/>
|
|
@@ -133,8 +133,8 @@ export function createCollectionEditRoute(path: string, opts: CollectionEditOpts
|
|
|
133
133
|
adminConfig={adminConfig ?? undefined}
|
|
134
134
|
initialData={data}
|
|
135
135
|
locale={locale}
|
|
136
|
-
contentLocales={
|
|
137
|
-
defaultContentLocale={
|
|
136
|
+
contentLocales={contentLocales}
|
|
137
|
+
defaultContentLocale={defaultContentLocale}
|
|
138
138
|
/>
|
|
139
139
|
</>
|
|
140
140
|
)
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
getCollectionDocumentAuditLog,
|
|
25
25
|
getCollectionDocumentHistory,
|
|
26
26
|
} from '../server-fns/collections/index.js'
|
|
27
|
-
import
|
|
27
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
28
|
+
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
28
29
|
|
|
29
30
|
const searchSchema = z.object({
|
|
30
31
|
page: z.coerce.number().min(1).optional(),
|
|
@@ -43,12 +44,7 @@ const searchSchema = z.object({
|
|
|
43
44
|
// than wiring a second, tab-specific pager into the shared history route.
|
|
44
45
|
const AUDIT_LOG_PAGE_SIZE = 100
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
contentLocales: ReadonlyArray<ContentLocaleOption>
|
|
48
|
-
defaultContentLocale: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function createCollectionHistoryRoute(path: string, opts: CollectionHistoryOpts) {
|
|
47
|
+
export function createCollectionHistoryRoute(path: string) {
|
|
52
48
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
53
49
|
const Route: any = createFileRoute(path as never)({
|
|
54
50
|
validateSearch: searchSchema,
|
|
@@ -118,20 +114,24 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
118
114
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
119
115
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
120
116
|
const { t } = useTranslation('byline-admin')
|
|
117
|
+
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
121
118
|
|
|
122
119
|
return (
|
|
123
120
|
<>
|
|
124
121
|
<BreadcrumbsClient
|
|
125
122
|
breadcrumbs={[
|
|
126
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
127
|
-
{
|
|
123
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
124
|
+
{
|
|
125
|
+
label: collectionDef.labels.plural,
|
|
126
|
+
href: getAdminRoutePath('collections', collection),
|
|
127
|
+
},
|
|
128
128
|
{
|
|
129
129
|
label: t('common.actions.edit'),
|
|
130
|
-
href:
|
|
130
|
+
href: getAdminRoutePath('collections', collection, Route.useParams().id),
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
label: t('collections.breadcrumbs.history'),
|
|
134
|
-
href:
|
|
134
|
+
href: getAdminRoutePath('collections', collection, Route.useParams().id, 'history'),
|
|
135
135
|
},
|
|
136
136
|
]}
|
|
137
137
|
/>
|
|
@@ -142,8 +142,8 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
142
142
|
data={history}
|
|
143
143
|
auditLog={auditLog}
|
|
144
144
|
currentDocument={currentDocument as Record<string, unknown> | null}
|
|
145
|
-
contentLocales={
|
|
146
|
-
defaultContentLocale={
|
|
145
|
+
contentLocales={contentLocales}
|
|
146
|
+
defaultContentLocale={defaultContentLocale}
|
|
147
147
|
/>
|
|
148
148
|
</>
|
|
149
149
|
)
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
placeTreeNode,
|
|
31
31
|
reorderCollectionDocument,
|
|
32
32
|
} from '../server-fns/collections/index.js'
|
|
33
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
33
34
|
import type { CollectionTreeRow } from '../server-fns/collections/tree.js'
|
|
34
35
|
|
|
35
36
|
const searchSchema = z.object({
|
|
@@ -167,10 +168,10 @@ export function createCollectionListRoute(path: string) {
|
|
|
167
168
|
<>
|
|
168
169
|
<BreadcrumbsClient
|
|
169
170
|
breadcrumbs={[
|
|
170
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
171
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
171
172
|
{
|
|
172
173
|
label: data.included.collection.labels.plural,
|
|
173
|
-
href:
|
|
174
|
+
href: getAdminRoutePath('collections', collection),
|
|
174
175
|
},
|
|
175
176
|
]}
|
|
176
177
|
/>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Sign-in page route factory.
|
|
11
11
|
*
|
|
12
|
-
* Lives outside the
|
|
12
|
+
* Lives outside the configured admin subtree so the authenticated-admin layout
|
|
13
13
|
* (and its `beforeLoad` guard) does not apply. Preserves a `callbackUrl`
|
|
14
14
|
* query param so sign-ins triggered by the admin guard redirect back to
|
|
15
15
|
* the originally-requested path.
|
|
@@ -21,6 +21,7 @@ import type { LocaleCode } from '@byline/i18n'
|
|
|
21
21
|
|
|
22
22
|
import { SignInPage } from '../admin-shell/chrome/sign-in-page.js'
|
|
23
23
|
import { getActiveLocaleFn } from '../server-fns/i18n/index.js'
|
|
24
|
+
import { resolveAdminCallbackPath, resolveAdminSignInRedirect } from './admin-path.js'
|
|
24
25
|
|
|
25
26
|
interface SignInSearch {
|
|
26
27
|
callbackUrl?: string
|
|
@@ -30,7 +31,7 @@ export function createSignInRoute(path: string) {
|
|
|
30
31
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
31
32
|
const Route: any = createFileRoute(path as never)({
|
|
32
33
|
validateSearch: (search: Record<string, unknown>): SignInSearch => {
|
|
33
|
-
const callbackUrl =
|
|
34
|
+
const callbackUrl = resolveAdminCallbackPath(search.callbackUrl)
|
|
34
35
|
return { callbackUrl }
|
|
35
36
|
},
|
|
36
37
|
beforeLoad: async () => {
|
|
@@ -45,7 +46,12 @@ export function createSignInRoute(path: string) {
|
|
|
45
46
|
component: function SignInRouteComponent() {
|
|
46
47
|
const { callbackUrl } = Route.useSearch() as SignInSearch
|
|
47
48
|
const { activeLocale } = Route.useRouteContext() as { activeLocale: LocaleCode }
|
|
48
|
-
return
|
|
49
|
+
return (
|
|
50
|
+
<SignInPage
|
|
51
|
+
redirectTo={resolveAdminSignInRedirect(callbackUrl)}
|
|
52
|
+
activeLocale={activeLocale}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
49
55
|
},
|
|
50
56
|
})
|
|
51
57
|
return Route
|
|
@@ -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
|
+
import { getClientConfig } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
import type { ContentLocaleOption } from '../admin-shell/collections/view-menu.js'
|
|
12
|
+
|
|
13
|
+
export function getContentLocaleRouteConfig(): {
|
|
14
|
+
contentLocales: ContentLocaleOption[]
|
|
15
|
+
defaultContentLocale: string
|
|
16
|
+
} {
|
|
17
|
+
const content = getClientConfig().i18n.content
|
|
18
|
+
const labels = new Map(
|
|
19
|
+
content.localeDefinitions?.map(({ code, nativeName }) => [code, nativeName]) ?? []
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
contentLocales: content.locales.map((code) => ({ code, label: labels.get(code) ?? code })),
|
|
24
|
+
defaultContentLocale: content.defaultLocale,
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/routes/index.ts
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export {
|
|
10
|
+
getAdminRouteId,
|
|
11
|
+
getAdminRoutePath,
|
|
12
|
+
isAdminRoutePathActive,
|
|
13
|
+
isRoutePathWithin,
|
|
14
|
+
resolveAdminCallbackPath,
|
|
15
|
+
resolveAdminSignInRedirect,
|
|
16
|
+
} from './admin-path.js'
|
|
17
|
+
export { configureSignInRoutePath, getSignInRoutePath } from './sign-in-path.js'
|
|
18
|
+
|
|
9
19
|
/**
|
|
10
20
|
* Route factories for the Byline admin UI on TanStack Start.
|
|
11
21
|
*
|
|
@@ -14,6 +24,7 @@
|
|
|
14
24
|
* `createFileRoute(path)({...})`. The host's route file collapses to:
|
|
15
25
|
*
|
|
16
26
|
* import { createCollectionListRoute } from '@byline/host-tanstack-start/routes'
|
|
27
|
+
* // Default admin mount example:
|
|
17
28
|
* export const Route = createCollectionListRoute('/_byline/admin/collections/$collection/')
|
|
18
29
|
*
|
|
19
30
|
* Routes that need host-specific data (i18n bridge component, content
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { defineClientConfig } from '@byline/core'
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
configureSignInRoutePath,
|
|
6
|
+
createSignInRoutePathResolver,
|
|
7
|
+
getSignInRoutePath,
|
|
8
|
+
} from './sign-in-path.js'
|
|
9
|
+
|
|
10
|
+
describe('sign-in route configuration', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
defineClientConfig({
|
|
13
|
+
serverURL: 'http://localhost:3000',
|
|
14
|
+
routes: { signIn: '/staff/login/' },
|
|
15
|
+
collections: [],
|
|
16
|
+
admin: [],
|
|
17
|
+
i18n: {
|
|
18
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
19
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('reads and normalizes routes.signIn', () => {
|
|
25
|
+
expect(getSignInRoutePath()).toBe('/staff/login')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('allows a deprecated signInPath override when it matches canonical config', () => {
|
|
29
|
+
expect(configureSignInRoutePath('staff/login/')).toBe('/staff/login')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('rejects an invalid or divergent deprecated override when resolved', () => {
|
|
33
|
+
expect(() => configureSignInRoutePath('/auth/login/')).toThrow(/must match routes\.signIn/)
|
|
34
|
+
expect(() => configureSignInRoutePath('/auth/../login')).toThrow(/routes\.signIn/)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('validates an override against configured rather than default route trees', () => {
|
|
38
|
+
defineClientConfig({
|
|
39
|
+
serverURL: 'http://localhost:3000',
|
|
40
|
+
routes: { admin: '/cms', api: '/rpc', signIn: '/admin/login' },
|
|
41
|
+
collections: [],
|
|
42
|
+
admin: [],
|
|
43
|
+
i18n: {
|
|
44
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
45
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
expect(configureSignInRoutePath('/admin/login/')).toBe('/admin/login')
|
|
50
|
+
expect(() => configureSignInRoutePath('/cms/login')).toThrow(/routes\.signIn/)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('defers a deprecated override until parent beforeLoad config registration', () => {
|
|
54
|
+
defineClientConfig({
|
|
55
|
+
serverURL: 'http://localhost:3000',
|
|
56
|
+
routes: { signIn: '/old/login' },
|
|
57
|
+
collections: [],
|
|
58
|
+
admin: [],
|
|
59
|
+
i18n: {
|
|
60
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
61
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
const resolveSignInPath = createSignInRoutePathResolver('/staff/login/')
|
|
65
|
+
|
|
66
|
+
defineClientConfig({
|
|
67
|
+
serverURL: 'http://localhost:3000',
|
|
68
|
+
routes: { signIn: '/staff/login' },
|
|
69
|
+
collections: [],
|
|
70
|
+
admin: [],
|
|
71
|
+
i18n: {
|
|
72
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
73
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
expect(resolveSignInPath()).toBe('/staff/login')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
import { getClientConfig, resolveRoutes } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
/** Read the canonical client-safe sign-in destination. */
|
|
12
|
+
export function getSignInRoutePath(): string {
|
|
13
|
+
return getClientConfig().routes.signIn
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @deprecated Configure `routes.signIn`; retained for route-factory input validation. */
|
|
17
|
+
export function configureSignInRoutePath(legacyOverride?: string): string {
|
|
18
|
+
const routes = getClientConfig().routes
|
|
19
|
+
const configured = routes.signIn
|
|
20
|
+
if (legacyOverride === undefined) return configured
|
|
21
|
+
|
|
22
|
+
const normalizedOverride = resolveRoutes({ ...routes, signIn: legacyOverride }).signIn
|
|
23
|
+
if (normalizedOverride !== configured) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`createAdminLayoutRoute signInPath (${normalizedOverride}) must match routes.signIn (${configured})`
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
return configured
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Defer deprecated override validation until the admin route's beforeLoad boundary. */
|
|
32
|
+
export function createSignInRoutePathResolver(legacyOverride?: string): () => string {
|
|
33
|
+
return legacyOverride === undefined
|
|
34
|
+
? getSignInRoutePath
|
|
35
|
+
: () => configureSignInRoutePath(legacyOverride)
|
|
36
|
+
}
|