betterstart-cli 0.0.12 → 0.0.14

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 (99) hide show
  1. package/dist/assets/adapters/next/plugins/blog/plugin.ts +1 -1
  2. package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +155 -0
  3. package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +2 -7
  4. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +9 -3
  5. package/dist/assets/adapters/next/templates/init/components/layouts/sidebar-branding.tsx +1 -1
  6. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid.tsx +107 -0
  7. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +58 -45
  8. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +1 -1
  9. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
  10. package/dist/assets/adapters/next/templates/init/components/shared/entity-filters-bar.tsx +1 -1
  11. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +1 -1
  12. package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +20 -17
  13. package/dist/assets/adapters/next/templates/init/components/shared/sort-indicator.tsx +7 -3
  14. package/dist/assets/adapters/next/templates/init/data/navigation.ts +4 -1
  15. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-slash-menu.ts +28 -2
  16. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor.ts +22 -1
  17. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-table-button.ts +85 -0
  18. package/dist/assets/adapters/next/templates/init/hooks/use-audit-log.ts +23 -0
  19. package/dist/assets/adapters/next/templates/init/lib/actions/audit/cleanup-audit-logs.ts +63 -0
  20. package/dist/assets/adapters/next/templates/init/lib/actions/audit/get-audit-logs.ts +122 -0
  21. package/dist/assets/adapters/next/templates/init/lib/actions/audit/index.ts +19 -0
  22. package/dist/assets/adapters/next/templates/init/lib/actions/audit/record-audit-event.ts +37 -0
  23. package/dist/assets/adapters/next/templates/init/lib/actions/audit/register.ts +13 -0
  24. package/dist/assets/adapters/next/templates/init/lib/actions/audit/request-metadata.ts +37 -0
  25. package/dist/assets/adapters/next/templates/init/lib/actions/audit/types.ts +77 -0
  26. package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +3 -0
  27. package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +19 -0
  28. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +23 -0
  29. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +21 -0
  30. package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +23 -1
  31. package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +17 -0
  32. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +3 -0
  33. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +3 -0
  34. package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +19 -1
  35. package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +23 -1
  36. package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +23 -1
  37. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +49 -0
  38. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/index.ts +6 -1
  39. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +3 -0
  40. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +36 -1
  41. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +117 -12
  42. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +35 -1
  43. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-drawer.tsx +104 -0
  44. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-row.tsx +15 -0
  45. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-content.tsx +565 -0
  46. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-skeleton.tsx +7 -0
  47. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page.tsx +14 -0
  48. package/dist/assets/adapters/next/templates/init/pages/users/users-page-content.tsx +83 -2
  49. package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +126 -78
  50. package/dist/assets/adapters/next/templates/init/utils/audit/audit.ts +54 -0
  51. package/dist/assets/adapters/next/templates/init/utils/editor/tiptap.ts +7 -32
  52. package/dist/assets/adapters/next/templates/init/utils/form/list-field.ts +17 -0
  53. package/dist/assets/adapters/next/templates/init/utils/user/user.ts +70 -0
  54. package/dist/assets/shared-assets/react-admin/custom/content-editor/horizontal-rule-button.tsx +1 -1
  55. package/dist/assets/shared-assets/react-admin/custom/content-editor/index.tsx +6 -0
  56. package/dist/assets/shared-assets/react-admin/custom/content-editor/main-toolbar-content.tsx +1 -1
  57. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-button.tsx +1 -1
  58. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx +27 -40
  59. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover-button.tsx +0 -1
  60. package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx +4 -4
  61. package/dist/assets/shared-assets/react-admin/custom/content-editor/mode-toggle-button.tsx +1 -1
  62. package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +45 -8
  63. package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +153 -27
  64. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx +1 -1
  65. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +9 -11
  66. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +1 -1
  67. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-button.tsx +24 -67
  68. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/blockquote-button.tsx +1 -1
  69. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/code-block-button.tsx +1 -1
  70. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-button.tsx +1 -1
  71. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +1 -1
  72. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +11 -2
  73. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +1 -1
  74. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu-item.tsx +1 -1
  75. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +9 -4
  76. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-button.tsx +1 -1
  77. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-control-popover.tsx +5 -12
  78. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-main.tsx +19 -45
  79. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-popover.tsx +2 -3
  80. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu-item.tsx +1 -1
  81. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +9 -4
  82. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/mark-button.tsx +1 -1
  83. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/undo-redo-button.tsx +1 -1
  84. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +2 -2
  85. package/dist/assets/shared-assets/react-admin/custom/dynamic-list-field.tsx +20 -8
  86. package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +1 -1
  87. package/dist/assets/shared-assets/react-admin/custom/nested-object-list-field.tsx +211 -0
  88. package/dist/assets/shared-assets/react-admin/schema.json +136 -23
  89. package/dist/assets/shared-assets/react-admin/ui/button.tsx +18 -18
  90. package/dist/assets/shared-assets/react-admin/ui/card.tsx +1 -1
  91. package/dist/assets/shared-assets/react-admin/ui/checkbox.tsx +1 -1
  92. package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +27 -3
  93. package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +9 -5
  94. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +27 -12
  95. package/dist/assets/shared-assets/react-admin/ui/form.tsx +1 -1
  96. package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +4 -4
  97. package/dist/cli.js +5396 -2385
  98. package/dist/cli.js.map +1 -1
  99. package/package.json +10 -2
