@postxl/generators 1.12.3 → 1.14.0

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 (195) hide show
  1. package/dist/backend-ai/ai.generator.d.ts +18 -0
  2. package/dist/backend-ai/ai.generator.js +174 -0
  3. package/dist/backend-ai/ai.generator.js.map +1 -0
  4. package/dist/backend-ai/generators/ai-agent-service.generator.d.ts +4 -0
  5. package/dist/backend-ai/generators/ai-agent-service.generator.js +264 -0
  6. package/dist/backend-ai/generators/ai-agent-service.generator.js.map +1 -0
  7. package/dist/backend-ai/generators/ai-cache-service.generator.d.ts +1 -0
  8. package/dist/backend-ai/generators/ai-cache-service.generator.js +110 -0
  9. package/dist/backend-ai/generators/ai-cache-service.generator.js.map +1 -0
  10. package/dist/backend-ai/generators/ai-config.generator.d.ts +1 -0
  11. package/dist/backend-ai/generators/ai-config.generator.js +27 -0
  12. package/dist/backend-ai/generators/ai-config.generator.js.map +1 -0
  13. package/dist/backend-ai/generators/ai-module.generator.d.ts +2 -0
  14. package/dist/backend-ai/generators/ai-module.generator.js +89 -0
  15. package/dist/backend-ai/generators/ai-module.generator.js.map +1 -0
  16. package/dist/backend-ai/generators/ai-route.generator.d.ts +1 -0
  17. package/dist/backend-ai/generators/ai-route.generator.js +29 -0
  18. package/dist/backend-ai/generators/ai-route.generator.js.map +1 -0
  19. package/dist/backend-ai/generators/ai-tools-service.generator.d.ts +4 -0
  20. package/dist/backend-ai/generators/ai-tools-service.generator.js +222 -0
  21. package/dist/backend-ai/generators/ai-tools-service.generator.js.map +1 -0
  22. package/dist/backend-ai/generators/model-provider-interface.generator.d.ts +1 -0
  23. package/dist/backend-ai/generators/model-provider-interface.generator.js +48 -0
  24. package/dist/backend-ai/generators/model-provider-interface.generator.js.map +1 -0
  25. package/dist/backend-ai/generators/openai-model-provider-service.generator.d.ts +1 -0
  26. package/dist/backend-ai/generators/openai-model-provider-service.generator.js +128 -0
  27. package/dist/backend-ai/generators/openai-model-provider-service.generator.js.map +1 -0
  28. package/dist/backend-ai/index.d.ts +4 -0
  29. package/dist/backend-ai/index.js +40 -0
  30. package/dist/backend-ai/index.js.map +1 -0
  31. package/dist/backend-authentication/generators/authentication-service.generator.js +1 -1
  32. package/dist/backend-core/backend.generator.js +0 -4
  33. package/dist/backend-core/backend.generator.js.map +1 -1
  34. package/dist/backend-core/generators/main.generator.js +4 -3
  35. package/dist/backend-core/generators/main.generator.js.map +1 -1
  36. package/dist/backend-core/modules/backend-module-xlport.generator.js +1 -1
  37. package/dist/backend-e2e/backend-e2e.generator.js +4 -4
  38. package/dist/backend-e2e/backend-e2e.generator.js.map +1 -1
  39. package/dist/backend-excel-io/excel-io.generator.d.ts +19 -0
  40. package/dist/backend-excel-io/excel-io.generator.js +106 -0
  41. package/dist/backend-excel-io/excel-io.generator.js.map +1 -0
  42. package/dist/backend-excel-io/generators/excel-io-service.generator.d.ts +9 -0
  43. package/dist/backend-excel-io/generators/excel-io-service.generator.js +680 -0
  44. package/dist/backend-excel-io/generators/excel-io-service.generator.js.map +1 -0
  45. package/dist/backend-excel-io/index.d.ts +2 -0
  46. package/dist/backend-excel-io/index.js +22 -0
  47. package/dist/backend-excel-io/index.js.map +1 -0
  48. package/dist/backend-excel-io/template/README.md +24 -0
  49. package/dist/backend-excel-io/template/excel-io.controller.ts +195 -0
  50. package/dist/backend-excel-io/template/excel-io.module.ts +17 -0
  51. package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js +149 -14
  52. package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js.map +1 -1
  53. package/dist/backend-import/generators/filter-error-rows.generator.d.ts +2 -0
  54. package/dist/backend-import/generators/filter-error-rows.generator.js +28 -0
  55. package/dist/backend-import/generators/filter-error-rows.generator.js.map +1 -0
  56. package/dist/backend-import/generators/import-service.generator.js +126 -2
  57. package/dist/backend-import/generators/import-service.generator.js.map +1 -1
  58. package/dist/backend-import/import.generator.js +2 -0
  59. package/dist/backend-import/import.generator.js.map +1 -1
  60. package/dist/backend-repositories/generators/model-repository.generator.js +17 -2
  61. package/dist/backend-repositories/generators/model-repository.generator.js.map +1 -1
  62. package/dist/backend-router-trpc/generators/app-routes.generator.js +8 -1
  63. package/dist/backend-router-trpc/generators/app-routes.generator.js.map +1 -1
  64. package/dist/backend-router-trpc/generators/excel-io-route.generator.d.ts +4 -0
  65. package/dist/backend-router-trpc/generators/excel-io-route.generator.js +35 -0
  66. package/dist/backend-router-trpc/generators/excel-io-route.generator.js.map +1 -0
  67. package/dist/backend-router-trpc/generators/trpc-plugin.generator.js +9 -0
  68. package/dist/backend-router-trpc/generators/trpc-plugin.generator.js.map +1 -1
  69. package/dist/backend-router-trpc/generators/trpc-router-module.generator.js +9 -1
  70. package/dist/backend-router-trpc/generators/trpc-router-module.generator.js.map +1 -1
  71. package/dist/backend-router-trpc/generators/trpc-shared.generator.js +16 -1
  72. package/dist/backend-router-trpc/generators/trpc-shared.generator.js.map +1 -1
  73. package/dist/backend-router-trpc/router-trpc.generator.d.ts +3 -1
  74. package/dist/backend-router-trpc/router-trpc.generator.js +6 -0
  75. package/dist/backend-router-trpc/router-trpc.generator.js.map +1 -1
  76. package/dist/backend-seed/seed.generator.js +10 -1
  77. package/dist/backend-seed/seed.generator.js.map +1 -1
  78. package/dist/base/base.generator.js +0 -4
  79. package/dist/base/base.generator.js.map +1 -1
  80. package/dist/base/template/scripts/setup.sh +9 -4
  81. package/dist/base/template/sonar-project.properties +9 -1
  82. package/dist/decoders/datamodel-decoder.generator.js +91 -1
  83. package/dist/decoders/datamodel-decoder.generator.js.map +1 -1
  84. package/dist/decoders/decoders.generator.d.ts +9 -0
  85. package/dist/decoders/decoders.generator.js +15 -0
  86. package/dist/decoders/decoders.generator.js.map +1 -1
  87. package/dist/devops/devops.generator.d.ts +5 -1
  88. package/dist/devops/devops.generator.js +5 -4
  89. package/dist/devops/devops.generator.js.map +1 -1
  90. package/dist/devops/generators/bitbucket-pipelines-yml.generator.js +1 -0
  91. package/dist/devops/generators/bitbucket-pipelines-yml.generator.js.map +1 -1
  92. package/dist/devops/generators/docker-compose-yml.generator.d.ts +1 -1
  93. package/dist/devops/generators/docker-compose-yml.generator.js +16 -1
  94. package/dist/devops/generators/docker-compose-yml.generator.js.map +1 -1
  95. package/dist/devops/generators/e2e-yml.generator.js +35 -10
  96. package/dist/devops/generators/e2e-yml.generator.js.map +1 -1
  97. package/dist/devops/generators/jenkinsfile.generator.js +25 -1
  98. package/dist/devops/generators/jenkinsfile.generator.js.map +1 -1
  99. package/dist/e2e/template/e2e/specs/example.spec.ts-snapshots/Navigate-to-homepage-and-take-snapshot-1-chromium-linux.png +0 -0
  100. package/dist/frontend-actions/actions.generator.d.ts +9 -0
  101. package/dist/frontend-actions/actions.generator.js +111 -0
  102. package/dist/frontend-actions/actions.generator.js.map +1 -0
  103. package/dist/frontend-actions/generators/ai-action-text.utils.generator.d.ts +1 -0
  104. package/dist/frontend-actions/generators/ai-action-text.utils.generator.js +52 -0
  105. package/dist/frontend-actions/generators/ai-action-text.utils.generator.js.map +1 -0
  106. package/dist/frontend-actions/generators/ai-assistant-store.generator.d.ts +1 -0
  107. package/dist/frontend-actions/generators/ai-assistant-store.generator.js +230 -0
  108. package/dist/frontend-actions/generators/ai-assistant-store.generator.js.map +1 -0
  109. package/dist/frontend-actions/generators/ai-sidebar-content.generator.d.ts +1 -0
  110. package/dist/frontend-actions/generators/ai-sidebar-content.generator.js +139 -0
  111. package/dist/frontend-actions/generators/ai-sidebar-content.generator.js.map +1 -0
  112. package/dist/frontend-actions/generators/ai-sidepane.generator.d.ts +1 -0
  113. package/dist/frontend-actions/generators/ai-sidepane.generator.js +98 -0
  114. package/dist/frontend-actions/generators/ai-sidepane.generator.js.map +1 -0
  115. package/dist/frontend-actions/generators/base-global-actions.generator.d.ts +1 -0
  116. package/dist/frontend-actions/generators/base-global-actions.generator.js +405 -0
  117. package/dist/frontend-actions/generators/base-global-actions.generator.js.map +1 -0
  118. package/dist/frontend-actions/generators/command-palette-action.generator.d.ts +1 -0
  119. package/dist/frontend-actions/generators/command-palette-action.generator.js +87 -0
  120. package/dist/frontend-actions/generators/command-palette-action.generator.js.map +1 -0
  121. package/dist/frontend-actions/generators/command-palette-store.generator.d.ts +1 -0
  122. package/dist/frontend-actions/generators/command-palette-store.generator.js +288 -0
  123. package/dist/frontend-actions/generators/command-palette-store.generator.js.map +1 -0
  124. package/dist/frontend-actions/generators/command-palette.generator.d.ts +5 -0
  125. package/dist/frontend-actions/generators/command-palette.generator.js +332 -0
  126. package/dist/frontend-actions/generators/command-palette.generator.js.map +1 -0
  127. package/dist/frontend-actions/generators/filter-utils.generator.d.ts +1 -0
  128. package/dist/frontend-actions/generators/filter-utils.generator.js +50 -0
  129. package/dist/frontend-actions/generators/filter-utils.generator.js.map +1 -0
  130. package/dist/frontend-actions/generators/sidepanel-toggle.generator.d.ts +1 -0
  131. package/dist/frontend-actions/generators/sidepanel-toggle.generator.js +37 -0
  132. package/dist/frontend-actions/generators/sidepanel-toggle.generator.js.map +1 -0
  133. package/dist/frontend-actions/index.d.ts +4 -0
  134. package/dist/frontend-actions/index.js +40 -0
  135. package/dist/frontend-actions/index.js.map +1 -0
  136. package/dist/frontend-admin/admin.generator.d.ts +5 -1
  137. package/dist/frontend-admin/admin.generator.js +36 -1
  138. package/dist/frontend-admin/admin.generator.js.map +1 -1
  139. package/dist/frontend-admin/generators/admin-global-actions.generator.d.ts +4 -0
  140. package/dist/frontend-admin/generators/admin-global-actions.generator.js +230 -0
  141. package/dist/frontend-admin/generators/admin-global-actions.generator.js.map +1 -0
  142. package/dist/frontend-admin/generators/admin-overview-page.generator.js +21 -1
  143. package/dist/frontend-admin/generators/admin-overview-page.generator.js.map +1 -1
  144. package/dist/frontend-admin/generators/admin-sidebar.generator.js +20 -0
  145. package/dist/frontend-admin/generators/admin-sidebar.generator.js.map +1 -1
  146. package/dist/frontend-admin/generators/detail-sidebar.generator.js +44 -32
  147. package/dist/frontend-admin/generators/detail-sidebar.generator.js.map +1 -1
  148. package/dist/frontend-admin/generators/excel-io-page.generator.d.ts +4 -0
  149. package/dist/frontend-admin/generators/excel-io-page.generator.js +258 -0
  150. package/dist/frontend-admin/generators/excel-io-page.generator.js.map +1 -0
  151. package/dist/frontend-admin/generators/import-review-page-result-stage.generator.d.ts +1 -0
  152. package/dist/frontend-admin/generators/import-review-page-result-stage.generator.js +104 -0
  153. package/dist/frontend-admin/generators/import-review-page-result-stage.generator.js.map +1 -0
  154. package/dist/frontend-admin/generators/import-review-page-review-stage.generator.d.ts +1 -0
  155. package/dist/frontend-admin/generators/import-review-page-review-stage.generator.js +1031 -0
  156. package/dist/frontend-admin/generators/import-review-page-review-stage.generator.js.map +1 -0
  157. package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.d.ts +1 -0
  158. package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.js +77 -0
  159. package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.js.map +1 -0
  160. package/dist/frontend-admin/generators/import-review-page.generator.d.ts +7 -0
  161. package/dist/frontend-admin/generators/import-review-page.generator.js +180 -0
  162. package/dist/frontend-admin/generators/import-review-page.generator.js.map +1 -0
  163. package/dist/frontend-admin/generators/model-admin-page.generator.js +198 -60
  164. package/dist/frontend-admin/generators/model-admin-page.generator.js.map +1 -1
  165. package/dist/frontend-admin/utils.d.ts +1 -0
  166. package/dist/frontend-admin/utils.js +26 -33
  167. package/dist/frontend-admin/utils.js.map +1 -1
  168. package/dist/frontend-core/frontend.generator.js +1 -2
  169. package/dist/frontend-core/frontend.generator.js.map +1 -1
  170. package/dist/frontend-core/generators/tsconfig.generator.js +1 -0
  171. package/dist/frontend-core/generators/tsconfig.generator.js.map +1 -1
  172. package/dist/frontend-core/template/.env.example +3 -0
  173. package/dist/frontend-core/template/src/components/admin/excel-io-actions.tsx +64 -0
  174. package/dist/frontend-core/template/src/components/admin/table-view-panel.tsx +41 -3
  175. package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +1 -1
  176. package/dist/frontend-core/template/src/hooks/use-excel-io.ts +137 -0
  177. package/dist/frontend-core/template/src/hooks/use-import-review.ts +143 -0
  178. package/dist/frontend-core/template/src/lib/color.ts +6 -3
  179. package/dist/frontend-core/template/src/lib/config.ts +3 -1
  180. package/dist/frontend-core/template/src/lib/excel-download.ts +28 -0
  181. package/dist/frontend-core/types/hook.d.ts +1 -1
  182. package/dist/frontend-tables/generators/model-table.generator.js +21 -13
  183. package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
  184. package/dist/generators.js +6 -0
  185. package/dist/generators.js.map +1 -1
  186. package/dist/index.d.ts +3 -0
  187. package/dist/index.js +11 -2
  188. package/dist/index.js.map +1 -1
  189. package/dist/seed-data/seed-data.generator.d.ts +3 -0
  190. package/dist/seed-data/seed-data.generator.js +45 -1
  191. package/dist/seed-data/seed-data.generator.js.map +1 -1
  192. package/dist/types/template/ai.types.ts +34 -0
  193. package/dist/types/types.generator.js +1 -0
  194. package/dist/types/types.generator.js.map +1 -1
  195. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-review-page-review-stage.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/import-review-page-review-stage.generator.ts"],"names":[],"mappings":";;AAAA,0EAkgCC;AAlgCD,SAAgB,+BAA+B;IAC7C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAggCR,CAAA;AACD,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateImportReviewUploadStage(): string;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateImportReviewUploadStage = generateImportReviewUploadStage;
