betterstart-cli 0.0.117 → 0.0.118
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 +20 -0
- 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/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/navigation-menu.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
|
@@ -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')
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
2
|
+
|
|
1
3
|
export function cloneDefaultValue(value: unknown): unknown {
|
|
2
4
|
if (Array.isArray(value)) return value.map(cloneDefaultValue)
|
|
3
5
|
|
|
4
|
-
if (value
|
|
5
|
-
const clonedEntries = Object.entries(value
|
|
6
|
+
if (isRecord(value)) {
|
|
7
|
+
const clonedEntries = Object.entries(value).map(
|
|
6
8
|
([key, childValue]) => [key, cloneDefaultValue(childValue)] as const
|
|
7
9
|
)
|
|
8
10
|
return Object.fromEntries(clonedEntries)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RowData } from '@tanstack/react-table'
|
|
2
|
+
import { isRecord } from '@admin/utils/shared/is-record'
|
|
2
3
|
|
|
3
4
|
export function defaultGetId<TData extends RowData>(row: TData): string | null | undefined {
|
|
4
|
-
|
|
5
|
-
return typeof id === 'string' ? id : null
|
|
5
|
+
if (!isRecord(row)) return null
|
|
6
|
+
return typeof row.id === 'string' ? row.id : null
|
|
6
7
|
}
|
|
@@ -15,7 +15,7 @@ export function EditorToolbar({
|
|
|
15
15
|
role="toolbar"
|
|
16
16
|
aria-label={ariaLabel}
|
|
17
17
|
className={cn(
|
|
18
|
-
'sticky top-0 z-10 flex min-h-12 w-full items-center gap-1 overflow-x-auto overscroll-x-contain border-b border-border bg-card px-2 [scrollbar-width:none] max-
|
|
18
|
+
'sticky top-0 z-10 flex min-h-12 w-full items-center gap-1 overflow-x-auto overscroll-x-contain border-b border-border bg-card px-2 [scrollbar-width:none] max-xs:absolute max-xs:top-auto max-xs:h-[calc(2.75rem+env(safe-area-inset-bottom,0px))] max-xs:flex-nowrap max-xs:justify-start max-xs:border-t max-xs:border-b-0 max-xs:pb-[env(safe-area-inset-bottom,0px)] [&::-webkit-scrollbar]:hidden',
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
>
|
|
@@ -33,6 +33,10 @@ export function SlashCommandMenu({
|
|
|
33
33
|
searchQuery: string
|
|
34
34
|
}) {
|
|
35
35
|
const activeItemRef = React.useRef<HTMLDivElement | null>(null)
|
|
36
|
+
|
|
37
|
+
function assignActiveItem(node: HTMLDivElement | null) {
|
|
38
|
+
activeItemRef.current = node
|
|
39
|
+
}
|
|
36
40
|
const searchInputRef = React.useRef<HTMLInputElement | null>(null)
|
|
37
41
|
const activeCommandId = commands[activeIndex]?.id
|
|
38
42
|
|
|
@@ -159,9 +163,7 @@ export function SlashCommandMenu({
|
|
|
159
163
|
index={index}
|
|
160
164
|
key={command.id}
|
|
161
165
|
data-active={isActive ? 'true' : undefined}
|
|
162
|
-
ref={
|
|
163
|
-
if (isActive) activeItemRef.current = node
|
|
164
|
-
}}
|
|
166
|
+
ref={isActive ? assignActiveItem : undefined}
|
|
165
167
|
onMouseDown={(event) => event.preventDefault()}
|
|
166
168
|
onMouseEnter={() => onActiveIndexChange(index)}
|
|
167
169
|
value={command}
|
|
@@ -120,7 +120,7 @@ export function SourceMode({
|
|
|
120
120
|
return (
|
|
121
121
|
<div
|
|
122
122
|
className={cn(
|
|
123
|
-
'content-editor-wrapper relative flex max-h-[90vh] min-h-
|
|
123
|
+
'content-editor-wrapper relative flex max-h-[90vh] min-h-source-editor resize-y flex-col overflow-hidden rounded-md border bg-background',
|
|
124
124
|
{ 'opacity-50': disabled },
|
|
125
125
|
className
|
|
126
126
|
)}
|
|
@@ -14,6 +14,8 @@ import { cn } from '@admin/utils/shared/cn'
|
|
|
14
14
|
import { NodeViewWrapper } from '@tiptap/react'
|
|
15
15
|
import NextImage from 'next/image'
|
|
16
16
|
|
|
17
|
+
const EDITOR_IMAGE_SIZES = '768px'
|
|
18
|
+
|
|
17
19
|
export const RemovableImageNode: React.FC<NodeViewProps> = (props) => {
|
|
18
20
|
const { editor, node } = props
|
|
19
21
|
const src = getStringAttr(node.attrs.src)
|
|
@@ -118,7 +120,7 @@ export const RemovableImageNode: React.FC<NodeViewProps> = (props) => {
|
|
|
118
120
|
alt={alt}
|
|
119
121
|
title={title || undefined}
|
|
120
122
|
fill
|
|
121
|
-
sizes=
|
|
123
|
+
sizes={EDITOR_IMAGE_SIZES}
|
|
122
124
|
unoptimized
|
|
123
125
|
className="!m-0 !h-full !w-full !max-w-full object-contain"
|
|
124
126
|
onLoad={handleImageLoad}
|
|
@@ -5,6 +5,7 @@ import { getTaskItemCheckboxLabel } from '@admin/utils/editor/get-task-item-chec
|
|
|
5
5
|
import { hasOwnAttribute } from '@admin/utils/editor/has-own-attribute'
|
|
6
6
|
import { isTaskItemChecked } from '@admin/utils/editor/is-task-item-checked'
|
|
7
7
|
import { setElementAttribute } from '@admin/utils/editor/set-element-attribute'
|
|
8
|
+
import { setTaskItemChecked } from '@admin/utils/editor/set-task-item-checked'
|
|
8
9
|
import { getRenderedAttributes } from '@tiptap/core'
|
|
9
10
|
import { TaskItem } from '@tiptap/extension-list'
|
|
10
11
|
import { createRoot } from 'react-dom/client'
|
|
@@ -20,7 +21,7 @@ export const ContentEditorTaskItem = TaskItem.extend({
|
|
|
20
21
|
const checkboxMount = document.createElement('span')
|
|
21
22
|
const content = document.createElement('div')
|
|
22
23
|
const checkboxRoot = createRoot(checkboxMount)
|
|
23
|
-
const staticAttributes = this.options.HTMLAttributes
|
|
24
|
+
const staticAttributes: Record<string, unknown> = this.options.HTMLAttributes
|
|
24
25
|
|
|
25
26
|
checkboxContainer.contentEditable = 'false'
|
|
26
27
|
checkboxContainer.className = 'content-editor-task-item-checkbox'
|
|
@@ -52,17 +53,7 @@ export const ContentEditorTaskItem = TaskItem.extend({
|
|
|
52
53
|
editor
|
|
53
54
|
.chain()
|
|
54
55
|
.focus(undefined, { scrollIntoView: false })
|
|
55
|
-
.command(({ tr }) =>
|
|
56
|
-
const position = getPos()
|
|
57
|
-
if (typeof position !== 'number') return false
|
|
58
|
-
|
|
59
|
-
const taskItem = tr.doc.nodeAt(position)
|
|
60
|
-
tr.setNodeMarkup(position, undefined, {
|
|
61
|
-
...taskItem?.attrs,
|
|
62
|
-
checked
|
|
63
|
-
})
|
|
64
|
-
return true
|
|
65
|
-
})
|
|
56
|
+
.command(({ tr }) => setTaskItemChecked(tr, getPos(), checked))
|
|
66
57
|
.run()
|
|
67
58
|
return
|
|
68
59
|
}
|
|
@@ -89,7 +80,7 @@ export const ContentEditorTaskItem = TaskItem.extend({
|
|
|
89
80
|
listItem.dataset.checked = String(renderedChecked)
|
|
90
81
|
}
|
|
91
82
|
|
|
92
|
-
applyAttributes(HTMLAttributes
|
|
83
|
+
applyAttributes(HTMLAttributes)
|
|
93
84
|
renderCheckbox()
|
|
94
85
|
|
|
95
86
|
let prevRenderedAttributeKeys = new Set(Object.keys(HTMLAttributes))
|
|
@@ -104,10 +95,10 @@ export const ContentEditorTaskItem = TaskItem.extend({
|
|
|
104
95
|
renderedChecked = isTaskItemChecked(updatedNode)
|
|
105
96
|
|
|
106
97
|
const extensionAttributes = editor.extensionManager.attributes
|
|
107
|
-
const newHTMLAttributes = getRenderedAttributes(
|
|
98
|
+
const newHTMLAttributes: Record<string, unknown> = getRenderedAttributes(
|
|
108
99
|
updatedNode,
|
|
109
100
|
extensionAttributes
|
|
110
|
-
)
|
|
101
|
+
)
|
|
111
102
|
const newKeys = new Set(Object.keys(newHTMLAttributes))
|
|
112
103
|
|
|
113
104
|
prevRenderedAttributeKeys.forEach((key) => {
|
|
@@ -93,12 +93,7 @@ export function HeadingDropdownMenu({
|
|
|
93
93
|
}
|
|
94
94
|
></DropdownMenuTrigger>
|
|
95
95
|
|
|
96
|
-
<DropdownMenuContent
|
|
97
|
-
align="start"
|
|
98
|
-
className="w-auto min-w-40"
|
|
99
|
-
finalFocus={false}
|
|
100
|
-
portalled={false}
|
|
101
|
-
>
|
|
96
|
+
<DropdownMenuContent align="start" className="w-auto min-w-40" finalFocus={false}>
|
|
102
97
|
<DropdownMenuGroup>
|
|
103
98
|
{levels.map((level) => (
|
|
104
99
|
<HeadingDropdownMenuItem key={`heading-${level}`} editor={editor} level={level} />
|
|
@@ -83,12 +83,7 @@ export function ListDropdownMenu({
|
|
|
83
83
|
<ChevronDownIcon className="tiptap-button-dropdown-small" />
|
|
84
84
|
</DropdownMenuTrigger>
|
|
85
85
|
|
|
86
|
-
<DropdownMenuContent
|
|
87
|
-
align="start"
|
|
88
|
-
className="w-auto min-w-40"
|
|
89
|
-
finalFocus={false}
|
|
90
|
-
portalled={false}
|
|
91
|
-
>
|
|
86
|
+
<DropdownMenuContent align="start" className="w-auto min-w-40" finalFocus={false}>
|
|
92
87
|
<DropdownMenuGroup>
|
|
93
88
|
{filteredLists.map((option) => (
|
|
94
89
|
<ListDropdownMenuItem
|
|
@@ -132,14 +132,21 @@ export function DateRangePicker({
|
|
|
132
132
|
>
|
|
133
133
|
<PopoverTrigger
|
|
134
134
|
render={
|
|
135
|
-
<Button
|
|
135
|
+
<Button
|
|
136
|
+
variant="outline"
|
|
137
|
+
className={cn('w-date-range-trigger', className)}
|
|
138
|
+
disabled={disabled}
|
|
139
|
+
/>
|
|
136
140
|
}
|
|
137
141
|
>
|
|
138
142
|
<CalendarIcon />
|
|
139
143
|
<span className="min-w-0 flex-1 truncate text-left tabular-nums">{triggerLabel}</span>
|
|
140
144
|
<ChevronDown />
|
|
141
145
|
</PopoverTrigger>
|
|
142
|
-
<PopoverContent
|
|
146
|
+
<PopoverContent
|
|
147
|
+
className="w-date-range-popover gap-0 overflow-hidden rounded-sm p-0"
|
|
148
|
+
align="start"
|
|
149
|
+
>
|
|
143
150
|
<Calendar
|
|
144
151
|
mode="range"
|
|
145
152
|
month={calendarMonth}
|