@byline/host-tanstack-start 6.8.0 → 6.10.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-app-bar.d.ts +0 -7
- package/dist/admin-shell/collections/api.d.ts +6 -2
- package/dist/admin-shell/collections/api.js +109 -3
- package/dist/admin-shell/collections/api.test.d.ts +8 -0
- package/dist/admin-shell/collections/edit.js +2 -0
- package/dist/admin-shell/collections/preview-link.js +17 -13
- package/dist/admin-shell/collections/view-menu.d.ts +8 -1
- package/dist/admin-shell/collections/view-menu.js +124 -107
- package/dist/admin-shell/collections/view-menu.module.js +1 -0
- package/dist/admin-shell/collections/view-menu_module.css +15 -1
- package/dist/admin-shell/scheduled-publications/list.js +2 -2
- package/dist/admin-shell/singletons/preview-link.js +17 -13
- package/dist/admin-shell/singletons/view-menu.js +78 -63
- package/dist/admin-shell/singletons/view.js +2 -0
- package/dist/server-fns/collections/get.d.ts +1 -0
- package/dist/server-fns/collections/get.js +4 -0
- package/package.json +15 -15
- package/src/admin-shell/chrome/admin-app-bar.tsx +2 -2
- package/src/admin-shell/chrome/dashboard.test.tsx +2 -2
- package/src/admin-shell/collections/api.test.tsx +223 -0
- package/src/admin-shell/collections/api.tsx +98 -3
- package/src/admin-shell/collections/edit.tsx +9 -1
- package/src/admin-shell/collections/preview-link.tsx +13 -12
- package/src/admin-shell/collections/view-menu.module.css +29 -3
- package/src/admin-shell/collections/view-menu.tsx +114 -96
- package/src/admin-shell/scheduled-publications/list.tsx +3 -4
- package/src/admin-shell/singletons/history.test.tsx +1 -1
- package/src/admin-shell/singletons/preview-link.tsx +13 -12
- package/src/admin-shell/singletons/view-menu.tsx +65 -59
- package/src/admin-shell/singletons/view.test.tsx +4 -4
- package/src/admin-shell/singletons/view.tsx +3 -1
- package/src/server-fns/collections/get.ts +3 -0
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
import { useState } from 'react'
|
|
10
10
|
|
|
11
11
|
import { FormRenderer } from '@byline/admin/react'
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
CollectionAdminConfig,
|
|
14
|
+
MultiCollectionDefinition,
|
|
15
|
+
UnavailableBlock,
|
|
16
|
+
} from '@byline/core'
|
|
13
17
|
import {
|
|
14
18
|
getDefaultStatus,
|
|
15
19
|
getWorkflow,
|
|
@@ -321,6 +325,9 @@ export const EditView = ({
|
|
|
321
325
|
// orphan row was skipped because the collection schema has moved on
|
|
322
326
|
// since the document was written.
|
|
323
327
|
// biome-ignore lint/suspicious/noExplicitAny: storage shape
|
|
328
|
+
const unavailableBlocks = (initialData as any)?._unavailableBlocks as
|
|
329
|
+
| UnavailableBlock[]
|
|
330
|
+
| undefined
|
|
324
331
|
const restoreWarnings = (initialData as any)?._restoreWarnings as string[] | undefined
|
|
325
332
|
|
|
326
333
|
const handleUnpublish = async () => {
|
|
@@ -854,6 +861,7 @@ export const EditView = ({
|
|
|
854
861
|
contentLocales={contentLocales}
|
|
855
862
|
publishedVersion={publishedVersion}
|
|
856
863
|
restoreWarnings={restoreWarnings}
|
|
864
|
+
unavailableBlocks={unavailableBlocks}
|
|
857
865
|
nextStatus={nextStatus}
|
|
858
866
|
workflowStatuses={workflowStatuses}
|
|
859
867
|
onCancel={() => {
|
|
@@ -47,7 +47,7 @@ import { useState } from 'react'
|
|
|
47
47
|
|
|
48
48
|
import type { CollectionAdminConfig, PreviewDocument } from '@byline/core'
|
|
49
49
|
import { useTranslation } from '@byline/i18n/react'
|
|
50
|
-
import { ExternalLinkIcon, IconButton, useToastManager } from '@byline/ui/react'
|
|
50
|
+
import { ExternalLinkIcon, IconButton, Tooltip, useToastManager } from '@byline/ui/react'
|
|
51
51
|
import cx from 'clsx'
|
|
52
52
|
|
|
53
53
|
import { enablePreviewModeFn } from '../../server-fns/preview/index.js'
|
|
@@ -110,16 +110,17 @@ export const PreviewLink = ({
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return (
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
<Tooltip text={t('collections.preview.title')} side="bottom" delay={0}>
|
|
114
|
+
<IconButton
|
|
115
|
+
className={cx('byline-preview-link', className)}
|
|
116
|
+
size="xs"
|
|
117
|
+
variant="text"
|
|
118
|
+
disabled={busy}
|
|
119
|
+
onClick={handleClick}
|
|
120
|
+
aria-label={t('collections.preview.openAriaLabel')}
|
|
121
|
+
>
|
|
122
|
+
<ExternalLinkIcon width="20px" height="20px" className="byline-preview-link-icon" />
|
|
123
|
+
</IconButton>
|
|
124
|
+
</Tooltip>
|
|
124
125
|
)
|
|
125
126
|
}
|
|
@@ -2,21 +2,47 @@
|
|
|
2
2
|
* ViewMenu — mini-nav bar for document-level views (Edit, History, API).
|
|
3
3
|
*
|
|
4
4
|
* Override handles:
|
|
5
|
-
* .byline-view-menu — outer
|
|
5
|
+
* .byline-view-menu — outer row; wraps between its two groups
|
|
6
|
+
* .byline-view-menu-data — locale/depth selects and the data actions that
|
|
7
|
+
* act on them (API view: Copy / Download JSON)
|
|
8
|
+
* .byline-view-menu-nav — preview link and view navigation
|
|
6
9
|
* .byline-view-menu-label — locale/depth label (lg-only)
|
|
7
10
|
* .byline-view-menu-button — primary view buttons (Edit/API)
|
|
8
|
-
* .byline-view-menu-icon-button — history icon
|
|
11
|
+
* .byline-view-menu-icon-button — preview, history, and data-action icon buttons
|
|
9
12
|
*/
|
|
10
13
|
|
|
14
|
+
/* Groups wrap as units, so a narrow viewport breaks the row between "what
|
|
15
|
+
* you are looking at" and "where you can go" rather than mid-group. The
|
|
16
|
+
* wider column gap marks the boundary when both groups share a line. */
|
|
11
17
|
.root,
|
|
12
18
|
:global(.byline-view-menu) {
|
|
13
19
|
display: flex;
|
|
20
|
+
flex-wrap: wrap;
|
|
14
21
|
align-items: center;
|
|
15
|
-
gap: 0.5rem;
|
|
22
|
+
gap: 0.5rem 1.25rem;
|
|
16
23
|
margin-top: 0.5rem;
|
|
17
24
|
margin-bottom: 0.5rem;
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
/* Beside the heading (every host switches to a row at 40rem), keep a wrapped
|
|
28
|
+
* second line flush with the right edge rather than stranded under the
|
|
29
|
+
* heading's side of the menu. */
|
|
30
|
+
@media (min-width: 40rem) {
|
|
31
|
+
.root,
|
|
32
|
+
:global(.byline-view-menu) {
|
|
33
|
+
justify-content: flex-end;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.group,
|
|
38
|
+
:global(.byline-view-menu-data),
|
|
39
|
+
:global(.byline-view-menu-nav) {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-wrap: wrap;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 0.5rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
20
46
|
.label,
|
|
21
47
|
:global(.byline-view-menu-label) {
|
|
22
48
|
display: none;
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { ReactNode } from 'react'
|
|
9
10
|
import { useEffect } from 'react'
|
|
10
11
|
|
|
11
12
|
import type { CollectionAdminConfig, PreviewDocument } from '@byline/core'
|
|
12
13
|
import { useTranslation } from '@byline/i18n/react'
|
|
13
|
-
import { Button, HistoryIcon, IconButton, Label, Select } from '@byline/ui/react'
|
|
14
|
+
import { Button, HistoryIcon, IconButton, Label, Select, Tooltip } from '@byline/ui/react'
|
|
14
15
|
import cx from 'clsx'
|
|
15
16
|
|
|
16
17
|
import { getAdminRoutePath } from '../../routes/admin-path.js'
|
|
@@ -51,6 +52,7 @@ export const ViewMenu = ({
|
|
|
51
52
|
defaultContentLocale,
|
|
52
53
|
adminConfig,
|
|
53
54
|
doc,
|
|
55
|
+
dataActions,
|
|
54
56
|
}: {
|
|
55
57
|
/** Collection path (e.g. "docs", "news"). */
|
|
56
58
|
collection: string
|
|
@@ -78,6 +80,12 @@ export const ViewMenu = ({
|
|
|
78
80
|
* entirely (e.g. on the create route, where no doc exists yet).
|
|
79
81
|
*/
|
|
80
82
|
doc?: PreviewDocument
|
|
83
|
+
/**
|
|
84
|
+
* Controls that act on the data selected by the locale and depth controls
|
|
85
|
+
* (the API view's Copy / Download JSON). Rendered first, in the same group
|
|
86
|
+
* as those controls, so they wrap together ahead of the view navigation.
|
|
87
|
+
*/
|
|
88
|
+
dataActions?: ReactNode
|
|
81
89
|
}) => {
|
|
82
90
|
const navigate = useNavigate()
|
|
83
91
|
const { t } = useTranslation('byline-admin')
|
|
@@ -141,103 +149,113 @@ export const ViewMenu = ({
|
|
|
141
149
|
|
|
142
150
|
return (
|
|
143
151
|
<div className={cx('byline-view-menu', styles.root)}>
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
152
|
+
<div className={cx('byline-view-menu-data', styles.group)}>
|
|
153
|
+
{dataActions}
|
|
154
|
+
<Label
|
|
155
|
+
className={cx('muted byline-view-menu-label', styles.label)}
|
|
156
|
+
id="contentLocaleLabel"
|
|
157
|
+
htmlFor="contentLocale"
|
|
158
|
+
label={t('collections.viewMenu.contentLocaleLabel')}
|
|
159
|
+
/>
|
|
160
|
+
<Select<string>
|
|
161
|
+
name="contentLocale"
|
|
162
|
+
id="contentLocale"
|
|
163
|
+
className={cx('byline-view-menu-locale-select', styles.localeSelect)}
|
|
164
|
+
size="xs"
|
|
165
|
+
variant="outlined"
|
|
166
|
+
value={locale ?? defaultContentLocale}
|
|
167
|
+
items={[
|
|
168
|
+
...(activeView !== 'edit'
|
|
169
|
+
? [{ value: 'all', label: t('collections.viewMenu.localeAll') }]
|
|
170
|
+
: []),
|
|
171
|
+
...contentLocales.map((loc) => ({ value: loc.code, label: loc.label })),
|
|
172
|
+
]}
|
|
173
|
+
onValueChange={handleLocaleChange}
|
|
174
|
+
/>
|
|
175
|
+
{activeView === 'api' && (
|
|
176
|
+
<>
|
|
177
|
+
<Label
|
|
178
|
+
className={cx('muted byline-view-menu-label', styles.label)}
|
|
179
|
+
id="populateDepthLabel"
|
|
180
|
+
htmlFor="populateDepth"
|
|
181
|
+
label={t('collections.viewMenu.depthLabel')}
|
|
182
|
+
/>
|
|
183
|
+
<Select<string>
|
|
184
|
+
name="populateDepth"
|
|
185
|
+
id="populateDepth"
|
|
186
|
+
className={cx('byline-view-menu-depth-select', styles.depthSelect)}
|
|
187
|
+
size="xs"
|
|
188
|
+
variant="outlined"
|
|
189
|
+
value={String(depth ?? 0)}
|
|
190
|
+
items={[
|
|
191
|
+
{ value: '0', label: '0' },
|
|
192
|
+
{ value: '1', label: '1' },
|
|
193
|
+
{ value: '2', label: '2' },
|
|
194
|
+
{ value: '3', label: '3' },
|
|
195
|
+
]}
|
|
196
|
+
onValueChange={handleDepthChange}
|
|
197
|
+
/>
|
|
198
|
+
</>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
<div className={cx('byline-view-menu-nav', styles.group)}>
|
|
202
|
+
{doc && (
|
|
203
|
+
<PreviewLink
|
|
204
|
+
collectionPath={collection}
|
|
205
|
+
doc={doc}
|
|
206
|
+
adminConfig={adminConfig}
|
|
207
|
+
locale={locale}
|
|
208
|
+
className={cx('byline-view-menu-icon-button', styles.iconButton)}
|
|
172
209
|
/>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
className={cx('byline-view-menu-
|
|
210
|
+
)}
|
|
211
|
+
<Tooltip text={t('collections.breadcrumbs.history')} side="bottom" delay={0}>
|
|
212
|
+
<IconButton
|
|
213
|
+
className={cx('byline-view-menu-icon-button', styles.iconButton)}
|
|
177
214
|
size="xs"
|
|
178
|
-
variant=
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
className={cx('byline-view-menu-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
>
|
|
225
|
-
{t('common.actions.edit')}
|
|
226
|
-
</Button>
|
|
227
|
-
<Button
|
|
228
|
-
size="xs"
|
|
229
|
-
variant={activeView === 'api' ? 'filled' : 'outlined'}
|
|
230
|
-
className={cx('byline-view-menu-button', styles.button)}
|
|
231
|
-
onClick={() =>
|
|
232
|
-
navigate({
|
|
233
|
-
to: getAdminRoutePath('collections', '$collection', '$id', 'api'),
|
|
234
|
-
params: { collection, id: documentId },
|
|
235
|
-
search: locale ? { locale } : {},
|
|
236
|
-
})
|
|
237
|
-
}
|
|
238
|
-
>
|
|
239
|
-
{t('collections.viewMenu.apiButton')}
|
|
240
|
-
</Button>
|
|
215
|
+
variant={activeView === 'history' ? 'filled' : 'text'}
|
|
216
|
+
aria-label={t('collections.breadcrumbs.history')}
|
|
217
|
+
onClick={() =>
|
|
218
|
+
navigate({
|
|
219
|
+
to: getAdminRoutePath('collections', '$collection', '$id', 'history'),
|
|
220
|
+
params: { collection, id: documentId },
|
|
221
|
+
search: locale ? { locale } : {},
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
>
|
|
225
|
+
<HistoryIcon
|
|
226
|
+
className={cx('byline-view-menu-icon-button-icon', styles.iconButtonIcon)}
|
|
227
|
+
/>
|
|
228
|
+
</IconButton>
|
|
229
|
+
</Tooltip>
|
|
230
|
+
<Button
|
|
231
|
+
size="xs"
|
|
232
|
+
variant={activeView === 'edit' ? 'filled' : 'outlined'}
|
|
233
|
+
className={cx('byline-view-menu-button', styles.button)}
|
|
234
|
+
onClick={() =>
|
|
235
|
+
navigate({
|
|
236
|
+
to: getAdminRoutePath('collections', '$collection', '$id'),
|
|
237
|
+
params: { collection, id: documentId },
|
|
238
|
+
search: locale ? { locale } : {},
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
>
|
|
242
|
+
{t('common.actions.edit')}
|
|
243
|
+
</Button>
|
|
244
|
+
<Button
|
|
245
|
+
size="xs"
|
|
246
|
+
variant={activeView === 'api' ? 'filled' : 'outlined'}
|
|
247
|
+
className={cx('byline-view-menu-button', styles.button)}
|
|
248
|
+
onClick={() =>
|
|
249
|
+
navigate({
|
|
250
|
+
to: getAdminRoutePath('collections', '$collection', '$id', 'api'),
|
|
251
|
+
params: { collection, id: documentId },
|
|
252
|
+
search: locale ? { locale } : {},
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
>
|
|
256
|
+
{t('collections.viewMenu.apiButton')}
|
|
257
|
+
</Button>
|
|
258
|
+
</div>
|
|
241
259
|
</div>
|
|
242
260
|
)
|
|
243
261
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { useDocumentMutationState } from '../document-mutation-state.js'
|
|
3
|
-
|
|
4
|
-
;('use client')
|
|
1
|
+
'use client'
|
|
5
2
|
|
|
6
3
|
/**
|
|
7
4
|
* This Source Code is subject to the terms of the Mozilla Public
|
|
@@ -32,6 +29,8 @@ import { getAdminRoutePath } from '../../routes/admin-path.js'
|
|
|
32
29
|
import { cancelCollectionDocumentScheduledPublish } from '../../server-fns/collections/index.js'
|
|
33
30
|
import { Link, useNavigate } from '../chrome/loose-router.js'
|
|
34
31
|
import { RouterPager } from '../chrome/router-pager.js'
|
|
32
|
+
import { DocumentMutationNotice } from '../document-mutation-notice.js'
|
|
33
|
+
import { useDocumentMutationState } from '../document-mutation-state.js'
|
|
35
34
|
import styles from './list.module.css'
|
|
36
35
|
import type { ScheduledPublicationListResponse } from '../../server-fns/collections/index.js'
|
|
37
36
|
|
|
@@ -372,7 +372,7 @@ describe('singleton version history', () => {
|
|
|
372
372
|
<HistoryView
|
|
373
373
|
collectionDefinition={collectionDefinition}
|
|
374
374
|
adminConfig={{
|
|
375
|
-
|
|
375
|
+
path: 'articles',
|
|
376
376
|
columns: [{ fieldName: 'title', label: 'Title' }],
|
|
377
377
|
}}
|
|
378
378
|
data={data as never}
|
|
@@ -12,7 +12,7 @@ import { useState } from 'react'
|
|
|
12
12
|
|
|
13
13
|
import type { SingletonAdminConfig, SingletonPreviewDocument } from '@byline/core'
|
|
14
14
|
import { useTranslation } from '@byline/i18n/react'
|
|
15
|
-
import { ExternalLinkIcon, IconButton, useToastManager } from '@byline/ui/react'
|
|
15
|
+
import { ExternalLinkIcon, IconButton, Tooltip, useToastManager } from '@byline/ui/react'
|
|
16
16
|
import cx from 'clsx'
|
|
17
17
|
|
|
18
18
|
import { enablePreviewModeFn } from '../../server-fns/preview/index.js'
|
|
@@ -60,16 +60,17 @@ export function SingletonPreviewLink({
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
<Tooltip text={t('collections.preview.title')} side="bottom" delay={0}>
|
|
64
|
+
<IconButton
|
|
65
|
+
className={cx('byline-preview-link', className)}
|
|
66
|
+
size="xs"
|
|
67
|
+
variant="text"
|
|
68
|
+
disabled={busy}
|
|
69
|
+
onClick={handleClick}
|
|
70
|
+
aria-label={t('collections.preview.openAriaLabel')}
|
|
71
|
+
>
|
|
72
|
+
<ExternalLinkIcon width="20px" height="20px" className="byline-preview-link-icon" />
|
|
73
|
+
</IconButton>
|
|
74
|
+
</Tooltip>
|
|
74
75
|
)
|
|
75
76
|
}
|
|
@@ -12,7 +12,7 @@ import { useEffect } from 'react'
|
|
|
12
12
|
|
|
13
13
|
import type { SingletonAdminConfig, SingletonPreviewDocument } from '@byline/core'
|
|
14
14
|
import { useTranslation } from '@byline/i18n/react'
|
|
15
|
-
import { Button, HistoryIcon, IconButton, Label, Select } from '@byline/ui/react'
|
|
15
|
+
import { Button, HistoryIcon, IconButton, Label, Select, Tooltip } from '@byline/ui/react'
|
|
16
16
|
import cx from 'clsx'
|
|
17
17
|
|
|
18
18
|
import { getAdminRoutePath } from '../../routes/admin-path.js'
|
|
@@ -71,66 +71,72 @@ export function SingletonViewMenu({
|
|
|
71
71
|
|
|
72
72
|
return (
|
|
73
73
|
<div className={cx('byline-view-menu', collectionStyles.root)}>
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<Select<string>
|
|
81
|
-
name="contentLocale"
|
|
82
|
-
id="contentLocale"
|
|
83
|
-
className={cx('byline-view-menu-locale-select', collectionStyles.localeSelect)}
|
|
84
|
-
size="xs"
|
|
85
|
-
variant="outlined"
|
|
86
|
-
value={locale ?? defaultContentLocale}
|
|
87
|
-
items={[
|
|
88
|
-
...(activeView === 'history'
|
|
89
|
-
? [{ value: 'all', label: t('collections.viewMenu.localeAll') }]
|
|
90
|
-
: []),
|
|
91
|
-
...contentLocales.map((entry) => ({ value: entry.code, label: entry.label })),
|
|
92
|
-
]}
|
|
93
|
-
onValueChange={handleLocaleChange}
|
|
94
|
-
/>
|
|
95
|
-
{adminConfig?.preview != null && doc != null && (
|
|
96
|
-
<SingletonPreviewLink
|
|
97
|
-
doc={doc}
|
|
98
|
-
adminConfig={adminConfig}
|
|
99
|
-
locale={locale}
|
|
100
|
-
className={cx('byline-view-menu-icon-button', collectionStyles.iconButton)}
|
|
74
|
+
<div className={cx('byline-view-menu-data', collectionStyles.group)}>
|
|
75
|
+
<Label
|
|
76
|
+
className={cx('muted byline-view-menu-label', collectionStyles.label)}
|
|
77
|
+
id="contentLocaleLabel"
|
|
78
|
+
htmlFor="contentLocale"
|
|
79
|
+
label={t('collections.viewMenu.contentLocaleLabel')}
|
|
101
80
|
/>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<HistoryIcon
|
|
117
|
-
className={cx('byline-view-menu-icon-button-icon', collectionStyles.iconButtonIcon)}
|
|
81
|
+
<Select<string>
|
|
82
|
+
name="contentLocale"
|
|
83
|
+
id="contentLocale"
|
|
84
|
+
className={cx('byline-view-menu-locale-select', collectionStyles.localeSelect)}
|
|
85
|
+
size="xs"
|
|
86
|
+
variant="outlined"
|
|
87
|
+
value={locale ?? defaultContentLocale}
|
|
88
|
+
items={[
|
|
89
|
+
...(activeView === 'history'
|
|
90
|
+
? [{ value: 'all', label: t('collections.viewMenu.localeAll') }]
|
|
91
|
+
: []),
|
|
92
|
+
...contentLocales.map((entry) => ({ value: entry.code, label: entry.label })),
|
|
93
|
+
]}
|
|
94
|
+
onValueChange={handleLocaleChange}
|
|
118
95
|
/>
|
|
119
|
-
</
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
96
|
+
</div>
|
|
97
|
+
<div className={cx('byline-view-menu-nav', collectionStyles.group)}>
|
|
98
|
+
{adminConfig?.preview != null && doc != null && (
|
|
99
|
+
<SingletonPreviewLink
|
|
100
|
+
doc={doc}
|
|
101
|
+
adminConfig={adminConfig}
|
|
102
|
+
locale={locale}
|
|
103
|
+
className={cx('byline-view-menu-icon-button', collectionStyles.iconButton)}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
<Tooltip text={t('collections.breadcrumbs.history')} side="bottom" delay={0}>
|
|
107
|
+
<IconButton
|
|
108
|
+
className={cx('byline-view-menu-icon-button', collectionStyles.iconButton)}
|
|
109
|
+
size="xs"
|
|
110
|
+
variant={activeView === 'history' ? 'filled' : 'text'}
|
|
111
|
+
aria-label={t('collections.breadcrumbs.history')}
|
|
112
|
+
onClick={() =>
|
|
113
|
+
navigate({
|
|
114
|
+
to: getAdminRoutePath('singletons', '$singleton', 'history'),
|
|
115
|
+
params: { singleton },
|
|
116
|
+
search: locale ? { locale } : {},
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
>
|
|
120
|
+
<HistoryIcon
|
|
121
|
+
className={cx('byline-view-menu-icon-button-icon', collectionStyles.iconButtonIcon)}
|
|
122
|
+
/>
|
|
123
|
+
</IconButton>
|
|
124
|
+
</Tooltip>
|
|
125
|
+
<Button
|
|
126
|
+
size="xs"
|
|
127
|
+
variant={activeView === 'edit' ? 'filled' : 'outlined'}
|
|
128
|
+
className={cx('byline-view-menu-button', collectionStyles.button)}
|
|
129
|
+
onClick={() =>
|
|
130
|
+
navigate({
|
|
131
|
+
to: getAdminRoutePath('singletons', '$singleton'),
|
|
132
|
+
params: { singleton },
|
|
133
|
+
search: locale ? { locale } : {},
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
>
|
|
137
|
+
{t('common.actions.edit')}
|
|
138
|
+
</Button>
|
|
139
|
+
</div>
|
|
134
140
|
</div>
|
|
135
141
|
)
|
|
136
142
|
}
|
|
@@ -142,7 +142,7 @@ const singleton = defineSingleton({
|
|
|
142
142
|
|
|
143
143
|
const adminConfig = {
|
|
144
144
|
singleton: true as const,
|
|
145
|
-
|
|
145
|
+
path: singleton.path,
|
|
146
146
|
preview: { url: () => '/preview-settings' },
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -181,7 +181,7 @@ let root: Root
|
|
|
181
181
|
function view(
|
|
182
182
|
currentDocument: typeof loadedDocument | null,
|
|
183
183
|
locale = 'en',
|
|
184
|
-
config: typeof adminConfig | { singleton: true;
|
|
184
|
+
config: typeof adminConfig | { singleton: true; path: string } = adminConfig
|
|
185
185
|
) {
|
|
186
186
|
return (
|
|
187
187
|
<I18nProvider
|
|
@@ -208,7 +208,7 @@ function view(
|
|
|
208
208
|
function render(
|
|
209
209
|
currentDocument: typeof loadedDocument | null,
|
|
210
210
|
locale = 'en',
|
|
211
|
-
config: typeof adminConfig | { singleton: true;
|
|
211
|
+
config: typeof adminConfig | { singleton: true; path: string } = adminConfig
|
|
212
212
|
) {
|
|
213
213
|
act(() => root.render(view(currentDocument, locale, config)))
|
|
214
214
|
}
|
|
@@ -332,7 +332,7 @@ describe('SingletonView', () => {
|
|
|
332
332
|
})
|
|
333
333
|
|
|
334
334
|
it('never derives a singleton preview from its internal stored path', () => {
|
|
335
|
-
render(loadedDocument, 'en', { singleton: true,
|
|
335
|
+
render(loadedDocument, 'en', { singleton: true, path: singleton.path })
|
|
336
336
|
|
|
337
337
|
expect(container.querySelector('.byline-view-menu')).not.toBeNull()
|
|
338
338
|
expect(container.querySelector('.byline-preview-link')).toBeNull()
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { useRouter } from '@tanstack/react-router'
|
|
12
12
|
|
|
13
13
|
import { FormRenderer } from '@byline/admin/react'
|
|
14
|
-
import type { SingletonAdminConfig, SingletonDefinition } from '@byline/core'
|
|
14
|
+
import type { SingletonAdminConfig, SingletonDefinition, UnavailableBlock } from '@byline/core'
|
|
15
15
|
import {
|
|
16
16
|
getDefaultStatus,
|
|
17
17
|
getWorkflow,
|
|
@@ -82,6 +82,7 @@ export function SingletonView({
|
|
|
82
82
|
? workflowStatuses[currentIndex + 1]
|
|
83
83
|
: undefined
|
|
84
84
|
const publishedVersion = document?._publishedVersion ?? null
|
|
85
|
+
const unavailableBlocks = document?._unavailableBlocks as UnavailableBlock[] | undefined
|
|
85
86
|
const restoreWarnings = document?._restoreWarnings as string[] | undefined
|
|
86
87
|
const scheduledPublicationEnabled = document?._scheduledPublicationEnabled === true
|
|
87
88
|
const canSchedulePublication = document?._canSchedulePublication === true
|
|
@@ -441,6 +442,7 @@ export function SingletonView({
|
|
|
441
442
|
contentLocales={document == null ? undefined : contentLocales}
|
|
442
443
|
publishedVersion={document == null ? null : publishedVersion}
|
|
443
444
|
restoreWarnings={restoreWarnings}
|
|
445
|
+
unavailableBlocks={unavailableBlocks}
|
|
444
446
|
nextStatus={document == null ? undefined : nextStatus}
|
|
445
447
|
workflowStatuses={document == null ? undefined : workflowStatuses}
|
|
446
448
|
onCancel={() => navigate({ to: getAdminRoutePath() })}
|
|
@@ -120,6 +120,8 @@ const getDocumentFn = createServerFn({ method: 'GET' })
|
|
|
120
120
|
| string[]
|
|
121
121
|
| undefined
|
|
122
122
|
|
|
123
|
+
const unavailableBlocks = document._unavailableBlocks
|
|
124
|
+
|
|
123
125
|
// Same preservation for the available-locales metadata — both are stripped
|
|
124
126
|
// by the Zod parse but the sidebar widget needs them at edit time:
|
|
125
127
|
// `availableLocales` — the stored editorial set → initialises the
|
|
@@ -155,6 +157,7 @@ const getDocumentFn = createServerFn({ method: 'GET' })
|
|
|
155
157
|
return {
|
|
156
158
|
...withEditableRevision(parsed, document),
|
|
157
159
|
_publishedVersion: publishedVersion,
|
|
160
|
+
...(unavailableBlocks?.length ? { _unavailableBlocks: unavailableBlocks } : {}),
|
|
158
161
|
...(restoreWarnings && restoreWarnings.length > 0
|
|
159
162
|
? { _restoreWarnings: restoreWarnings }
|
|
160
163
|
: {}),
|