betterstart-cli 0.0.117 → 0.0.119
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/templates/init/admin-globals.css +154 -38
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid-row.tsx +33 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-gallery-dialog.tsx +3 -3
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-preview.tsx +3 -1
- package/dist/assets/adapters/next/templates/init/hooks/use-field-error-visible.ts +25 -0
- package/dist/assets/adapters/next/templates/init/hooks/use-media.ts +1 -1
- package/dist/assets/adapters/next/templates/init/hooks/use-upload.ts +11 -5
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/cleanup-audit-logs.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/query-audit-logs.ts +2 -2
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/record-audit-event.ts +1 -2
- package/dist/assets/adapters/next/templates/init/lib/actions/auth/client.ts +4 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/get-entity-versions.ts +5 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-all-form-settings.ts +1 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +2 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +2 -4
- package/dist/assets/adapters/next/templates/init/lib/actions/profile/change-own-password.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +16 -6
- package/dist/assets/adapters/next/templates/init/lib/actions/users/change-user-password.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +2 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/users/types.ts +1 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +1 -0
- package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +12 -4
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +4 -4
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +3 -7
- package/dist/assets/adapters/next/templates/init/pages/forgot-password-form.tsx +5 -8
- package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +2 -2
- package/dist/assets/adapters/next/templates/init/pages/login-form.tsx +7 -9
- package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +2 -2
- package/dist/assets/adapters/next/templates/init/pages/reset-password-form.tsx +8 -16
- package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +2 -2
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-detail-row.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-table.tsx +1 -6
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-table.tsx +2 -7
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-table.tsx +1 -6
- package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/public/placeholder.png +0 -0
- package/dist/assets/adapters/next/templates/init/types/index.ts +2 -0
- package/dist/assets/adapters/next/templates/init/utils/audit/normalize-comparable-value.ts +4 -2
- package/dist/assets/adapters/next/templates/init/utils/date/format-action-date.ts +12 -0
- package/dist/assets/adapters/next/templates/init/utils/dev-mode/dev-mode-json-schemas.ts +66 -0
- package/dist/assets/adapters/next/templates/init/utils/dev-mode/parse-dev-mode-lifecycle-hooks.ts +11 -0
- package/dist/assets/adapters/next/templates/init/utils/dev-mode/parse-dev-mode-snippets.ts +9 -0
- package/dist/assets/adapters/next/templates/init/utils/editor/set-task-item-checked.ts +13 -0
- package/dist/assets/adapters/next/templates/init/utils/editor/update-nodes-attr.ts +2 -1
- package/dist/assets/adapters/next/templates/init/utils/email/email-theme.ts +13 -0
- package/dist/assets/adapters/next/templates/init/utils/email/render-form-submission-rows.ts +3 -2
- package/dist/assets/adapters/next/templates/init/utils/email/render-notification-email.ts +7 -6
- package/dist/assets/adapters/next/templates/init/utils/email/render-thank-you-email.ts +5 -4
- package/dist/assets/adapters/next/templates/init/utils/error/read-string-property.ts +4 -2
- package/dist/assets/adapters/next/templates/init/utils/error/recognize.ts +2 -4
- package/dist/assets/adapters/next/templates/init/utils/error/to-action-error.ts +0 -1
- package/dist/assets/adapters/next/templates/init/utils/form/clone-default-value.ts +4 -2
- package/dist/assets/adapters/next/templates/init/utils/shared/is-record.ts +3 -0
- package/dist/assets/adapters/next/templates/init/utils/table/default-get-id.ts +3 -2
- package/dist/assets/shared-assets/react-admin/custom/content-editor/editor-toolbar.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +5 -3
- package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node.tsx +3 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/task-item-node/task-item-node-extension.tsx +6 -15
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +1 -6
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +1 -6
- package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +9 -2
- package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +3 -3
- package/dist/assets/shared-assets/react-admin/custom/media-field.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/custom/nested-object-list-field.tsx +3 -1
- package/dist/assets/shared-assets/react-admin/custom/progressive-blur.tsx +3 -1
- package/dist/assets/shared-assets/react-admin/ui/badge.tsx +1 -2
- package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +20 -23
- package/dist/assets/shared-assets/react-admin/ui/form.tsx +6 -3
- package/dist/assets/shared-assets/react-admin/ui/input.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/native-select.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/select.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/textarea.tsx +1 -1
- package/dist/{chunk-FGEVDA53.js → chunk-BNHFSHPE.js} +12 -1
- package/dist/chunk-BNHFSHPE.js.map +1 -0
- package/dist/cli.js +909 -598
- package/dist/cli.js.map +1 -1
- package/dist/{read-namespaced-template-KUENIIDC.js → read-namespaced-template-WYIGUH7S.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-FGEVDA53.js.map +0 -1
- /package/dist/{read-namespaced-template-KUENIIDC.js.map → read-namespaced-template-WYIGUH7S.js.map} +0 -0
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuditLogAction,
|
|
3
|
+
AuditLogCategory,
|
|
4
|
+
AuditLogSourceType
|
|
5
|
+
} from '@admin/actions/audit/types'
|
|
1
6
|
import { sql } from 'drizzle-orm'
|
|
2
7
|
import {
|
|
3
8
|
boolean,
|
|
@@ -193,8 +198,8 @@ export const auditLog = pgTable(
|
|
|
193
198
|
'AuditLog',
|
|
194
199
|
{
|
|
195
200
|
id: uuid().defaultRandom().primaryKey().notNull(),
|
|
196
|
-
category: varchar('category', { length: 50 }).notNull(),
|
|
197
|
-
action: varchar('action', { length: 50 }).notNull(),
|
|
201
|
+
category: varchar('category', { length: 50 }).notNull().$type<AuditLogCategory>(),
|
|
202
|
+
action: varchar('action', { length: 50 }).notNull().$type<AuditLogAction>(),
|
|
198
203
|
targetType: varchar('target_type', { length: 100 }).notNull(),
|
|
199
204
|
targetId: text('target_id'),
|
|
200
205
|
targetLabel: text('target_label'),
|
|
@@ -204,14 +209,17 @@ export const auditLog = pgTable(
|
|
|
204
209
|
actorEmail: text('actor_email'),
|
|
205
210
|
actorName: text('actor_name'),
|
|
206
211
|
actorRole: text('actor_role'),
|
|
207
|
-
sourceType: varchar('source_type', { length: 50 })
|
|
212
|
+
sourceType: varchar('source_type', { length: 50 })
|
|
213
|
+
.notNull()
|
|
214
|
+
.default('server_action')
|
|
215
|
+
.$type<AuditLogSourceType>(),
|
|
208
216
|
ipAddress: varchar('ip_address', { length: 100 }),
|
|
209
217
|
userAgent: varchar('user_agent', { length: 500 }),
|
|
210
218
|
changedFields: text('changed_fields')
|
|
211
219
|
.array()
|
|
212
220
|
.notNull()
|
|
213
221
|
.default(sql`ARRAY[]::text[]`),
|
|
214
|
-
metadata: jsonb('metadata'),
|
|
222
|
+
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
|
|
215
223
|
createdAt: timestamp('created_at', { precision: 3, mode: 'string' })
|
|
216
224
|
.default(sql`CURRENT_TIMESTAMP`)
|
|
217
225
|
.notNull()
|
|
@@ -40,11 +40,11 @@ export function registerHook<Event extends LifecycleHookEvent>(
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function getRegisteredHooks
|
|
43
|
+
export function getRegisteredHooks(
|
|
44
44
|
entityType: string,
|
|
45
|
-
event:
|
|
46
|
-
): LifecycleHookHandler<
|
|
47
|
-
return [...(
|
|
45
|
+
event: LifecycleHookEvent
|
|
46
|
+
): LifecycleHookHandler<LifecycleHookEvent>[] {
|
|
47
|
+
return [...(lifecycleHooks.get(entityType)?.get(event) ?? [])]
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function getLifecycleHookSummary(): Record<
|
|
@@ -5,15 +5,11 @@ import type {
|
|
|
5
5
|
LifecycleHookAbort,
|
|
6
6
|
LifecycleHookContext
|
|
7
7
|
} from './types'
|
|
8
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
8
9
|
import { getRegisteredAdminHooks, getRegisteredHooks } from './registry'
|
|
9
10
|
|
|
10
11
|
function isLifecycleHookAbort(value: unknown): value is LifecycleHookAbort {
|
|
11
|
-
return (
|
|
12
|
-
typeof value === 'object' &&
|
|
13
|
-
value !== null &&
|
|
14
|
-
'abort' in value &&
|
|
15
|
-
(value as { abort: unknown }).abort === true
|
|
16
|
-
)
|
|
12
|
+
return isRecord(value) && value.abort === true
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
function createAbortError(event: BeforeLifecycleHookEvent, context: LifecycleHookContext): Error {
|
|
@@ -98,7 +94,7 @@ function toAdminEventContext(
|
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
function runSettledHook<Context>(
|
|
101
|
-
handler: (context: Context) => void
|
|
97
|
+
handler: (context: Context) => void,
|
|
102
98
|
context: Context
|
|
103
99
|
): Promise<void> {
|
|
104
100
|
try {
|
|
@@ -10,6 +10,7 @@ import { Field, FieldGroup, FieldLabel } from '@admin/components/ui/field'
|
|
|
10
10
|
import { Form } from '@admin/components/ui/form'
|
|
11
11
|
import { Input } from '@admin/components/ui/input'
|
|
12
12
|
import { Spinner } from '@admin/components/ui/spinner'
|
|
13
|
+
import { useFieldErrorVisible } from '@admin/hooks/use-field-error-visible'
|
|
13
14
|
import { cn } from '@admin/utils/shared/cn'
|
|
14
15
|
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
15
16
|
import Image from 'next/image'
|
|
@@ -33,6 +34,8 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
|
|
|
33
34
|
reValidateMode: 'onChange'
|
|
34
35
|
})
|
|
35
36
|
|
|
37
|
+
const isEmailErrorVisible = useFieldErrorVisible(form, 'email')
|
|
38
|
+
|
|
36
39
|
const handleSubmit = (values: ForgotPasswordFormValues) => {
|
|
37
40
|
startTransition(async () => {
|
|
38
41
|
try {
|
|
@@ -68,13 +71,7 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
|
|
|
68
71
|
<Card className="overflow-hidden p-0">
|
|
69
72
|
<CardContent className="grid grid-cols-[5fr_7fr] p-0">
|
|
70
73
|
<div className="relative block bg-muted">
|
|
71
|
-
<Image
|
|
72
|
-
src="https://assets.betterstart.dev/assets/placeholder.png"
|
|
73
|
-
alt=""
|
|
74
|
-
fill
|
|
75
|
-
sizes="42vw"
|
|
76
|
-
className="object-cover"
|
|
77
|
-
/>
|
|
74
|
+
<Image src="/placeholder.png" alt="" fill sizes="42vw" className="object-cover" />
|
|
78
75
|
</div>
|
|
79
76
|
<div className="p-20">
|
|
80
77
|
<Form {...form}>
|
|
@@ -99,7 +96,7 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
|
|
|
99
96
|
type="email"
|
|
100
97
|
autoComplete="off"
|
|
101
98
|
placeholder="m@example.com"
|
|
102
|
-
aria-invalid={
|
|
99
|
+
aria-invalid={isEmailErrorVisible}
|
|
103
100
|
{...form.register('email')}
|
|
104
101
|
required
|
|
105
102
|
disabled={isPending}
|
|
@@ -2,8 +2,8 @@ export function ForgotPasswordPageSkeleton() {
|
|
|
2
2
|
return (
|
|
3
3
|
<div className="overflow-hidden rounded-sm border bg-card">
|
|
4
4
|
<div className="grid grid-cols-[5fr_7fr]">
|
|
5
|
-
<div className="block min-h-
|
|
6
|
-
<div className="flex min-h-
|
|
5
|
+
<div className="block min-h-auth-forgot-panel bg-muted" />
|
|
6
|
+
<div className="flex min-h-auth-forgot-panel flex-col gap-10 p-20">
|
|
7
7
|
<div className="space-y-3">
|
|
8
8
|
<div className="h-6 w-40 animate-pulse rounded-md bg-muted" />
|
|
9
9
|
<div className="h-4 w-full max-w-72 animate-pulse rounded-md bg-muted" />
|
|
@@ -9,6 +9,7 @@ import { Field, FieldGroup, FieldLabel } from '@admin/components/ui/field'
|
|
|
9
9
|
import { Form } from '@admin/components/ui/form'
|
|
10
10
|
import { Input } from '@admin/components/ui/input'
|
|
11
11
|
import { Spinner } from '@admin/components/ui/spinner'
|
|
12
|
+
import { useFieldErrorVisible } from '@admin/hooks/use-field-error-visible'
|
|
12
13
|
import { getAdminPostLoginPath } from '@admin/utils/auth/get-admin-post-login-path'
|
|
13
14
|
import { cn } from '@admin/utils/shared/cn'
|
|
14
15
|
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
@@ -41,6 +42,9 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
41
42
|
reValidateMode: 'onChange'
|
|
42
43
|
})
|
|
43
44
|
|
|
45
|
+
const isEmailErrorVisible = useFieldErrorVisible(form, 'email')
|
|
46
|
+
const isPasswordErrorVisible = useFieldErrorVisible(form, 'password')
|
|
47
|
+
|
|
44
48
|
React.useEffect(() => {
|
|
45
49
|
if (reset === 'success') {
|
|
46
50
|
toast.success('Password reset. Sign in with your new password.', {
|
|
@@ -83,13 +87,7 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
83
87
|
<Card className="overflow-hidden p-0">
|
|
84
88
|
<CardContent className="grid grid-cols-[5fr_7fr] p-0">
|
|
85
89
|
<div className="relative block bg-muted">
|
|
86
|
-
<Image
|
|
87
|
-
src="https://assets.betterstart.dev/assets/placeholder.png"
|
|
88
|
-
alt=""
|
|
89
|
-
fill
|
|
90
|
-
sizes="42vw"
|
|
91
|
-
className="object-cover"
|
|
92
|
-
/>
|
|
90
|
+
<Image src="/placeholder.png" alt="" fill sizes="42vw" className="object-cover" />
|
|
93
91
|
</div>
|
|
94
92
|
<Form {...form}>
|
|
95
93
|
<form
|
|
@@ -110,7 +108,7 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
110
108
|
type="email"
|
|
111
109
|
autoComplete="off"
|
|
112
110
|
placeholder="m@example.com"
|
|
113
|
-
aria-invalid={
|
|
111
|
+
aria-invalid={isEmailErrorVisible}
|
|
114
112
|
{...form.register('email')}
|
|
115
113
|
required
|
|
116
114
|
disabled={isPending}
|
|
@@ -132,7 +130,7 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
132
130
|
type="password"
|
|
133
131
|
autoComplete="off"
|
|
134
132
|
placeholder="Enter your password"
|
|
135
|
-
aria-invalid={
|
|
133
|
+
aria-invalid={isPasswordErrorVisible}
|
|
136
134
|
{...form.register('password')}
|
|
137
135
|
required
|
|
138
136
|
disabled={isPending}
|
|
@@ -2,8 +2,8 @@ export function LoginPageSkeleton() {
|
|
|
2
2
|
return (
|
|
3
3
|
<div className="overflow-hidden rounded-sm border bg-card">
|
|
4
4
|
<div className="grid grid-cols-[5fr_7fr]">
|
|
5
|
-
<div className="block min-h-
|
|
6
|
-
<div className="flex min-h-
|
|
5
|
+
<div className="block min-h-auth-login-panel bg-muted" />
|
|
6
|
+
<div className="flex min-h-auth-login-panel flex-col gap-10 p-20">
|
|
7
7
|
<div className="space-y-3">
|
|
8
8
|
<div className="h-6 w-32 animate-pulse rounded-md bg-muted" />
|
|
9
9
|
<div className="h-4 w-40 animate-pulse rounded-md bg-muted" />
|
|
@@ -9,6 +9,7 @@ import { Field, FieldGroup, FieldLabel } from '@admin/components/ui/field'
|
|
|
9
9
|
import { Form } from '@admin/components/ui/form'
|
|
10
10
|
import { Input } from '@admin/components/ui/input'
|
|
11
11
|
import { Spinner } from '@admin/components/ui/spinner'
|
|
12
|
+
import { useFieldErrorVisible } from '@admin/hooks/use-field-error-visible'
|
|
12
13
|
import { cn } from '@admin/utils/shared/cn'
|
|
13
14
|
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
14
15
|
import Image from 'next/image'
|
|
@@ -49,19 +50,16 @@ export function ResetPasswordForm({ className, ...props }: React.ComponentProps<
|
|
|
49
50
|
reValidateMode: 'onChange'
|
|
50
51
|
})
|
|
51
52
|
|
|
53
|
+
const isNewPasswordErrorVisible = useFieldErrorVisible(form, 'newPassword')
|
|
54
|
+
const isConfirmPasswordErrorVisible = useFieldErrorVisible(form, 'confirmPassword')
|
|
55
|
+
|
|
52
56
|
if (!token) {
|
|
53
57
|
return (
|
|
54
58
|
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
|
55
59
|
<Card className="overflow-hidden p-0">
|
|
56
60
|
<CardContent className="grid grid-cols-[5fr_7fr] p-0">
|
|
57
61
|
<div className="relative block bg-muted">
|
|
58
|
-
<Image
|
|
59
|
-
src="https://assets.betterstart.dev/assets/placeholder.png"
|
|
60
|
-
alt=""
|
|
61
|
-
fill
|
|
62
|
-
sizes="42vw"
|
|
63
|
-
className="object-cover"
|
|
64
|
-
/>
|
|
62
|
+
<Image src="/placeholder.png" alt="" fill sizes="42vw" className="object-cover" />
|
|
65
63
|
</div>
|
|
66
64
|
<div className="p-20">
|
|
67
65
|
<FieldGroup className="gap-10 pb-12">
|
|
@@ -114,13 +112,7 @@ export function ResetPasswordForm({ className, ...props }: React.ComponentProps<
|
|
|
114
112
|
<Card className="overflow-hidden p-0">
|
|
115
113
|
<CardContent className="grid grid-cols-[5fr_7fr] p-0">
|
|
116
114
|
<div className="relative block bg-muted">
|
|
117
|
-
<Image
|
|
118
|
-
src="https://assets.betterstart.dev/assets/placeholder.png"
|
|
119
|
-
alt=""
|
|
120
|
-
fill
|
|
121
|
-
sizes="42vw"
|
|
122
|
-
className="object-cover"
|
|
123
|
-
/>
|
|
115
|
+
<Image src="/placeholder.png" alt="" fill sizes="42vw" className="object-cover" />
|
|
124
116
|
</div>
|
|
125
117
|
<Form {...form}>
|
|
126
118
|
<form
|
|
@@ -144,7 +136,7 @@ export function ResetPasswordForm({ className, ...props }: React.ComponentProps<
|
|
|
144
136
|
type="password"
|
|
145
137
|
autoComplete="off"
|
|
146
138
|
placeholder="Enter new password"
|
|
147
|
-
aria-invalid={
|
|
139
|
+
aria-invalid={isNewPasswordErrorVisible}
|
|
148
140
|
{...form.register('newPassword')}
|
|
149
141
|
required
|
|
150
142
|
disabled={isPending}
|
|
@@ -157,7 +149,7 @@ export function ResetPasswordForm({ className, ...props }: React.ComponentProps<
|
|
|
157
149
|
type="password"
|
|
158
150
|
autoComplete="off"
|
|
159
151
|
placeholder="Confirm new password"
|
|
160
|
-
aria-invalid={
|
|
152
|
+
aria-invalid={isConfirmPasswordErrorVisible}
|
|
161
153
|
{...form.register('confirmPassword')}
|
|
162
154
|
required
|
|
163
155
|
disabled={isPending}
|
|
@@ -2,8 +2,8 @@ export function ResetPasswordPageSkeleton() {
|
|
|
2
2
|
return (
|
|
3
3
|
<div className="overflow-hidden rounded-sm border bg-card">
|
|
4
4
|
<div className="grid grid-cols-[5fr_7fr]">
|
|
5
|
-
<div className="block min-h-
|
|
6
|
-
<div className="flex min-h-
|
|
5
|
+
<div className="block min-h-auth-reset-panel bg-muted" />
|
|
6
|
+
<div className="flex min-h-auth-reset-panel flex-col gap-10 p-20">
|
|
7
7
|
<div className="space-y-3">
|
|
8
8
|
<div className="h-6 w-36 animate-pulse rounded-md bg-muted" />
|
|
9
9
|
<div className="h-4 w-56 animate-pulse rounded-md bg-muted" />
|
package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-detail-row.tsx
CHANGED
|
@@ -7,7 +7,7 @@ interface AuditLogDetailRowProps {
|
|
|
7
7
|
|
|
8
8
|
export function AuditLogDetailRow({ label, value }: AuditLogDetailRowProps) {
|
|
9
9
|
return (
|
|
10
|
-
<div className="grid grid-cols-
|
|
10
|
+
<div className="grid grid-cols-audit-detail gap-3 border-b border-border px-4 py-2 last:border-b-0">
|
|
11
11
|
<dt className="text-sm font-medium text-muted-foreground">{label}</dt>
|
|
12
12
|
<dd className="min-w-0 text-sm break-words text-foreground">{value || 'Unknown'}</dd>
|
|
13
13
|
</div>
|
package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-table.tsx
CHANGED
|
@@ -248,12 +248,7 @@ export function AuditLogTable<TValue>({
|
|
|
248
248
|
<DataGridRow
|
|
249
249
|
key={row.id}
|
|
250
250
|
className="cursor-pointer hover:bg-muted/50"
|
|
251
|
-
|
|
252
|
-
const target = event.target as HTMLElement
|
|
253
|
-
if (!event.currentTarget.contains(target)) return
|
|
254
|
-
if (target.closest('button, a, input, [role="checkbox"]')) return
|
|
255
|
-
onRowClick(row.original)
|
|
256
|
-
}}
|
|
251
|
+
onActivate={() => onRowClick(row.original)}
|
|
257
252
|
>
|
|
258
253
|
{row.getVisibleCells().map((cell) => (
|
|
259
254
|
<DataGridCell
|
package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-table.tsx
CHANGED
|
@@ -36,7 +36,7 @@ export function FormsSettingsTable({
|
|
|
36
36
|
<div className={cn('flex h-full flex-col gap-6 pt-px pb-2', className)}>
|
|
37
37
|
<DataGrid
|
|
38
38
|
containerClassName="min-h-0 flex-1"
|
|
39
|
-
gridTemplateColumns="minmax(
|
|
39
|
+
gridTemplateColumns="minmax(var(--spacing-form-settings-name), 1fr) minmax(var(--spacing-form-settings-recipients), 2fr)"
|
|
40
40
|
>
|
|
41
41
|
<DataGridHeader>
|
|
42
42
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
@@ -57,12 +57,7 @@ export function FormsSettingsTable({
|
|
|
57
57
|
<DataGridRow
|
|
58
58
|
key={row.id}
|
|
59
59
|
className="cursor-pointer hover:bg-muted/50"
|
|
60
|
-
|
|
61
|
-
const target = event.target as HTMLElement
|
|
62
|
-
if (!event.currentTarget.contains(target)) return
|
|
63
|
-
if (target.closest('button, a, input, [role="checkbox"]')) return
|
|
64
|
-
onRowClick(row.original)
|
|
65
|
-
}}
|
|
60
|
+
onActivate={() => onRowClick(row.original)}
|
|
66
61
|
>
|
|
67
62
|
{row.getVisibleCells().map((cell) => (
|
|
68
63
|
<DataGridCell key={cell.id} data-column-id={cell.column.id}>
|
|
@@ -217,12 +217,7 @@ export function WebhooksTable<TValue>({
|
|
|
217
217
|
<DataGridRow
|
|
218
218
|
key={row.id}
|
|
219
219
|
className="cursor-pointer hover:bg-muted/50"
|
|
220
|
-
|
|
221
|
-
const target = e.target as HTMLElement
|
|
222
|
-
if (!e.currentTarget.contains(target)) return
|
|
223
|
-
if (target.closest('button, a, input, [role="checkbox"], [role="switch"]')) return
|
|
224
|
-
onRowClick(row.original)
|
|
225
|
-
}}
|
|
220
|
+
onActivate={() => onRowClick(row.original)}
|
|
226
221
|
>
|
|
227
222
|
{row.getVisibleCells().map((cell) => (
|
|
228
223
|
<DataGridCell
|
|
@@ -118,7 +118,7 @@ export function UsersTable<TValue>({
|
|
|
118
118
|
email: session.user.email,
|
|
119
119
|
name: session.user.name,
|
|
120
120
|
image: session.user.image,
|
|
121
|
-
role:
|
|
121
|
+
role: session.user.role || 'member'
|
|
122
122
|
}
|
|
123
123
|
: null
|
|
124
124
|
},
|
|
Binary file
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
2
|
+
|
|
1
3
|
export function normalizeComparableValue(value: unknown): string {
|
|
2
4
|
if (value instanceof Date) return value.toISOString()
|
|
3
5
|
if (Array.isArray(value)) {
|
|
4
6
|
return JSON.stringify(value.map((item) => normalizeComparableValue(item)))
|
|
5
7
|
}
|
|
6
|
-
if (value
|
|
7
|
-
const entries = Object.entries(value
|
|
8
|
+
if (isRecord(value)) {
|
|
9
|
+
const entries = Object.entries(value)
|
|
8
10
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
9
11
|
.map(([key, item]) => [key, normalizeComparableValue(item)])
|
|
10
12
|
return JSON.stringify(entries)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function formatActionDate(value: unknown): string {
|
|
2
|
+
if (typeof value !== 'string') return ''
|
|
3
|
+
const date = new Date(value)
|
|
4
|
+
if (Number.isNaN(date.getTime())) return ''
|
|
5
|
+
|
|
6
|
+
return date.toLocaleDateString('en-US', {
|
|
7
|
+
month: 'short',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
year: 'numeric',
|
|
10
|
+
timeZone: 'UTC'
|
|
11
|
+
})
|
|
12
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod/v3'
|
|
2
|
+
|
|
3
|
+
export const devModeCodeLanguageSchema = z.enum(['tsx', 'ts', 'json'])
|
|
4
|
+
|
|
5
|
+
export const devModeSnippetIconSchema = z.enum([
|
|
6
|
+
'eye',
|
|
7
|
+
'file-code',
|
|
8
|
+
'inbox',
|
|
9
|
+
'list',
|
|
10
|
+
'pencil',
|
|
11
|
+
'plus',
|
|
12
|
+
'save',
|
|
13
|
+
'send',
|
|
14
|
+
'trash'
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
export const devModeSnippetExampleSchema = z.object({
|
|
18
|
+
label: z.string(),
|
|
19
|
+
language: devModeCodeLanguageSchema,
|
|
20
|
+
code: z.string()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const devModeSnippetCodeTabSchema = z.object({
|
|
24
|
+
id: z.string(),
|
|
25
|
+
label: z.string(),
|
|
26
|
+
language: devModeCodeLanguageSchema,
|
|
27
|
+
code: z.string()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const devModeSnippetSchema = z.object({
|
|
31
|
+
id: z.string(),
|
|
32
|
+
label: z.string(),
|
|
33
|
+
kind: z.enum(['jsx', 'query', 'mutation']),
|
|
34
|
+
icon: devModeSnippetIconSchema,
|
|
35
|
+
language: devModeCodeLanguageSchema,
|
|
36
|
+
code: z.string(),
|
|
37
|
+
codeTabs: z.array(devModeSnippetCodeTabSchema).optional(),
|
|
38
|
+
payloadExample: devModeSnippetExampleSchema.optional(),
|
|
39
|
+
responseExample: devModeSnippetExampleSchema.optional()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const devModeLifecycleHookExampleSchema = z.object({
|
|
43
|
+
id: z.string(),
|
|
44
|
+
label: z.string(),
|
|
45
|
+
event: z.enum([
|
|
46
|
+
'beforeCreate',
|
|
47
|
+
'afterCreate',
|
|
48
|
+
'beforeUpdate',
|
|
49
|
+
'afterUpdate',
|
|
50
|
+
'beforeDelete',
|
|
51
|
+
'afterDelete',
|
|
52
|
+
'beforePublish',
|
|
53
|
+
'afterPublish',
|
|
54
|
+
'beforeUnpublish',
|
|
55
|
+
'afterUnpublish',
|
|
56
|
+
'beforeRestore',
|
|
57
|
+
'afterRestore',
|
|
58
|
+
'beforeReorder',
|
|
59
|
+
'afterReorder'
|
|
60
|
+
]),
|
|
61
|
+
phase: z.enum(['before', 'after']),
|
|
62
|
+
icon: devModeSnippetIconSchema,
|
|
63
|
+
language: devModeCodeLanguageSchema,
|
|
64
|
+
code: z.string(),
|
|
65
|
+
contextExample: devModeSnippetExampleSchema
|
|
66
|
+
})
|
package/dist/assets/adapters/next/templates/init/utils/dev-mode/parse-dev-mode-lifecycle-hooks.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DevModeLifecycleHookExample } from '@admin/components/shared/dev-mode/dev-mode-types'
|
|
2
|
+
import { devModeLifecycleHookExampleSchema } from '@admin/utils/dev-mode/dev-mode-json-schemas'
|
|
3
|
+
import { z } from 'zod/v3'
|
|
4
|
+
|
|
5
|
+
const lifecycleHooksSchema = z.array(devModeLifecycleHookExampleSchema)
|
|
6
|
+
|
|
7
|
+
export function parseDevModeLifecycleHooks<Raw>(
|
|
8
|
+
values: readonly Raw[]
|
|
9
|
+
): DevModeLifecycleHookExample[] {
|
|
10
|
+
return lifecycleHooksSchema.parse(values)
|
|
11
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DevModeSnippet } from '@admin/components/shared/dev-mode/dev-mode-types'
|
|
2
|
+
import { devModeSnippetSchema } from '@admin/utils/dev-mode/dev-mode-json-schemas'
|
|
3
|
+
import { z } from 'zod/v3'
|
|
4
|
+
|
|
5
|
+
const snippetsSchema = z.array(devModeSnippetSchema)
|
|
6
|
+
|
|
7
|
+
export function parseDevModeSnippets<Raw>(values: readonly Raw[]): DevModeSnippet[] {
|
|
8
|
+
return snippetsSchema.parse(values)
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Transaction } from '@tiptap/pm/state'
|
|
2
|
+
|
|
3
|
+
export function setTaskItemChecked(
|
|
4
|
+
tr: Transaction,
|
|
5
|
+
position: number | undefined,
|
|
6
|
+
checked: boolean
|
|
7
|
+
): boolean {
|
|
8
|
+
if (typeof position !== 'number') return false
|
|
9
|
+
|
|
10
|
+
const taskItem = tr.doc.nodeAt(position)
|
|
11
|
+
tr.setNodeMarkup(position, undefined, { ...taskItem?.attrs, checked })
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
@@ -15,7 +15,8 @@ export function updateNodesAttr<A extends string = string, V = unknown>(
|
|
|
15
15
|
const currentNode = tr.doc.nodeAt(pos)
|
|
16
16
|
if (!currentNode) continue
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const attrs: Record<string, unknown> = currentNode.attrs
|
|
19
|
+
const prevValue = attrs[attrName] as V | undefined
|
|
19
20
|
const resolvedNext =
|
|
20
21
|
typeof next === 'function' ? (next as (p: V | undefined) => V | undefined)(prevValue) : next
|
|
21
22
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const emailTheme = {
|
|
2
|
+
pageBackground: '#f8fafc',
|
|
3
|
+
surface: '#ffffff',
|
|
4
|
+
border: '#e2e8f0',
|
|
5
|
+
headerBackground: '#0f172a',
|
|
6
|
+
text: '#0f172a',
|
|
7
|
+
bodyText: '#334155',
|
|
8
|
+
mutedText: '#475569',
|
|
9
|
+
inverseText: '#ffffff',
|
|
10
|
+
mutedInverseText: '#cbd5e1',
|
|
11
|
+
compactWidth: '560px',
|
|
12
|
+
contentWidth: '640px'
|
|
13
|
+
} as const
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emailTheme } from '@admin/utils/email/email-theme'
|
|
1
2
|
import { escapeHtml } from '@admin/utils/email/escape-html'
|
|
2
3
|
import { formatEmailValue } from '@admin/utils/email/format-email-value'
|
|
3
4
|
|
|
@@ -8,8 +9,8 @@ export function renderFormSubmissionRows(submissionData: Record<string, unknown>
|
|
|
8
9
|
const displayValue = escapeHtml(formatEmailValue(value))
|
|
9
10
|
|
|
10
11
|
return `<tr>
|
|
11
|
-
<th style="border-bottom:1px solid
|
|
12
|
-
<td style="border-bottom:1px solid
|
|
12
|
+
<th style="border-bottom:1px solid ${emailTheme.border};color:${emailTheme.mutedText};font-size:12px;font-weight:600;padding:12px;text-align:left;text-transform:uppercase;vertical-align:top;width:36%;">${label}</th>
|
|
13
|
+
<td style="border-bottom:1px solid ${emailTheme.border};color:${emailTheme.text};font-size:14px;line-height:20px;padding:12px;vertical-align:top;">${displayValue}</td>
|
|
13
14
|
</tr>`
|
|
14
15
|
})
|
|
15
16
|
.join('\n')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FormEmailDeliveryInput } from '@admin/utils/email/form-email-delivery-input'
|
|
2
|
+
import { emailTheme } from '@admin/utils/email/email-theme'
|
|
2
3
|
import { escapeHtml } from '@admin/utils/email/escape-html'
|
|
3
4
|
import { renderFormSubmissionRows } from '@admin/utils/email/render-form-submission-rows'
|
|
4
5
|
|
|
@@ -10,15 +11,15 @@ export function renderNotificationEmail(input: FormEmailDeliveryInput): string {
|
|
|
10
11
|
|
|
11
12
|
return `<!doctype html>
|
|
12
13
|
<html>
|
|
13
|
-
<body style="margin:0;background
|
|
14
|
-
<div style="margin:0 auto;max-width
|
|
15
|
-
<div style="border-radius:12px;background
|
|
16
|
-
<div style="background
|
|
17
|
-
<p style="margin:0;color
|
|
14
|
+
<body style="margin:0;background:${emailTheme.pageBackground};font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:${emailTheme.text};">
|
|
15
|
+
<div style="margin:0 auto;max-width:${emailTheme.contentWidth};padding:40px 20px;">
|
|
16
|
+
<div style="border-radius:12px;background:${emailTheme.surface};border:1px solid ${emailTheme.border};overflow:hidden;">
|
|
17
|
+
<div style="background:${emailTheme.headerBackground};color:${emailTheme.inverseText};padding:24px 28px;">
|
|
18
|
+
<p style="margin:0;color:${emailTheme.mutedInverseText};font-size:13px;line-height:20px;">${formName}</p>
|
|
18
19
|
<h1 style="margin:4px 0 0;font-size:24px;line-height:32px;">New ${formLabel} submission</h1>
|
|
19
20
|
</div>
|
|
20
21
|
<div style="padding:24px 28px;">
|
|
21
|
-
<p style="margin:0 0 20px;color
|
|
22
|
+
<p style="margin:0 0 20px;color:${emailTheme.mutedText};font-size:14px;line-height:20px;">Submitted at ${submittedAt}</p>
|
|
22
23
|
<table cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;width:100%;">
|
|
23
24
|
<tbody>
|
|
24
25
|
${rows}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FormEmailDeliveryInput } from '@admin/utils/email/form-email-delivery-input'
|
|
2
|
+
import { emailTheme } from '@admin/utils/email/email-theme'
|
|
2
3
|
import { escapeHtml } from '@admin/utils/email/escape-html'
|
|
3
4
|
|
|
4
5
|
export function renderThankYouEmail(input: FormEmailDeliveryInput): string {
|
|
@@ -6,11 +7,11 @@ export function renderThankYouEmail(input: FormEmailDeliveryInput): string {
|
|
|
6
7
|
|
|
7
8
|
return `<!doctype html>
|
|
8
9
|
<html>
|
|
9
|
-
<body style="margin:0;background
|
|
10
|
-
<div style="margin:0 auto;max-width
|
|
11
|
-
<div style="border-radius:12px;background
|
|
10
|
+
<body style="margin:0;background:${emailTheme.pageBackground};font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:${emailTheme.text};">
|
|
11
|
+
<div style="margin:0 auto;max-width:${emailTheme.compactWidth};padding:40px 20px;">
|
|
12
|
+
<div style="border-radius:12px;background:${emailTheme.surface};padding:32px;border:1px solid ${emailTheme.border};">
|
|
12
13
|
<h1 style="margin:0 0 16px;font-size:24px;line-height:32px;">Thank you</h1>
|
|
13
|
-
<p style="margin:0;color
|
|
14
|
+
<p style="margin:0;color:${emailTheme.bodyText};font-size:16px;line-height:24px;">We received your ${formLabel} submission and will get back to you shortly.</p>
|
|
14
15
|
</div>
|
|
15
16
|
</div>
|
|
16
17
|
</body>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
2
|
+
|
|
1
3
|
export function readStringProperty(value: unknown, key: string): string | undefined {
|
|
2
|
-
if (
|
|
3
|
-
const property =
|
|
4
|
+
if (!isRecord(value)) return undefined
|
|
5
|
+
const property = value[key]
|
|
4
6
|
return typeof property === 'string' ? property : undefined
|
|
5
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readStringProperty } from '@admin/utils/error/read-string-property'
|
|
2
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
2
3
|
|
|
3
4
|
const POSTGRES_MESSAGES: Record<string, string> = {
|
|
4
5
|
'23502': 'A required value is missing.',
|
|
@@ -13,10 +14,7 @@ const AUTH_MESSAGES: Record<string, string> = {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function recognize(error: unknown): string | undefined {
|
|
16
|
-
const authCode = readStringProperty(
|
|
17
|
-
(error as { body?: unknown } | null)?.body ?? undefined,
|
|
18
|
-
'code'
|
|
19
|
-
)
|
|
17
|
+
const authCode = readStringProperty(isRecord(error) ? error.body : undefined, 'code')
|
|
20
18
|
if (authCode && AUTH_MESSAGES[authCode]) return AUTH_MESSAGES[authCode]
|
|
21
19
|
|
|
22
20
|
const sqlState = readStringProperty(error, 'code')
|