betterstart-cli 0.0.104 → 0.0.105

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 (140) hide show
  1. package/dist/cli.js +6416 -5203
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
  4. package/dist/assets/adapters/next/templates/renderers/commands/init/banner-art.ts +0 -7
  5. package/dist/assets/adapters/next/templates/renderers/commands/init/verify-database-reachable.ts +0 -30
  6. package/dist/assets/adapters/next/templates/renderers/commands/seed/build-seed-script.ts +0 -171
  7. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-alias-block.ts +0 -4
  8. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-data-fields.ts +0 -4
  9. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-format-action-date-source.ts +0 -18
  10. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-read-select-option-type.ts +0 -6
  11. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-relationship-data-types.ts +0 -25
  12. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-result-mapping.ts +0 -62
  13. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/generate-populate-list-rels-function.ts +0 -50
  14. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-restore-many-to-many-statements.ts +0 -27
  15. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-select-option-types.ts +0 -37
  16. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-bulk-sort-order-content.ts +0 -93
  17. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-content.ts +0 -157
  18. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-select-option-content.ts +0 -134
  19. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-bulk-content.ts +0 -85
  20. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-content.ts +0 -66
  21. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-distinct-files.ts +0 -45
  22. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-id-content.ts +0 -55
  23. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-slug-content.ts +0 -55
  24. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-plural-content.ts +0 -106
  25. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-select-options-content.ts +0 -55
  26. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-versions-content.ts +0 -19
  27. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-m2m-files.ts +0 -151
  28. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-restore-content.ts +0 -123
  29. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-update-content.ts +0 -187
  30. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-resolve-media-function.ts +0 -40
  31. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-slug-helpers.ts +0 -73
  32. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-version-snapshot-helpers.ts +0 -104
  33. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-context.ts +0 -22
  34. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-hook-import.ts +0 -4
  35. package/dist/assets/adapters/next/templates/renderers/generators/actions/markdown-renderer.ts +0 -411
  36. package/dist/assets/adapters/next/templates/renderers/generators/actions/select-fields-resolver.ts +0 -99
  37. package/dist/assets/adapters/next/templates/renderers/generators/actions/single-actions/generate-single-actions.ts +0 -292
  38. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-authorship-value.ts +0 -8
  39. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-column-def.ts +0 -297
  40. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-first-column-def.ts +0 -483
  41. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-cell-media.ts +0 -38
  42. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-element.ts +0 -22
  43. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-relationship-label.ts +0 -26
  44. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-select-label.ts +0 -17
  45. package/dist/assets/adapters/next/templates/renderers/generators/columns/custom-cell.ts +0 -24
  46. package/dist/assets/adapters/next/templates/renderers/generators/columns/generate-columns.ts +0 -55
  47. package/dist/assets/adapters/next/templates/renderers/generators/create-page.ts +0 -16
  48. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-database.ts +0 -5
  49. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-junction-table.ts +0 -27
  50. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-select-options-table.ts +0 -35
  51. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-table-definition.ts +0 -25
  52. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-entity-dev-mode-snippets.ts +0 -180
  53. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-form-dev-mode-snippets.ts +0 -97
  54. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-single-dev-mode-snippets.ts +0 -60
  55. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/create-dev-mode-integration-artifact.ts +0 -89
  56. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/lifecycle-hook-code.ts +0 -55
  57. package/dist/assets/adapters/next/templates/renderers/generators/edit-page.ts +0 -33
  58. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-email-template.ts +0 -248
  59. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-field-value.ts +0 -31
  60. package/dist/assets/adapters/next/templates/renderers/generators/emitted-helpers.ts +0 -16
  61. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/generate-icon-postfix.ts +0 -27
  62. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-boolean-field.ts +0 -47
  63. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-creatable-select-field.ts +0 -122
  64. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-date-field.ts +0 -47
  65. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-default-field.ts +0 -53
  66. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-gallery-field.ts +0 -46
  67. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-group-field.ts +0 -16
  68. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-icon-field.ts +0 -31
  69. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-markdown-field.ts +0 -50
  70. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-media-field.ts +0 -47
  71. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-relationship-field.ts +0 -91
  72. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-select-field.ts +0 -219
  73. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-number-field.ts +0 -48
  74. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-richtext-field.ts +0 -50
  75. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-select-field.ts +0 -61
  76. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-separator-field.ts +0 -16
  77. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-single-relationship-field.ts +0 -93
  78. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-textarea-field.ts +0 -53
  79. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-list-field.ts +0 -163
  80. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-field.ts +0 -164
  81. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-object-list-field.ts +0 -59
  82. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-text-input-field.ts +0 -27
  83. package/dist/assets/adapters/next/templates/renderers/generators/form/form-entity/generate-form.ts +0 -822
  84. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-default-value-from-sync-functions.ts +0 -41
  85. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-ui-imports.ts +0 -58
  86. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-default-value.ts +0 -67
  87. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-nested-list-default-value.ts +0 -13
  88. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/label-with-description.ts +0 -14
  89. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/render-tabs-trigger.ts +0 -18
  90. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/select-read-values-expression.ts +0 -21
  91. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-handlers.ts +0 -67
  92. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-state.ts +0 -29
  93. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-field-array-hooks.ts +0 -13
  94. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-fields-jsx.ts +0 -30
  95. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-card-component.ts +0 -129
  96. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-single-form.ts +0 -66
  97. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-column-def.ts +0 -72
  98. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-columns.ts +0 -89
  99. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page/generate-page.ts +0 -23
  100. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page-content.ts +0 -246
  101. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-submission-drawer.ts +0 -216
  102. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-table.ts +0 -273
  103. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/exclusive-date-upper-bound-helper.ts +0 -11
  104. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/generate-form-actions.ts +0 -505
  105. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-multistep/build-component-source.ts +0 -197
  106. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/build-field-error-helper.ts +0 -16
  107. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/error-message-line.ts +0 -13
  108. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-field-jsx.ts +0 -226
  109. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-file-upload-field-jsx.ts +0 -41
  110. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-list-field-jsx.ts +0 -160
  111. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-multiselect-field-jsx.ts +0 -40
  112. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-number-field-jsx.ts +0 -41
  113. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-text-field-jsx.ts +0 -39
  114. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/label-with-description-line.ts +0 -14
  115. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-single/build-single-step-form-component-source.ts +0 -100
  116. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-database.ts +0 -29
  117. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-hook.ts +0 -142
  118. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-hook.ts +0 -169
  119. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-single-hook.ts +0 -30
  120. package/dist/assets/adapters/next/templates/renderers/generators/navigation-extract.ts +0 -19
  121. package/dist/assets/adapters/next/templates/renderers/generators/page-content/generate-page-content.ts +0 -419
  122. package/dist/assets/adapters/next/templates/renderers/generators/page.ts +0 -24
  123. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-delete-dialog-source.ts +0 -108
  124. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-row-actions-source.ts +0 -61
  125. package/dist/assets/adapters/next/templates/renderers/generators/single-page.ts +0 -59
  126. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sort-label-expr.ts +0 -9
  127. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-button-jsx.ts +0 -20
  128. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-def.ts +0 -14
  129. package/dist/assets/adapters/next/templates/renderers/generators/table.ts +0 -284
  130. package/dist/assets/adapters/next/templates/renderers/generators/thankyou-email-template.ts +0 -110
  131. package/dist/assets/adapters/next/templates/renderers/generators/webhook-events/render-extract.ts +0 -14
  132. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/next-config/create-next-config-template.ts +0 -48
  133. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-shadow-input-utility.ts +0 -9
  134. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-theme-block.ts +0 -42
  135. package/dist/assets/adapters/next/templates/renderers/init/templates/app/admin.ts +0 -12
  136. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-email-provider.ts +0 -27
  137. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-storage-provider.ts +0 -27
  138. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-db-barrel.ts +0 -8
  139. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-navigation-barrel.ts +0 -63
  140. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-webhook-events-barrel.ts +0 -16
