@stacksjs/defaults 0.70.88 → 0.70.90

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.
Files changed (36) hide show
  1. package/app/Actions/Blog/BlogDestroyAction.ts +25 -0
  2. package/app/Actions/Blog/BlogIndexAction.ts +18 -0
  3. package/app/Actions/Blog/BlogShowAction.ts +25 -0
  4. package/app/Actions/Blog/BlogStoreAction.ts +21 -0
  5. package/app/Actions/Blog/BlogUpdateAction.ts +23 -0
  6. package/app/Actions/Blog/payload.ts +56 -0
  7. package/app/Actions/Cms/PostStoreAction.ts +10 -5
  8. package/app/Actions/Dashboard/Content/AuthorDestroyAction.ts +38 -0
  9. package/app/Actions/Dashboard/Content/AuthorIndexAction.ts +53 -39
  10. package/app/Actions/Dashboard/Content/AuthorStoreAction.ts +61 -0
  11. package/app/Actions/Dashboard/Content/CategoryDestroyAction.ts +30 -0
  12. package/app/Actions/Dashboard/Content/CategoryIndexAction.ts +40 -17
  13. package/app/Actions/Dashboard/Content/CategoryStoreAction.ts +58 -0
  14. package/app/Actions/Dashboard/Content/CommentDestroyAction.ts +30 -0
  15. package/app/Actions/Dashboard/Content/CommentIndexAction.ts +46 -31
  16. package/app/Actions/Dashboard/Content/CommentUpdateAction.ts +48 -0
  17. package/app/Actions/Dashboard/Content/PageDestroyAction.ts +25 -0
  18. package/app/Actions/Dashboard/Content/PageIndexAction.ts +43 -15
  19. package/app/Actions/Dashboard/Content/PageStoreAction.ts +49 -0
  20. package/app/Actions/Dashboard/Content/PostDestroyAction.ts +37 -0
  21. package/app/Actions/Dashboard/Content/PostIndexAction.ts +64 -83
  22. package/app/Actions/Dashboard/Content/PostStoreAction.ts +50 -0
  23. package/app/Actions/Dashboard/Content/PostUpdateAction.ts +54 -0
  24. package/app/Actions/Dashboard/Content/TagDestroyAction.ts +30 -0
  25. package/app/Actions/Dashboard/Content/TagIndexAction.ts +39 -13
  26. package/app/Actions/Dashboard/Content/TagStoreAction.ts +60 -0
  27. package/app/Actions/Dashboard/Content/comment-input.ts +35 -0
  28. package/app/Actions/Dashboard/Content/content-input.ts +65 -0
  29. package/app/Actions/Dashboard/Content/post-input.ts +54 -0
  30. package/app/Models/Content/Post.ts +4 -1
  31. package/package.json +1 -1
  32. package/resources/components/Dashboard/FileViewer/MediaCard.stx +119 -0
  33. package/resources/components/Dashboard/FileViewer/MediaInfoPanel.stx +143 -0
  34. package/resources/components/Dashboard/SidebarModern.stx +1 -0
  35. package/resources/functions/dashboard/files-tree.ts +162 -0
  36. package/resources/functions/dashboard/sidebar.ts +1 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Build the dashboard File Manager tree from a real Storage disk.
