betterstart-cli 0.0.103 → 0.0.104

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 (145) hide show
  1. package/dist/assets/adapters/next/templates/renderers/commands/init/banner-art.ts +7 -0
  2. package/dist/assets/adapters/next/templates/renderers/commands/init/verify-database-reachable.ts +30 -0
  3. package/dist/assets/adapters/next/templates/renderers/commands/seed/build-seed-script.ts +171 -0
  4. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-alias-block.ts +4 -0
  5. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-data-fields.ts +4 -0
  6. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-format-action-date-source.ts +18 -0
  7. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-read-select-option-type.ts +6 -0
  8. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-relationship-data-types.ts +25 -0
  9. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-result-mapping.ts +62 -0
  10. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/generate-populate-list-rels-function.ts +50 -0
  11. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-restore-many-to-many-statements.ts +27 -0
  12. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-select-option-types.ts +37 -0
  13. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-bulk-sort-order-content.ts +93 -0
  14. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-content.ts +157 -0
  15. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-select-option-content.ts +134 -0
  16. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-bulk-content.ts +85 -0
  17. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-content.ts +66 -0
  18. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-distinct-files.ts +45 -0
  19. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-id-content.ts +55 -0
  20. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-slug-content.ts +55 -0
  21. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-plural-content.ts +106 -0
  22. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-select-options-content.ts +55 -0
  23. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-versions-content.ts +19 -0
  24. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-m2m-files.ts +151 -0
  25. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-restore-content.ts +123 -0
  26. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-update-content.ts +187 -0
  27. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-resolve-media-function.ts +40 -0
  28. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-slug-helpers.ts +73 -0
  29. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-version-snapshot-helpers.ts +104 -0
  30. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-context.ts +22 -0
  31. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-hook-import.ts +4 -0
  32. package/dist/assets/adapters/next/templates/renderers/generators/actions/markdown-renderer.ts +411 -0
  33. package/dist/assets/adapters/next/templates/renderers/generators/actions/select-fields-resolver.ts +99 -0
  34. package/dist/assets/adapters/next/templates/renderers/generators/actions/single-actions/generate-single-actions.ts +292 -0
  35. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-authorship-value.ts +8 -0
  36. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-column-def.ts +297 -0
  37. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-first-column-def.ts +483 -0
  38. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-cell-media.ts +38 -0
  39. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-element.ts +22 -0
  40. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-relationship-label.ts +26 -0
  41. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-select-label.ts +17 -0
  42. package/dist/assets/adapters/next/templates/renderers/generators/columns/custom-cell.ts +24 -0
  43. package/dist/assets/adapters/next/templates/renderers/generators/columns/generate-columns.ts +55 -0
  44. package/dist/assets/adapters/next/templates/renderers/generators/create-page.ts +16 -0
  45. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-database.ts +5 -0
  46. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-junction-table.ts +27 -0
  47. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-select-options-table.ts +35 -0
  48. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-table-definition.ts +25 -0
  49. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-entity-dev-mode-snippets.ts +180 -0
  50. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-form-dev-mode-snippets.ts +97 -0
  51. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-single-dev-mode-snippets.ts +60 -0
  52. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/create-dev-mode-integration-artifact.ts +89 -0
  53. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/lifecycle-hook-code.ts +55 -0
  54. package/dist/assets/adapters/next/templates/renderers/generators/edit-page.ts +33 -0
  55. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-email-template.ts +248 -0
  56. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-field-value.ts +31 -0
  57. package/dist/assets/adapters/next/templates/renderers/generators/emitted-helpers.ts +16 -0
  58. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/generate-icon-postfix.ts +27 -0
  59. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-boolean-field.ts +47 -0
  60. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-creatable-select-field.ts +122 -0
  61. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-date-field.ts +47 -0
  62. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-default-field.ts +53 -0
  63. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-gallery-field.ts +46 -0
  64. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-group-field.ts +16 -0
  65. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-icon-field.ts +31 -0
  66. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-markdown-field.ts +50 -0
  67. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-media-field.ts +47 -0
  68. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-relationship-field.ts +91 -0
  69. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-select-field.ts +219 -0
  70. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-number-field.ts +48 -0
  71. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-richtext-field.ts +50 -0
  72. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-select-field.ts +61 -0
  73. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-separator-field.ts +16 -0
  74. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-single-relationship-field.ts +93 -0
  75. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-textarea-field.ts +53 -0
  76. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-list-field.ts +163 -0
  77. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-field.ts +164 -0
  78. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-object-list-field.ts +59 -0
  79. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-text-input-field.ts +27 -0
  80. package/dist/assets/adapters/next/templates/renderers/generators/form/form-entity/generate-form.ts +822 -0
  81. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-default-value-from-sync-functions.ts +41 -0
  82. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-ui-imports.ts +58 -0
  83. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-default-value.ts +67 -0
  84. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-nested-list-default-value.ts +13 -0
  85. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/label-with-description.ts +14 -0
  86. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/render-tabs-trigger.ts +18 -0
  87. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/select-read-values-expression.ts +21 -0
  88. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-handlers.ts +67 -0
  89. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-state.ts +29 -0
  90. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-field-array-hooks.ts +13 -0
  91. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-fields-jsx.ts +30 -0
  92. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-card-component.ts +129 -0
  93. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-single-form.ts +66 -0
  94. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-column-def.ts +72 -0
  95. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-columns.ts +89 -0
  96. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page/generate-page.ts +23 -0
  97. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page-content.ts +246 -0
  98. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-submission-drawer.ts +216 -0
  99. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-table.ts +273 -0
  100. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/exclusive-date-upper-bound-helper.ts +11 -0
  101. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/generate-form-actions.ts +505 -0
  102. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-multistep/build-component-source.ts +197 -0
  103. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/build-field-error-helper.ts +16 -0
  104. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/error-message-line.ts +13 -0
  105. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-field-jsx.ts +226 -0
  106. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-file-upload-field-jsx.ts +41 -0
  107. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-list-field-jsx.ts +160 -0
  108. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-multiselect-field-jsx.ts +40 -0
  109. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-number-field-jsx.ts +41 -0
  110. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-text-field-jsx.ts +39 -0
  111. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/label-with-description-line.ts +14 -0
  112. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-single/build-single-step-form-component-source.ts +100 -0
  113. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-database.ts +29 -0
  114. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-hook.ts +142 -0
  115. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-hook.ts +169 -0
  116. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-single-hook.ts +30 -0
  117. package/dist/assets/adapters/next/templates/renderers/generators/navigation-extract.ts +19 -0
  118. package/dist/assets/adapters/next/templates/renderers/generators/page-content/generate-page-content.ts +419 -0
  119. package/dist/assets/adapters/next/templates/renderers/generators/page.ts +24 -0
  120. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-delete-dialog-source.ts +108 -0
  121. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-row-actions-source.ts +61 -0
  122. package/dist/assets/adapters/next/templates/renderers/generators/single-page.ts +59 -0
  123. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sort-label-expr.ts +9 -0
  124. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-button-jsx.ts +20 -0
  125. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-def.ts +14 -0
  126. package/dist/assets/adapters/next/templates/renderers/generators/table.ts +284 -0
  127. package/dist/assets/adapters/next/templates/renderers/generators/thankyou-email-template.ts +110 -0
  128. package/dist/assets/adapters/next/templates/renderers/generators/webhook-events/render-extract.ts +14 -0
  129. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/next-config/create-next-config-template.ts +48 -0
  130. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-shadow-input-utility.ts +9 -0
  131. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-theme-block.ts +42 -0
  132. package/dist/assets/adapters/next/templates/renderers/init/templates/app/admin.ts +12 -0
  133. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-email-provider.ts +27 -0
  134. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-storage-provider.ts +27 -0
  135. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-db-barrel.ts +8 -0
  136. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-navigation-barrel.ts +63 -0
  137. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-webhook-events-barrel.ts +16 -0
  138. package/dist/{chunk-TIJEHWRF.js → chunk-OYU73Q2W.js} +1 -50
  139. package/dist/chunk-OYU73Q2W.js.map +1 -0
  140. package/dist/cli.js +8864 -4010
  141. package/dist/cli.js.map +1 -1
  142. package/dist/{read-namespaced-template-UXU7OCY3.js → read-namespaced-template-EIH2NPDJ.js} +2 -2
  143. package/package.json +1 -1
  144. package/dist/chunk-TIJEHWRF.js.map +0 -1
  145. /package/dist/{read-namespaced-template-UXU7OCY3.js.map → read-namespaced-template-EIH2NPDJ.js.map} +0 -0
