create-appraisejs 0.1.7 → 0.1.8

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 (316) hide show
  1. package/README.md +45 -45
  2. package/dist/cli.js +2 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/copy-template.d.ts.map +1 -1
  5. package/dist/copy-template.js.map +1 -1
  6. package/package.json +69 -67
  7. package/templates/default/.vscode/settings.json +5 -0
  8. package/templates/default/appraisejs.config.json +1 -1
  9. package/templates/default/components.json +24 -24
  10. package/templates/default/cucumber.mjs +16 -0
  11. package/templates/default/eslint.config.mjs +15 -15
  12. package/templates/default/next.config.ts +13 -7
  13. package/templates/default/package-lock.json +13732 -14321
  14. package/templates/default/package.json +11 -9
  15. package/templates/default/postcss.config.mjs +8 -8
  16. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  17. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  18. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  19. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  20. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  21. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  22. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  23. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  24. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  25. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  26. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  27. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  28. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  29. package/templates/default/prisma/schema.prisma +554 -554
  30. package/templates/default/scripts/regenerate-features.ts +94 -94
  31. package/templates/default/scripts/setup-env.ts +19 -19
  32. package/templates/default/scripts/sync-all.ts +341 -341
  33. package/templates/default/scripts/sync-appraise-base-template.ts +52 -2
  34. package/templates/default/scripts/sync-environments.ts +323 -323
  35. package/templates/default/scripts/sync-locator-groups.ts +413 -413
  36. package/templates/default/scripts/sync-locators.ts +402 -402
  37. package/templates/default/scripts/sync-modules.ts +349 -349
  38. package/templates/default/scripts/sync-tags.ts +292 -292
  39. package/templates/default/scripts/sync-template-step-groups.ts +399 -399
  40. package/templates/default/scripts/sync-template-steps.ts +806 -806
  41. package/templates/default/scripts/sync-test-cases.ts +905 -905
  42. package/templates/default/scripts/sync-test-suites.ts +411 -411
  43. package/templates/default/src/actions/conflict/conflict.action.ts +33 -33
  44. package/templates/default/src/actions/dashboard/dashboard-actions.ts +240 -240
  45. package/templates/default/src/actions/environments/environment-actions.ts +205 -205
  46. package/templates/default/src/actions/locator/locator-actions.ts +547 -547
  47. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -344
  48. package/templates/default/src/actions/modules/module-actions.ts +133 -133
  49. package/templates/default/src/actions/reports/report-actions.ts +613 -613
  50. package/templates/default/src/actions/review/review-actions.ts +147 -147
  51. package/templates/default/src/actions/tags/tag-actions.ts +104 -104
  52. package/templates/default/src/actions/template-step/template-step-actions.ts +332 -332
  53. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -278
  54. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -238
  55. package/templates/default/src/actions/test-case/test-case-actions.ts +419 -419
  56. package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -1185
  57. package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -253
  58. package/templates/default/src/actions/user/user-actions.ts +13 -13
  59. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  60. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -219
  61. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  62. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -24
  63. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -46
  64. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  65. package/templates/default/src/app/(base)/layout.tsx +10 -10
  66. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  67. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  68. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  69. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  70. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  71. package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -61
  72. package/templates/default/src/app/(base)/locators/create/page.tsx +38 -38
  73. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -163
  74. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +73 -90
  75. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -28
  76. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -45
  77. package/templates/default/src/app/(base)/locators/page.tsx +65 -65
  78. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -66
  79. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -34
  80. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -46
  81. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -126
  82. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -85
  83. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -24
  84. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  85. package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -517
  86. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  87. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -78
  88. package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -46
  89. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  90. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -16
  91. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  92. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  93. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -131
  94. package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -82
  95. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -42
  96. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  97. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  98. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  99. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  100. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  101. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -60
  102. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -26
  103. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -15
  104. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -26
  105. package/templates/default/src/app/(base)/reviews/page.tsx +26 -26
  106. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -26
  107. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -11
  108. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -9
  109. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -9
  110. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -15
  111. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  112. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  113. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  114. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -147
  115. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  116. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  117. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  118. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -45
  119. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  120. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -167
  121. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  122. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -32
  123. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -37
  124. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -49
  125. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -59
  126. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -213
  127. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -384
  128. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -158
  129. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -24
  130. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  131. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  132. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  133. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -84
  134. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -262
  135. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  136. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  137. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -76
  138. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -96
  139. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -38
  140. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -73
  141. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  142. package/templates/default/src/app/(base)/test-cases/page.tsx +60 -60
  143. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -82
  144. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -395
  145. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -90
  146. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -35
  147. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -56
  148. package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -47
  149. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  150. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +508 -512
  151. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  152. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  153. package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -45
  154. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -55
  155. package/templates/default/src/app/(base)/test-suites/page.tsx +82 -82
  156. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -269
  157. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -97
  158. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -29
  159. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  160. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  161. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  162. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  163. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  164. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  165. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -133
  166. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -420
  167. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
  168. package/templates/default/src/app/globals.css +147 -147
  169. package/templates/default/src/app/layout.tsx +171 -171
  170. package/templates/default/src/app/page.tsx +64 -64
  171. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  172. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  173. package/templates/default/src/components/base-node.tsx +21 -21
  174. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  175. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -460
  176. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  177. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  178. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  179. package/templates/default/src/components/devtools/providers.tsx +19 -13
  180. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  181. package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -438
  182. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  183. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -470
  184. package/templates/default/src/components/diagram/node-form.tsx +262 -262
  185. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  186. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  187. package/templates/default/src/components/form/error-message.tsx +7 -7
  188. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  189. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  190. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  191. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  192. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  193. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  194. package/templates/default/src/components/logo.tsx +15 -15
  195. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  196. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  197. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -116
  198. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  199. package/templates/default/src/components/navigation/nav-command.tsx +508 -508
  200. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  201. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  202. package/templates/default/src/components/node-header.tsx +159 -159
  203. package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -253
  204. package/templates/default/src/components/table/table-actions.tsx +172 -172
  205. package/templates/default/src/components/test-run/download-logs-button.tsx +99 -99
  206. package/templates/default/src/components/test-run/log-viewer.tsx +445 -445
  207. package/templates/default/src/components/test-run/test-run-details.tsx +611 -611
  208. package/templates/default/src/components/test-run/test-run-header.tsx +149 -149
  209. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  210. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  211. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  212. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  213. package/templates/default/src/components/typography/page-header.tsx +7 -7
  214. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  215. package/templates/default/src/components/ui/alert.tsx +43 -43
  216. package/templates/default/src/components/ui/avatar.tsx +40 -40
  217. package/templates/default/src/components/ui/badge.tsx +29 -29
  218. package/templates/default/src/components/ui/button.tsx +47 -47
  219. package/templates/default/src/components/ui/calendar.tsx +158 -158
  220. package/templates/default/src/components/ui/card.tsx +43 -43
  221. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  222. package/templates/default/src/components/ui/command.tsx +135 -135
  223. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  224. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  225. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  226. package/templates/default/src/components/ui/data-table.tsx +267 -267
  227. package/templates/default/src/components/ui/dialog.tsx +97 -97
  228. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  229. package/templates/default/src/components/ui/input.tsx +22 -22
  230. package/templates/default/src/components/ui/kbd.tsx +28 -28
  231. package/templates/default/src/components/ui/label.tsx +19 -19
  232. package/templates/default/src/components/ui/loading.tsx +12 -12
  233. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  234. package/templates/default/src/components/ui/multi-select.tsx +142 -142
  235. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  236. package/templates/default/src/components/ui/popover.tsx +33 -33
  237. package/templates/default/src/components/ui/progress.tsx +25 -25
  238. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  239. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  240. package/templates/default/src/components/ui/select.tsx +151 -144
  241. package/templates/default/src/components/ui/separator.tsx +22 -22
  242. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  243. package/templates/default/src/components/ui/table.tsx +76 -76
  244. package/templates/default/src/components/ui/tabs.tsx +55 -55
  245. package/templates/default/src/components/ui/textarea.tsx +21 -21
  246. package/templates/default/src/components/ui/toast.tsx +113 -113
  247. package/templates/default/src/components/ui/toaster.tsx +26 -26
  248. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -87
  249. package/templates/default/src/config/db-config.ts +10 -10
  250. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  251. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  252. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -20
  253. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  254. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  255. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -23
  256. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  257. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  258. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  259. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  260. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  261. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  262. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -31
  263. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  264. package/templates/default/src/hooks/use-toast.ts +187 -187
  265. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  266. package/templates/default/src/lib/database-sync.ts +531 -531
  267. package/templates/default/src/lib/environment-file-utils.ts +221 -221
  268. package/templates/default/src/lib/feature-file-generator.ts +411 -411
  269. package/templates/default/src/lib/gherkin-parser.ts +259 -259
  270. package/templates/default/src/lib/locator-group-file-utils.ts +370 -370
  271. package/templates/default/src/lib/metrics/metric-calculator.ts +613 -613
  272. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  273. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  274. package/templates/default/src/lib/test-case-utils.ts +6 -6
  275. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  276. package/templates/default/src/lib/test-run/process-manager.ts +191 -191
  277. package/templates/default/src/lib/test-run/report-parser.ts +316 -316
  278. package/templates/default/src/lib/test-run/test-run-executor.ts +144 -144
  279. package/templates/default/src/lib/test-run/winston-logger.ts +95 -95
  280. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  281. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  282. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  283. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  284. package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -167
  285. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -723
  286. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  287. package/templates/default/src/lib/utils.ts +31 -31
  288. package/templates/default/src/tests/config/executor/world.ts +41 -41
  289. package/templates/default/src/tests/executor.ts +80 -80
  290. package/templates/default/src/tests/hooks/hooks.ts +99 -99
  291. package/templates/default/src/tests/mapping/locator-map.json +1 -1
  292. package/templates/default/src/tests/steps/actions/click.step.ts +62 -62
  293. package/templates/default/src/tests/steps/actions/navigation.step.ts +73 -72
  294. package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -34
  295. package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +24 -23
  296. package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -111
  297. package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -30
  298. package/templates/default/src/tests/support/parameter-types.ts +12 -12
  299. package/templates/default/src/tests/utils/cache.util.ts +260 -260
  300. package/templates/default/src/tests/utils/cli.util.ts +177 -177
  301. package/templates/default/src/tests/utils/environment.util.ts +65 -65
  302. package/templates/default/src/tests/utils/locator.util.ts +248 -248
  303. package/templates/default/src/tests/utils/random-data.util.ts +44 -44
  304. package/templates/default/src/tests/utils/spawner.util.ts +617 -617
  305. package/templates/default/src/types/diagram/diagram.ts +34 -34
  306. package/templates/default/src/types/diagram/template-step.ts +11 -11
  307. package/templates/default/src/types/executor/browser.type.ts +1 -1
  308. package/templates/default/src/types/form/actionHandler.ts +6 -6
  309. package/templates/default/src/types/locator/locator.type.ts +11 -11
  310. package/templates/default/src/types/step/step.type.ts +1 -1
  311. package/templates/default/src/types/table/data-table.ts +6 -6
  312. package/templates/default/tailwind.config.ts +62 -62
  313. package/templates/default/.env +0 -2
  314. package/templates/default/next-env.d.ts +0 -6
  315. package/templates/default/prisma/prisma/dev.db +0 -0
  316. package/templates/default/src/tests/config/environments/environments.json +0 -14