4
+ function generateImportReviewUploadStage() {
5
+ return `
6
+ function UploadStage({
7
+ isLoading,
8
+ onUpload,
9
+ }: Readonly<{
10
+ isLoading: boolean
11
+ onUpload: (file: File, options: { includeDetailedUnchangedRecords: boolean }) => Promise<void>
12
+ }>) {
13
+ const [selectedFile, setSelectedFile] = useState<File | null>(null)
14
+ const [includeDetailedUnchangedRecords, setIncludeDetailedUnchangedRecords] = useState(false)
15
+
16
+ return (
17
+ <Card>
18
+ <CardHeader>
19
+ <CardTitle>Upload Excel File</CardTitle>
20
+ </CardHeader>
21
+ <CardContent className="space-y-4">
22
+ <div className="space-y-2">
23
+ <Label htmlFor="import-review-file">Select an .xlsx or .xls file to preview before importing</Label>
24
+ <Input
25
+ id="import-review-file"
26
+ type="file"
27
+ accept=".xlsx,.xls"
28
+ disabled={isLoading}
29
+ onChange={(event) => setSelectedFile(event.target.files?.[0] ?? null)}
30
+ />
31
+ {selectedFile && <p className="text-sm text-muted-foreground">Selected file: {selectedFile.name}</p>}
32
+ </div>
33
+
34
+ <div className="flex items-center gap-2">
35
+ <Checkbox
36
+ id="detailed-unchanged"
37
+ checked={includeDetailedUnchangedRecords}
38
+ checkIcon="check"
39
+ onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
40
+ setIncludeDetailedUnchangedRecords(event.target.checked)
41
+ }
42
+ />
43
+ <Label htmlFor="detailed-unchanged">Include detailed unchanged records</Label>
44
+ <TooltipProvider delayDuration={150}>
45
+ <Tooltip>
46
+ <TooltipTrigger asChild>
47
+ <button
48
+ type="button"
49
+ aria-label="Info about detailed unchanged records"
50
+ className="inline-flex h-5 w-5 items-center justify-center rounded-full border text-xs text-muted-foreground"
51
+ >
52
+ i
53
+ </button>
54
+ </TooltipTrigger>
55
+ <TooltipContent>
56
+ <p>On large datasets, including unchanged row details may take longer.</p>
57
+ </TooltipContent>
58
+ </Tooltip>
59
+ </TooltipProvider>
60
+ </div>
61
+
62
+ <div className="flex gap-3">
63
+ <Button
64
+ disabled={isLoading || !selectedFile}
65
+ onClick={() => selectedFile && void onUpload(selectedFile, { includeDetailedUnchangedRecords })}
66
+ >
67
+ {isLoading ? 'Building preview...' : 'Create Preview'}
68
+ </Button>
69
+ </div>
70
+ </CardContent>
71
+ </Card>
72
+ )
73
+ }
74
+
75
+ `;
76
+ }
77
+ //# sourceMappingURL=import-review-page-upload-stage.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-review-page-upload-stage.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/import-review-page-upload-stage.generator.ts"],"names":[],"mappings":";;AAAA,0EAwEC;AAxED,SAAgB,+BAA+B;IAC7C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsER,CAAA;AACD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { ContextResult } from '../admin.generator';
2
+ /**
3
+ * Generator function that generates the Import Review admin page.
4
+ */
5
+ export declare function generateImportReviewPage({ context }: {
6
+ context: ContextResult;
7
+ }): string;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateImportReviewPage = generateImportReviewPage;
4
+ const import_review_page_result_stage_generator_1 = require("./import-review-page-result-stage.generator");
5
+ const import_review_page_review_stage_generator_1 = require("./import-review-page-review-stage.generator");
6
+ const import_review_page_upload_stage_generator_1 = require("./import-review-page-upload-stage.generator");
7
+ /**
8
+ * Generator function that generates the Import Review admin page.
9
+ */
10
+ function generateImportReviewPage({ context }) {
11
+ const models = [...context.models.values()].sort((a, b) => a.userFriendlyName.localeCompare(b.userFriendlyName));
12
+ const modelSchemaMapEntries = models
13
+ .map((model) => ` ${model._conjugated.camelCasePlural}: '${model.databaseSchema}',`)
14
+ .join('\n');
15
+ const modelLabelMapEntries = models
16
+ .map((model) => ` ${model._conjugated.camelCasePlural}: '${model.userFriendlyName.replaceAll("'", "\\'")}',`)
17
+ .join('\n');
18
+ const modelFieldOrderEntries = models
19
+ .map((model) => {
20
+ const fields = Array.from(model.fields.values())
21
+ .map((field) => `'${field.name}'`)
22
+ .join(', ');
23
+ return ` ${model._conjugated.camelCasePlural}: [${fields}],`;
24
+ })
25
+ .join('\n');
26
+ const schemaOrderEntries = [...new Set(models.map((model) => model.databaseSchema))]
27
+ .sort((a, b) => a.localeCompare(b))
28
+ .map((schema) => `'${schema}'`)
29
+ .join(', ');
30
+ return `
31
+ import { AdminSidebar } from '@admin/admin-sidebar'
32
+
33
+ import type { ColumnDef } from '@tanstack/react-table'
34
+ import { useEffect, useMemo, useState } from 'react'
35
+ import { AlertTriangleIcon, MinusIcon, PencilIcon, PlusIcon, Trash2Icon } from 'lucide-react'
36
+
37
+ import type {
38
+ ImportExecutionSummary,
39
+ ImportPreviewItem,
40
+ ImportPreviewModelSummary,
41
+ ImportPreviewResponse,
42
+ SelectedDeltaIndicesByModel,
43
+ } from '@hooks/use-import-review'
44
+ import { useImportReview } from '@hooks/use-import-review'
45
+ import {
46
+ Alert,
47
+ AlertDialog,
48
+ AlertDialogAction,
49
+ AlertDialogCancel,
50
+ AlertDialogContent,
51
+ AlertDialogDescription,
52
+ AlertDialogFooter,
53
+ AlertDialogHeader,
54
+ AlertDialogTitle,
55
+ AlertDescription,
56
+ Badge,
57
+ Button,
58
+ Card,
59
+ CardContent,
60
+ CardHeader,
61
+ CardTitle,
62
+ Checkbox,
63
+ DataGrid,
64
+ Input,
65
+ Label,
66
+ SidebarInset,
67
+ SidebarProvider,
68
+ Tooltip,
69
+ TooltipContent,
70
+ TooltipProvider,
71
+ TooltipTrigger,
72
+ cn,
73
+ useDataGrid,
74
+ } from '@postxl/ui-components'
75
+
76
+ const MODEL_SCHEMA_MAP: Record<string, string> = {
77
+ ${modelSchemaMapEntries}
78
+ }
79
+
80
+ const MODEL_LABEL_MAP: Record<string, string> = {
81
+ ${modelLabelMapEntries}
82
+ }
83
+
84
+ const MODEL_FIELD_ORDER: Record<string, string[]> = {
85
+ ${modelFieldOrderEntries}
86
+ }
87
+
88
+ const SCHEMA_ORDER: string[] = [${schemaOrderEntries}]
89
+
90
+ const TYPE_ORDER: ImportPreviewItem['type'][] = ['errors', 'delete', 'update', 'create', 'unchanged']
91
+
92
+ const TYPE_BADGE_CLASSES: Record<ImportPreviewItem['type'], string> = {
93
+ create: 'text-green-700 border-green-300 dark:text-green-400 dark:border-green-700',
94
+ update: 'text-blue-700 border-blue-300 dark:text-blue-400 dark:border-blue-700',
95
+ delete: 'text-red-700 border-red-300 dark:text-red-400 dark:border-red-700',
96
+ unchanged: 'text-muted-foreground border-border',
97
+ errors: 'text-red-700 border-red-300 dark:text-red-400 dark:border-red-700',
98
+ }
99
+
100
+ const EXECUTABLE_TYPES = new Set<ImportPreviewItem['type']>(['create', 'update', 'delete'])
101
+
102
+ export default function ImportReviewPage() {
103
+ const { stage, preview, executionResult, error, isLoading, uploadAndPreview, execute, reset } = useImportReview()
104
+
105
+ const showAdminSidebar = stage === 'upload' || stage === 'previewing' || stage === 'result'
106
+
107
+ return (
108
+ <SidebarProvider defaultOpen>
109
+ {showAdminSidebar && <AdminSidebar />}
110
+
111
+ <SidebarInset className="p-4">
112
+ <div className="mx-auto flex w-full max-w-[1400px] flex-col gap-4">
113
+ <PageHeader stage={stage} />
114
+
115
+ {error && (
116
+ <Alert variant="destructive">
117
+ <AlertDescription>{error}</AlertDescription>
118
+ </Alert>
119
+ )}
120
+
121
+ {(stage === 'upload' || stage === 'previewing') && (
122
+ <UploadStage isLoading={isLoading} onUpload={uploadAndPreview} />
123
+ )}
124
+
125
+ {stage === 'review' && preview && (
126
+ <ReviewStage preview={preview} isLoading={isLoading} onExecute={execute} onReset={reset} />
127
+ )}
128
+
129
+ {stage === 'executing' && (
130
+ <Card>
131
+ <CardContent className="py-8 text-center">
132
+ <p className="text-lg font-medium">Executing selected changes...</p>
133
+ <p className="mt-2 text-sm text-muted-foreground">Applying selected rows to the database.</p>
134
+ </CardContent>
135
+ </Card>
136
+ )}
137
+
138
+ {stage === 'result' && executionResult && <ResultStage result={executionResult} onReset={reset} />}
139
+ </div>
140
+ </SidebarInset>
141
+ </SidebarProvider>
142
+ )
143
+ }
144
+
145
+ function PageHeader({ stage }: Readonly<{ stage: 'upload' | 'previewing' | 'review' | 'executing' | 'result' }>) {
146
+ const currentStep = stage === 'upload' || stage === 'previewing' ? 1 : stage === 'review' || stage === 'executing' ? 2 : 3
147
+
148
+ return (
149
+ <div className="space-y-3">
150
+ <h1 className="text-3xl font-bold">Import Review</h1>
151
+ <p className="text-sm text-muted-foreground">
152
+ Upload an Excel file, review and select changes, then execute a controlled import.
153
+ </p>
154
+ <div className="grid grid-cols-3 gap-2">
155
+ <StepChip label="1. Upload" active={currentStep === 1} done={currentStep > 1} />
156
+ <StepChip label="2. Preview & Select" active={currentStep === 2} done={currentStep > 2} />
157
+ <StepChip label="3. Results" active={currentStep === 3} done={false} />
158
+ </div>
159
+ </div>
160
+ )
161
+ }
162
+
163
+ function StepChip({ label, active, done }: Readonly<{ label: string; active: boolean; done: boolean }>) {
164
+ const stateClass = done
165
+ ? 'border-green-300 bg-green-50 text-green-700 dark:border-green-800 dark:bg-green-950/40 dark:text-green-300'
166
+ : active
167
+ ? 'border-blue-300 bg-blue-50 text-blue-700 dark:border-blue-800 dark:bg-blue-950/40 dark:text-blue-300'
168
+ : 'border-border bg-background text-muted-foreground'
169
+
170
+ return <div className={cn('rounded border px-3 py-2 text-sm font-medium', stateClass)}>{label}</div>
171
+ }
172
+
173
+ ${(0, import_review_page_upload_stage_generator_1.generateImportReviewUploadStage)()}
174
+
175
+ ${(0, import_review_page_review_stage_generator_1.generateImportReviewReviewStage)()}
176
+
177
+ ${(0, import_review_page_result_stage_generator_1.generateImportReviewResultStage)()}
178
+ `;
179
+ }
180
+ //# sourceMappingURL=import-review-page.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-review-page.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/import-review-page.generator.ts"],"names":[],"mappings":";;AASA,4DA8KC;AArLD,2GAA6F;AAC7F,2GAA6F;AAC7F,2GAA6F;AAE7F;;GAEG;AACH,SAAgB,wBAAwB,CAAC,EAAE,OAAO,EAA8B;IAC9E,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAA;IAEhH,MAAM,qBAAqB,GAAG,MAAM;SACjC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,eAAe,MAAM,KAAK,CAAC,cAAc,IAAI,CAAC;SACpF,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,oBAAoB,GAAG,MAAM;SAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,eAAe,MAAM,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC;SAC7G,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,sBAAsB,GAAG,MAAM;SAClC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;aAC7C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC;aACjC,IAAI,CAAC,IAAI,CAAC,CAAA;QACb,OAAO,KAAK,KAAK,CAAC,WAAW,CAAC,eAAe,MAAM,MAAM,IAAI,CAAA;IAC/D,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,kBAAkB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;SACjF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SAClC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,GAAG,CAAC;SAC9B,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CP,qBAAqB;;;;EAIrB,oBAAoB;;;;EAIpB,sBAAsB;;;kCAGU,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqFlD,IAAA,2EAA+B,GAAE;;EAEjC,IAAA,2EAA+B,GAAE;;EAEjC,IAAA,2EAA+B,GAAE;CAClC,CAAA;AACD,CAAC"}
@@ -52,6 +52,7 @@ function generateModelAdminPage({ model, context, }) {
52
52
  Generator.toFunctionName('useState'),
53
53
  Generator.toFunctionName('useCallback'),
54
54
  Generator.toFunctionName('useMemo'),
55
+ Generator.toFunctionName('useEffect'),
55
56
  ],
