@plutocms/pluto 0.10.0 → 0.12.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/CHANGELOG.md +27 -0
- package/FEATURES.md +17 -0
- package/README.md +30 -0
- package/app/assets/css/admin-theme.css +207 -0
- package/app/assets/css/pluto-layer.css +40 -0
- package/app/components/ColorModeButton.vue +4 -2
- package/app/components/Pluto/Content/PlutoContentForm.vue +325 -50
- package/app/components/Pluto/Content/PlutoContentList.vue +192 -108
- package/app/components/Pluto/Dashboard/PlutoDashboardContentActivity.vue +114 -0
- package/app/components/Pluto/Dashboard/PlutoDashboardContentOverview.vue +103 -0
- package/app/components/Pluto/Dashboard/PlutoDashboardQuickActions.vue +70 -0
- package/app/components/Pluto/Navbar/PlutoNavbarAdmin.vue +6 -1
- package/app/components/Pluto/View/PlutoViewAdminPanel.vue +119 -0
- package/app/components/Pluto/View/PlutoViewAside.vue +79 -0
- package/app/components/Pluto/View/PlutoViewToolbar.vue +64 -0
- package/app/composables/pluto-admin-theme.ts +14 -0
- package/app/composables/pluto-command-palette.ts +59 -0
- package/app/composables/pluto-content-actions.ts +17 -0
- package/app/composables/pluto-content-activity.ts +37 -0
- package/app/composables/pluto-content-stats.ts +78 -0
- package/app/composables/pluto-extension.ts +1 -0
- package/app/composables/pluto-registry.ts +1 -0
- package/app/composables/pluto-view.ts +9 -0
- package/app/layouts/admin.vue +59 -62
- package/app/layouts/auth.vue +4 -2
- package/app/pages/admin/home.vue +34 -7
- package/app/plugins/pluto-core-registrations.ts +34 -0
- package/modules/pluto-css/css-entries.ts +182 -0
- package/modules/pluto-css/index.ts +86 -0
- package/nuxt.config.ts +3 -1
- package/package.json +23 -22
- package/scripts/generate-admin-theme.cjs +96 -0
- package/server/api/_pluto/content/[type]/index.get.ts +4 -1
- package/server/api/_pluto/content-activity/[type].get.ts +15 -0
- package/server/utils/pluto-content-handlers.ts +65 -1
- package/shared/types/content.ts +37 -0
- package/shared/types/registry.ts +30 -0
- package/shared/utils/content-map.ts +15 -0
- package/app/assets/css/tailwind.css +0 -11
package/shared/types/content.ts
CHANGED
|
@@ -110,6 +110,8 @@ export interface PlutoContentTypeCapabilities {
|
|
|
110
110
|
read?: string
|
|
111
111
|
write?: string
|
|
112
112
|
delete?: string
|
|
113
|
+
/** Capability required to publish. Falls back to `write` when unset. */
|
|
114
|
+
publish?: string
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
export interface PlutoContentStatus {
|
|
@@ -209,6 +211,17 @@ export interface PlutoContentType {
|
|
|
209
211
|
editPath?: (id: string | number) => string
|
|
210
212
|
navOrder?: number
|
|
211
213
|
i18n?: PlutoContentI18n
|
|
214
|
+
/** Layout options for the generic content form (a later wave). */
|
|
215
|
+
form?: {
|
|
216
|
+
/** 'default' = heading + container. 'focus' = minimal chrome for a writing surface. */
|
|
217
|
+
layout?: 'default' | 'focus'
|
|
218
|
+
/** true = always show. false = never. 'auto' (default) = show when any field has region 'side'. */
|
|
219
|
+
aside?: boolean | 'auto'
|
|
220
|
+
/** Aside starts closed. Default false, or true when layout is 'focus'. */
|
|
221
|
+
asideCollapsed?: boolean
|
|
222
|
+
/** Drop the max-width container in the main region. Default false, or true when layout is 'focus'. */
|
|
223
|
+
width?: 'container' | 'full'
|
|
224
|
+
}
|
|
212
225
|
/**
|
|
213
226
|
* Server-only lifecycle hooks, run by the generic write handlers
|
|
214
227
|
* (`server/utils/pluto-content-handlers.ts`) after the adapter call
|
|
@@ -276,4 +289,28 @@ export interface PlutoContentAdapter {
|
|
|
276
289
|
/** Throws when `event`'s caller does not hold `capability`. Optional — see the fail-open rule in the content-model skill. */
|
|
277
290
|
authorize?: (event: H3Event, capability: string) => Promise<void>
|
|
278
291
|
describe?: (ctx: PlutoContentContext) => Promise<string[] | null>
|
|
292
|
+
/**
|
|
293
|
+
* Counts the items created from `range.from` (inclusive) to `range.to`
|
|
294
|
+
* (exclusive), both ISO timestamps. Optional. Return `null` when the
|
|
295
|
+
* content type has no created timestamp. The dashboard activity chart
|
|
296
|
+
* calls it once per week; without it, the chart leaves the type out.
|
|
297
|
+
*/
|
|
298
|
+
countCreated?: (ctx: PlutoContentContext, range: {
|
|
299
|
+
from: string
|
|
300
|
+
to: string
|
|
301
|
+
}) => Promise<number | null>
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** One week of the content activity series. */
|
|
305
|
+
export interface PlutoContentActivityPoint {
|
|
306
|
+
/** Start of the week, an ISO date (YYYY-MM-DD), Monday, UTC. */
|
|
307
|
+
start: string
|
|
308
|
+
count: number
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface PlutoContentActivity {
|
|
312
|
+
/** `false` when the adapter, or this content type, cannot count by creation date. */
|
|
313
|
+
supported: boolean
|
|
314
|
+
interval: 'week'
|
|
315
|
+
points: PlutoContentActivityPoint[]
|
|
279
316
|
}
|
package/shared/types/registry.ts
CHANGED
|
@@ -113,6 +113,34 @@ export interface PlutoMediaAdapterEntry extends PlutoEntry {
|
|
|
113
113
|
adapter: PlutoMediaAdapter
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/** State passed to a `PlutoContentAction`'s `show` test and `onSelect` handler. */
|
|
117
|
+
export interface PlutoContentActionContext {
|
|
118
|
+
type: PlutoContentType
|
|
119
|
+
item: Record<string, unknown>
|
|
120
|
+
editing: boolean
|
|
121
|
+
saving: boolean
|
|
122
|
+
save: () => Promise<void>
|
|
123
|
+
remove: () => Promise<void>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A button a layer adds to the generic content form (a later wave). Matched
|
|
128
|
+
* against a content type the same way `PlutoContentFieldWidget` matches a
|
|
129
|
+
* field: by exact name, or by `'*'` for every content type.
|
|
130
|
+
*/
|
|
131
|
+
export interface PlutoContentAction extends PlutoEntry {
|
|
132
|
+
/** Content type `name`, or '*' for every content type. */
|
|
133
|
+
contentType: string | '*'
|
|
134
|
+
label: string
|
|
135
|
+
icon?: string
|
|
136
|
+
color?: string
|
|
137
|
+
variant?: string
|
|
138
|
+
/** Where the action renders. Default 'toolbar'. */
|
|
139
|
+
placement?: 'toolbar' | 'aside'
|
|
140
|
+
show?: (ctx: PlutoContentActionContext) => boolean
|
|
141
|
+
onSelect: (ctx: PlutoContentActionContext) => void | Promise<void>
|
|
142
|
+
}
|
|
143
|
+
|
|
116
144
|
export interface PlutoExtension {
|
|
117
145
|
/** Layer name. Use the package's $meta.name, e.g. 'supabase-blog'. */
|
|
118
146
|
id: string
|
|
@@ -127,6 +155,7 @@ export interface PlutoExtension {
|
|
|
127
155
|
permissionsDriver?: PlutoPermissionsDriver
|
|
128
156
|
contentTypes?: PlutoContentTypeEntry[]
|
|
129
157
|
contentFieldWidgets?: PlutoContentFieldWidget[]
|
|
158
|
+
contentActions?: PlutoContentAction[]
|
|
130
159
|
mediaAdapters?: PlutoMediaAdapterEntry[]
|
|
131
160
|
}
|
|
132
161
|
|
|
@@ -154,5 +183,6 @@ export interface PlutoRegistry {
|
|
|
154
183
|
permissionsDrivers: PlutoRegistryBucket<PlutoPermissionsDriver>
|
|
155
184
|
contentTypes: PlutoRegistryBucket<PlutoContentTypeEntry>
|
|
156
185
|
contentFieldWidgets: PlutoRegistryBucket<PlutoContentFieldWidget>
|
|
186
|
+
contentActions: PlutoRegistryBucket<PlutoContentAction>
|
|
157
187
|
mediaAdapters: PlutoRegistryBucket<PlutoMediaAdapterEntry>
|
|
158
188
|
}
|
|
@@ -43,6 +43,14 @@ export function mapFieldsToColumns(
|
|
|
43
43
|
* `mapFieldsToColumns` — it has no column to read back from `row` in the
|
|
44
44
|
* first place, so this only guards against a stray column that happens to
|
|
45
45
|
* share the field's name.
|
|
46
|
+
*
|
|
47
|
+
* When `type.status` is set, this also maps the status column
|
|
48
|
+
* (`type.status.column`, default `'status'`) to `item.status`, unless a
|
|
49
|
+
* declared field already set `item.status` above. This mirrors the write
|
|
50
|
+
* side: `@plutocms/supabase`'s `content-adapter.ts` already writes the
|
|
51
|
+
* status column for a content type with workflow enabled, even when no
|
|
52
|
+
* field named `status` is declared. Without this, a generic list/form
|
|
53
|
+
* read back a row with no `status` key at all.
|
|
46
54
|
*/
|
|
47
55
|
export function mapColumnsToFields(
|
|
48
56
|
type: PlutoContentType,
|
|
@@ -61,5 +69,12 @@ export function mapColumnsToFields(
|
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
if (type.status) {
|
|
73
|
+
const statusColumn = type.status.column ?? 'status'
|
|
74
|
+
if (!Object.hasOwn(item, 'status') && Object.hasOwn(row, statusColumn)) {
|
|
75
|
+
item.status = row[statusColumn]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
64
79
|
return item as PlutoContentItem
|
|
65
80
|
}
|