@@ -8,7 +8,7 @@ export const blogPlugin: BetterstartPluginDefinition = {
8
8
  version: '1.0.0',
9
9
  dependencies: [],
10
10
  conflicts: [],
11
- schemaFiles: ['posts.json'],
11
+ schemaFiles: ['posts.json', 'menus.json'],
12
12
  files: [],
13
13
  packageDependencies: ['katex', 'markdown-it@^13.0.2', 'markdown-it-dollarmath', 'sanitize-html'],
14
14
  devDependencies: ['@types/markdown-it@13', '@types/sanitize-html'],
@@ -0,0 +1,155 @@
1
+ {
2
+ "name": "menus",
3
+ "label": "Menus",
4
+ "description": "Manage Menus",
5
+ "icon": "Menu",
6
+ "navGroup": {
7
+ "label": "General",
8
+ "position": 0
9
+ },
10
+ "navPosition": 1,
11
+ "slot": {
12
+ "main": {
13
+ "fields": [
14
+ {
15
+ "name": "title",
16
+ "type": "string",
17
+ "label": "Title",
18
+ "required": true
19
+ },
20
+ {
21
+ "name": "slug",
22
+ "type": "string",
23
+ "label": "Slug",
24
+ "hint": "URL-friendly identifier (auto-generated from title)"
25
+ },
26
+ {
27
+ "name": "items",
28
+ "type": "list",
29
+ "label": "Items",
30
+ "hint": "Items in the navigation menu",
31
+ "fields": [
32
+ {
33
+ "name": "parentFields",
34
+ "type": "group",
35
+ "columns": 2,
36
+ "fields": [
37
+ {
38
+ "name": "label",
39
+ "type": "string",
40
+ "label": "Label",
41
+ "required": true
42
+ },
43
+ {
44
+ "name": "href",
45
+ "type": "string",
46
+ "label": "Href",
47
+ "required": true
48
+ }
49
+ ]
50
+ },
51
+ {
52
+ "name": "openInNewTab",
53
+ "type": "boolean",
54
+ "label": "Open in New Tab",
55
+ "hint": "Open this link in a new browser tab",
56
+ "default": false
57
+ },
58
+ {
59
+ "name": "subItems",
60
+ "type": "list",
61
+ "label": "Sub Menu Items",
62
+ "fields": [
63
+ {
64
+ "name": "subItemFields",
65
+ "type": "group",
66
+ "columns": 2,
67
+ "fields": [
68
+ {
69
+ "name": "subItemLabel",
70
+ "type": "string",
71
+ "label": "Label",
72
+ "required": true
73
+ },
74
+ {
75
+ "name": "subItemHref",
76
+ "type": "string",
77
+ "label": "Href",
78
+ "required": true
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "name": "subItemOpenInNewTab",
84
+ "type": "boolean",
85
+ "label": "Open in New Tab",
86
+ "hint": "Open this link in a new browser tab",
87
+ "default": false
88
+ }
89
+ ]
90
+ }
91
+ ]
92
+ }
93
+ ]
94
+ },
95
+ "sidebar": {
96
+ "fields": [
97
+ {
98
+ "name": "generalSidebar",
99
+ "type": "section",
100
+ "heading": {
101
+ "icon": "bolt",
102
+ "title": "General"
103
+ },
104
+ "fields": [
105
+ {
106
+ "name": "description",
107
+ "type": "text",
108
+ "label": "Description",
109
+ "hint": "Internal note describing this menu's purpose"
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ }
115
+ },
116
+ "columns": [
117
+ {
118
+ "accessorKey": "title",
119
+ "header": "Title",
120
+ "type": "text",
121
+ "sortable": true,
122
+ "size": "xl"
123
+ },
124
+ {
125
+ "accessorKey": "published",
126
+ "header": "Status",
127
+ "type": "boolean",
128
+ "sortable": true,
129
+ "size": "sm"
130
+ },
131
+ {
132
+ "accessorKey": "updatedAt",
133
+ "header": "Updated",
134
+ "type": "date",
135
+ "sortable": true,
136
+ "size": "sm"
137
+ },
138
+ {
139
+ "accessorKey": "createdAt",
140
+ "header": "Created",
141
+ "type": "date",
142
+ "sortable": true,
143
+ "size": "sm"
144
+ }
145
+ ],
146
+ "actions": {
147
+ "create": true,
148
+ "edit": true,
149
+ "delete": true,
150
+ "draft": true
151
+ },
152
+ "search": {
153
+ "fields": ["title"]
154
+ }
155
+ }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "posts",
3
3
  "label": "Posts",
4
- "description": "Manage blog posts and articles",
4
+ "description": "Manage blog posts",
5
5
  "icon": "FileText",
6
6
  "navGroup": {
7
- "label": "Blog",
7
+ "label": "Collections",
8
8
  "position": 0
9
9
  },
10
10
  "navPosition": 0,
@@ -160,10 +160,5 @@
160
160
  },
161
161
  "search": {
162
162
  "fields": ["title", "excerpt"]
163
- },
164
- "autoSlugify": {
165
- "enabled": true,
166
- "sourceField": "title",
167
- "targetField": "slug"
168
163
  }
169
164
  }
@@ -11,7 +11,7 @@ import {
11
11
  } from '@admin/components/ui/sidebar'
12
12
  import { adminNavigation } from '@admin/data/navigation'
13
13
  import { groupAdminNavigationItems } from '@admin/utils/navigation/sidebar'
14
- import { Settings, Users } from 'lucide-react'
14
+ import { History, Settings, Users } from 'lucide-react'
15
15
  import type { ComponentProps } from 'react'
16
16
  import { Fragment, Suspense } from 'react'
17
17
  import { SidebarBrandingRsc } from './admin-sidebar-branding-rsc'
@@ -35,7 +35,7 @@ export function AdminSidebar(props: ComponentProps<typeof Sidebar>) {
35
35
  <Fragment key={group.label ?? '_ungrouped'}>
36
36
  <SidebarGroup className="py-0">
37
37
  {group.label && <SidebarGroupLabel>{group.label}</SidebarGroupLabel>}
38
- <SidebarMenu className="gap-px">
38
+ <SidebarMenu className="gap-0.5">
39
39
  {group.items.map((item) => (
40
40
  <SidebarMenuItem key={item.href}>
41
41
  <SidebarNavLink href={item.href}>
@@ -51,7 +51,7 @@ export function AdminSidebar(props: ComponentProps<typeof Sidebar>) {
51
51
 
52
52
  <SidebarGroup className="mt-auto gap-2 px-0">
53
53
  <Separator />
54
- <SidebarMenu className="px-2 gap-px">
54
+ <SidebarMenu className="px-2 gap-0.5">
55
55
  <SidebarMenuItem>
56
56
  <SidebarNavLink href="/admin/users">
57
57
  <Users strokeWidth={2} />
@@ -64,6 +64,12 @@ export function AdminSidebar(props: ComponentProps<typeof Sidebar>) {
64
64
  <span>Settings</span>
65
65
  </SidebarNavLink>
66
66
  </SidebarMenuItem>
67
+ <SidebarMenuItem>
68
+ <SidebarNavLink href="/admin/system/audit-log">
69
+ <History strokeWidth={2} />
70
+ <span>Audit Log</span>
71
+ </SidebarNavLink>
72
+ </SidebarMenuItem>
67
73
  </SidebarMenu>
68
74
  </SidebarGroup>
69
75
  </SidebarContent>
@@ -20,7 +20,7 @@ export function SidebarBranding({ initialData }: SidebarBrandingProps) {
20
20
  return <BrandingSkeleton />
21
21
  }
22
22
 
23
- const dashboardName = settings?.dashboardName ?? admin.name
23
+ const dashboardName = settings?.title ?? admin.name
24
24
  const dashboardLogo = settings?.dashboardLogo || DEFAULT_LOGO_URL
25
25
 
26
26
  return (
@@ -0,0 +1,107 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@admin/utils/shared/cn'
4
+ import type * as React from 'react'
5
+
6
+ type DataGridProps = React.ComponentProps<'div'> & {
7
+ containerClassName?: string
8
+ gridTemplateColumns: string
9
+ }
10
+
11
+ const dataGridTableRole = { role: 'table' } as const
12
+ const dataGridRowGroupRole = { role: 'rowgroup' } as const
13
+ const dataGridRowRole = { role: 'row' } as const
14
+ const dataGridColumnHeaderRole = { role: 'columnheader' } as const
15
+ const dataGridCellRole = { role: 'cell' } as const
16
+
17
+ function DataGrid({
18
+ className,
19
+ containerClassName,
20
+ gridTemplateColumns,
21
+ style,
22
+ ...props
23
+ }: DataGridProps) {
24
+ return (
25
+ <div
26
+ data-slot="data-grid-container"
27
+ className={cn('relative min-w-0 overflow-auto', containerClassName)}
28
+ >
29
+ <div
30
+ {...dataGridTableRole}
31
+ data-slot="data-grid"
32
+ className={cn('grid min-w-full text-sm', className)}
33
+ style={{ ...style, gridTemplateColumns }}
34
+ {...props}
35
+ />
36
+ </div>
37
+ )
38
+ }
39
+
40
+ function DataGridHeader({ className, ...props }: React.ComponentProps<'div'>) {
41
+ return (
42
+ <div
43
+ {...dataGridRowGroupRole}
44
+ data-slot="data-grid-header"
45
+ className={cn('contents', className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function DataGridBody({ className, ...props }: React.ComponentProps<'div'>) {
52
+ return (
53
+ <div
54
+ {...dataGridRowGroupRole}
55
+ data-slot="data-grid-body"
56
+ className={cn('contents', className)}
57
+ {...props}
58
+ />
59
+ )
60
+ }
61
+
62
+ function DataGridRow({ className, style, ...props }: React.ComponentProps<'div'>) {
63
+ return (
64
+ <div
65
+ {...dataGridRowRole}
66
+ data-slot="data-grid-row"
67
+ className={cn(
68
+ 'grid border-b transition-colors has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
69
+ className
70
+ )}
71
+ style={{ ...style, gridColumn: '1 / -1', gridTemplateColumns: 'subgrid' }}
72
+ tabIndex={-1}
73
+ {...props}
74
+ />
75
+ )
76
+ }
77
+
78
+ function DataGridHead({ className, ...props }: React.ComponentProps<'div'>) {
79
+ return (
80
+ <div
81
+ {...dataGridColumnHeaderRole}
82
+ data-slot="data-grid-head"
83
+ className={cn(
84
+ 'grid h-10 items-center px-3 text-left font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0',
85
+ className
86
+ )}
87
+ tabIndex={-1}
88
+ {...props}
89
+ />
90
+ )
91
+ }
92
+
93
+ function DataGridCell({ className, ...props }: React.ComponentProps<'div'>) {
94
+ return (
95
+ <div
96
+ {...dataGridCellRole}
97
+ data-slot="data-grid-cell"
98
+ className={cn(
99
+ 'grid min-h-10 items-center px-3 py-1.5 whitespace-nowrap [&:has([role=checkbox])]:pr-0',
100
+ className
101
+ )}
102
+ {...props}
103
+ />
104
+ )
105
+ }
106
+
107
+ export { DataGrid, DataGridHeader, DataGridBody, DataGridHead, DataGridRow, DataGridCell }
@@ -1,13 +1,13 @@
1
1
  'use client'
2
2
 
3
3
  import {
4
- Table,
5
- TableBody,
6
- TableCell,
7
- TableHead,
8
- TableHeader,
9
- TableRow
10
- } from '@admin/components/ui/table'
4
+ DataGrid,
5
+ DataGridBody,
6
+ DataGridCell,
7
+ DataGridHead,
8
+ DataGridHeader,
9
+ DataGridRow
10
+ } from '@admin/components/shared/data-table/data-grid'
11
11
  import { useTableUtils } from '@admin/hooks/use-table-utils'
12
12
  import { cn } from '@admin/utils/shared/cn'
13
13
  import {
@@ -108,19 +108,18 @@ export function DataTable<TData extends RowData, TValue>({
108
108
  }
109
109
  })
110
110
  registerTable(table)
111
+ const gridTemplateColumns = table
112
+ .getVisibleLeafColumns()
113
+ .map((column) => `${column.getSize()}px`)
114
+ .join(' ')
111
115
 
112
116
  return (
113
117
  <div className="space-y-6 pb-24">
114
- <div ref={tableContainerRef} className="overflow-hidden material-sm border-border">
115
- <Table className="table-fixed" style={{ width: table.getTotalSize() }}>
116
- <colgroup>
117
- {table.getVisibleLeafColumns().map((column) => (
118
- <col key={column.id} style={{ width: column.getSize() }} />
119
- ))}
120
- </colgroup>
121
- <TableHeader>
118
+ <div ref={tableContainerRef} className="min-w-0 overflow-hidden material-sm border-border">
119
+ <DataGrid gridTemplateColumns={gridTemplateColumns} style={{ width: table.getTotalSize() }}>
120
+ <DataGridHeader>
122
121
  {table.getHeaderGroups().map((headerGroup) => (
123
- <TableRow key={headerGroup.id}>
122
+ <DataGridRow key={headerGroup.id}>
124
123
  {headerGroup.headers.map((header, headerIndex) => {
125
124
  const meta = header.column.columnDef.meta as Record<string, unknown> | undefined
126
125
  const resizeLabel =
@@ -131,12 +130,15 @@ export function DataTable<TData extends RowData, TValue>({
131
130
  const canResizeColumn =
132
131
  header.column.getCanResize() && nextHeader?.column.getCanResize() === true
133
132
  return (
134
- <TableHead
133
+ <DataGridHead
135
134
  key={header.id}
136
- className={cn('group/resize-header relative overflow-hidden', {
137
- 'text-center': meta?.align === 'center',
138
- 'text-right': meta?.align === 'right'
139
- })}
135
+ className={cn(
136
+ 'group/resize-header relative overflow-hidden text-foreground',
137
+ {
138
+ 'text-center': meta?.align === 'center',
139
+ 'text-right': meta?.align === 'right'
140
+ }
141
+ )}
140
142
  data-column-id={header.column.id}
141
143
  onMouseEnter={() => setHoveredColumnId(header.column.id)}
142
144
  onMouseLeave={() =>
@@ -144,7 +146,6 @@ export function DataTable<TData extends RowData, TValue>({
144
146
  current === header.column.id ? null : current
145
147
  )
146
148
  }
147
- style={{ width: header.getSize() }}
148
149
  >
149
150
  {header.isPlaceholder
150
151
  ? null
@@ -166,32 +167,42 @@ export function DataTable<TData extends RowData, TValue>({
166
167
  <GripVertical aria-hidden="true" className="size-3.5 text-foreground" />
167
168
  </button>
168
169
  ) : null}
169
- </TableHead>
170
+ </DataGridHead>
170
171
  )
171
172
  })}
172
- </TableRow>
173
+ </DataGridRow>
173
174
  ))}
174
- </TableHeader>
175
- <TableBody>
175
+ </DataGridHeader>
176
+ <DataGridBody>
176
177
  {isPending ? (
177
- <TableRow>
178
- <TableCell colSpan={columns.length} className="h-24 text-center">
178
+ <DataGridRow>
179
+ <DataGridCell
180
+ className="flex h-24 items-center justify-center text-center"
181
+ style={{ gridColumn: '1 / -1' }}
182
+ >
179
183
  <div className="text-muted-foreground">{loadingMessage}</div>
180
- </TableCell>
181
- </TableRow>
184
+ </DataGridCell>
185
+ </DataGridRow>
182
186
  ) : error ? (
183
- <TableRow>
184
- <TableCell colSpan={columns.length} className="h-24 text-center">
187
+ <DataGridRow>
188
+ <DataGridCell
189
+ className="flex h-24 items-center justify-center text-center"
190
+ style={{ gridColumn: '1 / -1' }}
191
+ >
185
192
  <div className="text-destructive">Error: {error.message}</div>
186
- </TableCell>
187
- </TableRow>
193
+ </DataGridCell>
194
+ </DataGridRow>
188
195
  ) : table.getRowModel().rows?.length ? (
189
196
  table.getRowModel().rows.map((row) => (
190
- <TableRow key={row.id} data-state={row.getIsSelected() ? 'selected' : undefined}>
197
+ <DataGridRow
198
+ key={row.id}
199
+ className="hover:bg-muted/50"
200
+ data-state={row.getIsSelected() ? 'selected' : undefined}
201
+ >
191
202
  {row.getVisibleCells().map((cell) => {
192
203
  const meta = cell.column.columnDef.meta as Record<string, unknown> | undefined
193
204
  return (
194
- <TableCell
205
+ <DataGridCell
195
206
  key={cell.id}
196
207
  className={cn({
197
208
  'text-center': meta?.align === 'center',
@@ -204,23 +215,25 @@ export function DataTable<TData extends RowData, TValue>({
204
215
  current === cell.column.id ? null : current
205
216
  )
206
217
  }
207
- style={{ width: cell.column.getSize() }}
208
218
  >
209
219
  {flexRender(cell.column.columnDef.cell, cell.getContext())}
210
- </TableCell>
220
+ </DataGridCell>
211
221
  )
212
222
  })}
213
- </TableRow>
223
+ </DataGridRow>
214
224
  ))
215
225
  ) : (
216
- <TableRow>
217
- <TableCell colSpan={columns.length} className="h-24 text-center">
226
+ <DataGridRow>
227
+ <DataGridCell
228
+ className="flex h-24 items-center justify-center text-center"
229
+ style={{ gridColumn: '1 / -1' }}
230
+ >
218
231
  {emptyMessage}
219
- </TableCell>
220
- </TableRow>
232
+ </DataGridCell>
233
+ </DataGridRow>
221
234
  )}
222
- </TableBody>
223
- </Table>
235
+ </DataGridBody>
236
+ </DataGrid>
224
237
  </div>
225
238
 
226
239
  <DataTablePagination
@@ -54,7 +54,7 @@ export function LifecycleHooksTab({ lifecycleHooks }: LifecycleHooksTabProps) {
54
54
  if (!firstHook) {
55
55
  return (
56
56
  <div className="flex min-h-80 items-center justify-center rounded-lg border border-dashed border-border px-6 text-center text-sm text-muted-foreground">
57
- Lifecycle hook examples are generated for entity schemas in v1.
57
+ Lifecycle hook examples are generated for entity and form schemas.
58
58
  </div>
59
59
  )
60
60
  }
@@ -50,7 +50,7 @@ export function DevModeIntegrateButton({
50
50
  <DrawerTrigger asChild>
51
51
  <Button type="button" variant="outline">
52
52
  <Code2 data-icon="inline-start" />
53
- <span className="hidden sm:inline">Integration Snippets</span>
53
+ <span className="hidden sm:inline">Dev Mode</span>
54
54
  </Button>
55
55
  </DrawerTrigger>
56
56
  <DrawerContent className="w-full overflow-hidden data-[vaul-drawer-direction=right]:sm:max-w-2xl data-[vaul-drawer-direction=right]:lg:max-w-4xl">
@@ -181,4 +181,4 @@ export function EntityFiltersBar({
181
181
  )
182
182
  }
183
183
 
184
- export type { FilterDef, SelectFilterDef, DateRangeFilterDef, EntityFiltersBarProps }
184
+ export type { DateRangeFilterDef, EntityFiltersBarProps, FilterDef, SelectFilterDef }
@@ -44,7 +44,7 @@ export function EntityVersionsDrawer({
44
44
  return (
45
45
  <Drawer direction="right" open={open} onOpenChange={onOpenChange} modal>
46
46
  <DrawerContent className="h-full sm:max-w-lg">
47
- <DrawerHeader className="border-b border-border">
47
+ <DrawerHeader>
48
48
  <DrawerTitle>Versions</DrawerTitle>
49
49
  <DrawerDescription>
50
50
  Review previous {entityType} versions and restore a saved state.
@@ -22,22 +22,25 @@ export function PageHeader({
22
22
  const router = useRouter()
23
23
 
24
24
  return (
25
- <header className="sticky top-0 border-t right-0 left-(--admin-shell-content-left) z-20 grid h-14 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center border-b border-border/80 px-4 bg-sidebar transition-[left] duration-200 ease-linear">
26
- <div className="flex w-full items-center justify-start gap-2">
27
- {hasBackButton && (
28
- <Button variant="ghost" size="icon" onClick={() => router.back()}>
29
- <ChevronLeft className="size-4" />
30
- </Button>
31
- )}
32
- </div>
33
- <div className="flex min-w-0 items-center justify-center gap-2">
34
- <h2 className="truncate text-sm font-medium tracking-tight">{title}</h2>
35
- </div>
36
- <div className="flex w-full min-w-0 items-center justify-end gap-2">
37
- {children && children}
38
- {search && search}
39
- {actions && actions}
40
- </div>
41
- </header>
25
+ <>
26
+ <header className="fixed top-0 border-t right-0 left-(--admin-shell-content-left) z-20 grid h-14 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center border-b border-border/80 px-4 bg-sidebar transition-[left] duration-200 ease-linear">
27
+ <div className="flex w-full items-center justify-start gap-2">
28
+ {hasBackButton && (
29
+ <Button variant="ghost" size="icon" onClick={() => router.back()}>
30
+ <ChevronLeft className="size-4" />
31
+ </Button>
32
+ )}
33
+ </div>
34
+ <div className="flex min-w-0 items-center justify-center gap-2">
35
+ <h2 className="truncate text-sm font-medium tracking-tight">{title}</h2>
36
+ </div>
37
+ <div className="flex w-full min-w-0 items-center justify-end gap-2">
38
+ {children && children}
39
+ {search && search}
40
+ {actions && actions}
41
+ </div>
42
+ </header>
43
+ <div className="h-14 shrink-0" aria-hidden="true" />
44
+ </>
42
45
  )
43
46
  }
@@ -16,9 +16,13 @@ export function SortIndicator({ direction }: { direction: false | 'asc' | 'desc'
16
16
 
17
17
  return (
18
18
  <Icon
19
- className={cn('size-4 text-muted-foreground shrink-0 opacity-50 group-hover:opacity-100', {
20
- 'opacity-100 text-foreground': direction !== false
21
- })}
19
+ aria-hidden="true"
20
+ className={cn(
21
+ 'size-4 shrink-0 text-muted-foreground opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-visible:opacity-100 group-hover/resize-header:opacity-100',
22
+ {
23
+ 'text-foreground': direction !== false
24
+ }
25
+ )}
22
26
  />
23
27
  )
24
28
  }
@@ -1,5 +1,6 @@
1
1
  import type { AdminNavigationItem } from '@admin/types/navigation'
2
2
  import { House, ImagePlay } from 'lucide-react'
3
+ import { menusNav } from './navigation/menus'
3
4
  import { postsNav } from './navigation/posts'
4
5
 
5
6
  export type { AdminNavigationItem } from '@admin/types/navigation'
@@ -9,12 +10,14 @@ const coreNav: AdminNavigationItem[] = [
9
10
  label: 'Overview',
10
11
  href: '/admin',
11
12
  icon: House,
13
+ group: 'General',
12
14
  position: 0
13
15
  },
14
16
  {
15
17
  label: 'Media',
16
18
  href: '/admin/media',
17
19
  icon: ImagePlay,
20
+ group: 'General',
18
21
  position: 1
19
22
  }
20
23
  ]
@@ -26,7 +29,7 @@ function comparePositions(a?: number, b?: number): number {
26
29
  return 0
27
30
  }
28
31
 
29
- const combinedNavigation = [...coreNav, ...postsNav]
32
+ const combinedNavigation = [...coreNav, ...menusNav, ...postsNav]
30
33
 
31
34
  const dashboard = combinedNavigation.find((item) => item.href === '/admin')
32
35
  const others = combinedNavigation.filter((item) => item.href !== '/admin')