@@ -1,438 +1,438 @@
1
- 'use client'
2
-
3
- import { useState, useMemo, forwardRef, useImperativeHandle, useEffect } from 'react'
4
- import { Calendar } from '@/components/ui/calendar'
5
- import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
6
- import { Button } from '@/components/ui/button'
7
- import { Input } from '@/components/ui/input'
8
- import { Label } from '@/components/ui/label'
9
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
10
- import { CalendarIcon } from 'lucide-react'
11
- import { cn } from '@/lib/utils'
12
- import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
13
- import { StepParameterType, TemplateStepParameter, Locator, LocatorGroup } from '@prisma/client'
14
- import { format } from 'date-fns'
15
- import ErrorMessage from '@/components/form/error-message'
16
-
17
- interface DynamicFormFieldsProps {
18
- templateStepParams: TemplateStepParameter[]
19
- locators: Locator[]
20
- locatorGroups: LocatorGroup[]
21
- defaultValueInput?: boolean
22
- onChange?: (
23
- values: {
24
- name: string
25
- value: string
26
- type: StepParameterType
27
- order: number
28
- }[],
29
- ) => void
30
- initialParameterValues?: {
31
- name: string
32
- value: string
33
- type: StepParameterType
34
- order: number
35
- }[]
36
- }
37
-
38
- export interface DynamicFormFieldsRef {
39
- validate: () => boolean
40
- }
41
-
42
- const DynamicFormFields = forwardRef<DynamicFormFieldsRef, DynamicFormFieldsProps>((props, ref) => {
43
- const {
44
- templateStepParams,
45
- locators,
46
- locatorGroups,
47
- defaultValueInput = false,
48
- onChange,
49
- initialParameterValues,
50
- } = props
51
-
52
- const resetKey = useMemo(() => {
53
- return JSON.stringify({
54
- params: templateStepParams.map(p => ({ name: p.name, type: p.type })),
55
- initialParameterValues,
56
- })
57
- }, [templateStepParams, initialParameterValues])
58
-
59
- // Create initial values only once when component mounts
60
- const initialValues = useMemo(() => {
61
- const values: { [key: string]: string | number | boolean | Date } = {}
62
- // Build a map for quick lookup of initial values by name
63
- const initialValueMap: Record<string, { value: string; type: StepParameterType }> = {}
64
- initialParameterValues?.forEach(v => {
65
- initialValueMap[v.name] = { value: v.value, type: v.type }
66
- })
67
- templateStepParams.forEach(param => {
68
- const initial = initialValueMap[param.name]
69
- if (initial) {
70
- switch (param.type) {
71
- case 'NUMBER':
72
- values[param.name] = Number(initial.value)
73
- break
74
- case 'STRING':
75
- case 'LOCATOR':
76
- values[param.name] = initial.value
77
- break
78
- case 'DATE':
79
- // Try to parse date from string
80
- const date = new Date(initial.value)
81
- values[param.name] = isNaN(date.getTime()) ? new Date() : date
82
- break
83
- case 'BOOLEAN':
84
- values[param.name] = initial.value === 'true'
85
- break
86
- }
87
- } else {
88
- // fallback to default
89
- switch (param.type) {
90
- case 'NUMBER':
91
- values[param.name] = 0
92
- break
93
- case 'STRING':
94
- values[param.name] = ''
95
- break
96
- case 'DATE':
97
- values[param.name] = new Date()
98
- break
99
- case 'BOOLEAN':
100
- values[param.name] = false
101
- break
102
- case 'LOCATOR':
103
- values[param.name] = ''
104
- break
105
- }
106
- }
107
- })
108
- return values
109
- }, [templateStepParams, initialParameterValues])
110
-
111
- // Initialize state with initial values
112
- const [values, setValues] = useState<{
113
- [key: string]: string | number | boolean | Date
114
- }>(initialValues)
115
- const [errors, setErrors] = useState<Record<string, string>>({})
116
-
117
- // State for locator group selection
118
- const [selectedLocatorGroups, setSelectedLocatorGroups] = useState<Record<string, string>>({})
119
-
120
- useEffect(() => {
121
- queueMicrotask(() => setErrors({}))
122
- }, [templateStepParams])
123
-
124
- useImperativeHandle(ref, () => ({
125
- validate: () => {
126
- // Skip all validation if defaultValueInput is true (all fields are optional)
127
- if (defaultValueInput) {
128
- setErrors({})
129
- return true
130
- }
131
-
132
- const newErrors: Record<string, string> = {}
133
- templateStepParams.forEach(param => {
134
- const value = values[param.name]
135
-
136
- if (param.type === 'LOCATOR') {
137
- const selectedGroup = selectedLocatorGroups[param.name]
138
- if (!selectedGroup) {
139
- newErrors[param.name] = 'Locator group is required'
140
- } else if (!value) {
141
- newErrors[param.name] = 'Locator is required'
142
- }
143
- }
144
- if (param.type === 'STRING' && !value) {
145
- newErrors[param.name] = 'This field is required'
146
- }
147
- if (param.type === 'NUMBER' && !value) {
148
- newErrors[param.name] = 'This field is required'
149
- }
150
- // Add other validation rules here if needed
151
- })
152
- setErrors(newErrors)
153
- return Object.keys(newErrors).length === 0
154
- },
155
- }))
156
-
157
- // Update values when an input changes
158
- const handleInputChange = (name: string, value: string | number | boolean | Date) => {
159
- const newValues = {
160
- ...values,
161
- [name]: value,
162
- }
163
-
164
- setValues(newValues)
165
-
166
- // Clear error for the field being edited
167
- if (errors[name]) {
168
- const newErrors = { ...errors }
169
- delete newErrors[name]
170
- setErrors(newErrors)
171
- }
172
-
173
- // Notify parent component of changes
174
- if (onChange) {
175
- // We need to calculate formatted values based on the new state
176
- const formattedValues = templateStepParams.map(param => {
177
- let stringValue = ''
178
- // Use the new value if it's the one that changed, otherwise use the current state
179
- const currentValue = param.name === name ? value : values[param.name]
180
-
181
- switch (param.type) {
182
- case 'NUMBER':
183
- stringValue = currentValue !== undefined && currentValue !== null ? String(currentValue) : ''
184
- break
185
- case 'STRING':
186
- stringValue = currentValue !== undefined && currentValue !== null ? (currentValue as string) : ''
187
- break
188
- case 'DATE':
189
- if (
190
- currentValue &&
191
- currentValue instanceof Date &&
192
- typeof currentValue.getTime === 'function' &&
193
- !Number.isNaN(currentValue.getTime())
194
- ) {
195
- stringValue = format(currentValue as Date, 'PPP')
196
- } else {
197
- stringValue = ''
198
- }
199
- break
200
- case 'BOOLEAN':
201
- stringValue = currentValue !== undefined && currentValue !== null ? String(currentValue) : ''
202
- break
203
- case 'LOCATOR':
204
- stringValue = currentValue !== undefined && currentValue !== null ? (currentValue as string) : ''
205
- break
206
- }
207
-
208
- return {
209
- name: param.name,
210
- value: stringValue,
211
- type: param.type,
212
- order: param.order,
213
- }
214
- })
215
-
216
- onChange(formattedValues)
217
- }
218
- }
219
-
220
- // Handle locator group selection
221
- const handleLocatorGroupChange = (paramName: string, groupId: string) => {
222
- setSelectedLocatorGroups(prev => ({
223
- ...prev,
224
- [paramName]: groupId,
225
- }))
226
-
227
- // Clear the locator selection when group changes
228
- setValues(prev => ({
229
- ...prev,
230
- [paramName]: '',
231
- }))
232
-
233
- // Clear errors for this field
234
- if (errors[paramName]) {
235
- const newErrors = { ...errors }
236
- delete newErrors[paramName]
237
- setErrors(newErrors)
238
- }
239
- }
240
-
241
- // Get locators for a specific group
242
- const getLocatorsForGroup = (groupId: string) => {
243
- return locators.filter(locator => locator.locatorGroupId === groupId)
244
- }
245
-
246
- // Render the appropriate input field based on the parameter type
247
- const renderInputField = (param: TemplateStepParameter) => {
248
- const { name, type } = param
249
- const errorMessage = errors[name]
250
-
251
- switch (type) {
252
- case 'NUMBER':
253
- return (
254
- <div className="grid w-full items-center gap-1.5">
255
- <Label htmlFor={`input-${name}`}>
256
- {defaultValueInput ? `Default ${name}` : name}{' '}
257
- {!defaultValueInput && <span className="text-red-500">*</span>}
258
- </Label>
259
- <Input
260
- id={`input-${name}`}
261
- type="number"
262
- value={typeof values[name] === 'number' ? values[name] : 0}
263
- onChange={e => handleInputChange(name, Number(e.target.value))}
264
- className="w-full"
265
- />
266
- <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
267
- </div>
268
- )
269
-
270
- case 'STRING':
271
- return (
272
- <div className="grid w-full items-center gap-1.5">
273
- <Label htmlFor={`input-${name}`}>
274
- {defaultValueInput ? `Default ${name}` : name}{' '}
275
- {!defaultValueInput && <span className="text-red-500">*</span>}
276
- </Label>
277
- <Input
278
- id={`input-${name}`}
279
- type="text"
280
- value={typeof values[name] === 'string' ? values[name] : ''}
281
- onChange={e => handleInputChange(name, e.target.value)}
282
- className="w-full"
283
- />
284
- <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
285
- </div>
286
- )
287
-
288
- case 'DATE':
289
- return (
290
- <div className="grid w-full items-center gap-1.5">
291
- <Label>
292
- {defaultValueInput ? `Default ${name}` : name}{' '}
293
- {!defaultValueInput && <span className="text-red-500">*</span>}
294
- </Label>
295
- <Popover>
296
- <PopoverTrigger asChild>
297
- <Button
298
- variant="outline"
299
- className={cn('w-full justify-start text-left font-normal', !values[name] && 'text-muted-foreground')}
300
- aria-required={!defaultValueInput}
301
- >
302
- <CalendarIcon className="mr-2 h-4 w-4" />
303
- {values[name] instanceof Date ? (
304
- format(values[name] as Date, 'PPP')
305
- ) : (
306
- <span className={defaultValueInput ? 'text-muted-foreground' : 'text-red-500'}>
307
- {defaultValueInput ? 'Pick a date (optional)' : 'Pick a date *'}
308
- </span>
309
- )}
310
- </Button>
311
- </PopoverTrigger>
312
- <PopoverContent className="w-auto p-0" align="start">
313
- <Calendar
314
- mode="single"
315
- selected={values[name] instanceof Date ? (values[name] as Date) : undefined}
316
- onSelect={(date: Date | undefined) => handleInputChange(name, date as Date)}
317
- initialFocus
318
- required={!defaultValueInput}
319
- />
320
- </PopoverContent>
321
- </Popover>
322
- </div>
323
- )
324
-
325
- case 'BOOLEAN':
326
- return (
327
- <div className="grid w-full items-center gap-1.5">
328
- <Label htmlFor={`select-${name}`}>
329
- {defaultValueInput ? `Default ${name}` : name}{' '}
330
- {!defaultValueInput && <span className="text-red-500">*</span>}
331
- </Label>
332
- <Select
333
- value={typeof values[name] === 'boolean' ? String(values[name]) : 'false'}
334
- onValueChange={value => handleInputChange(name, value === 'true')}
335
- required={!defaultValueInput}
336
- >
337
- <SelectTrigger id={`select-${name}`} className="w-full">
338
- <SelectValue placeholder={defaultValueInput ? 'Select a value (optional)' : 'Select a value *'} />
339
- </SelectTrigger>
340
- <SelectContent>
341
- <SelectItem value="true">True</SelectItem>
342
- <SelectItem value="false">False</SelectItem>
343
- </SelectContent>
344
- </Select>
345
- </div>
346
- )
347
-
348
- case 'LOCATOR':
349
- const selectedGroupId = selectedLocatorGroups[name] || ''
350
- const availableLocators = selectedGroupId ? getLocatorsForGroup(selectedGroupId) : []
351
-
352
- return (
353
- <div className="grid w-full items-center gap-1.5">
354
- <Label htmlFor={`select-${name}`}>
355
- {defaultValueInput ? `Default ${name}` : name}{' '}
356
- {!defaultValueInput && <span className="text-red-500">*</span>}
357
- </Label>
358
-
359
- {/* Locator Group Selection */}
360
- <div className="mb-2">
361
- <Label htmlFor={`group-${name}`} className="text-sm text-muted-foreground">
362
- Locator Group
363
- </Label>
364
- <Select
365
- value={selectedGroupId}
366
- onValueChange={value => handleLocatorGroupChange(name, value)}
367
- required={!defaultValueInput}
368
- >
369
- <SelectTrigger id={`group-${name}`} className="w-full">
370
- <SelectValue placeholder="Select a locator group" />
371
- </SelectTrigger>
372
- <SelectContent>
373
- {locatorGroups.map(group => (
374
- <SelectItem key={group.id} value={group.id}>
375
- {group.name}
376
- </SelectItem>
377
- ))}
378
- </SelectContent>
379
- </Select>
380
- </div>
381
-
382
- {/* Locator Selection */}
383
- <Select
384
- value={typeof values[name] === 'string' ? values[name] : ''}
385
- onValueChange={value => handleInputChange(name, value)}
386
- required={!defaultValueInput}
387
- disabled={!selectedGroupId}
388
- >
389
- <SelectTrigger id={`select-${name}`} className="w-full">
390
- <SelectValue
391
- placeholder={
392
- !selectedGroupId
393
- ? 'Select a locator group first'
394
- : defaultValueInput
395
- ? 'Select a locator (optional)'
396
- : 'Select a locator *'
397
- }
398
- />
399
- </SelectTrigger>
400
- <SelectContent>
401
- {availableLocators.map(locator => (
402
- <SelectItem key={locator.id} value={locator.name}>
403
- {locator.name}
404
- </SelectItem>
405
- ))}
406
- </SelectContent>
407
- </Select>
408
- <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
409
- </div>
410
- )
411
-
412
- default:
413
- return null
414
- }
415
- }
416
-
417
- // Guard: do not render if no parameters
418
- if (!templateStepParams || templateStepParams.length === 0) {
419
- return null
420
- }
421
-
422
- return (
423
- <Card className="border-none shadow-none" key={resetKey}>
424
- <CardHeader className='py-3'>
425
- <CardTitle className='text-xs font-bold text-primary'>Parameters</CardTitle>
426
- </CardHeader>
427
- <CardContent>
428
- <div className="space-y-6">
429
- {templateStepParams.map(param => (
430
- <div key={param.name}>{renderInputField(param)}</div>
431
- ))}
432
- </div>
433
- </CardContent>
434
- </Card>
435
- )
436
- })
437
- DynamicFormFields.displayName = 'DynamicFormFields'
438
- export default DynamicFormFields
1
+ 'use client'
2
+
3
+ import { useState, useMemo, forwardRef, useImperativeHandle, useEffect } from 'react'
4
+ import { Calendar } from '@/components/ui/calendar'
5
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
6
+ import { Button } from '@/components/ui/button'
7
+ import { Input } from '@/components/ui/input'
8
+ import { Label } from '@/components/ui/label'
9
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
10
+ import { CalendarIcon } from 'lucide-react'
11
+ import { cn } from '@/lib/utils'
12
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
13
+ import { StepParameterType, TemplateStepParameter, Locator, LocatorGroup } from '@prisma/client'
14
+ import { format } from 'date-fns'
15
+ import ErrorMessage from '@/components/form/error-message'
16
+
17
+ interface DynamicFormFieldsProps {
18
+ templateStepParams: TemplateStepParameter[]
19
+ locators: Locator[]
20
+ locatorGroups: LocatorGroup[]
21
+ defaultValueInput?: boolean
22
+ onChange?: (
23
+ values: {
24
+ name: string
25
+ value: string
26
+ type: StepParameterType
27
+ order: number
28
+ }[],
29
+ ) => void
30
+ initialParameterValues?: {
31
+ name: string
32
+ value: string
33
+ type: StepParameterType
34
+ order: number
35
+ }[]
36
+ }
37
+
38
+ export interface DynamicFormFieldsRef {
39
+ validate: () => boolean
40
+ }
41
+
42
+ const DynamicFormFields = forwardRef<DynamicFormFieldsRef, DynamicFormFieldsProps>((props, ref) => {
43
+ const {
44
+ templateStepParams,
45
+ locators,
46
+ locatorGroups,
47
+ defaultValueInput = false,
48
+ onChange,
49
+ initialParameterValues,
50
+ } = props
51
+
52
+ const resetKey = useMemo(() => {
53
+ return JSON.stringify({
54
+ params: templateStepParams.map(p => ({ name: p.name, type: p.type })),
55
+ initialParameterValues,
56
+ })
57
+ }, [templateStepParams, initialParameterValues])
58
+
59
+ // Create initial values only once when component mounts
60
+ const initialValues = useMemo(() => {
61
+ const values: { [key: string]: string | number | boolean | Date } = {}
62
+ // Build a map for quick lookup of initial values by name
63
+ const initialValueMap: Record<string, { value: string; type: StepParameterType }> = {}
64
+ initialParameterValues?.forEach(v => {
65
+ initialValueMap[v.name] = { value: v.value, type: v.type }
66
+ })
67
+ templateStepParams.forEach(param => {
68
+ const initial = initialValueMap[param.name]
69
+ if (initial) {
70
+ switch (param.type) {
71
+ case 'NUMBER':
72
+ values[param.name] = Number(initial.value)
73
+ break
74
+ case 'STRING':
75
+ case 'LOCATOR':
76
+ values[param.name] = initial.value
77
+ break
78
+ case 'DATE':
79
+ // Try to parse date from string
80
+ const date = new Date(initial.value)
81
+ values[param.name] = isNaN(date.getTime()) ? new Date() : date
82
+ break
83
+ case 'BOOLEAN':
84
+ values[param.name] = initial.value === 'true'
85
+ break
86
+ }
87
+ } else {
88
+ // fallback to default
89
+ switch (param.type) {
90
+ case 'NUMBER':
91
+ values[param.name] = 0
92
+ break
93
+ case 'STRING':
94
+ values[param.name] = ''
95
+ break
96
+ case 'DATE':
97
+ values[param.name] = new Date()
98
+ break
99
+ case 'BOOLEAN':
100
+ values[param.name] = false
101
+ break
102
+ case 'LOCATOR':
103
+ values[param.name] = ''
104
+ break
105
+ }
106
+ }
107
+ })
108
+ return values
109
+ }, [templateStepParams, initialParameterValues])
110
+
111
+ // Initialize state with initial values
112
+ const [values, setValues] = useState<{
113
+ [key: string]: string | number | boolean | Date
114
+ }>(initialValues)
115
+ const [errors, setErrors] = useState<Record<string, string>>({})
116
+
117
+ // State for locator group selection
118
+ const [selectedLocatorGroups, setSelectedLocatorGroups] = useState<Record<string, string>>({})
119
+
120
+ useEffect(() => {
121
+ queueMicrotask(() => setErrors({}))
122
+ }, [templateStepParams])
123
+
124
+ useImperativeHandle(ref, () => ({
125
+ validate: () => {
126
+ // Skip all validation if defaultValueInput is true (all fields are optional)
127
+ if (defaultValueInput) {
128
+ setErrors({})
129
+ return true
130
+ }
131
+
132
+ const newErrors: Record<string, string> = {}
133
+ templateStepParams.forEach(param => {
134
+ const value = values[param.name]
135
+
136
+ if (param.type === 'LOCATOR') {
137
+ const selectedGroup = selectedLocatorGroups[param.name]
138
+ if (!selectedGroup) {
139
+ newErrors[param.name] = 'Locator group is required'
140
+ } else if (!value) {
141
+ newErrors[param.name] = 'Locator is required'
142
+ }
143
+ }
144
+ if (param.type === 'STRING' && !value) {
145
+ newErrors[param.name] = 'This field is required'
146
+ }
147
+ if (param.type === 'NUMBER' && !value) {
148
+ newErrors[param.name] = 'This field is required'
149
+ }
150
+ // Add other validation rules here if needed
151
+ })
152
+ setErrors(newErrors)
153
+ return Object.keys(newErrors).length === 0
154
+ },
155
+ }))
156
+
157
+ // Update values when an input changes
158
+ const handleInputChange = (name: string, value: string | number | boolean | Date) => {
159
+ const newValues = {
160
+ ...values,
161
+ [name]: value,
162
+ }
163
+
164
+ setValues(newValues)
165
+
166
+ // Clear error for the field being edited
167
+ if (errors[name]) {
168
+ const newErrors = { ...errors }
169
+ delete newErrors[name]
170
+ setErrors(newErrors)
171
+ }
172
+
173
+ // Notify parent component of changes
174
+ if (onChange) {
175
+ // We need to calculate formatted values based on the new state
176
+ const formattedValues = templateStepParams.map(param => {
177
+ let stringValue = ''
178
+ // Use the new value if it's the one that changed, otherwise use the current state
179
+ const currentValue = param.name === name ? value : values[param.name]
180
+
181
+ switch (param.type) {
182
+ case 'NUMBER':
183
+ stringValue = currentValue !== undefined && currentValue !== null ? String(currentValue) : ''
184
+ break
185
+ case 'STRING':
186
+ stringValue = currentValue !== undefined && currentValue !== null ? (currentValue as string) : ''
187
+ break
188
+ case 'DATE':
189
+ if (
190
+ currentValue &&
191
+ currentValue instanceof Date &&
192
+ typeof currentValue.getTime === 'function' &&
193
+ !Number.isNaN(currentValue.getTime())
194
+ ) {
195
+ stringValue = format(currentValue as Date, 'PPP')
196
+ } else {
197
+ stringValue = ''
198
+ }
199
+ break
200
+ case 'BOOLEAN':
201
+ stringValue = currentValue !== undefined && currentValue !== null ? String(currentValue) : ''
202
+ break
203
+ case 'LOCATOR':
204
+ stringValue = currentValue !== undefined && currentValue !== null ? (currentValue as string) : ''
205
+ break
206
+ }
207
+
208
+ return {
209
+ name: param.name,
210
+ value: stringValue,
211
+ type: param.type,
212
+ order: param.order,
213
+ }
214
+ })
215
+
216
+ onChange(formattedValues)
217
+ }
218
+ }
219
+
220
+ // Handle locator group selection
221
+ const handleLocatorGroupChange = (paramName: string, groupId: string) => {
222
+ setSelectedLocatorGroups(prev => ({
223
+ ...prev,
224
+ [paramName]: groupId,
225
+ }))
226
+
227
+ // Clear the locator selection when group changes
228
+ setValues(prev => ({
229
+ ...prev,
230
+ [paramName]: '',
231
+ }))
232
+
233
+ // Clear errors for this field
234
+ if (errors[paramName]) {
235
+ const newErrors = { ...errors }
236
+ delete newErrors[paramName]
237
+ setErrors(newErrors)
238
+ }
239
+ }
240
+
241
+ // Get locators for a specific group
242
+ const getLocatorsForGroup = (groupId: string) => {
243
+ return locators.filter(locator => locator.locatorGroupId === groupId)
244
+ }
245
+
246
+ // Render the appropriate input field based on the parameter type
247
+ const renderInputField = (param: TemplateStepParameter) => {
248
+ const { name, type } = param
249
+ const errorMessage = errors[name]
250
+
251
+ switch (type) {
252
+ case 'NUMBER':
253
+ return (
254
+ <div className="grid w-full items-center gap-1.5">
255
+ <Label htmlFor={`input-${name}`}>
256
+ {defaultValueInput ? `Default ${name}` : name}{' '}
257
+ {!defaultValueInput && <span className="text-red-500">*</span>}
258
+ </Label>
259
+ <Input
260
+ id={`input-${name}`}
261
+ type="number"
262
+ value={typeof values[name] === 'number' ? values[name] : 0}
263
+ onChange={e => handleInputChange(name, Number(e.target.value))}
264
+ className="w-full"
265
+ />
266
+ <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
267
+ </div>
268
+ )
269
+
270
+ case 'STRING':
271
+ return (
272
+ <div className="grid w-full items-center gap-1.5">
273
+ <Label htmlFor={`input-${name}`}>
274
+ {defaultValueInput ? `Default ${name}` : name}{' '}
275
+ {!defaultValueInput && <span className="text-red-500">*</span>}
276
+ </Label>
277
+ <Input
278
+ id={`input-${name}`}
279
+ type="text"
280
+ value={typeof values[name] === 'string' ? values[name] : ''}
281
+ onChange={e => handleInputChange(name, e.target.value)}
282
+ className="w-full"
283
+ />
284
+ <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
285
+ </div>
286
+ )
287
+
288
+ case 'DATE':
289
+ return (
290
+ <div className="grid w-full items-center gap-1.5">
291
+ <Label>
292
+ {defaultValueInput ? `Default ${name}` : name}{' '}
293
+ {!defaultValueInput && <span className="text-red-500">*</span>}
294
+ </Label>
295
+ <Popover>
296
+ <PopoverTrigger asChild>
297
+ <Button
298
+ variant="outline"
299
+ className={cn('w-full justify-start text-left font-normal', !values[name] && 'text-muted-foreground')}
300
+ aria-required={!defaultValueInput}
301
+ >
302
+ <CalendarIcon className="mr-2 h-4 w-4" />
303
+ {values[name] instanceof Date ? (
304
+ format(values[name] as Date, 'PPP')
305
+ ) : (
306
+ <span className={defaultValueInput ? 'text-muted-foreground' : 'text-red-500'}>
307
+ {defaultValueInput ? 'Pick a date (optional)' : 'Pick a date *'}
308
+ </span>
309
+ )}
310
+ </Button>
311
+ </PopoverTrigger>
312
+ <PopoverContent className="w-auto p-0" align="start">
313
+ <Calendar
314
+ mode="single"
315
+ selected={values[name] instanceof Date ? (values[name] as Date) : undefined}
316
+ onSelect={(date: Date | undefined) => handleInputChange(name, date as Date)}
317
+ initialFocus
318
+ required={!defaultValueInput}
319
+ />
320
+ </PopoverContent>
321
+ </Popover>
322
+ </div>
323
+ )
324
+
325
+ case 'BOOLEAN':
326
+ return (
327
+ <div className="grid w-full items-center gap-1.5">
328
+ <Label htmlFor={`select-${name}`}>
329
+ {defaultValueInput ? `Default ${name}` : name}{' '}
330
+ {!defaultValueInput && <span className="text-red-500">*</span>}
331
+ </Label>
332
+ <Select
333
+ value={typeof values[name] === 'boolean' ? String(values[name]) : 'false'}
334
+ onValueChange={value => handleInputChange(name, value === 'true')}
335
+ required={!defaultValueInput}
336
+ >
337
+ <SelectTrigger id={`select-${name}`} className="w-full">
338
+ <SelectValue placeholder={defaultValueInput ? 'Select a value (optional)' : 'Select a value *'} />
339
+ </SelectTrigger>
340
+ <SelectContent>
341
+ <SelectItem value="true">True</SelectItem>
342
+ <SelectItem value="false">False</SelectItem>
343
+ </SelectContent>
344
+ </Select>
345
+ </div>
346
+ )
347
+
348
+ case 'LOCATOR':
349
+ const selectedGroupId = selectedLocatorGroups[name] || ''
350
+ const availableLocators = selectedGroupId ? getLocatorsForGroup(selectedGroupId) : []
351
+
352
+ return (
353
+ <div className="grid w-full items-center gap-1.5">
354
+ <Label htmlFor={`select-${name}`}>
355
+ {defaultValueInput ? `Default ${name}` : name}{' '}
356
+ {!defaultValueInput && <span className="text-red-500">*</span>}
357
+ </Label>
358
+
359
+ {/* Locator Group Selection */}
360
+ <div className="mb-2">
361
+ <Label htmlFor={`group-${name}`} className="text-sm text-muted-foreground">
362
+ Locator Group
363
+ </Label>
364
+ <Select
365
+ value={selectedGroupId}
366
+ onValueChange={value => handleLocatorGroupChange(name, value)}
367
+ required={!defaultValueInput}
368
+ >
369
+ <SelectTrigger id={`group-${name}`} className="w-full">
370
+ <SelectValue placeholder="Select a locator group" />
371
+ </SelectTrigger>
372
+ <SelectContent isEmpty={locatorGroups.length === 0}>
373
+ {locatorGroups.map(group => (
374
+ <SelectItem key={group.id} value={group.id}>
375
+ {group.name}
376
+ </SelectItem>
377
+ ))}
378
+ </SelectContent>
379
+ </Select>
380
+ </div>
381
+
382
+ {/* Locator Selection */}
383
+ <Select
384
+ value={typeof values[name] === 'string' ? values[name] : ''}
385
+ onValueChange={value => handleInputChange(name, value)}
386
+ required={!defaultValueInput}
387
+ disabled={!selectedGroupId}
388
+ >
389
+ <SelectTrigger id={`select-${name}`} className="w-full">
390
+ <SelectValue
391
+ placeholder={
392
+ !selectedGroupId
393
+ ? 'Select a locator group first'
394
+ : defaultValueInput
395
+ ? 'Select a locator (optional)'
396
+ : 'Select a locator *'
397
+ }
398
+ />
399
+ </SelectTrigger>
400
+ <SelectContent isEmpty={availableLocators.length === 0}>
401
+ {availableLocators.map(locator => (
402
+ <SelectItem key={locator.id} value={locator.name}>
403
+ {locator.name}
404
+ </SelectItem>
405
+ ))}
406
+ </SelectContent>
407
+ </Select>
408
+ <ErrorMessage message={errorMessage || ''} visible={!!errorMessage} />
409
+ </div>
410
+ )
411
+
412
+ default:
413
+ return null
414
+ }
415
+ }
416
+
417
+ // Guard: do not render if no parameters
418
+ if (!templateStepParams || templateStepParams.length === 0) {
419
+ return null
420
+ }
421
+
422
+ return (
423
+ <Card className="border-none shadow-none" key={resetKey}>
424
+ <CardHeader className='py-3'>
425
+ <CardTitle className='text-xs font-bold text-primary'>Parameters</CardTitle>
426
+ </CardHeader>
427
+ <CardContent>
428
+ <div className="space-y-6">
429
+ {templateStepParams.map(param => (
430
+ <div key={param.name}>{renderInputField(param)}</div>
431
+ ))}
432
+ </div>
433
+ </CardContent>
434
+ </Card>
435
+ )
436
+ })
437
+ DynamicFormFields.displayName = 'DynamicFormFields'
438
+ export default DynamicFormFields