@byline/cli 1.4.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/templates/byline-examples/collections/docs/admin.tsx +19 -5
- package/dist/templates/byline-examples/collections/docs/schema.ts +2 -6
- package/dist/templates/byline-examples/collections/media/admin.tsx +9 -3
- package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +10 -2
- package/dist/templates/byline-examples/collections/news/admin.tsx +17 -3
- package/dist/templates/byline-examples/collections/news/schema.ts +2 -6
- package/dist/templates/byline-examples/collections/pages/admin.tsx +43 -10
- package/dist/templates/byline-examples/collections/pages/schema.ts +13 -22
- package/dist/templates/byline-examples/fields/published-on-field.ts +25 -0
- package/dist/templates/byline-examples/scripts/regenerate-media.ts +1 -8
- package/dist/templates/byline-examples/server.config.ts +2 -1
- package/package.json +1 -1
|
@@ -119,11 +119,25 @@ export const DocsAdmin: CollectionAdminConfig = defineAdmin(Docs, {
|
|
|
119
119
|
},
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Preview URL builder for live preview links.
|
|
123
|
-
*
|
|
122
|
+
* Preview URL builder for live preview links. Returns a URL string (relative
|
|
123
|
+
* or absolute), or `null` to hide the preview affordance.
|
|
124
|
+
*
|
|
125
|
+
* `doc.path` is the top-level slug (derived from `useAsPath`), not a field.
|
|
126
|
+
* Direct relations are auto-populated by the edit view (depth 1, picker
|
|
127
|
+
* projection) and appear under `doc.fields.<name>?.document`.
|
|
124
128
|
*
|
|
125
129
|
* @example
|
|
126
|
-
* preview:
|
|
130
|
+
* preview: {
|
|
131
|
+
* url: (doc, { locale }) => {
|
|
132
|
+
* if (!doc.path) return null
|
|
133
|
+
* // `category` is a direct relation — auto-populated to depth 1.
|
|
134
|
+
* const category = doc.fields.category?.document?.path
|
|
135
|
+
* const prefix = locale && locale !== 'en' ? `/${locale}` : ''
|
|
136
|
+
* return category
|
|
137
|
+
* ? `${prefix}/docs/${category}/${doc.path}`
|
|
138
|
+
* : `${prefix}/docs/${doc.path}`
|
|
139
|
+
* },
|
|
140
|
+
* }
|
|
127
141
|
*/
|
|
128
142
|
// preview: undefined,
|
|
129
143
|
|
|
@@ -147,7 +161,7 @@ export const DocsAdmin: CollectionAdminConfig = defineAdmin(Docs, {
|
|
|
147
161
|
*/
|
|
148
162
|
tabSets: [
|
|
149
163
|
{
|
|
150
|
-
name: '
|
|
164
|
+
name: 'tabs',
|
|
151
165
|
tabs: [
|
|
152
166
|
{
|
|
153
167
|
name: 'details',
|
|
@@ -198,7 +212,7 @@ export const DocsAdmin: CollectionAdminConfig = defineAdmin(Docs, {
|
|
|
198
212
|
* schema field in `main` in declaration order.
|
|
199
213
|
*/
|
|
200
214
|
layout: {
|
|
201
|
-
main: ['
|
|
215
|
+
main: ['tabs'],
|
|
202
216
|
sidebar: ['publishedOn', 'availableLanguages'],
|
|
203
217
|
},
|
|
204
218
|
})
|
|
@@ -12,6 +12,7 @@ import { defineCollection, defineWorkflow } from '@byline/core'
|
|
|
12
12
|
import { PhotoBlock } from '../../blocks/photo-block.js'
|
|
13
13
|
import { RichTextBlock } from '../../blocks/richtext-block.js'
|
|
14
14
|
import { availableLanguagesField } from '../../fields/available-languages-field.js'
|
|
15
|
+
import { publishedOnField } from '../../fields/published-on-field.js'
|
|
15
16
|
|
|
16
17
|
// ---- Schema (server-safe, no UI concerns) ----
|
|
17
18
|
|
|
@@ -146,12 +147,7 @@ export const Docs = defineCollection({
|
|
|
146
147
|
displayField: 'title',
|
|
147
148
|
optional: true,
|
|
148
149
|
},
|
|
149
|
-
|
|
150
|
-
name: 'publishedOn',
|
|
151
|
-
label: 'Published On',
|
|
152
|
-
type: 'datetime',
|
|
153
|
-
mode: 'datetime',
|
|
154
|
-
},
|
|
150
|
+
publishedOnField,
|
|
155
151
|
{
|
|
156
152
|
name: 'category',
|
|
157
153
|
label: 'Category',
|
|
@@ -127,11 +127,17 @@ export const MediaAdmin: CollectionAdminConfig = defineAdmin(Media, {
|
|
|
127
127
|
// fields: {},
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* Preview URL builder for live preview links.
|
|
131
|
-
*
|
|
130
|
+
* Preview URL builder for live preview links. Returns a URL string (relative
|
|
131
|
+
* or absolute), or `null` to hide the preview affordance.
|
|
132
|
+
*
|
|
133
|
+
* `doc.path` is the top-level slug (derived from `useAsPath`), not a field.
|
|
134
|
+
* Direct relations are auto-populated by the edit view (depth 1, picker
|
|
135
|
+
* projection) and appear under `doc.fields.<name>?.document`.
|
|
132
136
|
*
|
|
133
137
|
* @example
|
|
134
|
-
* preview:
|
|
138
|
+
* preview: {
|
|
139
|
+
* url: (doc) => (doc.path ? `/media/${doc.path}` : null),
|
|
140
|
+
* }
|
|
135
141
|
*/
|
|
136
142
|
// preview: undefined,
|
|
137
143
|
|
|
@@ -24,8 +24,16 @@ import { Link, useNavigate, useRouterState } from '@tanstack/react-router'
|
|
|
24
24
|
import type { ListViewComponentProps, StoredFileValue, WorkflowStatus } from '@byline/core'
|
|
25
25
|
import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
|
|
26
26
|
import { RouterPager } from '@byline/host-tanstack-start/admin-shell/chrome/router-pager'
|
|
27
|
-
import {
|
|
28
|
-
|
|
27
|
+
import {
|
|
28
|
+
Container,
|
|
29
|
+
IconButton,
|
|
30
|
+
LoaderRing,
|
|
31
|
+
LocalDateTime,
|
|
32
|
+
PlusIcon,
|
|
33
|
+
Search,
|
|
34
|
+
Section,
|
|
35
|
+
Select,
|
|
36
|
+
} from '@byline/ui/react'
|
|
29
37
|
|
|
30
38
|
import { FormatBadge } from './media-thumbnail'
|
|
31
39
|
|
|
@@ -111,11 +111,25 @@ export const NewsAdmin: CollectionAdminConfig = defineAdmin(News, {
|
|
|
111
111
|
},
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
* Preview URL builder for live preview links.
|
|
115
|
-
*
|
|
114
|
+
* Preview URL builder for live preview links. Returns a URL string (relative
|
|
115
|
+
* or absolute), or `null` to hide the preview affordance.
|
|
116
|
+
*
|
|
117
|
+
* `doc.path` is the top-level slug (derived from `useAsPath`), not a field.
|
|
118
|
+
* Direct relations are auto-populated by the edit view (depth 1, picker
|
|
119
|
+
* projection) and appear under `doc.fields.<name>?.document`.
|
|
116
120
|
*
|
|
117
121
|
* @example
|
|
118
|
-
* preview:
|
|
122
|
+
* preview: {
|
|
123
|
+
* url: (doc, { locale }) => {
|
|
124
|
+
* if (!doc.path) return null
|
|
125
|
+
* // `category` is a direct relation — auto-populated to depth 1.
|
|
126
|
+
* const category = doc.fields.category?.document?.path
|
|
127
|
+
* const prefix = locale && locale !== 'en' ? `/${locale}` : ''
|
|
128
|
+
* return category
|
|
129
|
+
* ? `${prefix}/news/${category}/${doc.path}`
|
|
130
|
+
* : `${prefix}/news/${doc.path}`
|
|
131
|
+
* },
|
|
132
|
+
* }
|
|
119
133
|
*/
|
|
120
134
|
// preview: undefined,
|
|
121
135
|
|
|
@@ -10,6 +10,7 @@ import type { CollectionFieldData } from '@byline/core'
|
|
|
10
10
|
import { defineCollection, defineWorkflow } from '@byline/core'
|
|
11
11
|
|
|
12
12
|
import { availableLanguagesField } from '~/fields/available-languages-field.js'
|
|
13
|
+
import { publishedOnField } from '~/fields/published-on-field.js'
|
|
13
14
|
|
|
14
15
|
// ---- Schema (server-safe, no UI concerns) ----
|
|
15
16
|
|
|
@@ -73,12 +74,7 @@ export const News = defineCollection({
|
|
|
73
74
|
localized: true,
|
|
74
75
|
embedRelationsOnSave: true, // See type definition for details.
|
|
75
76
|
},
|
|
76
|
-
|
|
77
|
-
name: 'publishedOn',
|
|
78
|
-
label: 'Published On',
|
|
79
|
-
type: 'datetime',
|
|
80
|
-
mode: 'datetime',
|
|
81
|
-
},
|
|
77
|
+
publishedOnField,
|
|
82
78
|
availableLanguagesField(),
|
|
83
79
|
],
|
|
84
80
|
})
|
|
@@ -10,6 +10,7 @@ import { type CollectionAdminConfig, type ColumnDefinition, defineAdmin } from '
|
|
|
10
10
|
import { DateTimeFormatter } from '@byline/ui/react'
|
|
11
11
|
|
|
12
12
|
import { SummaryLength } from '~/components/summary-length.js'
|
|
13
|
+
import { i18n } from '~/i18n'
|
|
13
14
|
|
|
14
15
|
import { Pages } from './schema.js'
|
|
15
16
|
|
|
@@ -37,11 +38,10 @@ const listViewColumns: ColumnDefinition[] = [
|
|
|
37
38
|
className: 'w-[30%]',
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
|
-
fieldName: '
|
|
41
|
-
label: '
|
|
41
|
+
fieldName: 'area',
|
|
42
|
+
label: 'Area',
|
|
42
43
|
align: 'center',
|
|
43
44
|
className: 'w-[10%]',
|
|
44
|
-
formatter: (value) => (value ? '★' : ''),
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
fieldName: 'status',
|
|
@@ -119,13 +119,30 @@ export const PagesAdmin: CollectionAdminConfig = defineAdmin(Pages, {
|
|
|
119
119
|
},
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Preview URL builder for live preview links.
|
|
123
|
-
*
|
|
122
|
+
* Preview URL builder for live preview links. Returns a URL string (relative
|
|
123
|
+
* or absolute), or `null` to hide the preview affordance.
|
|
124
|
+
*
|
|
125
|
+
* `doc.path` is the top-level slug (derived from `useAsPath`), not a field.
|
|
126
|
+
* Direct relations are auto-populated by the edit view (depth 1, picker
|
|
127
|
+
* projection) and appear under `doc.fields.<name>?.document`.
|
|
124
128
|
*
|
|
125
129
|
* @example
|
|
126
|
-
* preview:
|
|
130
|
+
* preview: {
|
|
131
|
+
* url: (doc, { locale }) => {
|
|
132
|
+
* if (!doc.path) return null
|
|
133
|
+
* const prefix = locale && locale !== 'en' ? `/${locale}` : ''
|
|
134
|
+
* return `${prefix}/${doc.path}`
|
|
135
|
+
* },
|
|
136
|
+
* }
|
|
127
137
|
*/
|
|
128
|
-
|
|
138
|
+
preview: {
|
|
139
|
+
url: (doc, { locale }) => {
|
|
140
|
+
if (!doc.path) return null
|
|
141
|
+
const prefix = locale && locale !== i18n.interface.defaultLocale ? `/${locale}` : ''
|
|
142
|
+
const pathWithArea = doc.fields?.area && doc.fields.area !== 'root' ? `${doc.fields.area}/${doc.path}` : doc.path
|
|
143
|
+
return `${prefix}/${pathWithArea}`
|
|
144
|
+
},
|
|
145
|
+
},
|
|
129
146
|
|
|
130
147
|
// ---------------------------------------------------------------------------
|
|
131
148
|
// UI Layout
|
|
@@ -145,7 +162,23 @@ export const PagesAdmin: CollectionAdminConfig = defineAdmin(Pages, {
|
|
|
145
162
|
* Each tab's `fields` array accepts schema field names, row names, and group names.
|
|
146
163
|
* An optional `condition` function can show/hide a tab based on live form data.
|
|
147
164
|
*/
|
|
148
|
-
|
|
165
|
+
tabSets: [
|
|
166
|
+
{
|
|
167
|
+
name: 'tabs',
|
|
168
|
+
tabs: [
|
|
169
|
+
{
|
|
170
|
+
name: 'details',
|
|
171
|
+
label: 'Details',
|
|
172
|
+
fields: ['title', 'area', 'summary', 'featureImage'],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'content',
|
|
176
|
+
label: 'Content',
|
|
177
|
+
fields: ['content'],
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
149
182
|
|
|
150
183
|
/**
|
|
151
184
|
* Named horizontal-row layouts. Fields listed inside a row are rendered
|
|
@@ -177,7 +210,7 @@ export const PagesAdmin: CollectionAdminConfig = defineAdmin(Pages, {
|
|
|
177
210
|
* schema field in `main` in declaration order.
|
|
178
211
|
*/
|
|
179
212
|
layout: {
|
|
180
|
-
main: ['
|
|
181
|
-
sidebar: ['publishedOn', '
|
|
213
|
+
main: ['tabs'],
|
|
214
|
+
sidebar: ['publishedOn', 'availableLanguages'],
|
|
182
215
|
},
|
|
183
216
|
})
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
import type { CollectionFieldData } from '@byline/core'
|
|
10
10
|
import { defineCollection, defineWorkflow } from '@byline/core'
|
|
11
11
|
|
|
12
|
+
import { PhotoBlock } from '~/blocks/photo-block'
|
|
13
|
+
import { RichTextBlock } from '~/blocks/richtext-block'
|
|
12
14
|
import { availableLanguagesField } from '~/fields/available-languages-field.js'
|
|
15
|
+
import { publishedOnField } from '~/fields/published-on-field'
|
|
13
16
|
|
|
14
17
|
// ---- Schema (server-safe, no UI concerns) ----
|
|
15
18
|
|
|
@@ -58,37 +61,25 @@ export const Pages = defineCollection({
|
|
|
58
61
|
optional: true,
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
|
-
name: '
|
|
62
|
-
label: '
|
|
64
|
+
name: 'area',
|
|
65
|
+
label: 'Area',
|
|
63
66
|
type: 'select',
|
|
64
|
-
|
|
65
|
-
helpText: 'Select
|
|
67
|
+
defaultValue: 'root',
|
|
68
|
+
helpText: 'Select an area for this page',
|
|
66
69
|
options: [
|
|
67
|
-
{ label: '
|
|
68
|
-
{ label: '
|
|
69
|
-
{ label: '
|
|
70
|
+
{ label: 'Root', value: 'root' },
|
|
71
|
+
{ label: 'About', value: 'about' },
|
|
72
|
+
{ label: 'Legal', value: 'legal' },
|
|
70
73
|
],
|
|
71
74
|
},
|
|
72
75
|
{
|
|
73
76
|
name: 'content',
|
|
74
77
|
label: 'Content',
|
|
75
|
-
type: '
|
|
76
|
-
helpText: 'Enter the main content for this page.',
|
|
77
|
-
localized: true,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'publishedOn',
|
|
81
|
-
label: 'Published On',
|
|
82
|
-
type: 'datetime',
|
|
83
|
-
mode: 'datetime',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'featured',
|
|
87
|
-
label: 'Featured',
|
|
88
|
-
type: 'checkbox',
|
|
78
|
+
type: 'blocks',
|
|
89
79
|
optional: true,
|
|
90
|
-
|
|
80
|
+
blocks: [RichTextBlock, PhotoBlock],
|
|
91
81
|
},
|
|
82
|
+
publishedOnField,
|
|
92
83
|
availableLanguagesField(),
|
|
93
84
|
],
|
|
94
85
|
})
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { defineField, type FieldData } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Common "published on" datetime field. Defined once via `defineField`
|
|
13
|
+
* so the literal `name: 'publishedOn'` and `type: 'datetime'` survive
|
|
14
|
+
* inference, and dropped into any collection that needs a publish
|
|
15
|
+
* timestamp.
|
|
16
|
+
*/
|
|
17
|
+
export const publishedOnField = defineField({
|
|
18
|
+
name: 'publishedOn',
|
|
19
|
+
label: 'Published On',
|
|
20
|
+
type: 'datetime',
|
|
21
|
+
mode: 'datetime',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/** Data shape contributed by `publishedOnField` (resolves to `Date`). */
|
|
25
|
+
export type PublishedOnFieldData = FieldData<typeof publishedOnField>
|
|
@@ -146,14 +146,7 @@ async function run(): Promise<void> {
|
|
|
146
146
|
extractMeta: extractImageMeta,
|
|
147
147
|
isBypassMimeType,
|
|
148
148
|
generateVariants: ({ buffer, mimeType, storedFile, storage, upload, logger }) =>
|
|
149
|
-
generateImageVariants(
|
|
150
|
-
buffer,
|
|
151
|
-
mimeType,
|
|
152
|
-
storedFile,
|
|
153
|
-
storage,
|
|
154
|
-
upload.sizes ?? [],
|
|
155
|
-
logger
|
|
156
|
-
),
|
|
149
|
+
generateImageVariants(buffer, mimeType, storedFile, storage, upload.sizes ?? [], logger),
|
|
157
150
|
},
|
|
158
151
|
}
|
|
159
152
|
|
|
@@ -128,7 +128,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
128
128
|
}),
|
|
129
129
|
// S3-compatible alternative (AWS S3 / Cloudflare R2 / MinIO). Replace
|
|
130
130
|
// the `localStorageProvider` block above with the call below and add
|
|
131
|
-
// the corresponding `BYLINE_STORAGE_S3_*` entries to your `.env
|
|
131
|
+
// the corresponding `BYLINE_STORAGE_S3_*` entries to your `.env`
|
|
132
|
+
// (see `apps/webapp/.env.example`).
|
|
132
133
|
//
|
|
133
134
|
// On AWS with an IAM role / instance profile, omit `accessKeyId` and
|
|
134
135
|
// `secretAccessKey` so the SDK resolves credentials via its default
|