betterstart-cli 0.0.116 → 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.
Files changed (85) hide show
  1. package/dist/assets/adapters/next/templates/init/admin-globals.css +20 -0
  2. package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid-row.tsx +33 -1
  3. package/dist/assets/adapters/next/templates/init/components/shared/media/media-gallery-dialog.tsx +3 -3
  4. package/dist/assets/adapters/next/templates/init/components/shared/media/media-preview.tsx +3 -1
  5. package/dist/assets/adapters/next/templates/init/hooks/use-copy-to-clipboard.ts +1 -3
  6. package/dist/assets/adapters/next/templates/init/hooks/use-field-error-visible.ts +25 -0
  7. package/dist/assets/adapters/next/templates/init/hooks/use-media.ts +1 -1
  8. package/dist/assets/adapters/next/templates/init/hooks/use-upload.ts +11 -5
  9. package/dist/assets/adapters/next/templates/init/lib/actions/audit/cleanup-audit-logs.ts +1 -0
  10. package/dist/assets/adapters/next/templates/init/lib/actions/audit/query-audit-logs.ts +2 -2
  11. package/dist/assets/adapters/next/templates/init/lib/actions/audit/record-audit-event.ts +1 -2
  12. package/dist/assets/adapters/next/templates/init/lib/actions/auth/client.ts +4 -1
  13. package/dist/assets/adapters/next/templates/init/lib/actions/entity-versions/get-entity-versions.ts +5 -1
  14. package/dist/assets/adapters/next/templates/init/lib/actions/forms/get-all-form-settings.ts +1 -1
  15. package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +2 -1
  16. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +1 -0
  17. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +1 -0
  18. package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +2 -4
  19. package/dist/assets/adapters/next/templates/init/lib/actions/profile/change-own-password.ts +1 -0
  20. package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +1 -0
  21. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +1 -0
  22. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +16 -6
  23. package/dist/assets/adapters/next/templates/init/lib/actions/users/change-user-password.ts +1 -0
  24. package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +2 -1
  25. package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +1 -0
  26. package/dist/assets/adapters/next/templates/init/lib/actions/users/types.ts +1 -1
  27. package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +1 -0
  28. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +12 -4
  29. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +4 -4
  30. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +3 -7
  31. package/dist/assets/adapters/next/templates/init/pages/container-layout.tsx +3 -0
  32. package/dist/assets/adapters/next/templates/init/pages/dashboard-page.tsx +78 -76
  33. package/dist/assets/adapters/next/templates/init/pages/forgot-password-form.tsx +5 -8
  34. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +2 -2
  35. package/dist/assets/adapters/next/templates/init/pages/login-form.tsx +7 -9
  36. package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +2 -2
  37. package/dist/assets/adapters/next/templates/init/pages/reset-password-form.tsx +8 -16
  38. package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +2 -2
  39. package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-detail-row.tsx +1 -1
  40. package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-table.tsx +1 -6
  41. package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-table.tsx +2 -7
  42. package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-table.tsx +1 -6
  43. package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +1 -1
  44. package/dist/assets/adapters/next/templates/init/public/placeholder.png +0 -0
  45. package/dist/assets/adapters/next/templates/init/types/index.ts +2 -0
  46. package/dist/assets/adapters/next/templates/init/utils/audit/normalize-comparable-value.ts +4 -2
  47. package/dist/assets/adapters/next/templates/init/utils/date/format-action-date.ts +12 -0
  48. package/dist/assets/adapters/next/templates/init/utils/dev-mode/dev-mode-json-schemas.ts +66 -0
  49. package/dist/assets/adapters/next/templates/init/utils/dev-mode/parse-dev-mode-lifecycle-hooks.ts +11 -0
  50. package/dist/assets/adapters/next/templates/init/utils/dev-mode/parse-dev-mode-snippets.ts +9 -0
  51. package/dist/assets/adapters/next/templates/init/utils/editor/set-task-item-checked.ts +13 -0
  52. package/dist/assets/adapters/next/templates/init/utils/editor/update-nodes-attr.ts +2 -1
  53. package/dist/assets/adapters/next/templates/init/utils/email/email-theme.ts +13 -0
  54. package/dist/assets/adapters/next/templates/init/utils/email/render-form-submission-rows.ts +3 -2
  55. package/dist/assets/adapters/next/templates/init/utils/email/render-notification-email.ts +7 -6
  56. package/dist/assets/adapters/next/templates/init/utils/email/render-thank-you-email.ts +5 -4
  57. package/dist/assets/adapters/next/templates/init/utils/error/read-string-property.ts +4 -2
  58. package/dist/assets/adapters/next/templates/init/utils/error/recognize.ts +2 -4
  59. package/dist/assets/adapters/next/templates/init/utils/error/to-action-error.ts +0 -1
  60. package/dist/assets/adapters/next/templates/init/utils/form/clone-default-value.ts +4 -2
  61. package/dist/assets/adapters/next/templates/init/utils/shared/is-record.ts +3 -0
  62. package/dist/assets/adapters/next/templates/init/utils/table/default-get-id.ts +3 -2
  63. package/dist/assets/shared-assets/react-admin/custom/content-editor/editor-toolbar.tsx +1 -1
  64. package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +5 -3
  65. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +1 -1
  66. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/removable-image-node/removable-image-node.tsx +3 -1
  67. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-node/task-item-node/task-item-node-extension.tsx +6 -15
  68. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +1 -6
  69. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +1 -6
  70. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +9 -2
  71. package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +3 -3
  72. package/dist/assets/shared-assets/react-admin/custom/media-field.tsx +2 -2
  73. package/dist/assets/shared-assets/react-admin/custom/nested-object-list-field.tsx +3 -1
  74. package/dist/assets/shared-assets/react-admin/custom/progressive-blur.tsx +3 -1
  75. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +20 -23
  76. package/dist/assets/shared-assets/react-admin/ui/form.tsx +6 -3
  77. package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +1 -1
  78. package/dist/{chunk-SG4U6EQY.js → chunk-BNHFSHPE.js} +14 -1
  79. package/dist/chunk-BNHFSHPE.js.map +1 -0
  80. package/dist/cli.js +962 -633
  81. package/dist/cli.js.map +1 -1
  82. package/dist/{read-namespaced-template-D2CD4Q7U.js → read-namespaced-template-WYIGUH7S.js} +2 -2
  83. package/package.json +1 -1
  84. package/dist/chunk-SG4U6EQY.js.map +0 -1
  85. /package/dist/{read-namespaced-template-D2CD4Q7U.js.map → read-namespaced-template-WYIGUH7S.js.map} +0 -0
