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