56
57
  })
57
58
  .addImport({
@@ -69,7 +70,34 @@ function generateModelAdminPage({ model, context, }) {
69
70
  })
70
71
  .addImport({
71
72
  from: Generator.toPackageName('@tanstack/react-router'),
72
- items: [Generator.toFunctionName('useNavigate')],
73
+ items: [Generator.toFunctionName('useNavigate'), Generator.toFunctionName('useSearch')],
74
+ })
75
+ .addImport({
76
+ from: Generator.toBackendModuleLocation('@app-actions/command-palette-action'),
77
+ items: [
78
+ Generator.toFunctionName('command'),
79
+ Generator.toFunctionName('input'),
80
+ Generator.toFunctionName('waitForNextFrame'),
81
+ ],
82
+ })
83
+ .addImport({
84
+ from: Generator.toBackendModuleLocation('@app-actions/command-palette-store'),
85
+ items: [Generator.toFunctionName('useCommandPaletteActions')],
86
+ })
87
+ .addImport({
88
+ from: Generator.toBackendModuleLocation('@app-actions/ai-assistant-store'),
89
+ items: [Generator.toFunctionName('useAiAssistantActions'), Generator.toFunctionName('useAiAssistantState')],
90
+ })
91
+ .addImport({
92
+ from: Generator.toBackendModuleLocation('@lib/command-palette-filter.utils'),
93
+ items: [
94
+ Generator.toConstantName('stringFilterInputSchema'),
95
+ Generator.toConstantName('numberFilterInputSchema'),
96
+ Generator.toConstantName('dateFilterInputSchema'),
97
+ Generator.toConstantName('booleanFilterInputSchema'),
98
+ Generator.toFunctionName('parseBooleanQuery'),
99
+ Generator.toFunctionName('toFilterActionLabel'),
100
+ ],
73
101
  });
74
102
  const showComments = model.name !== 'Comment' && !!context.admin.components.commentSidebar;
75
103
  const relatedModels = [];
@@ -83,6 +111,7 @@ function generateModelAdminPage({ model, context, }) {
83
111
  });