@@ -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:#f8fafc;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#0f172a;">
14
- <div style="margin:0 auto;max-width:640px;padding:40px 20px;">
15
- <div style="border-radius:12px;background:#ffffff;border:1px solid #e2e8f0;overflow:hidden;">
16
- <div style="background:#0f172a;color:#ffffff;padding:24px 28px;">
17
- <p style="margin:0;color:#cbd5e1;font-size:13px;line-height:20px;">${formName}</p>
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:#475569;font-size:14px;line-height:20px;">Submitted at ${submittedAt}</p>
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:#f8fafc;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#0f172a;">
10
- <div style="margin:0 auto;max-width:560px;padding:40px 20px;">
11
- <div style="border-radius:12px;background:#ffffff;padding:32px;border:1px solid #e2e8f0;">
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:#334155;font-size:16px;line-height:24px;">We received your ${formLabel} submission and will get back to you shortly.</p>
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 (typeof value !== 'object' || value === null) return undefined
3
- const property = (value as Record<string, unknown>)[key]
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,6 +1,5 @@
1
1
  import { recognize } from '@admin/utils/error/recognize'
2
2
 
3
3
  export function toActionError(error: unknown, fallback: string): string {
4
- console.error(error)
5
4
  return recognize(error) ?? fallback
6
5
  }
