@byline/host-tanstack-start 4.15.0 → 4.17.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/chrome/admin-menu-drawer.d.ts +2 -1
- package/dist/admin-shell/chrome/admin-menu-drawer.js +17 -5
- package/dist/admin-shell/chrome/dashboard.js +41 -5
- package/dist/admin-shell/chrome/dashboard.test.d.ts +8 -0
- package/dist/admin-shell/collections/api.d.ts +2 -2
- package/dist/admin-shell/collections/create.d.ts +2 -2
- package/dist/admin-shell/collections/create.js +1 -0
- package/dist/admin-shell/collections/edit.d.ts +2 -2
- package/dist/admin-shell/collections/edit.js +1 -0
- package/dist/admin-shell/collections/history.d.ts +4 -12
- package/dist/admin-shell/collections/history.js +112 -303
- package/dist/admin-shell/collections/reindex-button.d.ts +1 -1
- package/dist/admin-shell/collections/reindex-button.js +5 -1
- package/dist/admin-shell/collections/restore-version-modal.d.ts +4 -4
- package/dist/admin-shell/collections/restore-version-modal.js +8 -24
- package/dist/admin-shell/collections/version-history.d.ts +72 -0
- package/dist/admin-shell/collections/version-history.js +259 -0
- package/dist/admin-shell/singletons/history.d.ts +19 -0
- package/dist/admin-shell/singletons/history.js +170 -0
- package/dist/admin-shell/singletons/history.test.d.ts +8 -0
- package/dist/admin-shell/singletons/preview-link.d.ts +12 -0
- package/dist/admin-shell/singletons/preview-link.js +54 -0
- package/dist/admin-shell/singletons/view-menu.d.ts +13 -0
- package/dist/admin-shell/singletons/view-menu.js +116 -0
- package/dist/admin-shell/singletons/view.d.ts +18 -0
- package/dist/admin-shell/singletons/view.js +259 -0
- package/dist/admin-shell/singletons/view.test.d.ts +8 -0
- package/dist/integrations/analytics-agent.d.ts +20 -0
- package/dist/integrations/analytics-agent.js +57 -0
- package/dist/integrations/analytics-events.d.ts +51 -0
- package/dist/integrations/analytics-events.js +129 -0
- package/dist/integrations/api-utils.d.ts +18 -4
- package/dist/integrations/api-utils.js +10 -3
- package/dist/integrations/serve-analytics-agent.d.ts +8 -0
- package/dist/integrations/serve-analytics-agent.js +11 -0
- package/dist/routes/analytics-range.d.ts +12 -0
- package/dist/routes/analytics-range.js +35 -0
- package/dist/routes/create-admin-analytics-route.d.ts +8 -0
- package/dist/routes/create-admin-analytics-route.js +108 -0
- package/dist/routes/create-admin-dashboard-route.js +2 -2
- package/dist/routes/create-admin-layout-route.js +10 -4
- package/dist/routes/create-collection-api-route.js +3 -3
- package/dist/routes/create-collection-create-route.js +4 -3
- package/dist/routes/create-collection-edit-route.js +4 -3
- package/dist/routes/create-collection-history-route.js +4 -3
- package/dist/routes/create-collection-list-route.js +4 -3
- package/dist/routes/create-singleton-history-route.d.ts +8 -0
- package/dist/routes/create-singleton-history-route.js +99 -0
- package/dist/routes/create-singleton-route.d.ts +35 -0
- package/dist/routes/create-singleton-route.js +130 -0
- package/dist/routes/get-multi-collection-definition.d.ts +10 -0
- package/dist/routes/get-multi-collection-definition.js +6 -0
- package/dist/routes/get-singleton-definition.d.ts +10 -0
- package/dist/routes/get-singleton-definition.js +6 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.js +3 -0
- package/dist/server-fns/actors.d.ts +16 -0
- package/dist/server-fns/actors.js +25 -0
- package/dist/server-fns/admin-document-presentation.d.ts +30 -0
- package/dist/server-fns/admin-document-presentation.js +32 -0
- package/dist/server-fns/analytics/index.d.ts +9 -0
- package/dist/server-fns/analytics/index.js +2 -0
- package/dist/server-fns/analytics/maintenance.d.ts +22 -0
- package/dist/server-fns/analytics/maintenance.js +42 -0
- package/dist/server-fns/analytics/queries.d.ts +25 -0
- package/dist/server-fns/analytics/queries.js +54 -0
- package/dist/server-fns/collections/actors.d.ts +1 -10
- package/dist/server-fns/collections/actors.js +1 -25
- package/dist/server-fns/collections/get.js +9 -26
- package/dist/server-fns/collections/reorder.js +1 -1
- package/dist/server-fns/collections/scheduled-publication.js +2 -2
- package/dist/server-fns/collections/upload.js +3 -3
- package/dist/server-fns/collections/utils.d.ts +1 -7
- package/dist/server-fns/collections/utils.js +1 -4
- package/dist/server-fns/serialise.d.ts +14 -0
- package/dist/server-fns/serialise.js +4 -0
- package/dist/server-fns/singleton-document-read.d.ts +20 -0
- package/dist/server-fns/singleton-document-read.js +10 -0
- package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +12 -0
- package/dist/server-fns/singletons/cancel-scheduled-publish.js +7 -0
- package/dist/server-fns/singletons/change-status.d.ts +14 -0
- package/dist/server-fns/singletons/change-status.js +7 -0
- package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +14 -0
- package/dist/server-fns/singletons/confirm-scheduled-publish.js +9 -0
- package/dist/server-fns/singletons/copy-to-locale.d.ts +18 -0
- package/dist/server-fns/singletons/copy-to-locale.js +11 -0
- package/dist/server-fns/singletons/find-by-version.d.ts +16 -0
- package/dist/server-fns/singletons/find-by-version.js +9 -0
- package/dist/server-fns/singletons/get-scheduled-publish.d.ts +12 -0
- package/dist/server-fns/singletons/get-scheduled-publish.js +7 -0
- package/dist/server-fns/singletons/get.d.ts +24 -0
- package/dist/server-fns/singletons/get.js +6 -0
- package/dist/server-fns/singletons/history.d.ts +31 -0
- package/dist/server-fns/singletons/history.js +22 -0
- package/dist/server-fns/singletons/index.d.ts +19 -0
- package/dist/server-fns/singletons/index.js +12 -0
- package/dist/server-fns/singletons/restore-version.d.ts +14 -0
- package/dist/server-fns/singletons/restore-version.js +7 -0
- package/dist/server-fns/singletons/schedule-publish.d.ts +16 -0
- package/dist/server-fns/singletons/schedule-publish.js +10 -0
- package/dist/server-fns/singletons/unpublish.d.ts +12 -0
- package/dist/server-fns/singletons/unpublish.js +7 -0
- package/dist/server-fns/singletons/update.d.ts +14 -0
- package/dist/server-fns/singletons/update.js +14 -0
- package/dist/vite/analytics-dev.d.ts +22 -0
- package/dist/vite/analytics-dev.js +28 -0
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.js +1 -0
- package/dist/vitest-jsdom.test.d.ts +8 -0
- package/package.json +28 -16
- package/src/admin-shell/chrome/admin-menu-drawer.tsx +19 -5
- package/src/admin-shell/chrome/dashboard.test.tsx +199 -0
- package/src/admin-shell/chrome/dashboard.tsx +43 -9
- package/src/admin-shell/collections/api.tsx +2 -2
- package/src/admin-shell/collections/create.tsx +6 -2
- package/src/admin-shell/collections/edit.tsx +6 -2
- package/src/admin-shell/collections/history.module.css +1 -1
- package/src/admin-shell/collections/history.tsx +162 -442
- package/src/admin-shell/collections/reindex-button.tsx +4 -2
- package/src/admin-shell/collections/restore-version-modal.tsx +19 -25
- package/src/admin-shell/collections/version-history.tsx +349 -0
- package/src/admin-shell/singletons/history.test.tsx +407 -0
- package/src/admin-shell/singletons/history.tsx +185 -0
- package/src/admin-shell/singletons/preview-link.tsx +75 -0
- package/src/admin-shell/singletons/view-menu.tsx +136 -0
- package/src/admin-shell/singletons/view.test.tsx +400 -0
- package/src/admin-shell/singletons/view.tsx +351 -0
- package/src/integrations/analytics-agent.tsx +91 -0
- package/src/integrations/analytics-events.test.node.ts +245 -0
- package/src/integrations/analytics-events.ts +224 -0
- package/src/integrations/api-utils.ts +34 -9
- package/src/integrations/serve-analytics-agent.test.node.ts +20 -0
- package/src/integrations/serve-analytics-agent.ts +19 -0
- package/src/routes/analytics-range.ts +55 -0
- package/src/routes/create-admin-analytics-route.test.node.ts +46 -0
- package/src/routes/create-admin-analytics-route.tsx +109 -0
- package/src/routes/create-admin-dashboard-route.test.node.ts +70 -0
- package/src/routes/create-admin-dashboard-route.tsx +10 -2
- package/src/routes/create-admin-layout-route.tsx +15 -8
- package/src/routes/create-collection-api-route.tsx +4 -4
- package/src/routes/create-collection-create-route.tsx +5 -8
- package/src/routes/create-collection-edit-route.tsx +5 -4
- package/src/routes/create-collection-history-route.tsx +5 -8
- package/src/routes/create-collection-list-route.tsx +5 -8
- package/src/routes/create-singleton-history-route.test.node.ts +148 -0
- package/src/routes/create-singleton-history-route.tsx +116 -0
- package/src/routes/create-singleton-route.test.node.ts +172 -0
- package/src/routes/create-singleton-route.tsx +176 -0
- package/src/routes/get-multi-collection-definition.ts +16 -0
- package/src/routes/get-singleton-definition.ts +16 -0
- package/src/routes/index.ts +3 -0
- package/src/server-fns/actors.ts +43 -0
- package/src/server-fns/admin-document-presentation.ts +78 -0
- package/src/server-fns/analytics/index.ts +23 -0
- package/src/server-fns/analytics/maintenance.ts +54 -0
- package/src/server-fns/analytics/queries.ts +86 -0
- package/src/server-fns/collections/actors.ts +1 -52
- package/src/server-fns/collections/get.ts +10 -49
- package/src/server-fns/collections/reorder.ts +1 -1
- package/src/server-fns/collections/scheduled-publication.ts +7 -1
- package/src/server-fns/collections/upload-singleton.test.node.ts +164 -0
- package/src/server-fns/collections/upload.ts +7 -3
- package/src/server-fns/collections/utils.ts +1 -9
- package/src/server-fns/serialise.ts +17 -0
- package/src/server-fns/singleton-document-read.ts +32 -0
- package/src/server-fns/singletons/cancel-scheduled-publish.ts +19 -0
- package/src/server-fns/singletons/change-status.ts +19 -0
- package/src/server-fns/singletons/confirm-scheduled-publish.ts +23 -0
- package/src/server-fns/singletons/copy-to-locale.ts +32 -0
- package/src/server-fns/singletons/find-by-version.ts +23 -0
- package/src/server-fns/singletons/get-scheduled-publish.ts +19 -0
- package/src/server-fns/singletons/get.ts +32 -0
- package/src/server-fns/singletons/history.ts +41 -0
- package/src/server-fns/singletons/index.ts +20 -0
- package/src/server-fns/singletons/restore-version.ts +19 -0
- package/src/server-fns/singletons/schedule-publish.ts +24 -0
- package/src/server-fns/singletons/singletons.test.node.ts +298 -0
- package/src/server-fns/singletons/unpublish.ts +19 -0
- package/src/server-fns/singletons/update.ts +31 -0
- package/src/vite/analytics-dev.test.node.ts +126 -0
- package/src/vite/analytics-dev.ts +56 -0
- package/src/vite/index.ts +1 -0
- package/src/vitest-jsdom.test.tsx +27 -0
|
@@ -41,6 +41,7 @@ import { buildLocaleDefinitions } from '../i18n/locale-definitions.js'
|
|
|
41
41
|
import { bylineAdminServices } from '../integrations/byline-admin-services.js'
|
|
42
42
|
import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
|
|
43
43
|
import { bylineFieldServices } from '../integrations/byline-field-services.js'
|
|
44
|
+
import { getAnalyticsRuntime } from '../server-fns/analytics/index.js'
|
|
44
45
|
import { getCurrentAdminUser } from '../server-fns/auth/index.js'
|
|
45
46
|
import { getScheduledPublicationRuntime } from '../server-fns/collections/index.js'
|
|
46
47
|
import { getActiveLocaleFn, setAdminLocaleFn } from '../server-fns/i18n/index.js'
|
|
@@ -58,9 +59,12 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
58
59
|
// than calling `resolveRequestLocale` directly) keeps
|
|
59
60
|
// `@tanstack/react-start/server` out of the client bundle — the
|
|
60
61
|
// same pattern `getCurrentAdminUser` uses for `getAdminRequestContext`.
|
|
61
|
-
const activeLocale = await
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
const [activeLocale, scheduledPublicationRuntime, analyticsRuntime] = await Promise.all([
|
|
63
|
+
getActiveLocaleFn(),
|
|
64
|
+
getScheduledPublicationRuntime(),
|
|
65
|
+
getAnalyticsRuntime(),
|
|
66
|
+
])
|
|
67
|
+
return { user, activeLocale, scheduledPublicationRuntime, analyticsRuntime }
|
|
64
68
|
} catch {
|
|
65
69
|
// `getCurrentAdminUser` (via `getAdminRequestContext`) throws
|
|
66
70
|
// `ERR_UNAUTHENTICATED` or a related auth error when no valid
|
|
@@ -73,11 +77,13 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
73
77
|
}
|
|
74
78
|
},
|
|
75
79
|
component: function AdminLayoutComponent() {
|
|
76
|
-
const { user, activeLocale, scheduledPublicationRuntime } =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const { user, activeLocale, scheduledPublicationRuntime, analyticsRuntime } =
|
|
81
|
+
Route.useRouteContext() as {
|
|
82
|
+
user: Awaited<ReturnType<typeof getCurrentAdminUser>>
|
|
83
|
+
activeLocale: LocaleCode
|
|
84
|
+
scheduledPublicationRuntime: Awaited<ReturnType<typeof getScheduledPublicationRuntime>>
|
|
85
|
+
analyticsRuntime: Awaited<ReturnType<typeof getAnalyticsRuntime>>
|
|
86
|
+
}
|
|
81
87
|
const { i18n } = getAdminConfig()
|
|
82
88
|
const localeDefinitions = buildLocaleDefinitions(
|
|
83
89
|
i18n.admin.locales,
|
|
@@ -113,6 +119,7 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
113
119
|
<AdminAppBar user={user} />
|
|
114
120
|
<main className={cx('byline-admin-layout-main', layoutStyles.main)}>
|
|
115
121
|
<AdminMenuDrawer
|
|
122
|
+
analyticsEnabled={analyticsRuntime.enabled}
|
|
116
123
|
scheduledPublicationEnabled={scheduledPublicationRuntime.enabled}
|
|
117
124
|
/>
|
|
118
125
|
<Content>
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
import { getCollectionDefinition } from '@byline/core'
|
|
11
|
+
import type { MultiCollectionDefinition } from '@byline/core'
|
|
13
12
|
import { useTranslation } from '@byline/i18n/react'
|
|
14
13
|
import { z } from 'zod'
|
|
15
14
|
|
|
@@ -18,6 +17,7 @@ import { ApiView } from '../admin-shell/collections/api.js'
|
|
|
18
17
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
19
18
|
import { getAdminRoutePath } from './admin-path.js'
|
|
20
19
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
20
|
+
import { getMultiCollectionDefinition } from './get-multi-collection-definition.js'
|
|
21
21
|
|
|
22
22
|
const searchSchema = z.object({
|
|
23
23
|
locale: z.string().optional(),
|
|
@@ -44,7 +44,7 @@ export function createCollectionApiRoute(path: string) {
|
|
|
44
44
|
params: { collection: string; id: string }
|
|
45
45
|
deps: { locale?: string; depth?: number }
|
|
46
46
|
}) => {
|
|
47
|
-
const collectionDef =
|
|
47
|
+
const collectionDef = getMultiCollectionDefinition(params.collection)
|
|
48
48
|
if (!collectionDef) {
|
|
49
49
|
throw notFound()
|
|
50
50
|
}
|
|
@@ -74,7 +74,7 @@ export function createCollectionApiRoute(path: string) {
|
|
|
74
74
|
const data = Route.useLoaderData()
|
|
75
75
|
const { collection, id } = Route.useParams() as { collection: string; id: string }
|
|
76
76
|
const { locale, depth } = Route.useSearch() as z.infer<typeof searchSchema>
|
|
77
|
-
const collectionDef =
|
|
77
|
+
const collectionDef = getMultiCollectionDefinition(collection) as MultiCollectionDefinition
|
|
78
78
|
const { t } = useTranslation('byline-admin')
|
|
79
79
|
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
80
80
|
|
|
@@ -8,18 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
13
|
-
buildInitialDataFromFields,
|
|
14
|
-
getCollectionAdminConfig,
|
|
15
|
-
getCollectionDefinition,
|
|
16
|
-
} from '@byline/core'
|
|
11
|
+
import type { MultiCollectionDefinition } from '@byline/core'
|
|
12
|
+
import { buildInitialDataFromFields, getCollectionAdminConfig } from '@byline/core'
|
|
17
13
|
import { useTranslation } from '@byline/i18n/react'
|
|
18
14
|
import { z } from 'zod'
|
|
19
15
|
|
|
20
16
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
21
17
|
import { CreateView } from '../admin-shell/collections/create.js'
|
|
22
18
|
import { getAdminRoutePath } from './admin-path.js'
|
|
19
|
+
import { getMultiCollectionDefinition } from './get-multi-collection-definition.js'
|
|
23
20
|
|
|
24
21
|
const searchSchema = z.object({
|
|
25
22
|
/** URL-encoded list search state to return to on cancel — see list-return-state.ts. */
|
|
@@ -36,7 +33,7 @@ export function createCollectionCreateRoute(path: string) {
|
|
|
36
33
|
params: { collection: string }
|
|
37
34
|
// biome-ignore lint/suspicious/noExplicitAny: collection-specific shape
|
|
38
35
|
}): Promise<{ initialData: any }> => {
|
|
39
|
-
const collectionDef =
|
|
36
|
+
const collectionDef = getMultiCollectionDefinition(params.collection)
|
|
40
37
|
if (!collectionDef) {
|
|
41
38
|
throw notFound()
|
|
42
39
|
}
|
|
@@ -50,7 +47,7 @@ export function createCollectionCreateRoute(path: string) {
|
|
|
50
47
|
const { collection } = Route.useParams() as { collection: string }
|
|
51
48
|
// biome-ignore lint/suspicious/noExplicitAny: collection-specific shape
|
|
52
49
|
const { initialData } = Route.useLoaderData() as { initialData: any }
|
|
53
|
-
const collectionDef =
|
|
50
|
+
const collectionDef = getMultiCollectionDefinition(collection) as MultiCollectionDefinition
|
|
54
51
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
55
52
|
const { t } = useTranslation('byline-admin')
|
|
56
53
|
const search = Route.useSearch() as z.infer<typeof searchSchema>
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
import { useEffect, useRef, useState } from 'react'
|
|
10
10
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
11
11
|
|
|
12
|
-
import type {
|
|
13
|
-
import { getCollectionAdminConfig
|
|
12
|
+
import type { MultiCollectionDefinition } from '@byline/core'
|
|
13
|
+
import { getCollectionAdminConfig } from '@byline/core'
|
|
14
14
|
import { useTranslation } from '@byline/i18n/react'
|
|
15
15
|
import { useToastManager } from '@byline/ui/react'
|
|
16
16
|
import { z } from 'zod'
|
|
@@ -21,6 +21,7 @@ import { EditView } from '../admin-shell/collections/edit.js'
|
|
|
21
21
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
22
22
|
import { getAdminRoutePath } from './admin-path.js'
|
|
23
23
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
24
|
+
import { getMultiCollectionDefinition } from './get-multi-collection-definition.js'
|
|
24
25
|
import { decodeListReturnState } from './list-return-state.js'
|
|
25
26
|
import { persistListReturnState, readListReturnState } from './list-return-storage.js'
|
|
26
27
|
|
|
@@ -46,7 +47,7 @@ export function createCollectionEditRoute(path: string) {
|
|
|
46
47
|
params: { collection: string; id: string }
|
|
47
48
|
deps: { locale?: string }
|
|
48
49
|
}) => {
|
|
49
|
-
const collectionDef =
|
|
50
|
+
const collectionDef = getMultiCollectionDefinition(params.collection)
|
|
50
51
|
if (!collectionDef) {
|
|
51
52
|
throw notFound()
|
|
52
53
|
}
|
|
@@ -77,7 +78,7 @@ export function createCollectionEditRoute(path: string) {
|
|
|
77
78
|
const { collection, id } = Route.useParams() as { collection: string; id: string }
|
|
78
79
|
const search = Route.useSearch() as z.infer<typeof searchSchema>
|
|
79
80
|
const { locale } = search
|
|
80
|
-
const collectionDef =
|
|
81
|
+
const collectionDef = getMultiCollectionDefinition(collection) as MultiCollectionDefinition
|
|
81
82
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
82
83
|
const { t } = useTranslation('byline-admin')
|
|
83
84
|
const toastManager = useToastManager()
|
|
@@ -8,12 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
13
|
-
getCollectionAdminConfig,
|
|
14
|
-
getCollectionDefinition,
|
|
15
|
-
getWorkflowStatuses,
|
|
16
|
-
} from '@byline/core'
|
|
11
|
+
import type { MultiCollectionDefinition } from '@byline/core'
|
|
12
|
+
import { getCollectionAdminConfig, getWorkflowStatuses } from '@byline/core'
|
|
17
13
|
import { useTranslation } from '@byline/i18n/react'
|
|
18
14
|
import { z } from 'zod'
|
|
19
15
|
|
|
@@ -26,6 +22,7 @@ import {
|
|
|
26
22
|
} from '../server-fns/collections/index.js'
|
|
27
23
|
import { getAdminRoutePath } from './admin-path.js'
|
|
28
24
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
25
|
+
import { getMultiCollectionDefinition } from './get-multi-collection-definition.js'
|
|
29
26
|
|
|
30
27
|
const searchSchema = z.object({
|
|
31
28
|
page: z.coerce.number().min(1).optional(),
|
|
@@ -68,7 +65,7 @@ export function createCollectionHistoryRoute(path: string) {
|
|
|
68
65
|
locale?: string
|
|
69
66
|
}
|
|
70
67
|
}) => {
|
|
71
|
-
const collectionDef =
|
|
68
|
+
const collectionDef = getMultiCollectionDefinition(params.collection)
|
|
72
69
|
if (!collectionDef) {
|
|
73
70
|
throw notFound()
|
|
74
71
|
}
|
|
@@ -111,7 +108,7 @@ export function createCollectionHistoryRoute(path: string) {
|
|
|
111
108
|
component: function CollectionHistoryComponent() {
|
|
112
109
|
const { history, currentDocument, auditLog } = Route.useLoaderData()
|
|
113
110
|
const { collection } = Route.useParams() as { collection: string; id: string }
|
|
114
|
-
const collectionDef =
|
|
111
|
+
const collectionDef = getMultiCollectionDefinition(collection) as MultiCollectionDefinition
|
|
115
112
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
116
113
|
const { t } = useTranslation('byline-admin')
|
|
117
114
|
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
@@ -10,12 +10,8 @@ import { useEffect, useRef } from 'react'
|
|
|
10
10
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
11
11
|
|
|
12
12
|
import { applyRelationColumnFormatters } from '@byline/admin/react'
|
|
13
|
-
import type {
|
|
14
|
-
import {
|
|
15
|
-
getCollectionAdminConfig,
|
|
16
|
-
getCollectionDefinition,
|
|
17
|
-
getWorkflowStatuses,
|
|
18
|
-
} from '@byline/core'
|
|
13
|
+
import type { MultiCollectionDefinition } from '@byline/core'
|
|
14
|
+
import { getCollectionAdminConfig, getWorkflowStatuses } from '@byline/core'
|
|
19
15
|
import { useTranslation } from '@byline/i18n/react'
|
|
20
16
|
import { useToastManager } from '@byline/ui/react'
|
|
21
17
|
import type { z } from 'zod'
|
|
@@ -31,6 +27,7 @@ import {
|
|
|
31
27
|
reorderCollectionDocument,
|
|
32
28
|
} from '../server-fns/collections/index.js'
|
|
33
29
|
import { getAdminRoutePath } from './admin-path.js'
|
|
30
|
+
import { getMultiCollectionDefinition } from './get-multi-collection-definition.js'
|
|
34
31
|
import { collectionListSearchSchema as searchSchema } from './list-return-state.js'
|
|
35
32
|
import type { CollectionTreeRow } from '../server-fns/collections/tree.js'
|
|
36
33
|
|
|
@@ -62,7 +59,7 @@ export function createCollectionListRoute(path: string) {
|
|
|
62
59
|
status?: string
|
|
63
60
|
}
|
|
64
61
|
}) => {
|
|
65
|
-
const collectionDef =
|
|
62
|
+
const collectionDef = getMultiCollectionDefinition(params.collection)
|
|
66
63
|
if (!collectionDef) {
|
|
67
64
|
throw notFound()
|
|
68
65
|
}
|
|
@@ -108,7 +105,7 @@ export function createCollectionListRoute(path: string) {
|
|
|
108
105
|
const { collection } = Route.useParams() as { collection: string }
|
|
109
106
|
const search = Route.useSearch() as z.infer<typeof searchSchema>
|
|
110
107
|
const navigate = useNavigate()
|
|
111
|
-
const collectionDef =
|
|
108
|
+
const collectionDef = getMultiCollectionDefinition(collection) as MultiCollectionDefinition
|
|
112
109
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
113
110
|
// Apply the built-in relation formatter to any relation column without an
|
|
114
111
|
// explicit one, so relation cells render the target's title (the list
|
|
@@ -0,0 +1,148 @@
|
|
|
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 { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
10
|
+
|
|
11
|
+
const mocks = vi.hoisted(() => ({
|
|
12
|
+
getSingletonHistory: vi.fn(),
|
|
13
|
+
getSingleton: vi.fn(),
|
|
14
|
+
definition: {
|
|
15
|
+
singleton: true,
|
|
16
|
+
path: 'site-settings',
|
|
17
|
+
label: 'Site settings',
|
|
18
|
+
fields: [],
|
|
19
|
+
},
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
vi.mock('@tanstack/react-router', () => ({
|
|
23
|
+
createFileRoute: () => (options: Record<string, unknown>) => ({ options }),
|
|
24
|
+
notFound: () => new Error('not found'),
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
vi.mock('@byline/core', () => ({
|
|
28
|
+
getCollectionDefinition: (path: string) =>
|
|
29
|
+
path === mocks.definition.path ? mocks.definition : null,
|
|
30
|
+
isSingleton: (definition: { singleton?: boolean }) => definition.singleton === true,
|
|
31
|
+
getSingletonAdminConfig: () => null,
|
|
32
|
+
getWorkflowStatuses: () => [],
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
vi.mock('../server-fns/singletons/index.js', () => ({
|
|
36
|
+
getSingletonHistory: mocks.getSingletonHistory,
|
|
37
|
+
getSingleton: mocks.getSingleton,
|
|
38
|
+
}))
|
|
39
|
+
|
|
40
|
+
vi.mock('../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js', () => ({
|
|
41
|
+
BreadcrumbsClient: () => null,
|
|
42
|
+
}))
|
|
43
|
+
vi.mock('../admin-shell/singletons/history.js', () => ({ SingletonHistoryView: () => null }))
|
|
44
|
+
vi.mock('@byline/i18n/react', () => ({ useTranslation: () => ({ t: (key: string) => key }) }))
|
|
45
|
+
vi.mock('./admin-path.js', () => ({ getAdminRoutePath: () => '/admin' }))
|
|
46
|
+
vi.mock('./get-content-locale-route-config.js', () => ({
|
|
47
|
+
getContentLocaleRouteConfig: () => ({ contentLocales: [], defaultContentLocale: 'en' }),
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
import { createSingletonHistoryRoute } from './create-singleton-history-route.js'
|
|
51
|
+
|
|
52
|
+
interface RouteLoaderInput {
|
|
53
|
+
params: { singleton: string }
|
|
54
|
+
deps: {
|
|
55
|
+
page?: number
|
|
56
|
+
page_size?: number
|
|
57
|
+
order?: string
|
|
58
|
+
desc?: boolean
|
|
59
|
+
locale?: string
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type RouteLoader = (input: RouteLoaderInput) => Promise<unknown>
|
|
64
|
+
|
|
65
|
+
function loader(): RouteLoader {
|
|
66
|
+
const route = createSingletonHistoryRoute('/admin/singletons/$singleton/history') as {
|
|
67
|
+
options: { loader: RouteLoader }
|
|
68
|
+
}
|
|
69
|
+
return route.options.loader
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('singleton history route loader', () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
vi.clearAllMocks()
|
|
75
|
+
mocks.getSingleton.mockResolvedValue(null)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('loads a paginated materialised singleton history without a document id input', async () => {
|
|
79
|
+
const history = {
|
|
80
|
+
docs: [{ id: 'document-settings', versionId: 'version-1', status: 'draft' }],
|
|
81
|
+
meta: { total: 3, page: 2, pageSize: 1, totalPages: 3 },
|
|
82
|
+
actors: {},
|
|
83
|
+
}
|
|
84
|
+
const currentDocument = {
|
|
85
|
+
id: 'document-settings',
|
|
86
|
+
versionId: 'version-3',
|
|
87
|
+
status: 'draft',
|
|
88
|
+
fields: {},
|
|
89
|
+
}
|
|
90
|
+
mocks.getSingletonHistory.mockResolvedValue(history)
|
|
91
|
+
mocks.getSingleton.mockResolvedValue(currentDocument)
|
|
92
|
+
|
|
93
|
+
await expect(
|
|
94
|
+
loader()({
|
|
95
|
+
params: { singleton: 'site-settings' },
|
|
96
|
+
deps: { page: 2, page_size: 1, order: 'created_at', desc: true, locale: 'fr' },
|
|
97
|
+
})
|
|
98
|
+
).resolves.toEqual({ history, currentDocument })
|
|
99
|
+
|
|
100
|
+
expect(mocks.getSingletonHistory).toHaveBeenCalledWith({
|
|
101
|
+
data: {
|
|
102
|
+
singleton: 'site-settings',
|
|
103
|
+
params: {
|
|
104
|
+
page: 2,
|
|
105
|
+
page_size: 1,
|
|
106
|
+
order: 'created_at',
|
|
107
|
+
desc: true,
|
|
108
|
+
locale: 'fr',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
expect(mocks.getSingleton).toHaveBeenCalledWith({
|
|
113
|
+
data: { singleton: 'site-settings', locale: 'fr' },
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('preserves the requested empty FindResult envelope for an unmaterialised slot', async () => {
|
|
118
|
+
const history = {
|
|
119
|
+
docs: [],
|
|
120
|
+
meta: { total: 0, page: 4, pageSize: 30, totalPages: 0 },
|
|
121
|
+
actors: {},
|
|
122
|
+
}
|
|
123
|
+
mocks.getSingletonHistory.mockResolvedValue(history)
|
|
124
|
+
|
|
125
|
+
await expect(
|
|
126
|
+
loader()({
|
|
127
|
+
params: { singleton: 'site-settings' },
|
|
128
|
+
deps: { page: 4, page_size: 30 },
|
|
129
|
+
})
|
|
130
|
+
).resolves.toEqual({ history, currentDocument: null })
|
|
131
|
+
|
|
132
|
+
expect(mocks.getSingleton).toHaveBeenCalledWith({
|
|
133
|
+
data: { singleton: 'site-settings', locale: 'all' },
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('preserves a kind-aware read denial from the singleton handle', async () => {
|
|
138
|
+
const forbidden = {
|
|
139
|
+
code: 'ERR_FORBIDDEN',
|
|
140
|
+
details: { ability: 'singletons.site-settings.read' },
|
|
141
|
+
}
|
|
142
|
+
mocks.getSingletonHistory.mockRejectedValue(forbidden)
|
|
143
|
+
|
|
144
|
+
await expect(loader()({ params: { singleton: 'site-settings' }, deps: {} })).rejects.toBe(
|
|
145
|
+
forbidden
|
|
146
|
+
)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
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 { createFileRoute, notFound } from '@tanstack/react-router'
|
|
10
|
+
|
|
11
|
+
import { getSingletonAdminConfig, getWorkflowStatuses } from '@byline/core'
|
|
12
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
13
|
+
import { z } from 'zod'
|
|
14
|
+
|
|
15
|
+
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
16
|
+
import { SingletonHistoryView } from '../admin-shell/singletons/history.js'
|
|
17
|
+
import { getSingleton, getSingletonHistory } from '../server-fns/singletons/index.js'
|
|
18
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
19
|
+
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
20
|
+
import { getSingletonDefinition } from './get-singleton-definition.js'
|
|
21
|
+
import type { VersionHistoryData } from '../admin-shell/collections/version-history.js'
|
|
22
|
+
|
|
23
|
+
const searchSchema = z.object({
|
|
24
|
+
page: z.coerce.number().min(1).optional(),
|
|
25
|
+
page_size: z.coerce.number().max(100).optional(),
|
|
26
|
+
order: z.string().optional(),
|
|
27
|
+
desc: z.coerce.boolean().optional(),
|
|
28
|
+
locale: z.string().optional(),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export function createSingletonHistoryRoute(path: string) {
|
|
32
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
33
|
+
const Route: any = createFileRoute(path as never)({
|
|
34
|
+
validateSearch: searchSchema,
|
|
35
|
+
loaderDeps: ({ search }: { search: z.infer<typeof searchSchema> }) => ({
|
|
36
|
+
page: search.page,
|
|
37
|
+
page_size: search.page_size,
|
|
38
|
+
order: search.order,
|
|
39
|
+
desc: search.desc,
|
|
40
|
+
locale: search.locale,
|
|
41
|
+
}),
|
|
42
|
+
loader: async ({
|
|
43
|
+
params,
|
|
44
|
+
deps,
|
|
45
|
+
}: {
|
|
46
|
+
params: { singleton: string }
|
|
47
|
+
deps: z.infer<typeof searchSchema>
|
|
48
|
+
}) => {
|
|
49
|
+
if (getSingletonDefinition(params.singleton) == null) throw notFound()
|
|
50
|
+
|
|
51
|
+
const [history, currentDocument] = await Promise.all([
|
|
52
|
+
getSingletonHistory({
|
|
53
|
+
data: {
|
|
54
|
+
singleton: params.singleton,
|
|
55
|
+
params: {
|
|
56
|
+
page: deps.page,
|
|
57
|
+
page_size: deps.page_size,
|
|
58
|
+
order: deps.order,
|
|
59
|
+
desc: deps.desc,
|
|
60
|
+
locale: deps.locale,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
// Use the same locale shape as the version stream so DiffModal compares
|
|
65
|
+
// like with like. An empty slot returns null while history returns its
|
|
66
|
+
// requested empty FindResult envelope.
|
|
67
|
+
getSingleton({
|
|
68
|
+
data: { singleton: params.singleton, locale: deps.locale ?? 'all' },
|
|
69
|
+
}),
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
return { history, currentDocument }
|
|
73
|
+
},
|
|
74
|
+
staleTime: 0,
|
|
75
|
+
gcTime: 0,
|
|
76
|
+
shouldReload: true,
|
|
77
|
+
component: function SingletonHistoryRouteComponent() {
|
|
78
|
+
const { history, currentDocument } = Route.useLoaderData()
|
|
79
|
+
const { singleton } = Route.useParams() as { singleton: string }
|
|
80
|
+
const definition = getSingletonDefinition(singleton)
|
|
81
|
+
if (definition == null) throw notFound()
|
|
82
|
+
|
|
83
|
+
const { t } = useTranslation('byline-admin')
|
|
84
|
+
const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig()
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
<BreadcrumbsClient
|
|
89
|
+
breadcrumbs={[
|
|
90
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
91
|
+
{
|
|
92
|
+
label: definition.label,
|
|
93
|
+
href: getAdminRoutePath('singletons', singleton),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: t('collections.breadcrumbs.history'),
|
|
97
|
+
href: getAdminRoutePath('singletons', singleton, 'history'),
|
|
98
|
+
},
|
|
99
|
+
]}
|
|
100
|
+
/>
|
|
101
|
+
<SingletonHistoryView
|
|
102
|
+
singletonDefinition={definition}
|
|
103
|
+
adminConfig={getSingletonAdminConfig(singleton) ?? undefined}
|
|
104
|
+
data={history as VersionHistoryData}
|
|
105
|
+
currentDocument={currentDocument as Record<string, unknown> | null}
|
|
106
|
+
contentLocales={contentLocales}
|
|
107
|
+
defaultContentLocale={defaultContentLocale}
|
|
108
|
+
workflowStatuses={getWorkflowStatuses(definition)}
|
|
109
|
+
/>
|
|
110
|
+
</>
|
|
111
|
+
)
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
return Route
|
|
116
|
+
}
|