betterstart-cli 0.0.27 → 0.0.29
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/assets/adapters/next/integrations/mailchimp/actions/mailchimp.ts +0 -1
- package/dist/assets/adapters/next/integrations/vercel-blob/actions/vercel-blob.ts +39 -0
- package/dist/assets/adapters/next/integrations/vercel-blob/integration.ts +36 -0
- package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +1 -1
- package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +1 -1
- package/dist/assets/adapters/next/templates/init/admin-globals.css +1 -1
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-nav-link.tsx +12 -2
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-settings-sidebar.tsx +75 -0
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link.tsx +12 -2
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +2 -8
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid.tsx +14 -5
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +111 -112
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-types.ts +4 -0
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +45 -47
- package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/data/webhook-events.ts +5 -0
- package/dist/assets/adapters/next/templates/init/hooks/use-table-utils.ts +27 -7
- package/dist/assets/adapters/next/templates/init/hooks/use-webhooks.ts +211 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/types.ts +2 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/index.ts +0 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/types.ts +0 -4
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +0 -2
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/create-webhook.ts +55 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/delete-webhook.ts +26 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/dispatch.ts +135 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-deliveries.ts +124 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-secret.ts +28 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-subscriptions.ts +32 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhooks.ts +154 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/index.ts +30 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/internal-get-active-endpoints.ts +20 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/regenerate-webhook-secret.ts +35 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/register.ts +70 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/send-test-webhook.ts +81 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/types.ts +104 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/update-webhook.ts +72 -0
- package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +71 -2
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +2 -0
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +3 -1
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +6 -1
- package/dist/assets/adapters/next/templates/init/pages/auth-gate-rsc.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/media/media-page-content.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/media/media-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-columns.tsx +150 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-page-content.tsx +137 -0
- package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-table.tsx +295 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/edit-form-notifications-dialog.tsx +128 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page-content.tsx +111 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page-skeleton.tsx +7 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/settings-layout.tsx +10 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/delete-webhook-dialog.tsx +82 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-actions.tsx +53 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-enabled-switch.tsx +18 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-endpoint-dialog.tsx +370 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-columns.tsx +258 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-columns.tsx +200 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-page-content.tsx +117 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-table.tsx +253 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page-content.tsx +161 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page-skeleton.tsx +7 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-table.tsx +264 -0
- package/dist/assets/adapters/next/templates/init/pages/users/users-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +103 -109
- package/dist/assets/adapters/next/templates/init/types/webhooks.ts +7 -0
- package/dist/assets/adapters/next/templates/init/utils/webhook/signature.ts +13 -0
- package/dist/assets/shared-assets/react-admin/ui/card.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +3 -3
- package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +1 -1
- package/dist/cli.js +2191 -1304
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/test-form-webhook.ts +0 -40
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-content.tsx +0 -550
- package/dist/assets/adapters/next/templates/init/utils/webhook/webhook.ts +0 -28
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-detail-drawer.tsx +0 -0
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-detail-row.tsx +0 -0
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-page.tsx +0 -0
|
@@ -9,7 +9,6 @@ if (process.env.BETTERSTART_MAILCHIMP_API_KEY && process.env.BETTERSTART_MAILCHI
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Add an email address to the Mailchimp audience (fire-and-forget, non-blocking)
|
|
12
|
-
* Follows the same pattern as sendWebhook() in utils/webhook/webhook.ts
|
|
13
12
|
*/
|
|
14
13
|
export function addToMailchimpAudience(email: string): void {
|
|
15
14
|
if (
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import type { AdminStorageProvider, AdminStorageUploadInput } from '@admin/actions/storage/types'
|
|
3
|
+
import { put } from '@vercel/blob'
|
|
4
|
+
|
|
5
|
+
function getToken(): string {
|
|
6
|
+
return process.env.BLOB_READ_WRITE_TOKEN?.trim() ?? ''
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function sanitizeFilename(filename: string): string {
|
|
10
|
+
return filename.replace(/[^a-zA-Z0-9.-]/g, '_')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function buildKey(filename: string, prefix = 'uploads'): string {
|
|
14
|
+
return `${prefix}/${Date.now()}-${randomUUID()}-${sanitizeFilename(filename)}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const vercelBlobStorageProvider: AdminStorageProvider = {
|
|
18
|
+
id: 'vercel-blob',
|
|
19
|
+
isConfigured() {
|
|
20
|
+
return Boolean(getToken())
|
|
21
|
+
},
|
|
22
|
+
async saveFile(input: AdminStorageUploadInput) {
|
|
23
|
+
if (!this.isConfigured()) {
|
|
24
|
+
throw new Error('Vercel Blob is not configured.')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const key = buildKey(input.filename, input.prefix)
|
|
28
|
+
const blob = await put(key, input.buffer, {
|
|
29
|
+
access: 'public',
|
|
30
|
+
contentType: input.contentType,
|
|
31
|
+
token: getToken()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
key,
|
|
36
|
+
url: blob.url
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BetterstartIntegrationDefinition } from '@integration-engine/types.js'
|
|
2
|
+
|
|
3
|
+
export const vercelBlobIntegration: BetterstartIntegrationDefinition = {
|
|
4
|
+
id: 'vercel-blob',
|
|
5
|
+
title: 'Vercel Blob',
|
|
6
|
+
description: 'Remote media storage on Vercel Blob',
|
|
7
|
+
kind: 'storage',
|
|
8
|
+
version: '1.0.0',
|
|
9
|
+
dependencies: [],
|
|
10
|
+
conflicts: [],
|
|
11
|
+
files: [
|
|
12
|
+
{
|
|
13
|
+
outputPath: 'lib/actions/vercel-blob/provider.ts',
|
|
14
|
+
templatePath: 'actions/vercel-blob.ts'
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
packageDependencies: ['@vercel/blob'],
|
|
18
|
+
devDependencies: [],
|
|
19
|
+
capabilities: {
|
|
20
|
+
storageProvider: {
|
|
21
|
+
provider: 'vercel-blob',
|
|
22
|
+
importPath: '@admin/actions/vercel-blob/provider',
|
|
23
|
+
exportName: 'vercelBlobStorageProvider'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
envSections: [
|
|
27
|
+
{
|
|
28
|
+
// BLOB_READ_WRITE_TOKEN is deliberately unprefixed: it is the name the
|
|
29
|
+
// @vercel/blob SDK reads by default and the one Vercel injects into
|
|
30
|
+
// deployments when a Blob store is connected to the project.
|
|
31
|
+
header: 'Storage (Vercel Blob)',
|
|
32
|
+
vars: [{ key: 'BLOB_READ_WRITE_TOKEN', value: '' }]
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
configure: 'vercel-blob'
|
|
36
|
+
}
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
--accent-foreground: oklch(0.15 0 0);
|
|
98
98
|
--destructive: oklch(0.577 0.245 27.325);
|
|
99
99
|
--warning: oklch(0.625 0.165 57.4);
|
|
100
|
-
--border: oklch(0.
|
|
100
|
+
--border: oklch(0.93 0 0);
|
|
101
101
|
--highlight: oklch(0.9 0.004 286.32);
|
|
102
102
|
--input: oklch(0.994 0 0);
|
|
103
103
|
--ring: oklch(0.708 0 0);
|
|
@@ -4,10 +4,20 @@ import { SidebarMenuButton } from '@admin/components/ui/sidebar'
|
|
|
4
4
|
import Link from 'next/link'
|
|
5
5
|
import { usePathname } from 'next/navigation'
|
|
6
6
|
|
|
7
|
-
export function AdminNavLink({
|
|
7
|
+
export function AdminNavLink({
|
|
8
|
+
href,
|
|
9
|
+
exact = false,
|
|
10
|
+
children
|
|
11
|
+
}: {
|
|
12
|
+
href: string
|
|
13
|
+
exact?: boolean
|
|
14
|
+
children: React.ReactNode
|
|
15
|
+
}) {
|
|
8
16
|
const pathname = usePathname()
|
|
9
17
|
const isActive =
|
|
10
|
-
|
|
18
|
+
exact || href === '/admin'
|
|
19
|
+
? pathname === href
|
|
20
|
+
: pathname === href || pathname.startsWith(`${href}/`)
|
|
11
21
|
|
|
12
22
|
return (
|
|
13
23
|
<SidebarMenuButton asChild>
|
package/dist/assets/adapters/next/templates/init/components/layouts/admin-settings-sidebar.tsx
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Sidebar,
|
|
5
|
+
SidebarContent,
|
|
6
|
+
SidebarGroup,
|
|
7
|
+
SidebarGroupContent,
|
|
8
|
+
SidebarGroupLabel,
|
|
9
|
+
SidebarMenu,
|
|
10
|
+
SidebarMenuItem
|
|
11
|
+
} from '@admin/components/ui/sidebar'
|
|
12
|
+
import { SidebarNavLink } from './admin-sidebar-nav-link'
|
|
13
|
+
|
|
14
|
+
const settingsNav = [
|
|
15
|
+
{
|
|
16
|
+
label: 'Global',
|
|
17
|
+
items: [
|
|
18
|
+
{ label: 'General', href: '/admin/settings', exact: true },
|
|
19
|
+
{ label: 'Audit logs', href: '/admin/settings/audit-log', exact: true }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'Forms',
|
|
24
|
+
items: [
|
|
25
|
+
{
|
|
26
|
+
label: 'Forms',
|
|
27
|
+
href: '/admin/settings/forms',
|
|
28
|
+
exact: true
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 'Webhooks',
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
label: 'Webhook endpoints',
|
|
37
|
+
href: '/admin/settings/webhooks',
|
|
38
|
+
exact: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: 'Webhook logs',
|
|
42
|
+
href: '/admin/settings/webhooks/logs',
|
|
43
|
+
exact: true
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
export function AdminSettingsSidebar() {
|
|
50
|
+
return (
|
|
51
|
+
<Sidebar
|
|
52
|
+
collapsible="none"
|
|
53
|
+
className="h-full w-(--sidebar-width) shrink-0 border-r border-border/80 pt-14"
|
|
54
|
+
>
|
|
55
|
+
<SidebarContent className="gap-0 py-2">
|
|
56
|
+
{settingsNav.map((item) => (
|
|
57
|
+
<SidebarGroup key={item.label} className="gap-1.5">
|
|
58
|
+
<SidebarGroupLabel>{item.label}</SidebarGroupLabel>
|
|
59
|
+
<SidebarGroupContent>
|
|
60
|
+
<SidebarMenu className="gap-px">
|
|
61
|
+
{item.items.map((item) => (
|
|
62
|
+
<SidebarMenuItem key={item.href}>
|
|
63
|
+
<SidebarNavLink href={item.href} exact={item.exact}>
|
|
64
|
+
<span>{item.label}</span>
|
|
65
|
+
</SidebarNavLink>
|
|
66
|
+
</SidebarMenuItem>
|
|
67
|
+
))}
|
|
68
|
+
</SidebarMenu>
|
|
69
|
+
</SidebarGroupContent>
|
|
70
|
+
</SidebarGroup>
|
|
71
|
+
))}
|
|
72
|
+
</SidebarContent>
|
|
73
|
+
</Sidebar>
|
|
74
|
+
)
|
|
75
|
+
}
|
package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link.tsx
CHANGED
|
@@ -3,10 +3,20 @@ import { Suspense } from 'react'
|
|
|
3
3
|
import { AdminNavLink } from './admin-nav-link'
|
|
4
4
|
import { SidebarNavLinkSkeleton } from './admin-sidebar-nav-link-skeleton'
|
|
5
5
|
|
|
6
|
-
export function SidebarNavLink({
|
|
6
|
+
export function SidebarNavLink({
|
|
7
|
+
href,
|
|
8
|
+
exact,
|
|
9
|
+
children
|
|
10
|
+
}: {
|
|
11
|
+
href: string
|
|
12
|
+
exact?: boolean
|
|
13
|
+
children: ReactNode
|
|
14
|
+
}) {
|
|
7
15
|
return (
|
|
8
16
|
<Suspense fallback={<SidebarNavLinkSkeleton href={href}>{children}</SidebarNavLinkSkeleton>}>
|
|
9
|
-
<AdminNavLink href={href}
|
|
17
|
+
<AdminNavLink href={href} exact={exact}>
|
|
18
|
+
{children}
|
|
19
|
+
</AdminNavLink>
|
|
10
20
|
</Suspense>
|
|
11
21
|
)
|
|
12
22
|
}
|
|
@@ -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 {
|
|
14
|
+
import { 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-
|
|
38
|
+
<SidebarMenu className="gap-px">
|
|
39
39
|
{group.items.map((item) => (
|
|
40
40
|
<SidebarMenuItem key={item.href}>
|
|
41
41
|
<SidebarNavLink href={item.href}>
|
|
@@ -64,12 +64,6 @@ 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>
|
|
73
67
|
</SidebarMenu>
|
|
74
68
|
</SidebarGroup>
|
|
75
69
|
</SidebarContent>
|
|
@@ -5,6 +5,7 @@ import type * as React from 'react'
|
|
|
5
5
|
|
|
6
6
|
type DataGridProps = React.ComponentProps<'div'> & {
|
|
7
7
|
containerClassName?: string
|
|
8
|
+
containerRef?: React.Ref<HTMLDivElement>
|
|
8
9
|
gridTemplateColumns: string
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -17,12 +18,14 @@ const dataGridCellRole = { role: 'cell' } as const
|
|
|
17
18
|
function DataGrid({
|
|
18
19
|
className,
|
|
19
20
|
containerClassName,
|
|
21
|
+
containerRef,
|
|
20
22
|
gridTemplateColumns,
|
|
21
23
|
style,
|
|
22
24
|
...props
|
|
23
25
|
}: DataGridProps) {
|
|
24
26
|
return (
|
|
25
27
|
<div
|
|
28
|
+
ref={containerRef}
|
|
26
29
|
data-slot="data-grid-container"
|
|
27
30
|
className={cn('relative min-w-0 overflow-auto', containerClassName)}
|
|
28
31
|
>
|
|
@@ -42,7 +45,10 @@ function DataGridHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
42
45
|
<div
|
|
43
46
|
{...dataGridRowGroupRole}
|
|
44
47
|
data-slot="data-grid-header"
|
|
45
|
-
className={cn(
|
|
48
|
+
className={cn(
|
|
49
|
+
"relative col-span-full -mb-1 grid grid-cols-subgrid rounded-t-sm bg-muted pb-1 after:pointer-events-none after:absolute after:inset-0 after:rounded-t-sm after:border-x after:border-t after:border-border after:content-['']",
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
46
52
|
{...props}
|
|
47
53
|
/>
|
|
48
54
|
)
|
|
@@ -53,7 +59,10 @@ function DataGridBody({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
53
59
|
<div
|
|
54
60
|
{...dataGridRowGroupRole}
|
|
55
61
|
data-slot="data-grid-body"
|
|
56
|
-
className={cn(
|
|
62
|
+
className={cn(
|
|
63
|
+
"relative col-span-full grid grid-cols-subgrid overflow-hidden rounded-sm bg-card shadow-input after:pointer-events-none after:absolute after:inset-0 after:rounded-sm after:border after:border-border after:content-['']",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
57
66
|
{...props}
|
|
58
67
|
/>
|
|
59
68
|
)
|
|
@@ -65,7 +74,7 @@ function DataGridRow({ className, style, ...props }: React.ComponentProps<'div'>
|
|
|
65
74
|
{...dataGridRowRole}
|
|
66
75
|
data-slot="data-grid-row"
|
|
67
76
|
className={cn(
|
|
68
|
-
'grid border-b transition-colors has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
|
|
77
|
+
'grid border-b transition-colors last:border-b-0 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
|
|
69
78
|
className
|
|
70
79
|
)}
|
|
71
80
|
style={{ ...style, gridColumn: '1 / -1', gridTemplateColumns: 'subgrid' }}
|
|
@@ -81,7 +90,7 @@ function DataGridHead({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
81
90
|
{...dataGridColumnHeaderRole}
|
|
82
91
|
data-slot="data-grid-head"
|
|
83
92
|
className={cn(
|
|
84
|
-
'grid h-
|
|
93
|
+
'grid h-9.5 items-center px-3 text-left font-medium whitespace-nowrap text-muted-foreground [&:has([role=checkbox])]:pr-0 *:h-full',
|
|
85
94
|
className
|
|
86
95
|
)}
|
|
87
96
|
tabIndex={-1}
|
|
@@ -96,7 +105,7 @@ function DataGridCell({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
96
105
|
{...dataGridCellRole}
|
|
97
106
|
data-slot="data-grid-cell"
|
|
98
107
|
className={cn(
|
|
99
|
-
'grid min-h-
|
|
108
|
+
'grid min-h-12 items-center px-3 py-1.5 whitespace-nowrap [&:has([role=checkbox])]:pr-0',
|
|
100
109
|
className
|
|
101
110
|
)}
|
|
102
111
|
{...props}
|
package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx
CHANGED
|
@@ -46,7 +46,7 @@ export function DataTable<TData extends RowData, TValue>({
|
|
|
46
46
|
isPending = false,
|
|
47
47
|
error = null,
|
|
48
48
|
emptyMessage = 'No results found.',
|
|
49
|
-
loadingMessage = 'Loading
|
|
49
|
+
loadingMessage = 'Loading…',
|
|
50
50
|
selectedIds,
|
|
51
51
|
onSelectedIdsChange,
|
|
52
52
|
meta,
|
|
@@ -115,126 +115,125 @@ export function DataTable<TData extends RowData, TValue>({
|
|
|
115
115
|
|
|
116
116
|
return (
|
|
117
117
|
<div className="space-y-6 pb-24">
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
header.column.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
118
|
+
<DataGrid
|
|
119
|
+
containerRef={tableContainerRef}
|
|
120
|
+
gridTemplateColumns={gridTemplateColumns}
|
|
121
|
+
style={{ width: table.getTotalSize() }}
|
|
122
|
+
>
|
|
123
|
+
<DataGridHeader>
|
|
124
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
125
|
+
<DataGridRow key={headerGroup.id}>
|
|
126
|
+
{headerGroup.headers.map((header, headerIndex) => {
|
|
127
|
+
const meta = header.column.columnDef.meta as Record<string, unknown> | undefined
|
|
128
|
+
const resizeLabel =
|
|
129
|
+
typeof header.column.columnDef.header === 'string'
|
|
130
|
+
? header.column.columnDef.header
|
|
131
|
+
: header.column.id
|
|
132
|
+
const nextHeader = headerGroup.headers[headerIndex + 1]
|
|
133
|
+
const canResizeColumn =
|
|
134
|
+
header.column.getCanResize() && nextHeader?.column.getCanResize() === true
|
|
135
|
+
return (
|
|
136
|
+
<DataGridHead
|
|
137
|
+
key={header.id}
|
|
138
|
+
className={cn('group/resize-header relative overflow-hidden', {
|
|
139
|
+
'text-center': meta?.align === 'center',
|
|
140
|
+
'text-right': meta?.align === 'right'
|
|
141
|
+
})}
|
|
142
|
+
data-column-id={header.column.id}
|
|
143
|
+
onMouseEnter={() => setHoveredColumnId(header.column.id)}
|
|
144
|
+
onMouseLeave={() =>
|
|
145
|
+
setHoveredColumnId((current) =>
|
|
146
|
+
current === header.column.id ? null : current
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
{header.isPlaceholder
|
|
151
|
+
? null
|
|
152
|
+
: flexRender(header.column.columnDef.header, header.getContext())}
|
|
153
|
+
{canResizeColumn ? (
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
aria-label={`Resize ${resizeLabel} column`}
|
|
157
|
+
className="group/resize-handle absolute right-0 top-0 z-10 flex h-full w-5 cursor-col-resize touch-none select-none items-center justify-center text-foreground opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none group-focus-within/resize-header:opacity-100 data-[column-hovered=true]:opacity-100 data-[resizing=true]:opacity-100"
|
|
158
|
+
data-resize-handle
|
|
159
|
+
data-column-hovered={
|
|
160
|
+
hoveredColumnId === header.column.id ? 'true' : 'false'
|
|
140
161
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
162
|
+
data-resizing={header.column.getIsResizing() ? 'true' : 'false'}
|
|
163
|
+
onDoubleClick={resetColumnSizingToContainer}
|
|
164
|
+
onMouseDown={header.getResizeHandler()}
|
|
165
|
+
onTouchStart={header.getResizeHandler()}
|
|
166
|
+
>
|
|
167
|
+
<GripVertical aria-hidden="true" className="size-3.5 text-foreground" />
|
|
168
|
+
</button>
|
|
169
|
+
) : null}
|
|
170
|
+
</DataGridHead>
|
|
171
|
+
)
|
|
172
|
+
})}
|
|
173
|
+
</DataGridRow>
|
|
174
|
+
))}
|
|
175
|
+
</DataGridHeader>
|
|
176
|
+
<DataGridBody>
|
|
177
|
+
{isPending ? (
|
|
178
|
+
<DataGridRow>
|
|
179
|
+
<DataGridCell
|
|
180
|
+
className="flex h-24 items-center justify-center text-center"
|
|
181
|
+
style={{ gridColumn: '1 / -1' }}
|
|
182
|
+
>
|
|
183
|
+
<div className="text-muted-foreground">{loadingMessage}</div>
|
|
184
|
+
</DataGridCell>
|
|
185
|
+
</DataGridRow>
|
|
186
|
+
) : error ? (
|
|
187
|
+
<DataGridRow>
|
|
188
|
+
<DataGridCell
|
|
189
|
+
className="flex h-24 items-center justify-center text-center"
|
|
190
|
+
style={{ gridColumn: '1 / -1' }}
|
|
191
|
+
>
|
|
192
|
+
<div className="text-destructive">Error: {error.message}</div>
|
|
193
|
+
</DataGridCell>
|
|
194
|
+
</DataGridRow>
|
|
195
|
+
) : table.getRowModel().rows?.length ? (
|
|
196
|
+
table.getRowModel().rows.map((row) => (
|
|
197
|
+
<DataGridRow
|
|
198
|
+
key={row.id}
|
|
199
|
+
className="hover:bg-muted/50"
|
|
200
|
+
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
201
|
+
>
|
|
202
|
+
{row.getVisibleCells().map((cell) => {
|
|
203
|
+
const meta = cell.column.columnDef.meta as Record<string, unknown> | undefined
|
|
204
|
+
return (
|
|
205
|
+
<DataGridCell
|
|
206
|
+
key={cell.id}
|
|
207
|
+
className={cn({
|
|
208
|
+
'text-center': meta?.align === 'center',
|
|
209
|
+
'text-right': meta?.align === 'right'
|
|
210
|
+
})}
|
|
211
|
+
data-column-id={cell.column.id}
|
|
212
|
+
onMouseEnter={() => setHoveredColumnId(cell.column.id)}
|
|
144
213
|
onMouseLeave={() =>
|
|
145
214
|
setHoveredColumnId((current) =>
|
|
146
|
-
current ===
|
|
215
|
+
current === cell.column.id ? null : current
|
|
147
216
|
)
|
|
148
217
|
}
|
|
149
218
|
>
|
|
150
|
-
{
|
|
151
|
-
|
|
152
|
-
: flexRender(header.column.columnDef.header, header.getContext())}
|
|
153
|
-
{canResizeColumn ? (
|
|
154
|
-
<button
|
|
155
|
-
type="button"
|
|
156
|
-
aria-label={`Resize ${resizeLabel} column`}
|
|
157
|
-
className="group/resize-handle absolute right-0 top-0 z-10 flex h-full w-5 cursor-col-resize touch-none select-none items-center justify-center text-foreground opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none group-focus-within/resize-header:opacity-100 data-[column-hovered=true]:opacity-100 data-[resizing=true]:opacity-100"
|
|
158
|
-
data-resize-handle
|
|
159
|
-
data-column-hovered={
|
|
160
|
-
hoveredColumnId === header.column.id ? 'true' : 'false'
|
|
161
|
-
}
|
|
162
|
-
data-resizing={header.column.getIsResizing() ? 'true' : 'false'}
|
|
163
|
-
onDoubleClick={resetColumnSizingToContainer}
|
|
164
|
-
onMouseDown={header.getResizeHandler()}
|
|
165
|
-
onTouchStart={header.getResizeHandler()}
|
|
166
|
-
>
|
|
167
|
-
<GripVertical aria-hidden="true" className="size-3.5 text-foreground" />
|
|
168
|
-
</button>
|
|
169
|
-
) : null}
|
|
170
|
-
</DataGridHead>
|
|
219
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
220
|
+
</DataGridCell>
|
|
171
221
|
)
|
|
172
222
|
})}
|
|
173
223
|
</DataGridRow>
|
|
174
|
-
))
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<DataGridRow>
|
|
188
|
-
<DataGridCell
|
|
189
|
-
className="flex h-24 items-center justify-center text-center"
|
|
190
|
-
style={{ gridColumn: '1 / -1' }}
|
|
191
|
-
>
|
|
192
|
-
<div className="text-destructive">Error: {error.message}</div>
|
|
193
|
-
</DataGridCell>
|
|
194
|
-
</DataGridRow>
|
|
195
|
-
) : table.getRowModel().rows?.length ? (
|
|
196
|
-
table.getRowModel().rows.map((row) => (
|
|
197
|
-
<DataGridRow
|
|
198
|
-
key={row.id}
|
|
199
|
-
className="hover:bg-muted/50"
|
|
200
|
-
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
201
|
-
>
|
|
202
|
-
{row.getVisibleCells().map((cell) => {
|
|
203
|
-
const meta = cell.column.columnDef.meta as Record<string, unknown> | undefined
|
|
204
|
-
return (
|
|
205
|
-
<DataGridCell
|
|
206
|
-
key={cell.id}
|
|
207
|
-
className={cn({
|
|
208
|
-
'text-center': meta?.align === 'center',
|
|
209
|
-
'text-right': meta?.align === 'right'
|
|
210
|
-
})}
|
|
211
|
-
data-column-id={cell.column.id}
|
|
212
|
-
onMouseEnter={() => setHoveredColumnId(cell.column.id)}
|
|
213
|
-
onMouseLeave={() =>
|
|
214
|
-
setHoveredColumnId((current) =>
|
|
215
|
-
current === cell.column.id ? null : current
|
|
216
|
-
)
|
|
217
|
-
}
|
|
218
|
-
>
|
|
219
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
220
|
-
</DataGridCell>
|
|
221
|
-
)
|
|
222
|
-
})}
|
|
223
|
-
</DataGridRow>
|
|
224
|
-
))
|
|
225
|
-
) : (
|
|
226
|
-
<DataGridRow>
|
|
227
|
-
<DataGridCell
|
|
228
|
-
className="flex h-24 items-center justify-center text-center"
|
|
229
|
-
style={{ gridColumn: '1 / -1' }}
|
|
230
|
-
>
|
|
231
|
-
{emptyMessage}
|
|
232
|
-
</DataGridCell>
|
|
233
|
-
</DataGridRow>
|
|
234
|
-
)}
|
|
235
|
-
</DataGridBody>
|
|
236
|
-
</DataGrid>
|
|
237
|
-
</div>
|
|
224
|
+
))
|
|
225
|
+
) : (
|
|
226
|
+
<DataGridRow>
|
|
227
|
+
<DataGridCell
|
|
228
|
+
className="flex h-24 items-center justify-center text-center"
|
|
229
|
+
style={{ gridColumn: '1 / -1' }}
|
|
230
|
+
>
|
|
231
|
+
{emptyMessage}
|
|
232
|
+
</DataGridCell>
|
|
233
|
+
</DataGridRow>
|
|
234
|
+
)}
|
|
235
|
+
</DataGridBody>
|
|
236
|
+
</DataGrid>
|
|
238
237
|
|
|
239
238
|
<DataTablePagination
|
|
240
239
|
table={table}
|
|
@@ -84,7 +84,7 @@ export function DevModeIntegrateButton({
|
|
|
84
84
|
</Tabs>
|
|
85
85
|
) : isPending ? (
|
|
86
86
|
<div className="flex min-h-80 items-center justify-center px-6 text-sm text-muted-foreground">
|
|
87
|
-
Loading integration details
|
|
87
|
+
Loading integration details…
|
|
88
88
|
</div>
|
|
89
89
|
) : loadError ? (
|
|
90
90
|
<div
|
|
@@ -55,7 +55,7 @@ export function EntityVersionsDrawer({
|
|
|
55
55
|
<EntityVersionsCurrentRow updatedAt={currentUpdatedAt} author={currentAuthor} />
|
|
56
56
|
{isPending ? (
|
|
57
57
|
<p className="rounded-sm border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground">
|
|
58
|
-
Loading versions
|
|
58
|
+
Loading versions…
|
|
59
59
|
</p>
|
|
60
60
|
) : null}
|
|
61
61
|
{!isPending && versions.length === 0 ? (
|