@@ -1,822 +0,0 @@
1
- export function renderFormContentTemplate({
2
- slugValueHelperName,
3
- slugFallbackValue
4
- }: {
5
- slugValueHelperName: string
6
- slugFallbackValue: string
7
- }): string {
8
- return `
9
- function ${slugValueHelperName}(value: unknown): string {
10
- if (typeof value !== 'string') return '${slugFallbackValue}'
11
- return slugify(value) || '${slugFallbackValue}'
12
- }
13
- `
14
- }
15
-
16
- export function renderFormTabContentTemplate({
17
- indent,
18
- tName,
19
- tabFields
20
- }: {
21
- indent: string
22
- tName: string
23
- tabFields: string
24
- }): string {
25
- return `${indent} <TabsContent value="${tName}" className="space-y-4 pt-4 pb-6 px-4 rounded-sm border bg-card">
26
- ${tabFields}
27
- ${indent} </TabsContent>`
28
- }
29
-
30
- export function renderFormTabsLayoutTemplate({
31
- indent,
32
- tabsList,
33
- tabsContent
34
- }: {
35
- indent: string
36
- tabsList: string
37
- tabsContent: string
38
- }): string {
39
- return `${indent}<Tabs value={activeTab} onValueChange={(value) => void setActiveTab(value as TabValue)} className="w-full">
40
- ${indent} <TabsList>
41
- ${tabsList}
42
- ${indent} </TabsList>
43
- ${tabsContent}
44
- ${indent}</Tabs>`
45
- }
46
-
47
- export function renderFormContentSerializedValueTemplate({
48
- cardFieldIndent,
49
- stringify,
50
- fieldJsx
51
- }: {
52
- cardFieldIndent: string
53
- stringify: string
54
- fieldJsx: string
55
- }): string {
56
- return `${cardFieldIndent}{activeTab === ${stringify} ? (
57
- ${fieldJsx}
58
- ${cardFieldIndent}) : null}`
59
- }
60
-
61
- export function renderFormContentSerializedValueBranchTemplate({
62
- cardFieldIndent,
63
- stringify,
64
- fieldJsx
65
- }: {
66
- cardFieldIndent: string
67
- stringify: string
68
- fieldJsx: string
69
- }): string {
70
- return `${cardFieldIndent}{activeTab === ${stringify} ? (
71
- ${cardFieldIndent} <>
72
- ${fieldJsx}
73
- ${cardFieldIndent} </>
74
- ${cardFieldIndent}) : null}`
75
- }
76
-
77
- export function renderFormLayoutTemplate({
78
- indent,
79
- iconName
80
- }: {
81
- indent: string
82
- iconName: string
83
- }): string {
84
- return `
85
- ${indent} <${iconName} className="size-4 shrink-0" />`
86
- }
87
-
88
- export function renderFormCardHeaderTemplate({
89
- indent,
90
- iconJsx,
91
- stringify
92
- }: {
93
- indent: string
94
- iconJsx: string
95
- stringify: string
96
- }): string {
97
- return `${indent} <CardHeader>
98
- ${indent} <CardTitle className="flex items-center gap-1.5">${iconJsx}
99
- ${indent} <span>{${stringify}}</span>
100
- ${indent} </CardTitle>
101
- ${indent} </CardHeader>`
102
- }
103
-
104
- export function renderFormCardContentTemplate({
105
- indent,
106
- headingJsx,
107
- fieldsJsx
108
- }: {
109
- indent: string
110
- headingJsx: string
111
- fieldsJsx: string
112
- }): string {
113
- return `${indent}<Card className="gap-0">
114
- ${headingJsx}
115
- ${indent} <CardContent className="space-y-5 p-5">
116
- ${fieldsJsx}
117
- ${indent} </CardContent>
118
- ${indent}</Card>`
119
- }
120
-
121
- export function renderFormCardLayoutTemplate({
122
- indent,
123
- fieldsJsx
124
- }: {
125
- indent: string
126
- fieldsJsx: string
127
- }): string {
128
- return `${indent}<Card className="space-y-4 p-5 gap-0">
129
- ${fieldsJsx}
130
- ${indent}</Card>`
131
- }
132
-
133
- export function renderFormContentSerializedValueBranchBodyTemplate({
134
- stringify,
135
- cardJsx
136
- }: {
137
- stringify: string
138
- cardJsx: string
139
- }): string {
140
- return ` {activeTab === ${stringify} ? (
141
- ${cardJsx}
142
- ) : null}`
143
- }
144
-
145
- export function renderFormContentClassNameTemplate({
146
- optionsScrollableClassName,
147
- join
148
- }: {
149
- optionsScrollableClassName: string
150
- join: string
151
- }): string {
152
- return ` <div ${optionsScrollableClassName}>
153
- ${join}
154
- </div>`
155
- }
156
-
157
- export function renderFormLayoutDetailsTemplate(): string {
158
- return `className={initialData?.id != null
159
- ? 'lg:sticky lg:top-32 space-y-4 pb-4'
160
- : 'lg:sticky lg:top-14 space-y-4 pb-4'}`
161
- }
162
-
163
- export function renderFormCardContentGridTemplate({
164
- tabSidebarEntriesGrid,
165
- tabMainFieldsJsx,
166
- sidebarColumn
167
- }: {
168
- tabSidebarEntriesGrid: string
169
- tabMainFieldsJsx: string
170
- sidebarColumn: string
171
- }): string {
172
- return ` <div className="grid grid-cols-1 ${tabSidebarEntriesGrid}h-full min-h-0 gap-4 items-stretch">
173
- <div className="h-full min-h-0 min-w-0">
174
- <Card className="min-h-0 min-w-0 h-full">
175
- <CardContent className="space-y-5 gap-0 h-full min-h-0">
176
- ${tabMainFieldsJsx}
177
- </CardContent>
178
- </Card>
179
- </div>
180
- ${sidebarColumn}
181
- </div>`
182
- }
183
-
184
- export function renderFormLayoutDetailsBranchTemplate(): string {
185
- return ` <div
186
- className={cn(
187
- 'grid grid-cols-1 gap-4 items-start h-full',
188
- {
189
- 'lg:grid-cols-[1fr_360px]': hasVisibleSidebarFields
190
- }
191
- )}
192
- >`
193
- }
194
-
195
- export function renderFormContentSidebarColumnTemplate({
196
- renderSidebarColumn
197
- }: {
198
- renderSidebarColumn: string
199
- }): string {
200
- return ` {hasVisibleSidebarFields ? (
201
- ${renderSidebarColumn}
202
- ) : null}`
203
- }
204
-
205
- export function renderFormCardContentDetailsTemplate({
206
- formGridOpen,
207
- mainFieldsJsx,
208
- sidebarCardJsx
209
- }: {
210
- formGridOpen: string
211
- mainFieldsJsx: string
212
- sidebarCardJsx: string
213
- }): string {
214
- return `${formGridOpen}
215
- <div className="h-full min-w-0">
216
- <Card className="min-w-0 h-full">
217
- <CardContent className="space-y-5 gap-0 h-full">
218
- ${mainFieldsJsx}
219
- </CardContent>
220
- </Card>
221
- </div>
222
- ${sidebarCardJsx}
223
- </div>`
224
- }
225
-
226
- export function renderFormStaticOptionsTemplate({
227
- fieldName,
228
- fieldPascal,
229
- singular,
230
- schemaName,
231
- staticOptions
232
- }: {
233
- fieldName: string
234
- fieldPascal: string
235
- singular: string
236
- schemaName: string
237
- staticOptions: string
238
- }): string {
239
- return ` const [${fieldName}Open, set${fieldPascal}Open] = React.useState(false)
240
- const [${fieldName}Search, set${fieldPascal}Search] = React.useState('')
241
- const [create${fieldPascal}OptionPending, startCreate${fieldPascal}OptionTransition] = React.useTransition()
242
- const { data: ${fieldName}CreatedOptions = [] as ${singular}SelectOption[] } = useQuery<${singular}SelectOption[]>({
243
- queryKey: ['${schemaName}', 'select-options', '${fieldName}'],
244
- queryFn: () => get${singular}SelectOptions('${fieldName}')
245
- })
246
- const ${fieldName}StaticOptions: Array<{ label: string; value: string }> = [${staticOptions}]
247
- const ${fieldName}Options = [
248
- ...${fieldName}StaticOptions,
249
- ...${fieldName}CreatedOptions.filter(
250
- (createdOption: ${singular}SelectOption) =>
251
- !${fieldName}StaticOptions.some((option) => option.value === createdOption.value)
252
- )
253
- ]`
254
- }
255
-
256
- export function renderFormFieldArrayTemplate({
257
- fieldName,
258
- pascalFieldName
259
- }: {
260
- fieldName: string
261
- pascalFieldName: string
262
- }): string {
263
- return ` const [${fieldName}Expanded, set${pascalFieldName}Expanded] = React.useState('')
264
- const ${fieldName}FieldArray = useFieldArray({
265
- control: form.control,
266
- name: '${fieldName}'
267
- })`
268
- }
269
-
270
- export function renderFormCurrentValueTemplate({
271
- fieldName,
272
- defaultValueFromHandlerContent
273
- }: {
274
- fieldName: string
275
- defaultValueFromHandlerContent: string
276
- }): string {
277
- return `const currentValue = form.getValues('${fieldName}')
278
- const selectedValues = Array.isArray(currentValue) ? currentValue : []
279
- const nextValues = [...new Set([...selectedValues, result.option.value])]
280
- ${defaultValueFromHandlerContent}form.setValue('${fieldName}', nextValues, {
281
- shouldDirty: true,
282
- shouldValidate: true
283
- })`
284
- }
285
-
286
- export function renderFormContentFieldNameTemplate({
287
- defaultValueFromHandlerContent,
288
- fieldName
289
- }: {
290
- defaultValueFromHandlerContent: string
291
- fieldName: string
292
- }): string {
293
- return `${defaultValueFromHandlerContent}form.setValue('${fieldName}', result.option.value, {
294
- shouldDirty: true,
295
- shouldValidate: true
296
- })`
297
- }
298
-
299
- export function renderFormTrimmedLabelTemplate({
300
- fieldPascal,
301
- singular,
302
- fieldName,
303
- schemaName,
304
- setCreatedOptionValue
305
- }: {
306
- fieldPascal: string
307
- singular: string
308
- fieldName: string
309
- schemaName: string
310
- setCreatedOptionValue: string
311
- }): string {
312
- return ` function handleCreate${fieldPascal}Option(label: string) {
313
- const trimmedLabel = label.trim()
314
- if (!trimmedLabel) return
315
-
316
- startCreate${fieldPascal}OptionTransition(async () => {
317
- const result = await create${singular}SelectOption({
318
- fieldName: '${fieldName}',
319
- label: trimmedLabel
320
- })
321
-
322
- if (!result.success || !result.option) {
323
- toast.error(result.error || 'Failed to create option')
324
- return
325
- }
326
-
327
- await queryClient.invalidateQueries({
328
- queryKey: ['${schemaName}', 'select-options', '${fieldName}']
329
- })
330
- ${setCreatedOptionValue}
331
- set${fieldPascal}Search('')
332
- set${fieldPascal}Open(false)
333
- toast.success('Option created')
334
- })
335
- }`
336
- }
337
-
338
- export function renderFormTabValuesTemplate({ map }: { map: string }): string {
339
- return `const TAB_VALUES = [${map}] as const
340
- const TAB_PARSER = parseAsStringLiteral(TAB_VALUES).withDefault(TAB_VALUES[0])
341
-
342
- export type TabValue = (typeof TAB_VALUES)[number]
343
-
344
- `
345
- }
346
-
347
- export function renderFormLayoutDetailsBranchBodyTemplate({
348
- plural,
349
- singular
350
- }: {
351
- plural: string
352
- singular: string
353
- }): string {
354
- return ` {initialData?.id != null && (
355
- <div
356
- className={cn(
357
- 'sticky z-100 flex flex-col gap-1 px-4 bg-background h-14 xl:flex-row xl:items-center xl:justify-between top-14 transition-shadow overflow-x-clip',
358
- {
359
- 'shadow-modal/2': hasScrolled,
360
- 'border-b border-border/80': !hasScrolled
361
- }
362
- )}
363
- >
364
- <div className="flex flex-1 items-center gap-1 w-full">
365
- <EntityMetadata
366
- data={initialData}
367
- entityType="${plural}"
368
- entityId={initialData.id}
369
- getVersionsAction={getVersionsBy${singular}Id}
370
- restoreVersionAction={restore${singular}Version}
371
- />
372
- </div>
373
- </div>
374
- )}
375
-
376
- `
377
- }
378
-
379
- export function renderFormLayoutDetailsBranchBodyFragmentTemplate(): string {
380
- return `className={cn('min-h-[calc(100svh-56px)] w-full gap-0', {
381
- 'min-h-[calc(100svh-56px-56px)]': initialData?.id != null
382
- })}`
383
- }
384
-
385
- export function renderFormTabsLayoutTabsTriggerTemplate({
386
- topLevelTabsClassName,
387
- map,
388
- mapTab
389
- }: {
390
- topLevelTabsClassName: string
391
- map: string
392
- mapTab: string
393
- }): string {
394
- return ` <Tabs value={entityForm.activeTab} onValueChange={(value) => void entityForm.setActiveTab(value as TabValue)} ${topLevelTabsClassName}>
395
- <div className="flex h-14 shrink-0 w-full bg-background border-b border-border/80 px-4 items-center">
396
- <TabsList className="bg-transparent border-none">
397
- ${map}
398
- </TabsList>
399
- </div>
400
-
401
- <main className="w-full flex-1 min-h-0 p-4">
402
- ${mapTab}
403
- </main>
404
- </Tabs>`
405
- }
406
-
407
- export function renderFormTabContentDetailsTemplate({
408
- tabName,
409
- contentComponentName
410
- }: {
411
- tabName: string
412
- contentComponentName: string
413
- }): string {
414
- return ` <TabsContent value="${tabName}" className="h-full min-h-0 w-full flex-1">
415
- <Form {...entityForm.form}>
416
- <${contentComponentName} state={entityForm} tab="${tabName}" />
417
- </Form>
418
- </TabsContent>`
419
- }
420
-
421
- export function renderFormLayoutDetailsBranchBodyFragmentSectionTemplate({
422
- contentComponentName
423
- }: {
424
- contentComponentName: string
425
- }): string {
426
- return ` <main className="w-full p-4 h-full">
427
- <Form {...entityForm.form}>
428
- <${contentComponentName} state={entityForm} />
429
- </Form>
430
- </main>`
431
- }
432
-
433
- export function renderFormEditRowClickHandlerTemplate({
434
- hasCreatableSelectUseQuery,
435
- hasNestedListUseFieldArray,
436
- hasTabsFieldImport,
437
- visibleSlugFieldSlugify,
438
- relHookImportsContent,
439
- singular,
440
- hasCreatableSelectContent,
441
- actionImportPath,
442
- hasCreatableSelectContentText,
443
- tabParserConst,
444
- zodFields,
445
- hookOptionsName,
446
- slugValueHelper,
447
- hookName,
448
- hasTabsFieldActiveTab,
449
- relStateContent,
450
- creatableSelectStateContent,
451
- staticMultiSelectStateContent,
452
- hasDraftSubmitIntent,
453
- defaultValues,
454
- plural,
455
- adminRoutePath,
456
- schemaName,
457
- singularText,
458
- hasDraftOnSettled,
459
- hasDraftCanPublishPristineDraft,
460
- defaultValueFromSyncFunctionsContent,
461
- fieldArrayHooksContent,
462
- creatableSelectHandlersContent,
463
- hasDraftPublishedValue,
464
- hasDraftPublishedValuePublishedValue,
465
- hasDraftPublished,
466
- hasDraftSetSubmitIntent,
467
- hookReturnObject
468
- }: {
469
- hasCreatableSelectUseQuery: string
470
- hasNestedListUseFieldArray: string
471
- hasTabsFieldImport: string
472
- visibleSlugFieldSlugify: string
473
- relHookImportsContent: string
474
- singular: string
475
- hasCreatableSelectContent: string
476
- actionImportPath: string
477
- hasCreatableSelectContentText: string
478
- tabParserConst: string
479
- zodFields: string
480
- hookOptionsName: string
481
- slugValueHelper: string
482
- hookName: string
483
- hasTabsFieldActiveTab: string
484
- relStateContent: string
485
- creatableSelectStateContent: string
486
- staticMultiSelectStateContent: string
487
- hasDraftSubmitIntent: string
488
- defaultValues: string
489
- plural: string
490
- adminRoutePath: string
491
- schemaName: string
492
- singularText: string
493
- hasDraftOnSettled: string
494
- hasDraftCanPublishPristineDraft: string
495
- defaultValueFromSyncFunctionsContent: string
496
- fieldArrayHooksContent: string
497
- creatableSelectHandlersContent: string
498
- hasDraftPublishedValue: string
499
- hasDraftPublishedValuePublishedValue: string
500
- hasDraftPublished: string
501
- hasDraftSetSubmitIntent: string
502
- hookReturnObject: string
503
- }): string {
504
- return `'use client'
505
-
506
- import * as React from 'react'
507
- import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
508
- import { useMutation, ${hasCreatableSelectUseQuery}useQueryClient } from '@tanstack/react-query'
509
- import { useRouter } from 'next/navigation'
510
- import {${hasNestedListUseFieldArray} useForm } from 'react-hook-form'
511
- import { toast } from 'sonner'
512
- import { z } from 'zod/v3'${hasTabsFieldImport}
513
- ${visibleSlugFieldSlugify}${relHookImportsContent}
514
- import type {
515
- ${singular},
516
- ${singular}CreateInput,${hasCreatableSelectContent}
517
- ${singular}UpdateInput
518
- } from '@admin/actions/${actionImportPath}'
519
- import { create${singular}, ${hasCreatableSelectContentText}update${singular} } from '@admin/actions/${actionImportPath}'
520
-
521
- ${tabParserConst}const formSchema = z.object({
522
- ${zodFields}
523
- })
524
-
525
- export type FormValues = z.infer<typeof formSchema>
526
-
527
- interface ${hookOptionsName} {
528
- initialData?: ${singular}
529
- }
530
- ${slugValueHelper}
531
-
532
- export function ${hookName}({ initialData }: ${hookOptionsName}) {
533
- const router = useRouter()
534
- const queryClient = useQueryClient()${hasTabsFieldActiveTab}${relStateContent}${creatableSelectStateContent}${staticMultiSelectStateContent}${hasDraftSubmitIntent}
535
-
536
- const defaultValues: FormValues = {
537
- ${defaultValues}
538
- }
539
-
540
- const form = useForm<FormValues>({
541
- resolver: standardSchemaResolver(formSchema),
542
- mode: 'onBlur',
543
- reValidateMode: 'onChange',
544
- defaultValues
545
- })
546
- const defaultValuesRef = React.useRef(defaultValues)
547
- defaultValuesRef.current = defaultValues
548
- React.useEffect(
549
- () => () => {
550
- form.reset(defaultValuesRef.current)
551
- },
552
- [form]
553
- )
554
-
555
- const createMutation = useMutation({
556
- mutationFn: (data: ${singular}CreateInput) => create${singular}(data),
557
- onSuccess: async () => {
558
- toast.success('${singular} created successfully')
559
- await queryClient.invalidateQueries({ queryKey: ['${plural}'] })
560
- form.reset(defaultValues)
561
- router.push('${adminRoutePath}/${schemaName}')
562
- },
563
- onError: (error: Error) => {
564
- toast.error(error.message || 'Failed to create ${singularText}')
565
- }${hasDraftOnSettled}
566
- })
567
-
568
- const updateMutation = useMutation({
569
- mutationFn: (data: ${singular}UpdateInput) => update${singular}(data),
570
- onSuccess: async () => {
571
- toast.success('${singular} updated successfully')
572
- await queryClient.invalidateQueries({ queryKey: ['${plural}'] })
573
- router.refresh()
574
- },
575
- onError: (error: Error) => {
576
- toast.error(error.message || 'Failed to update ${singularText}')
577
- }${hasDraftOnSettled}
578
- })
579
-
580
- const isPending = createMutation.isPending || updateMutation.isPending
581
- const { isDirty } = form.formState
582
- ${hasDraftCanPublishPristineDraft}
583
- ${defaultValueFromSyncFunctionsContent}
584
- ${fieldArrayHooksContent}
585
- ${creatableSelectHandlersContent}
586
- function onSubmit(values: FormValues${hasDraftPublishedValue}) {
587
- ${hasDraftPublishedValuePublishedValue} if (initialData) {
588
- updateMutation.mutate({
589
- id: initialData.id,
590
- ...values${hasDraftPublished}
591
- })
592
- } else {
593
- createMutation.mutate({
594
- ...values${hasDraftPublished}
595
- } as ${singular}CreateInput)
596
- }
597
- }
598
-
599
- function handleErrors(errors: Record<string, unknown>) {
600
- ${hasDraftSetSubmitIntent} const firstError = Object.values(errors)[0] as { message?: string } | undefined
601
- if (firstError?.message) {
602
- toast.error(String(firstError.message))
603
- } else {
604
- toast.error('Please fix the form errors before submitting')
605
- }
606
- }
607
-
608
- return {
609
- ${hookReturnObject}
610
- }
611
- }
612
- `
613
- }
614
-
615
- export function renderFormContentLucideTemplate({
616
- lucideIconsLucide,
617
- hasTabsFieldHookImportPath,
618
- needsCnImport,
619
- join
620
- }: {
621
- lucideIconsLucide: string
622
- hasTabsFieldHookImportPath: string
623
- needsCnImport: string
624
- join: string
625
- }): string {
626
- return `${lucideIconsLucide}${hasTabsFieldHookImportPath}${needsCnImport}
627
- ${join}`
628
- }
629
-
630
- export function renderFormGeneratedValueTemplate({ map }: { map: string }): string {
631
- return ` const {
632
- ${map}
633
- } = state
634
-
635
- `
636
- }
637
-
638
- export function renderFormClientModuleTemplate({
639
- tabImports,
640
- specComponentName,
641
- hookName,
642
- tabStateDestructure,
643
- body
644
- }: {
645
- tabImports: string
646
- specComponentName: string
647
- hookName: string
648
- tabStateDestructure: string
649
- body: string
650
- }): string {
651
- return `'use client'
652
-
653
- ${tabImports}
654
-
655
- interface ${specComponentName}Props {
656
- state: ReturnType<typeof ${hookName}>
657
- }
658
-
659
- export function ${specComponentName}({ state }: ${specComponentName}Props) {
660
- ${tabStateDestructure} return (
661
- ${body}
662
- )
663
- }
664
- `
665
- }
666
-
667
- export function renderFormClientModuleSchemaNameTemplate({
668
- contentImports,
669
- contentComponentName,
670
- hookName,
671
- topLevelTabsFieldTab,
672
- topLevelTabsFieldTabTab,
673
- contentStateDestructure,
674
- contentSidebarVisibilityState,
675
- schemaName,
676
- hasDraftInitialData,
677
- topLevelTabsFieldClassName,
678
- formBodyJsx
679
- }: {
680
- contentImports: string
681
- contentComponentName: string
682
- hookName: string
683
- topLevelTabsFieldTab: string
684
- topLevelTabsFieldTabTab: string
685
- contentStateDestructure: string
686
- contentSidebarVisibilityState: string
687
- schemaName: string
688
- hasDraftInitialData: string
689
- topLevelTabsFieldClassName: string
690
- formBodyJsx: string
691
- }): string {
692
- return `'use client'
693
-
694
- ${contentImports}
695
-
696
- interface ${contentComponentName}Props {
697
- state: ReturnType<typeof ${hookName}>
698
- ${topLevelTabsFieldTab}
699
- }
700
-
701
- export function ${contentComponentName}({ state${topLevelTabsFieldTabTab} }: ${contentComponentName}Props) {
702
- const {
703
- ${contentStateDestructure}
704
- } = state
705
- ${contentSidebarVisibilityState}
706
- return (
707
- <form id="${schemaName}-form" onSubmit={form.handleSubmit((values) => onSubmit(values${hasDraftInitialData}), handleErrors)} ${topLevelTabsFieldClassName}>
708
- ${formBodyJsx}
709
- </form>
710
- )
711
- }
712
- `
713
- }
714
-
715
- export function renderFormClientModuleSchemaNameImportTemplate({
716
- showMetadataWidgetScrollThresholdImport,
717
- topLevelTabIconNamesLucide,
718
- topLevelTabsFieldTabs,
719
- showMetadataWidgetEntityMetadataImport,
720
- singular,
721
- actionImportPath,
722
- showMetadataWidgetAdmin,
723
- topLevelTabsFieldHookImportPath,
724
- hookName,
725
- hookImportPath,
726
- contentComponentName,
727
- schemaName,
728
- metadataOffsetConst,
729
- metadataScrollState,
730
- hasDraftFlex,
731
- metadataStrip,
732
- shellBody
733
- }: {
734
- showMetadataWidgetScrollThresholdImport: string
735
- topLevelTabIconNamesLucide: string
736
- topLevelTabsFieldTabs: string
737
- showMetadataWidgetEntityMetadataImport: string
738
- singular: string
739
- actionImportPath: string
740
- showMetadataWidgetAdmin: string
741
- topLevelTabsFieldHookImportPath: string
742
- hookName: string
743
- hookImportPath: string
744
- contentComponentName: string
745
- schemaName: string
746
- metadataOffsetConst: string
747
- metadataScrollState: string
748
- hasDraftFlex: string
749
- metadataStrip: string
750
- shellBody: string
751
- }): string {
752
- return `'use client'
753
- ${showMetadataWidgetScrollThresholdImport}
754
- ${topLevelTabIconNamesLucide}
755
- import { Button } from '@admin/components/ui/button'
756
- import { Form } from '@admin/components/ui/form'
757
- ${topLevelTabsFieldTabs}import { PageHeader } from '@admin/components/shared/page-header'${showMetadataWidgetEntityMetadataImport}
758
- import type { ${singular} } from '@admin/actions/${actionImportPath}'
759
- ${showMetadataWidgetAdmin}
760
- ${topLevelTabsFieldHookImportPath}
761
- import { ${hookName} } from '${hookImportPath}'
762
- import { ${contentComponentName} } from './${schemaName}-form-content'
763
-
764
- ${metadataOffsetConst}interface ${singular}FormProps {
765
- initialData?: ${singular}
766
- pageTitle: string
767
- }
768
-
769
- export function ${singular}Form({ initialData, pageTitle }: ${singular}FormProps) {
770
- ${metadataScrollState} const entityForm = ${hookName}({ initialData })
771
-
772
- return (
773
- <>
774
- <PageHeader
775
- title={pageTitle}
776
- actions={
777
- ${hasDraftFlex}
778
- }
779
- />
780
- ${metadataStrip}${shellBody}
781
- </>
782
- )
783
- }
784
- `
785
- }
786
-
787
- export function renderFormActionButtonsTemplate(): string {
788
- return ` <div className="flex items-center gap-1">
789
- <Button
790
- type="button"
791
- variant="outline"
792
- onClick={() => entityForm.form.handleSubmit((values) => entityForm.onSubmit(values, false), entityForm.handleErrors)()}
793
- disabled={!entityForm.canSubmitDraftAction || entityForm.isPending}
794
- aria-busy={entityForm.isPending && entityForm.submitIntent === 'draft'}
795
- >
796
- {entityForm.isPending && entityForm.submitIntent === 'draft'
797
- ? initialData?.published ? 'Unpublishing…' : 'Saving Changes…'
798
- : initialData?.published ? 'Unpublish' : 'Save Changes'}
799
- </Button>
800
- <Button
801
- type="button"
802
- onClick={() => entityForm.form.handleSubmit((values) => entityForm.onSubmit(values, true), entityForm.handleErrors)()}
803
- disabled={entityForm.isPending || !entityForm.canSubmitPublishAction}
804
- aria-busy={entityForm.isPending && entityForm.submitIntent === 'publish'}
805
- >
806
- {entityForm.isPending && entityForm.submitIntent === 'publish'
807
- ? initialData?.published ? 'Publishing Changes…' : 'Publishing…'
808
- : initialData?.published ? 'Publish Changes' : 'Publish'}
809
- </Button>
810
- </div>`
811
- }
812
-
813
- export function renderFormActionButtonsDetailsTemplate(): string {
814
- return ` <Button
815
- type="button"
816
- onClick={() => entityForm.form.handleSubmit((values) => entityForm.onSubmit(values), entityForm.handleErrors)()}
817
- disabled={entityForm.isPending || !entityForm.isDirty}
818
- aria-busy={entityForm.isPending}
819
- >
820
- {entityForm.isPending ? 'Saving…' : initialData ? 'Update' : 'Create'}
821
- </Button>`
822
- }