84
112
  }
85
113
  let createModalHook = '';
114
+ let createModalIntentHook = '';
86
115
  let createModal = '';
87
116
  if (!model.isReadonly) {
88
117
  imports
@@ -96,6 +125,33 @@ function generateModelAdminPage({ model, context, }) {
96
125
  const variableName = hasLabelField ? `created${model.name}` : '_';
97
126
  const label = hasLabelField ? ` "\${${variableName}.${model.labelField.name}}"` : '';
98
127
  createModalHook = ` const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)`;
128
+ createModalIntentHook = `
129
+ useEffect(() => {
130
+ if (typeof globalThis === 'undefined') {
131
+ return
132
+ }
133
+
134
+ const storageKey = 'pxl:create-intent-model'
135
+ const targetModel = '${model.name}'
136
+ const openFromIntent = (intentModel: unknown) => {
137
+ if (intentModel === targetModel) {
138
+ setIsCreateModalOpen(true)
139
+ globalThis.sessionStorage.removeItem(storageKey)
140
+ }
141
+ }
142
+
143
+ openFromIntent(globalThis.sessionStorage.getItem(storageKey))
144
+
145
+ const onIntent = (event: Event) => {
146
+ const detail = (event as CustomEvent<{ model?: string }>).detail
147
+ openFromIntent(detail?.model)
148
+ }
149
+
150
+ globalThis.addEventListener('pxl:create-intent', onIntent)
151
+ return () => {
152
+ globalThis.removeEventListener('pxl:create-intent', onIntent)
153
+ }
154
+ }, [])`;
99
155
  createModal = `
100
156
  <${model.forms.createModal.name}
101
157
  open={isCreateModalOpen}
@@ -110,44 +166,34 @@ function generateModelAdminPage({ model, context, }) {
110
166
  />`;
111
167
  }
112
168
  return `
113
- import { SidebarInset, SidebarProvider, SidebarTrigger, useDebouncedCallback } from '@postxl/ui-components'
169
+ import { SidebarInset, SidebarProvider, useDebouncedCallback } from '@postxl/ui-components'
114
170
  import useLocalStorageState from 'use-local-storage-state'
115
- import useSessionStorageState from 'use-session-storage-state'
116
171
  import { useAuth } from '@context-providers/auth-context-provider'
117
172
  ${imports.generate()}
118
173
 
119
- type CellSelection = {
120
- rowIndex: number
121
- columnId: string
122
- }
123
-
124
- const STORAGE_KEY = 'audit-log-collapse-state'
125
174
  const SIDEBAR_WIDTH_KEY = 'audit-log-sidebar-width'
126
175
 
127
- type ${model.admin.page.pageComponent.name}Props = {
128
- selectedKey?: string
129
- }
130
-
131
- export default function ${model.admin.page.pageComponent.name}({ selectedKey }: ${model.admin.page.pageComponent.name}Props) {
176
+ export default function ${model.admin.page.pageComponent.name}() {
132
177
  const navigate = useNavigate()
133
178
  const { viewerData } = useAuth()
134
179
  const currentUserId = viewerData?.user?.id
135
180
  const isAdmin = viewerData?.userRoles?.includes('admin') ?? false
136
- ${createModalHook}
137
-
138
- // audit log sidebar collapse state
139
- const [collapseState, setCollapseState] = useSessionStorageState<boolean>(STORAGE_KEY, {
140
- defaultValue: false,
181
+
182
+ const { selectedKey, focusedCell } = useSearch({
183
+ from: '/_auth-routes${model.admin.page.route}',
184
+ select: (search) => ({ selectedKey: search.selectedKey, focusedCell: search.focusedCell }),
141
185
  })
186
+
187
+ ${createModalHook}
188
+ ${createModalIntentHook}
189
+ const sidepanelOpen = useAiAssistantState((state) => state.open)
190
+ const { setOpen: setSidepanelOpen } = useAiAssistantActions()
142
191
 
143
192
  // audit log sidebar width
144
193
  const [sidebarWidth, setSidebarWidth] = useLocalStorageState(SIDEBAR_WIDTH_KEY, {
145
194
  defaultValue: 350,
146
195
  })
147
196
 
148
- // Track the currently focused cell for audit log filtering
149
- const [focusedCell, setFocusedCell] = useState<CellSelection | null>(null)
150
-
151
197
  // Track selected row IDs from checkbox selection
152
198
  const [selectedRowIds, setSelectedRowIds] = useState<string[]>([])
153
199
 
@@ -158,6 +204,7 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
158
204
 
159
205
  // Global search input state (debounced filter update happens via handleSearchInputChange)
160
206
  const [searchInput, setSearchInput] = useState('')
207
+ const { registerComponentActions, deregisterComponentActions } = useCommandPaletteActions()
161
208
 
162
209
  const {
163
210
  ${model.itemsHook.filteredList.name}: filteredDataList,
@@ -171,21 +218,15 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
171
218
  ${relatedModels.map((rm) => rm.mapDefinition).join('\n ')}
172
219
 
173
220
 
174
- // Get the selected entity ID based on focused cell
175
- const selectedEntityId = useMemo(() => {
176
- if (!filteredDataList || !focusedCell || focusedCell.rowIndex < 0 || focusedCell.rowIndex >= filteredDataList.length) {
177
- return null
178
- }
179
- const row = filteredDataList[focusedCell.rowIndex]
180
- return row?.id ?? null
181
- }, [focusedCell, filteredDataList])
221
+ // The selected entity ID comes from the URL selectedKey (which is the entity's id)
222
+ const selectedEntityId = selectedKey ?? null
182
223
 
183
- // Get the selected field name based on focused cell
224
+ // Get the selected field name based on focused cell (columnId from search params)
184
225
  const selectedField = useMemo(() => {
185
- if (!focusedCell || focusedCell.columnId === 'select' || focusedCell.columnId === 'actions') {
226
+ if (!focusedCell || focusedCell === 'select' || focusedCell === 'actions') {
186
227
  return null
187
228
  }
188
- return focusedCell.columnId
229
+ return focusedCell
189
230
  }, [focusedCell])
190
231
 
191
232
  ${showComments
@@ -200,35 +241,25 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
200
241
  )
201
242
  `
202
243
  : ''}