@@ -0,0 +1,89 @@
1
+ export function renderColumnsValueTemplate({
2
+ accessor,
3
+ tableColumnSize,
4
+ buildSortableHeaderDefBuildSortableHeaderDef,
5
+ fTextarea
6
+ }: {
7
+ accessor: string
8
+ tableColumnSize: string
9
+ buildSortableHeaderDefBuildSortableHeaderDef: string
10
+ fTextarea: string
11
+ }): string {
12
+ return ` {
13
+ accessorKey: '${accessor}',
14
+ size: ${tableColumnSize},
15
+ ${buildSortableHeaderDefBuildSortableHeaderDef},
16
+ cell: ({ row }) => {
17
+ const value = row.getValue('${accessor}')
18
+ return <div>${fTextarea}</div>
19
+ }
20
+ }`
21
+ }
22
+
23
+ export function renderColumnsLayoutTemplate({ pascal }: { pascal: string }): string {
24
+ return ` {
25
+ id: 'actions',
26
+ size: 64,
27
+ enableResizing: false,
28
+ meta: { width: '64px' },
29
+ header: () => <span className="sr-only">Actions</span>,
30
+ cell: ({ row }) => (
31
+ <div className="flex justify-end">
32
+ <${pascal}Actions id={row.original.id} />
33
+ </div>
34
+ )
35
+ }`
36
+ }
37
+
38
+ export function renderColumnsClientModuleTemplate({
39
+ pascal,
40
+ actionImportPath,
41
+ actionsImport,
42
+ hasBadgeBadge,
43
+ columnDefsContent,
44
+ actionsColumnContent
45
+ }: {
46
+ pascal: string
47
+ actionImportPath: string
48
+ actionsImport: string
49
+ hasBadgeBadge: string
50
+ columnDefsContent: string
51
+ actionsColumnContent: string
52
+ }): string {
53
+ return `'use client'
54
+
55
+ import { SortIndicator } from '@admin/components/shared/sort-indicator'
56
+ import type { ${pascal}SubmissionData } from '@admin/actions/${actionImportPath}'
57
+ ${actionsImport}${hasBadgeBadge}import { Button } from '@admin/components/ui/button'
58
+ import { Checkbox } from '@admin/components/ui/checkbox'
59
+ import type { ColumnDef } from '@tanstack/react-table'
60
+
61
+ export const columns: ColumnDef<${pascal}SubmissionData>[] = [
62
+ {
63
+ id: 'select',
64
+ size: 50,
65
+ enableResizing: false,
66
+ meta: { width: '50px' },
67
+ header: ({ table }) => (
68
+ <div className="flex items-center">
69
+ <Checkbox
70
+ checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate')}
71
+ onCheckedChange={(value: boolean) => table.toggleAllPageRowsSelected(!!value)}
72
+ aria-label="Select all"
73
+ />
74
+ </div>
75
+ ),
76
+ cell: ({ row }) => (
77
+ <Checkbox
78
+ checked={row.getIsSelected()}
79
+ onCheckedChange={(value: boolean) => row.toggleSelected(!!value)}
80
+ aria-label="Select row"
81
+ />
82
+ ),
83
+ enableSorting: false,
84
+ enableHiding: false
85
+ },
86
+ ${columnDefsContent}${actionsColumnContent}
87
+ ]
88
+ `
89
+ }
@@ -0,0 +1,23 @@
1
+ export function renderPageImportsTemplate({
2
+ pascal,
3
+ kebab
4
+ }: {
5
+ pascal: string
6
+ kebab: string
7
+ }): string {
8
+ return `import { getSetting } from '@admin/actions/settings'
9
+ import * as React from 'react'
10
+ import { ${pascal}SubmissionsPageContent } from './${kebab}-submissions-page-content'
11
+ import { ${pascal}SubmissionsPageSkeleton } from './${kebab}-submissions-page-skeleton'
12
+
13
+ export default async function Page() {
14
+ const initialSettings = await getSetting()
15
+
16
+ return (
17
+ <React.Suspense fallback={<${pascal}SubmissionsPageSkeleton />}>
18
+ <${pascal}SubmissionsPageContent initialSettings={initialSettings} />
19
+ </React.Suspense>
20
+ )
21
+ }
22
+ `
23
+ }
@@ -0,0 +1,246 @@
1
+ export function renderPageContentActionButtonsTemplate(): string {
2
+ return `
3
+ <Button
4
+ variant="outline"
5
+ onClick={() => exportCsv.mutate()}
6
+ disabled={exportCsv.isPending}
7
+ aria-busy={exportCsv.isPending}
8
+ >
9
+ {exportCsv.isPending ? <Spinner className="size-3.5" /> : <Download className="size-3.5" />}
10
+ {exportCsv.isPending ? 'Exporting CSV…' : 'Export CSV'}
11
+ </Button>`
12
+ }
13
+
14
+ export function renderPageContentHandleBulkDeleteTemplate({
15
+ pascal,
16
+ camel
17
+ }: {
18
+ pascal: string
19
+ camel: string
20
+ }): string {
21
+ return `
22
+ const handleBulkDelete = () => {
23
+ startTransition(async () => {
24
+ try {
25
+ const result = await deleteBulk${pascal}Submissions(selectedIds)
26
+
27
+ if (result.success) {
28
+ toast.success(
29
+ selectedIds.length === 1
30
+ ? \`\${selectedIds.length} submission deleted\`
31
+ : \`\${selectedIds.length} submissions deleted\`
32
+ )
33
+ queryClient.refetchQueries({ queryKey: ['${camel}-submissions'] })
34
+ void setSelectedIds([])
35
+ } else {
36
+ toast.error(result.error || 'Failed to delete the selected submissions')
37
+ }
38
+ } catch (error) {
39
+ toast.error('Failed to delete the selected submissions. Try again.')
40
+ console.error(error)
41
+ }
42
+ })
43
+ }
44
+ `
45
+ }
46
+
47
+ export function renderPageContentLayoutTemplate(): string {
48
+ return `
49
+ actions={
50
+ <div className="flex items-center gap-1">
51
+ {selectedIds.length > 0 && (
52
+ <DeleteDialog
53
+ count={selectedIds.length}
54
+ entityName="submission"
55
+ onConfirm={handleBulkDelete}
56
+ isPending={isPending}
57
+ />
58
+ )}
59
+ </div>
60
+ }`
61
+ }
62
+
63
+ export function renderPageContentClientModuleTemplate({
64
+ queryClientImport,
65
+ exportIconImport,
66
+ toastImport,
67
+ deleteActionImport,
68
+ deleteDialogImport,
69
+ exportSpinnerImport,
70
+ devModeIntegrateButtonExportName,
71
+ devModeIntegrateButtonImportPath,
72
+ exportHookImport,
73
+ pascal,
74
+ kebab,
75
+ queryClient,
76
+ camel,
77
+ exportHook,
78
+ deleteTransition,
79
+ bulkDeleteHandler,
80
+ label,
81
+ exportButton,
82
+ adminRoutePath,
83
+ toLowerCase,
84
+ bulkDeleteActions
85
+ }: {
86
+ queryClientImport: string
87
+ exportIconImport: string
88
+ toastImport: string
89
+ deleteActionImport: string
90
+ deleteDialogImport: string
91
+ exportSpinnerImport: string
92
+ devModeIntegrateButtonExportName: string
93
+ devModeIntegrateButtonImportPath: string
94
+ exportHookImport: string
95
+ pascal: string
96
+ kebab: string
97
+ queryClient: string
98
+ camel: string
99
+ exportHook: string
100
+ deleteTransition: string
101
+ bulkDeleteHandler: string
102
+ label: string
103
+ exportButton: string
104
+ adminRoutePath: string
105
+ toLowerCase: string
106
+ bulkDeleteActions: string
107
+ }): string {
108
+ return `'use client'
109
+
110
+ import type { Setting } from '@admin/actions/settings'
111
+ import { useIsFetching${queryClientImport} } from '@tanstack/react-query'
112
+ import { ${exportIconImport}Settings } from 'lucide-react'
113
+ import Link from 'next/link'
114
+ import { parseAsArrayOf, parseAsInteger, parseAsString, useQueryState, useQueryStates } from 'nuqs'
115
+ import * as React from 'react'
116
+ ${toastImport}
117
+ ${deleteActionImport}
118
+ ${deleteDialogImport}
119
+ import { EntityFiltersBar } from '@admin/components/shared/entity-filters-bar'
120
+ import { PageHeader } from '@admin/components/shared/page-header'
121
+ import { Button } from '@admin/components/ui/button'${exportSpinnerImport}
122
+ import { ${devModeIntegrateButtonExportName} } from '${devModeIntegrateButtonImportPath}'${exportHookImport}
123
+ import { columns } from './columns'
124
+ import { ${pascal}SubmissionDrawer } from './${kebab}-submission-drawer'
125
+ import { ${pascal}SubmissionsTable } from './${kebab}-submissions-table'
126
+
127
+ interface ${pascal}SubmissionsPageContentProps {
128
+ initialSettings?: Setting | null
129
+ }
130
+
131
+ export function ${pascal}SubmissionsPageContent({
132
+ initialSettings
133
+ }: ${pascal}SubmissionsPageContentProps) {
134
+ ${queryClient} const isSearchFetching = useIsFetching({ queryKey: ['${camel}-submissions'] }) > 0${exportHook}
135
+ const [search, setSearch] = useQueryState('q', parseAsString.withDefault(''))
136
+ const [, setPageIndex] = useQueryState('page', parseAsInteger.withDefault(0))
137
+ const [selectedSubmissionId, setSelectedSubmissionId] = useQueryState(
138
+ 'submission',
139
+ parseAsString.withDefault('')
140
+ )
141
+ const filterParsers = {
142
+ submittedAtFrom: parseAsString.withDefault(''),
143
+ submittedAtTo: parseAsString.withDefault(''),
144
+ } as const
145
+ const [filterValues, setFilters] = useQueryStates(filterParsers)
146
+ const submittedAtFrom = filterValues.submittedAtFrom
147
+ const submittedAtTo = filterValues.submittedAtTo
148
+ const [selectedIds, setSelectedIds] = useQueryState(
149
+ 'selected',
150
+ parseAsArrayOf(parseAsString).withDefault([])
151
+ )
152
+ ${deleteTransition}${bulkDeleteHandler}
153
+
154
+ const activeFilterCount = [search, (submittedAtFrom || submittedAtTo)].filter(Boolean).length
155
+
156
+ const handleViewSubmission = React.useCallback((id: string) => {
157
+ void setSelectedSubmissionId(id)
158
+ }, [setSelectedSubmissionId])
159
+
160
+ const handleSubmissionDrawerOpenChange = React.useCallback((open: boolean) => {
161
+ if (!open) {
162
+ void setSelectedSubmissionId(null)
163
+ }
164
+ }, [setSelectedSubmissionId])
165
+
166
+ const handleSearch = React.useCallback((value: string | null) => {
167
+ React.startTransition(() => {
168
+ void setPageIndex(0)
169
+ void setSearch(value)
170
+ })
171
+ }, [setSearch, setPageIndex])
172
+
173
+ const handleClearAll = React.useCallback(() => {
174
+ React.startTransition(() => {
175
+ void setPageIndex(0)
176
+ void setSearch(null)
177
+ void setFilters({
178
+ submittedAtFrom: null,
179
+ submittedAtTo: null
180
+ })
181
+ })
182
+ }, [setSearch, setFilters, setPageIndex])
183
+
184
+ const filters = [
185
+ {
186
+ key: 'submittedAt',
187
+ label: 'Submitted',
188
+ type: 'date-range' as const,
189
+ from: submittedAtFrom,
190
+ to: submittedAtTo
191
+ }
192
+ ]
193
+
194
+ return (
195
+ <React.Fragment>
196
+ <PageHeader
197
+ title="${label}"
198
+ actions={
199
+ <div className="flex items-center gap-1">
200
+ <${devModeIntegrateButtonExportName} initialSettings={initialSettings} />${exportButton}
201
+ <Button variant="outline" asChild>
202
+ <Link href="${adminRoutePath}/settings/forms">
203
+ <Settings className="size-3.5" />
204
+ Settings
205
+ </Link>
206
+ </Button>
207
+ </div>
208
+ }
209
+ />
210
+ <EntityFiltersBar
211
+ filters={filters}
212
+ activeFilterCount={activeFilterCount}
213
+ onFilterChange={(updates) => {
214
+ React.startTransition(() => {
215
+ void setPageIndex(0)
216
+ void setFilters(updates)
217
+ })
218
+ }}
219
+ onClearAll={handleClearAll}
220
+ search={search}
221
+ searchPlaceholder="Search ${toLowerCase} submissions..."
222
+ isPending={isSearchFetching}
223
+ onSearch={handleSearch}
224
+ ${bulkDeleteActions}
225
+ />
226
+ <main className="space-y-4 px-4 pb-4 flex-1">
227
+ <${pascal}SubmissionsTable
228
+ columns={columns}
229
+ selectedIds={selectedIds}
230
+ setSelectedIds={setSelectedIds}
231
+ onViewSubmission={handleViewSubmission}
232
+ search={search}
233
+ submittedAtFrom={submittedAtFrom}
234
+ submittedAtTo={submittedAtTo}
235
+ />
236
+ </main>
237
+ <${pascal}SubmissionDrawer
238
+ open={Boolean(selectedSubmissionId)}
239
+ submissionId={selectedSubmissionId || null}
240
+ onOpenChange={handleSubmissionDrawerOpenChange}
241
+ />
242
+ </React.Fragment>
243
+ )
244
+ }
245
+ `
246
+ }
@@ -0,0 +1,216 @@
1
+ export function renderSubmissionDrawerLayoutTemplate({
2
+ lbl,
3
+ name
4
+ }: {
5
+ lbl: string
6
+ name: string
7
+ }): string {
8
+ return ` <div className="space-y-1">
9
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
10
+ <p className="text-sm">
11
+ {submission.${name} ? (
12
+ <a href={\`mailto:\${submission.${name}}\`} className="text-primary hover:underline">{submission.${name}}</a>
13
+ ) : '-'}
14
+ </p>
15
+ </div>`
16
+ }
17
+
18
+ export function renderSubmissionDrawerLayoutDetailsTemplate({
19
+ lbl,
20
+ name
21
+ }: {
22
+ lbl: string
23
+ name: string
24
+ }): string {
25
+ return ` <div className="space-y-1">
26
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
27
+ <p className="text-sm whitespace-pre-wrap">{submission.${name} || '-'}</p>
28
+ </div>`
29
+ }
30
+
31
+ export function renderSubmissionDrawerLayoutDetailsBranchTemplate({
32
+ lbl,
33
+ name
34
+ }: {
35
+ lbl: string
36
+ name: string
37
+ }): string {
38
+ return ` <div className="space-y-1">
39
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
40
+ <p className="text-sm">{submission.${name} ? 'Yes' : 'No'}</p>
41
+ </div>`
42
+ }
43
+
44
+ export function renderSubmissionDrawerLayoutDetailsBranchBodyTemplate({
45
+ lbl,
46
+ name
47
+ }: {
48
+ lbl: string
49
+ name: string
50
+ }): string {
51
+ return ` <div className="space-y-1">
52
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
53
+ <p className="text-sm">{submission.${name} ? new Date(submission.${name}).toLocaleDateString() : '-'}</p>
54
+ </div>`
55
+ }
56
+
57
+ export function renderSubmissionDrawerLayoutDetailsBranchBodyFragmentTemplate({
58
+ lbl,
59
+ name
60
+ }: {
61
+ lbl: string
62
+ name: string
63
+ }): string {
64
+ return ` <div className="space-y-1">
65
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
66
+ <p className="text-sm">
67
+ {submission.${name} ? (
68
+ <a href={submission.${name}} target="_blank" rel="noopener noreferrer" className="text-primary hover:underline break-all">{submission.${name}}</a>
69
+ ) : '-'}
70
+ </p>
71
+ </div>`
72
+ }
73
+
74
+ export function renderSubmissionDrawerLayoutDetailsBranchBodyFragmentSectionTemplate({
75
+ lbl,
76
+ name
77
+ }: {
78
+ lbl: string
79
+ name: string
80
+ }): string {
81
+ return ` <div className="space-y-1">
82
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
83
+ <div className="text-sm">
84
+ {Array.isArray(submission.${name}) && submission.${name}.length > 0 ? (
85
+ <ul className="list-inside list-disc space-y-1">
86
+ {submission.${name}.map((item: unknown) => {
87
+ const itemLabel = typeof item === 'string'
88
+ ? item
89
+ : item && typeof item === 'object'
90
+ ? Object.entries(item).map(([key, value]) => \`\${key}: \${String(value)}\`).join(', ')
91
+ : String(item)
92
+ return <li key={itemLabel}>{itemLabel}</li>
93
+ })}
94
+ </ul>
95
+ ) : '-'}
96
+ </div>
97
+ </div>`
98
+ }
99
+
100
+ export function renderSubmissionDrawerLayoutDetailsBranchBodyFragmentSectionVariantTemplate({
101
+ lbl,
102
+ name
103
+ }: {
104
+ lbl: string
105
+ name: string
106
+ }): string {
107
+ return ` <div className="space-y-1">
108
+ <p className="text-sm font-medium text-muted-foreground">${lbl}</p>
109
+ <p className="text-sm">{submission.${name} ?? '-'}</p>
110
+ </div>`
111
+ }
112
+
113
+ export function renderSubmissionDrawerLayoutDetailsBranchBodyFragmentSectionVariantContinuationTemplate(): string {
114
+ return `
115
+ {submission.customFields && Object.keys(submission.customFields).length > 0 && (
116
+ <div className="space-y-3 border-t pt-4">
117
+ <p className="text-sm font-semibold text-muted-foreground">Custom Fields</p>
118
+ {Object.entries(submission.customFields).map(([key, value]) => (
119
+ <div key={key} className="space-y-1">
120
+ <p className="text-sm font-medium capitalize">{key.replace(/([A-Z])/g, ' $1').trim()}</p>
121
+ <p className="text-sm">{value !== null && value !== undefined ? String(value) : '-'}</p>
122
+ </div>
123
+ ))}
124
+ </div>
125
+ )}`
126
+ }
127
+
128
+ export function renderSubmissionDrawerClientModuleTemplate({
129
+ pascal,
130
+ kebab,
131
+ label,
132
+ fieldItems,
133
+ customFieldsSection
134
+ }: {
135
+ pascal: string
136
+ kebab: string
137
+ label: string
138
+ fieldItems: string
139
+ customFieldsSection: string
140
+ }): string {
141
+ return `'use client'
142
+
143
+ import {
144
+ Drawer,
145
+ DrawerContent,
146
+ DrawerDescription,
147
+ DrawerHeader,
148
+ DrawerTitle
149
+ } from '@admin/components/ui/drawer'
150
+ import { ScrollArea } from '@admin/components/ui/scroll-area'
151
+ import { use${pascal}Submission } from '@admin/hooks/use-${kebab}-form'
152
+
153
+ interface ${pascal}SubmissionDrawerProps {
154
+ open: boolean
155
+ submissionId: string | null
156
+ onOpenChange: (open: boolean) => void
157
+ }
158
+
159
+ function formatDateTime(value: string): string {
160
+ const date = new Date(value)
161
+ if (Number.isNaN(date.getTime())) return value
162
+
163
+ return new Intl.DateTimeFormat('en-US', {
164
+ dateStyle: 'medium',
165
+ timeStyle: 'medium'
166
+ }).format(date)
167
+ }
168
+
169
+ export function ${pascal}SubmissionDrawer({
170
+ open,
171
+ submissionId,
172
+ onOpenChange
173
+ }: ${pascal}SubmissionDrawerProps) {
174
+ const { data: submission, error, isPending } = use${pascal}Submission(
175
+ open ? submissionId : null
176
+ )
177
+ const description = submission
178
+ ? \`Submitted \${formatDateTime(submission.submittedAt)}\`
179
+ : submissionId
180
+ ? \`Submission \${submissionId}\`
181
+ : 'No submission selected'
182
+
183
+ return (
184
+ <Drawer direction="right" open={open} onOpenChange={onOpenChange} modal>
185
+ <DrawerContent className="h-full w-full overflow-hidden data-[vaul-drawer-direction=right]:sm:max-w-xl">
186
+ <DrawerHeader>
187
+ <DrawerTitle>${label} Submission</DrawerTitle>
188
+ <DrawerDescription>{description}</DrawerDescription>
189
+ </DrawerHeader>
190
+ <ScrollArea className="min-h-0 flex-1">
191
+ {isPending ? (
192
+ <div className="p-4 text-sm text-muted-foreground">Loading submission…</div>
193
+ ) : error ? (
194
+ <div className="p-4 text-sm text-destructive">
195
+ Error loading submission: {error.message}
196
+ </div>
197
+ ) : submission ? (
198
+ <div className="p-4">
199
+ <div className="space-y-4 rounded-sm border border-border bg-card p-4">
200
+ ${fieldItems}${customFieldsSection}
201
+ <div className="space-y-1 border-t pt-4">
202
+ <p className="text-sm font-medium text-muted-foreground">Submitted At</p>
203
+ <p className="text-sm">{formatDateTime(submission.submittedAt)}</p>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ ) : submissionId ? (
208
+ <div className="p-4 text-sm text-muted-foreground">Submission not found.</div>
209
+ ) : null}
210
+ </ScrollArea>
211
+ </DrawerContent>
212
+ </Drawer>
213
+ )
214
+ }
215
+ `
216
+ }