@@ -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 && typeof value === 'object') {
5
- const clonedEntries = Object.entries(value as Record<string, unknown>).map(
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)
@@ -0,0 +1,3 @@
1
+ export function isRecord(value: unknown): value is Record<string, unknown> {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
3
+ }
@@ -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
- const id = (row as { id?: unknown }).id
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-[480px]:absolute max-[480px]:top-auto max-[480px]:h-[calc(2.75rem+env(safe-area-inset-bottom,0px))] max-[480px]:flex-nowrap max-[480px]:justify-start max-[480px]:border-t max-[480px]:border-b-0 max-[480px]:pb-[env(safe-area-inset-bottom,0px)] [&::-webkit-scrollbar]:hidden',
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={(node) => {
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-[240px] resize-y flex-col overflow-hidden rounded-md border bg-background',
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="768px"
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 as Record<string, unknown>
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 as Record<string, unknown>)
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
- ) as Record<string, unknown>
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 variant="outline" className={cn('w-[206px]', className)} disabled={disabled} />
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 className="w-[282px] gap-0 overflow-hidden rounded-sm p-0" align="start">
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}
@@ -189,7 +189,7 @@ export function GalleryField({
189
189
  >
190
190
  <CarouselContent className="-ml-3">
191
191
  {canAdd && !disabled ? (
192
- <CarouselItem className="basis-[184px] pl-3">
192
+ <CarouselItem className="basis-gallery-item pl-3">
193
193
  <Card className="h-full" onClick={() => setOpen(true)}>
194
194
  <CardContent className="flex cursor-pointer flex-col p-2">
195
195
  <div className="flex aspect-square size-full flex-col items-center justify-center gap-1 overflow-hidden rounded-sm border border-border bg-background text-muted-foreground transition-colors hover:text-foreground">
@@ -208,7 +208,7 @@ export function GalleryField({
208
208
  {value.map((id) => {
209
209
  const item = mediaMap.get(id)
210
210
  return (
211
- <CarouselItem key={id} className="basis-[184px] pl-3">
211
+ <CarouselItem key={id} className="basis-gallery-item pl-3">
212
212
  <SortableGalleryItem id={id} disabled={disabled}>
213
213
  {item ? (
214
214
  <MediaField
@@ -243,7 +243,7 @@ export function GalleryField({
243
243
  </div>
244
244
  <DragOverlay>
245
245
  {activeId ? (
246
- <div className="w-[184px] scale-[0.98] cursor-grabbing opacity-95">
246
+ <div className="w-gallery-item scale-[0.98] cursor-grabbing opacity-95">
247
247
  {activeItem ? (
248
248
  <MediaField
249
249
  value={activeId}
@@ -102,7 +102,7 @@ export function MediaField({
102
102
  data-media-field-action="true"
103
103
  variant="outline"
104
104
  onClick={handleChangeImage}
105
- className="w-[100px] border-primary/10"
105
+ className="w-media-thumb border-primary/10"
106
106
  >
107
107
  Change
108
108
  </Button>
@@ -111,7 +111,7 @@ export function MediaField({
111
111
  data-media-field-action="true"
112
112
  variant="default"
113
113
  onClick={handleRemove}
114
- className="w-[100px]"
114
+ className="w-media-thumb"
115
115
  >
116
116
  Remove
117
117
  </Button>
@@ -13,6 +13,7 @@ import { FormDescription, FormField, FormItem, FormLabel } from '@admin/componen
13
13
  import { cloneDefaultValue } from '@admin/utils/form/clone-default-value'
14
14
  import { createListItemKey } from '@admin/utils/form/create-list-item-key'
15
15
  import { cn } from '@admin/utils/shared/cn'
16
+ import { isRecord } from '@admin/utils/shared/is-record'
16
17
  import { Plus, X } from 'lucide-react'
17
18
  import { useFormContext, useWatch } from 'react-hook-form'
18
19
 
@@ -75,7 +76,8 @@ export function NestedObjectListField({
75
76
  if (!isExpandedItemValid) return
76
77
  }
77
78
 
78
- const nextValue = cloneDefaultValue(defaultValue) as Record<string, unknown>
79
+ const clonedDefaultValue = cloneDefaultValue(defaultValue)
80
+ const nextValue = isRecord(clonedDefaultValue) ? clonedDefaultValue : {}
79
81
  const nextItems = [...items, nextValue]
80
82
  setValue(name, nextItems, {
81
83
  shouldDirty: true,
@@ -5,6 +5,8 @@ import { usePageBoundaryBlurVisibility } from '@admin/hooks/use-page-boundary-bl
5
5
  import { cn } from '@admin/utils/shared/cn'
6
6
  import { motion } from 'motion/react'
7
7
 
8
+ const DEFAULT_PROGRESSIVE_BLUR_HEIGHT = '150px'
9
+
8
10
  type ProgressiveBlurProps = React.ComponentProps<typeof motion.div> & {
9
11
  backgroundColor?: string
10
12
  position?: 'top' | 'bottom'
@@ -18,7 +20,7 @@ function ProgressiveBlur({
18
20
  style,
19
21
  backgroundColor = 'var(--background)',
20
22
  position = 'top',
21
- height = '150px',
23
+ height = DEFAULT_PROGRESSIVE_BLUR_HEIGHT,
22
24
  blurAmount = '0',
23
25
  scrollAware = true,
24
26
  ...props
@@ -22,33 +22,30 @@ function DropdownMenuContent({
22
22
  alignOffset = 0,
23
23
  side = 'bottom',
24
24
  sideOffset = 4,
25
- portalled = true,
26
25
  className,
27
26
  ...props
28
27
  }: MenuPrimitive.Popup.Props &
29
- Pick<MenuPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'> & {
30
- portalled?: boolean
31
- }) {
32
- const content = (
33
- <MenuPrimitive.Positioner
34
- className="isolate z-50 outline-none"
35
- align={align}
36
- alignOffset={alignOffset}
37
- side={side}
38
- sideOffset={sideOffset}
39
- >
40
- <MenuPrimitive.Popup
41
- data-slot="dropdown-menu-content"
42
- className={cn(
43
- 'cn-menu-target cn-menu-translucent z-50 max-h-(--available-height) w-(--anchor-width) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-2xl bg-popover p-1 text-popover-foreground shadow-2xl ring-1 ring-foreground/5 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95',
44
- className
45
- )}
46
- {...props}
47
- />
48
- </MenuPrimitive.Positioner>
28
+ Pick<MenuPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
29
+ return (
30
+ <MenuPrimitive.Portal>
31
+ <MenuPrimitive.Positioner
32
+ className="isolate z-50 outline-none"
33
+ align={align}
34
+ alignOffset={alignOffset}
35
+ side={side}
36
+ sideOffset={sideOffset}
37
+ >
38
+ <MenuPrimitive.Popup
39
+ data-slot="dropdown-menu-content"
40
+ className={cn(
41
+ 'cn-menu-target cn-menu-translucent z-50 max-h-(--available-height) w-(--anchor-width) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-2xl bg-popover p-1 text-popover-foreground shadow-2xl ring-1 ring-foreground/5 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95',
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ </MenuPrimitive.Positioner>
47
+ </MenuPrimitive.Portal>
49
48
  )
50
-
51
- return portalled ? <MenuPrimitive.Portal>{content}</MenuPrimitive.Portal> : content
52
49
  }
53
50
 
54
51
  function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
@@ -3,6 +3,7 @@
3
3
  import * as React from 'react'
4
4
  import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form'
5
5
  import { Label } from '@admin/components/ui/label'
6
+ import { useFieldErrorVisible } from '@admin/hooks/use-field-error-visible'
6
7
  import { cn } from '@admin/utils/shared/cn'
7
8
  import { Controller, FormProvider, useFormContext } from 'react-hook-form'
8
9
 
@@ -33,7 +34,8 @@ const FormField = <
33
34
  const useFormField = () => {
34
35
  const fieldContext = React.useContext(FormFieldContext)
35
36
  const itemContext = React.useContext(FormItemContext)
36
- const { getFieldState, formState } = useFormContext()
37
+ const form = useFormContext()
38
+ const isErrorVisible = useFieldErrorVisible(form, fieldContext?.name ?? '')
37
39
 
38
40
  if (!fieldContext) {
39
41
  throw new Error('useFormField should be used within <FormField>')
@@ -43,7 +45,7 @@ const useFormField = () => {
43
45
  throw new Error('useFormField should be used within <FormItem>')
44
46
  }
45
47
 
46
- const fieldState = getFieldState(fieldContext.name, formState)
48
+ const fieldState = form.getFieldState(fieldContext.name, form.formState)
47
49
 
48
50
  const { id } = itemContext
49
51
 
@@ -53,7 +55,8 @@ const useFormField = () => {
53
55
  formItemId: `${id}-form-item`,
54
56
  formDescriptionId: `${id}-form-item-description`,
55
57
  formMessageId: `${id}-form-item-message`,
56
- ...fieldState
58
+ ...fieldState,
59
+ error: isErrorVisible ? fieldState.error : undefined
57
60
  }
58
61
  }
59
62
 
@@ -108,7 +108,7 @@ function NavigationMenuPositioner({
108
108
  )}
109
109
  {...props}
110
110
  >
111
- <NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) rounded-2xl bg-popover text-popover-foreground shadow ring-1 ring-foreground/5 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0">
111
+ <NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) rounded-2xl bg-popover text-popover-foreground shadow ring-1 ring-foreground/5 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0 xs:w-(--popup-width)">
112
112
  <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
113
113
  </NavigationMenuPrimitive.Popup>
114
114
  </NavigationMenuPrimitive.Positioner>
@@ -12,6 +12,7 @@ var CLI_PACKAGE_NAME = "betterstart-cli";
12
12
  var REDACTED = "[redacted]";
13
13
 
14
14
  // constants/paths.ts
15
+ var ADMIN_CONTAINER_ROUTE_GROUP = "(container)";
15
16
  var BETTERSTART_DIR = ".betterstart";
16
17
  var CONFIG_FILE_NAME = "betterstart.config.ts";
17
18
  var DEVELOPMENT_ENV_FILES = [
@@ -181,6 +182,14 @@ function safeWriteFile(filePath, content, force = false) {
181
182
  fs.writeFileSync(filePath, content, "utf-8");
182
183
  return true;
183
184
  }
185
+ function safeCopyFile(src, dest, force = false) {
186
+ if (!force && fs.existsSync(dest)) {
187
+ return false;
188
+ }
189
+ ensureDir(path.dirname(dest));
190
+ fs.copyFileSync(src, dest);
191
+ return true;
192
+ }
184
193
  function createGeneratedFile(filePath, content) {
185
194
  return {
186
195
  path: filePath.split(path.sep).join("/"),
@@ -498,6 +507,7 @@ var R2_ENV_KEYS = [
498
507
  "BETTERSTART_R2_BUCKET_NAME",
499
508
  "BETTERSTART_R2_PUBLIC_URL"
500
509
  ];
510
+ var PUBLIC_ASSET_FILE_NAMES = ["placeholder.png"];
501
511
  var PROXY_FILE_NAME = "proxy.ts";
502
512
  var PROXY_CONVENTION_FILE_NAMES = [
503
513
  "proxy.ts",
@@ -693,6 +703,7 @@ function applyAdminNamespaceToContent(content, namespaceValue) {
693
703
  }
694
704
 
695
705
  export {
706
+ ADMIN_CONTAINER_ROUTE_GROUP,
696
707
  BETTERSTART_DIR,
697
708
  CONFIG_FILE_NAME,
698
709
  LOCKFILE_MAP,
@@ -728,6 +739,7 @@ export {
728
739
  hashContent,
729
740
  ensureDir,
730
741
  safeWriteFile,
742
+ safeCopyFile,
731
743
  createGeneratedFile,
732
744
  toStrictJson,
733
745
  parseDotenvFile,
@@ -761,6 +773,7 @@ export {
761
773
  TIPTAP_CONTENT_EDITOR_DIRECTORIES,
762
774
  RAILWAY_BUCKET_ENV_KEYS,
763
775
  R2_ENV_KEYS,
776
+ PUBLIC_ASSET_FILE_NAMES,
764
777
  PROXY_FILE_NAME,
765
778
  PROXY_CONVENTION_FILE_NAMES,
766
779
  REMOVABLE_NAMESPACE_PATTERN,
@@ -779,4 +792,4 @@ export {
779
792
  replaceAdminDashToken,
780
793
  applyAdminNamespaceToContent
781
794
  };
782
- //# sourceMappingURL=chunk-SG4U6EQY.js.map
795
+ //# sourceMappingURL=chunk-BNHFSHPE.js.map