203
- // Callback when cell focus changes in the table
204
- const handleCellChange = useCallback((args: { rowIndex: number; columnId: string }) => {
205
- setFocusedCell({ rowIndex: args.rowIndex, columnId: args.columnId })
206
- // Clear checkbox selection when focusing a cell
207
- setSelectedRowIds([])
208
- }, [])
209
-
210
244
  // Callback when row selection changes (checkbox)
211
245
  const handleRowSelectionChange = useCallback((rowIds: string[]) => {
212
246
  setSelectedRowIds(rowIds)
213
247
  // Clear cell selection when using checkbox selection
214
248
  if (rowIds.length > 0) {
215
- setFocusedCell(null)
249
+ void navigate({ to: '${model.admin.page.route}', search: { selectedKey: undefined, focusedCell: undefined } })
216
250
  }
217
- }, [])
251
+ }, [navigate])
218
252
 
219
253
  // Clear field filter (go up to entity level)
220
254
  const handleClearField = useCallback(() => {
221
- if (focusedCell) {
222
- // Keep the entity selected but clear the field
223
- setFocusedCell({ rowIndex: focusedCell.rowIndex, columnId: '' })
224
- }
225
- }, [focusedCell])
255
+ void navigate({ to: '${model.admin.page.route}', search: { selectedKey, focusedCell: undefined } })
256
+ }, [navigate, selectedKey])
226
257
 