3
+ *
4
+ * The `content/files` page seeds its grid with this at request time (from a
5
+ * `<script server>` block) so it browses actual files through @stacksjs/storage
6
+ * instead of demo data. Defaults to the `public` disk, which carries real media
7
+ * plus public URLs, so image/video previews render. Bounded in depth and entry
8
+ * count so a large disk can't blow up SSR, and returns `null` on any error or an
9
+ * empty disk so the page falls back to its built-in demo tree.
10
+ *
11
+ * Usage from a page's `<script server>`:
12
+ *
13
+ * const { buildStorageFileTree } =
14
+ * await import('../../../resources/functions/dashboard/files-tree')
15
+ * const tree = await buildStorageFileTree({ disk: 'public' })
16
+ */
17
+ import { Storage } from '@stacksjs/storage'
18
+
19
+ export interface FileTreeNode {
20
+ id: number
21
+ name: string
22
+ type: string
23
+ size?: number
24
+ path: string
25
+ lastModified: string
26
+ starred: boolean
27
+ shared: boolean
28
+ mime_type?: string
29
+ url?: string
30
+ items?: FileTreeNode[]
31
+ }
32
+
33
+ export interface BuildFileTreeOptions {
34
+ /** Storage disk to list. @default 'public' */
35
+ disk?: string
36
+ /** How many directory levels to descend. @default 3 */
37
+ maxDepth?: number
38
+ /** Hard cap on total entries, to bound SSR cost. @default 600 */
39
+ maxEntries?: number
40
+ }
41
+
42
+ function extensionOf(name: string): string {
43
+ const dot = name.lastIndexOf('.')
44
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : 'file'
45
+ }
46
+
47
+ function baseNameOf(path: string): string {
48
+ const trimmed = path.replace(/\/+$/, '')
49
+ const idx = trimmed.lastIndexOf('/')
50
+ return idx >= 0 ? trimmed.slice(idx + 1) : trimmed
51
+ }
52
+
53
+ function normalizePath(path: string): string {
54
+ return `/${path}`.replace(/\/+/g, '/')
55
+ }
56
+
57
+ /**
58
+ * List `disk` into a File-Manager tree, or `null` when it can't be listed or is
59
+ * empty (so callers can fall back to demo data). Never throws.
60
+ */
61
+ export async function buildStorageFileTree(options: BuildFileTreeOptions = {}): Promise<FileTreeNode | null> {
62
+ const { disk = 'public', maxDepth = 3, maxEntries = 600 } = options
63
+
64
+ let adapter: any
65
+ try {
66
+ adapter = Storage.disk(disk)
67
+ }
68
+ catch {
69
+ return null
70
+ }
71
+
72
+ let idCounter = 1
73
+ let entryCount = 0
74
+
75
+ async function walk(dir: string, name: string, depth: number): Promise<FileTreeNode> {
76
+ const node: FileTreeNode = {
77
+ id: idCounter++,
78
+ name,
79
+ type: 'folder',
80
+ path: normalizePath(dir),
81
+ lastModified: new Date().toISOString(),
82
+ starred: false,
83
+ shared: true,
84
+ items: [],
85
+ }
86
+ if (depth <= 0)
87
+ return node
88
+
89
+ const entries: { path: string, type: string }[] = []
90
+ try {
91
+ for await (const entry of adapter.list(dir)) {
92
+ entries.push({ path: String(entry.path), type: String(entry.type) })
93
+ if (entries.length >= maxEntries)
94
+ break
95
+ }
96
+ }
97
+ catch {
98
+ return node
99
+ }
100
+
101
+ // Folders first, then files, each alphabetical — matches Finder ordering.
102
+ entries.sort((a, b) => {
103
+ if (a.type !== b.type)
104
+ return a.type === 'directory' ? -1 : 1
105
+ return baseNameOf(a.path).localeCompare(baseNameOf(b.path))
106
+ })
107
+
108
+ for (const entry of entries) {
109
+ if (entryCount >= maxEntries)
110
+ break
111
+ const base = baseNameOf(entry.path)
112
+ if (!base || base.startsWith('.'))
113
+ continue
114
+ entryCount++
115
+
116
+ if (entry.type === 'directory') {
117
+ node.items!.push(await walk(entry.path, base, depth - 1))
118
+ continue
119
+ }
120
+
121
+ let size = 0
122
+ let lastModified = new Date().toISOString()
123
+ let mimeType = ''
124
+ let url = ''
125
+ try {
126
+ const stat = await adapter.stat(entry.path)
127
+ size = Number(stat?.size ?? 0)
128
+ if (stat?.lastModified)
129
+ lastModified = new Date(stat.lastModified).toISOString()
130
+ mimeType = String(stat?.mimeType ?? '')
131
+ }
132
+ catch {}
133
+ try {
134
+ url = await adapter.publicUrl(entry.path)
135
+ }
136
+ catch {}
137
+
138
+ node.items!.push({
139
+ id: idCounter++,
140
+ name: base,
141
+ type: extensionOf(base),
142
+ size,
143
+ path: normalizePath(entry.path),
144
+ lastModified,
145
+ mime_type: mimeType,
146
+ url,
147
+ starred: false,
148
+ shared: true,
149
+ })
150
+ }
151
+
152
+ return node
153
+ }
154
+
155
+ try {
156
+ const tree = await walk('', 'Home', maxDepth)
157
+ return tree.items && tree.items.length > 0 ? tree : null
158
+ }
159
+ catch {
160
+ return null
161
+ }
162
+ }
@@ -341,6 +341,7 @@ export function buildNavSections(
341
341
  sections.push(['content', 'content', [
342
342
  { to: '/content/dashboard', icon: 'dashboard', text: 'Dashboard' },
343
343
  { to: '/content/files', icon: 'files', text: 'Files' },
344
+ { to: '/content/blog', icon: 'post', text: 'Blog' },
344
345
  { to: '/content/pages', icon: 'file', text: 'Pages' },
345
346
  { to: '/content/posts', icon: 'post', text: 'Posts' },
346
347
  { to: '/content/categories', icon: 'tags', text: 'Categories' },