227
258
  // Clear entity filter (go back to model overview)
228
259
  const handleClearEntity = useCallback(() => {
229
- setFocusedCell(null)
260
+ void navigate({ to: '${model.admin.page.route}', search: { selectedKey: undefined, focusedCell: undefined } })
230
261
  setSelectedRowIds([])
231
- }, [])
262
+ }, [navigate])
232
263
 
233
264
  const handleFilterChange = useCallback((key: ${model.types.filter.fieldType.name} | 'globalSearch', val: any) => {
234
265
  setFilters((prev) => {
@@ -249,14 +280,124 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
249
280
  debouncedFilterChange('globalSearch', value)
250
281
  }, [debouncedFilterChange])
251
282
 
252
- // Handle row click - navigate to URL with keyField value
253
- const handleRowClick = useCallback((key: string | null) => {
254
- if (key) {
255
- void navigate({ to: '${model.admin.page.route}/$key', params: { key } })
256
- } else {
257
- void navigate({ to: '${model.admin.page.route}' })
283
+ // Callback when cell focus changes in the table also sets selectedKey from the focused row
284
+ const handleCellChange = useCallback((args: { rowIndex: number; columnId: string }) => {
285
+ const row = filteredDataList?.[args.rowIndex]
286
+ const key = row?.${model.keyField.name} != null ? String(row.${model.keyField.name}) : undefined
287
+ void navigate({ to: '${model.admin.page.route}', search: { selectedKey: key, focusedCell: args.columnId || undefined } })
288
+ // Clear checkbox selection when focusing a cell
289
+ setSelectedRowIds([])
290
+ }, [navigate, filteredDataList])
291
+
292
+ useEffect(() => {
293
+ const prefix = 'admin-${model._conjugated.kebabCase}-filter'
294
+
295
+ const filterActions = Object.entries(${model.types.filter.config.name}).map(([field, fieldConfig]) =>
296
+ input({
297
+ key: prefix + '-' + field,
298
+ visibility: 'visible',
299
+ group: 'Filter records by...',
300
+ label: toFilterActionLabel(field),
301
+ inputSchema:
302
+ fieldConfig.valueType === 'number'
303
+ ? numberFilterInputSchema
304
+ : fieldConfig.valueType === 'date'
305
+ ? dateFilterInputSchema
306
+ : fieldConfig.valueType === 'boolean'
307
+ ? booleanFilterInputSchema
308
+ : stringFilterInputSchema,
309
+ run: async (query, inputValue) => {
310
+ if (fieldConfig.valueType === 'number') {
311
+ const parsed = numberFilterInputSchema.safeParse(inputValue)
312
+ if (parsed.success && Object.values(parsed.data).some((value) => value !== undefined)) {
313
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, parsed.data)
314
+ } else {
315
+ const numericValue = Number(query.trim())
316
+ if (!Number.isNaN(numericValue)) {
317
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, { values: [numericValue] })
318
+ }
319
+ }
320
+ } else if (fieldConfig.valueType === 'date') {
321
+ const parsed = dateFilterInputSchema.safeParse(inputValue)
322
+ if (parsed.success && Object.values(parsed.data).some((value) => value !== undefined)) {
323
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, parsed.data)
324
+ } else if (query.trim()) {
325
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, { values: [query.trim()] })
326
+ }
327
+ } else if (fieldConfig.valueType === 'boolean') {
328
+ const parsed = booleanFilterInputSchema.safeParse(inputValue)
329
+ if (parsed.success && parsed.data.values && parsed.data.values.length > 0) {
330
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, parsed.data.values)
331
+ } else {
332
+ const parsedFromQuery = parseBooleanQuery(query)
333
+ if (parsedFromQuery) {
334
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, parsedFromQuery)
335
+ }
336
+ }
337
+ } else {
338
+ const parsed = stringFilterInputSchema.safeParse(inputValue)
339
+ if (parsed.success && Object.values(parsed.data).some((value) => value !== undefined && value !== '')) {
340
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, parsed.data)
341
+ } else if (query.trim()) {
342
+ handleFilterChange(fieldConfig.filterKey as ${model.types.filter.fieldType.name}, { contains: query.trim() })
343
+ }
344
+ }
345
+
346
+ await waitForNextFrame()
347
+ return { kind: 'close' }
348
+ },
349
+ }),
350
+ )
351
+
352
+ const actions = [
353
+ command({
354
+ key: 'admin-${model._conjugated.kebabCase}-clear-all-filters',
355
+ visibility: 'visible',
356
+ group: 'Filter records by...',
357
+ label: 'Clear all filters',
358
+ run: async () => {
359
+ setFilters({})
360
+ setSearchInput('')
361
+ await waitForNextFrame()
362
+ return { kind: 'close' }
363
+ },
364
+ }),
365
+ command({
366
+ key: 'admin-${model._conjugated.kebabCase}-query-filters',
367
+ visibility: 'llm',
368
+ group: 'Filter records by...',
369
+ label: 'Query active filters',
370
+ run: () => Promise.resolve({ kind: 'output', output: { filters, searchInput } }),
371
+ }),
372
+ input({
373
+ key: 'admin-${model._conjugated.kebabCase}-search',
374
+ visibility: 'visible',
375
+ group: 'Filter records by...',
376
+ label: 'Global search',
377
+ run: async (query) => {
378
+ handleSearchInputChange(query)
379
+ await waitForNextFrame()
380
+ return { kind: 'close' }
381
+ },
382
+ }),
383
+ ...filterActions,
384
+ ]
385
+
386
+ registerComponentActions(actions)
387
+
388
+ return () => {
389
+ deregisterComponentActions(actions)
258
390
  }
259
- }, [navigate])
391
+ }, [
392
+ deregisterComponentActions,
393
+ filters,
394
+ handleFilterChange,
395
+ handleSearchInputChange,
396
+ registerComponentActions,
397
+ searchInput,
398
+ setFilters,
399
+ setSearchInput,
400
+ ])
260
401
 
261
402
  ${model.isReadonly
262
403
  ? ''
@@ -276,7 +417,7 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
276
417
 
277
418
  return (
278
419
  ${model.isReadonly ? '' : '<>'}
279
- <SidebarProvider open={collapseState} onOpenChange={setCollapseState} width={sidebarWidth} onWidthChange={setSidebarWidth}>
420
+ <SidebarProvider open={sidepanelOpen} onOpenChange={setSidepanelOpen} width={sidebarWidth} onWidthChange={setSidebarWidth}>
280
421
  <AdminSidebar collapsible="none" className="w-64" />
281
422
 
282
423
  <SidebarInset className="p-4 overflow-hidden gap-4">
@@ -307,7 +448,6 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
307
448
  onCellChange={handleCellChange}
308
449
  onRowSelectionChange={handleRowSelectionChange}
309
450
  selectedKey={selectedKey}
310
- onRowClick={handleRowClick}
311
451
  keyField="${model.keyField.name}"
312
452
  hasNextPage={hasNextPage}
313
453
  fetchNextPage={fetchNextPage}
@@ -321,8 +461,6 @@ export default function ${model.admin.page.pageComponent.name}({ selectedKey }:
321
461
  />
322
462
  </SidebarInset>
323
463
 
324
- <SidebarTrigger />
325
-
326
464
  <AdminDetailSidebar
327
465
  side="right"
328
466
  collapsible="offcanvas"