create-appraise 0.1.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 (330) hide show
  1. package/README.md +52 -0
  2. package/package.json +63 -0
  3. package/templates/default/.env.example +2 -0
  4. package/templates/default/README.md +51 -0
  5. package/templates/default/appraise.config.json +4 -0
  6. package/templates/default/components.json +24 -0
  7. package/templates/default/eslint.config.mjs +15 -0
  8. package/templates/default/next-env.d.ts +6 -0
  9. package/templates/default/next.config.ts +7 -0
  10. package/templates/default/package-lock.json +14321 -0
  11. package/templates/default/package.json +124 -0
  12. package/templates/default/postcss.config.mjs +8 -0
  13. package/templates/default/prisma/migrations/20251026202316_migrate_back_to_sqlite/migration.sql +257 -0
  14. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -0
  15. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -0
  16. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -0
  17. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -0
  18. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -0
  19. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -0
  20. package/templates/default/prisma/migrations/20251130190737_add_trace_path_to_test_run_test_case/migration.sql +2 -0
  21. package/templates/default/prisma/migrations/20251213074835_add_log_path_to_test_run/migration.sql +2 -0
  22. package/templates/default/prisma/migrations/20251213183952_add_name_property_for_the_test_run_entities/migration.sql +30 -0
  23. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -0
  24. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -0
  25. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -0
  26. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -0
  27. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -0
  28. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -0
  29. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -0
  30. package/templates/default/prisma/migrations/migration_lock.toml +3 -0
  31. package/templates/default/prisma/schema.prisma +554 -0
  32. package/templates/default/public/favicon.ico +0 -0
  33. package/templates/default/public/file.svg +1 -0
  34. package/templates/default/public/globe.svg +1 -0
  35. package/templates/default/public/next.svg +1 -0
  36. package/templates/default/public/vercel.svg +1 -0
  37. package/templates/default/public/window.svg +1 -0
  38. package/templates/default/scripts/regenerate-features.ts +94 -0
  39. package/templates/default/scripts/setup-env.ts +19 -0
  40. package/templates/default/scripts/sync-all.ts +341 -0
  41. package/templates/default/scripts/sync-environments.ts +323 -0
  42. package/templates/default/scripts/sync-locator-groups.ts +413 -0
  43. package/templates/default/scripts/sync-locators.ts +402 -0
  44. package/templates/default/scripts/sync-modules.ts +349 -0
  45. package/templates/default/scripts/sync-tags.ts +292 -0
  46. package/templates/default/scripts/sync-template-step-groups.ts +399 -0
  47. package/templates/default/scripts/sync-template-steps.ts +806 -0
  48. package/templates/default/scripts/sync-test-cases.ts +905 -0
  49. package/templates/default/scripts/sync-test-suites.ts +411 -0
  50. package/templates/default/src/actions/conflict/conflict.action.ts +33 -0
  51. package/templates/default/src/actions/dashboard/dashboard-actions.ts +241 -0
  52. package/templates/default/src/actions/environments/environment-actions.ts +205 -0
  53. package/templates/default/src/actions/locator/locator-actions.ts +547 -0
  54. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -0
  55. package/templates/default/src/actions/modules/module-actions.ts +133 -0
  56. package/templates/default/src/actions/reports/report-actions.ts +614 -0
  57. package/templates/default/src/actions/review/review-actions.ts +147 -0
  58. package/templates/default/src/actions/tags/tag-actions.ts +104 -0
  59. package/templates/default/src/actions/template-step/template-step-actions.ts +332 -0
  60. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -0
  61. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -0
  62. package/templates/default/src/actions/test-case/test-case-actions.ts +419 -0
  63. package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -0
  64. package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -0
  65. package/templates/default/src/actions/user/user-actions.ts +13 -0
  66. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -0
  67. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -0
  68. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -0
  69. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -0
  70. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -0
  71. package/templates/default/src/app/(base)/environments/page.tsx +59 -0
  72. package/templates/default/src/app/(base)/layout.tsx +10 -0
  73. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -0
  74. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -0
  75. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -0
  76. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -0
  77. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -0
  78. package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -0
  79. package/templates/default/src/app/(base)/locators/create/page.tsx +38 -0
  80. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -0
  81. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +90 -0
  82. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -0
  83. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -0
  84. package/templates/default/src/app/(base)/locators/page.tsx +65 -0
  85. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -0
  86. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -0
  87. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -0
  88. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -0
  89. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -0
  90. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -0
  91. package/templates/default/src/app/(base)/modules/page.tsx +59 -0
  92. package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -0
  93. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -0
  94. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -0
  95. package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -0
  96. package/templates/default/src/app/(base)/reports/page.tsx +98 -0
  97. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -0
  98. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -0
  99. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -0
  100. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -0
  101. package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -0
  102. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -0
  103. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -0
  104. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -0
  105. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -0
  106. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -0
  107. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -0
  108. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -0
  109. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -0
  110. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -0
  111. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -0
  112. package/templates/default/src/app/(base)/reviews/page.tsx +26 -0
  113. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -0
  114. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -0
  115. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -0
  116. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -0
  117. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -0
  118. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -0
  119. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -0
  120. package/templates/default/src/app/(base)/tags/page.tsx +58 -0
  121. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -0
  122. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -0
  123. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -0
  124. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -0
  125. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -0
  126. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -0
  127. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -0
  128. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -0
  129. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -0
  130. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -0
  131. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -0
  132. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -0
  133. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -0
  134. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -0
  135. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -0
  136. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -0
  137. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -0
  138. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -0
  139. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -0
  140. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -0
  141. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -0
  142. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -0
  143. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -0
  144. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -0
  145. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -0
  146. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -0
  147. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -0
  148. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -0
  149. package/templates/default/src/app/(base)/test-cases/page.tsx +60 -0
  150. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -0
  151. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -0
  152. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -0
  153. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -0
  154. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -0
  155. package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -0
  156. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -0
  157. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +512 -0
  158. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -0
  159. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -0
  160. package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -0
  161. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -0
  162. package/templates/default/src/app/(base)/test-suites/page.tsx +82 -0
  163. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -0
  164. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -0
  165. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -0
  166. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -0
  167. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +13 -0
  168. package/templates/default/src/app/(dashboard-components)/data-card.tsx +27 -0
  169. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +57 -0
  170. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -0
  171. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +45 -0
  172. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -0
  173. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -0
  174. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -0
  175. package/templates/default/src/app/favicon.ico +0 -0
  176. package/templates/default/src/app/globals.css +147 -0
  177. package/templates/default/src/app/layout.tsx +171 -0
  178. package/templates/default/src/app/page.tsx +64 -0
  179. package/templates/default/src/assets/icons/empty-tube.tsx +23 -0
  180. package/templates/default/src/assets/icons/tube-plus.tsx +29 -0
  181. package/templates/default/src/components/base-node.tsx +21 -0
  182. package/templates/default/src/components/chart/pie-chart.tsx +73 -0
  183. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -0
  184. package/templates/default/src/components/data-state/empty-state.tsx +40 -0
  185. package/templates/default/src/components/data-visualization/info-card.tsx +70 -0
  186. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -0
  187. package/templates/default/src/components/devtools/providers.tsx +13 -0
  188. package/templates/default/src/components/diagram/button-edge.tsx +54 -0
  189. package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -0
  190. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -0
  191. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -0
  192. package/templates/default/src/components/diagram/node-form.tsx +262 -0
  193. package/templates/default/src/components/diagram/options-header-node.tsx +57 -0
  194. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -0
  195. package/templates/default/src/components/form/error-message.tsx +7 -0
  196. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -0
  197. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -0
  198. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -0
  199. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -0
  200. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -0
  201. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -0
  202. package/templates/default/src/components/logo.tsx +15 -0
  203. package/templates/default/src/components/navigation/command-badge.tsx +34 -0
  204. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -0
  205. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -0
  206. package/templates/default/src/components/navigation/nav-card.tsx +31 -0
  207. package/templates/default/src/components/navigation/nav-command.tsx +508 -0
  208. package/templates/default/src/components/navigation/nav-link.tsx +60 -0
  209. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -0
  210. package/templates/default/src/components/node-header.tsx +159 -0
  211. package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -0
  212. package/templates/default/src/components/table/table-actions.tsx +172 -0
  213. package/templates/default/src/components/test-run/download-logs-button.tsx +99 -0
  214. package/templates/default/src/components/test-run/log-viewer.tsx +445 -0
  215. package/templates/default/src/components/test-run/test-run-details.tsx +611 -0
  216. package/templates/default/src/components/test-run/test-run-header.tsx +149 -0
  217. package/templates/default/src/components/test-run/view-report-button.tsx +102 -0
  218. package/templates/default/src/components/theme/mode-toggle.tsx +54 -0
  219. package/templates/default/src/components/theme/theme-provider.tsx +8 -0
  220. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -0
  221. package/templates/default/src/components/typography/page-header.tsx +7 -0
  222. package/templates/default/src/components/ui/alert-dialog.tsx +106 -0
  223. package/templates/default/src/components/ui/alert.tsx +43 -0
  224. package/templates/default/src/components/ui/avatar.tsx +40 -0
  225. package/templates/default/src/components/ui/badge.tsx +29 -0
  226. package/templates/default/src/components/ui/button.tsx +47 -0
  227. package/templates/default/src/components/ui/calendar.tsx +158 -0
  228. package/templates/default/src/components/ui/card.tsx +43 -0
  229. package/templates/default/src/components/ui/chart.tsx +369 -0
  230. package/templates/default/src/components/ui/checkbox.tsx +28 -0
  231. package/templates/default/src/components/ui/command.tsx +135 -0
  232. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -0
  233. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -0
  234. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -0
  235. package/templates/default/src/components/ui/data-table.tsx +267 -0
  236. package/templates/default/src/components/ui/dialog.tsx +97 -0
  237. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -0
  238. package/templates/default/src/components/ui/empty.tsx +104 -0
  239. package/templates/default/src/components/ui/input.tsx +22 -0
  240. package/templates/default/src/components/ui/kbd.tsx +28 -0
  241. package/templates/default/src/components/ui/label.tsx +19 -0
  242. package/templates/default/src/components/ui/loading.tsx +12 -0
  243. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -0
  244. package/templates/default/src/components/ui/multi-select.tsx +142 -0
  245. package/templates/default/src/components/ui/navigation-menu.tsx +120 -0
  246. package/templates/default/src/components/ui/popover.tsx +33 -0
  247. package/templates/default/src/components/ui/progress.tsx +25 -0
  248. package/templates/default/src/components/ui/radio-group.tsx +44 -0
  249. package/templates/default/src/components/ui/scroll-area.tsx +40 -0
  250. package/templates/default/src/components/ui/select.tsx +144 -0
  251. package/templates/default/src/components/ui/separator.tsx +22 -0
  252. package/templates/default/src/components/ui/skeleton.tsx +7 -0
  253. package/templates/default/src/components/ui/table.tsx +76 -0
  254. package/templates/default/src/components/ui/tabs.tsx +55 -0
  255. package/templates/default/src/components/ui/textarea.tsx +21 -0
  256. package/templates/default/src/components/ui/toast.tsx +113 -0
  257. package/templates/default/src/components/ui/toaster.tsx +26 -0
  258. package/templates/default/src/components/ui/tooltip.tsx +32 -0
  259. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -0
  260. package/templates/default/src/config/db-config.ts +10 -0
  261. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -0
  262. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -0
  263. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -0
  264. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -0
  265. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -0
  266. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -0
  267. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -0
  268. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -0
  269. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -0
  270. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -0
  271. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -0
  272. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -0
  273. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -0
  274. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -0
  275. package/templates/default/src/hooks/use-toast.ts +187 -0
  276. package/templates/default/src/lib/bidirectional-sync.ts +432 -0
  277. package/templates/default/src/lib/database-sync.ts +531 -0
  278. package/templates/default/src/lib/environment-file-utils.ts +221 -0
  279. package/templates/default/src/lib/feature-file-generator.ts +411 -0
  280. package/templates/default/src/lib/gherkin-parser.ts +259 -0
  281. package/templates/default/src/lib/locator-group-file-utils.ts +370 -0
  282. package/templates/default/src/lib/metrics/metric-calculator.ts +613 -0
  283. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -0
  284. package/templates/default/src/lib/path-helpers/module-path.ts +71 -0
  285. package/templates/default/src/lib/test-case-utils.ts +6 -0
  286. package/templates/default/src/lib/test-run/log-formatter.ts +83 -0
  287. package/templates/default/src/lib/test-run/process-manager.ts +191 -0
  288. package/templates/default/src/lib/test-run/report-parser.ts +316 -0
  289. package/templates/default/src/lib/test-run/test-run-executor.ts +144 -0
  290. package/templates/default/src/lib/test-run/winston-logger.ts +95 -0
  291. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -0
  292. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -0
  293. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -0
  294. package/templates/default/src/lib/utils/node-param-validation.ts +81 -0
  295. package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -0
  296. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -0
  297. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -0
  298. package/templates/default/src/lib/utils.ts +31 -0
  299. package/templates/default/src/tests/config/environments/environments.json +14 -0
  300. package/templates/default/src/tests/config/executor/world.ts +41 -0
  301. package/templates/default/src/tests/executor.ts +80 -0
  302. package/templates/default/src/tests/hooks/hooks.ts +99 -0
  303. package/templates/default/src/tests/mapping/locator-map.json +1 -0
  304. package/templates/default/src/tests/steps/actions/click.step.ts +62 -0
  305. package/templates/default/src/tests/steps/actions/hover.step.ts +31 -0
  306. package/templates/default/src/tests/steps/actions/input.step.ts +149 -0
  307. package/templates/default/src/tests/steps/actions/navigation.step.ts +72 -0
  308. package/templates/default/src/tests/steps/actions/random_data.step.ts +146 -0
  309. package/templates/default/src/tests/steps/actions/store.step.ts +90 -0
  310. package/templates/default/src/tests/steps/actions/wait.step.ts +107 -0
  311. package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -0
  312. package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +23 -0
  313. package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -0
  314. package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -0
  315. package/templates/default/src/tests/support/parameter-types.ts +12 -0
  316. package/templates/default/src/tests/utils/cache.util.ts +260 -0
  317. package/templates/default/src/tests/utils/cli.util.ts +177 -0
  318. package/templates/default/src/tests/utils/environment.util.ts +65 -0
  319. package/templates/default/src/tests/utils/locator.util.ts +248 -0
  320. package/templates/default/src/tests/utils/random-data.util.ts +45 -0
  321. package/templates/default/src/tests/utils/spawner.util.ts +617 -0
  322. package/templates/default/src/types/diagram/diagram.ts +34 -0
  323. package/templates/default/src/types/diagram/template-step.ts +11 -0
  324. package/templates/default/src/types/executor/browser.type.ts +1 -0
  325. package/templates/default/src/types/form/actionHandler.ts +6 -0
  326. package/templates/default/src/types/locator/locator.type.ts +11 -0
  327. package/templates/default/src/types/step/step.type.ts +1 -0
  328. package/templates/default/src/types/table/data-table.ts +6 -0
  329. package/templates/default/tailwind.config.ts +62 -0
  330. package/templates/default/tsconfig.json +28 -0
@@ -0,0 +1,39 @@
1
+ import { createTagAction } from '@/actions/tags/tag-actions'
2
+ import PageHeader from '@/components/typography/page-header'
3
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
4
+ import { Tag } from 'lucide-react'
5
+ import React from 'react'
6
+ import TagForm from '../tag-form'
7
+ import { Metadata } from 'next'
8
+
9
+ export const metadata: Metadata = {
10
+ title: 'Appraise | Create Tag',
11
+ description: 'Create a new tag to be used in your test cases',
12
+ }
13
+
14
+ const CreateTag = () => {
15
+ return (
16
+ <>
17
+ <div className="mb-8">
18
+ <PageHeader>
19
+ <span className="flex items-center">
20
+ <Tag className="mr-2 h-8 w-8" />
21
+ Create Tag
22
+ </span>
23
+ </PageHeader>
24
+ <HeaderSubtitle>Create a new tag to be used in your test cases.</HeaderSubtitle>
25
+ </div>
26
+ <TagForm
27
+ successTitle="Tag created"
28
+ successMessage="Tag created successfully"
29
+ onSubmitAction={createTagAction}
30
+ defaultValues={{
31
+ name: '',
32
+ tagExpression: '',
33
+ }}
34
+ />
35
+ </>
36
+ )
37
+ }
38
+
39
+ export default CreateTag
@@ -0,0 +1,50 @@
1
+ import PageHeader from '@/components/typography/page-header'
2
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
3
+ import React from 'react'
4
+ import TagForm from '../../tag-form'
5
+ import { getTagByIdAction, updateTagAction } from '@/actions/tags/tag-actions'
6
+ import { Tag } from 'lucide-react'
7
+ import { Tag as TagType } from '@prisma/client'
8
+ import { Metadata } from 'next'
9
+
10
+ export const metadata: Metadata = {
11
+ title: 'Appraise | Modify Tag',
12
+ description: 'Modify a tag',
13
+ }
14
+
15
+ const ModifyTag = async ({ params }: { params: Promise<{ id: string }> }) => {
16
+ const { id } = await params
17
+ const { data: tagToBeEditedData, error: tagToBeEditedError } = await getTagByIdAction(id)
18
+
19
+ if (tagToBeEditedError) {
20
+ return <div>Error: {tagToBeEditedError}</div>
21
+ }
22
+
23
+ const tagData = tagToBeEditedData as TagType
24
+
25
+ return (
26
+ <>
27
+ <div className="mb-8">
28
+ <PageHeader>
29
+ <span className="flex items-center">
30
+ <Tag className="mr-2 h-8 w-8" />
31
+ Modify Tag
32
+ </span>
33
+ </PageHeader>
34
+ <HeaderSubtitle>Modify a tag.</HeaderSubtitle>
35
+ </div>
36
+ <TagForm
37
+ successTitle="Tag modified"
38
+ successMessage="Tag modified successfully"
39
+ onSubmitAction={updateTagAction}
40
+ defaultValues={{
41
+ name: tagData.name,
42
+ tagExpression: tagData.tagExpression,
43
+ }}
44
+ id={id}
45
+ />
46
+ </>
47
+ )
48
+ }
49
+
50
+ export default ModifyTag
@@ -0,0 +1,58 @@
1
+ import DataTableSkeleton from '@/components/loading-skeleton/data-table/data-table-skeleton'
2
+ import PageHeader from '@/components/typography/page-header'
3
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
4
+ import { Tag } from 'lucide-react'
5
+ import React, { Suspense } from 'react'
6
+ import TagTable from './tag-table'
7
+ import { getAllTagsAction } from '@/actions/tags/tag-actions'
8
+ import EmptyState from '@/components/data-state/empty-state'
9
+ import { Tag as TagModel } from '@prisma/client'
10
+ import { Metadata } from 'next'
11
+
12
+ export const metadata: Metadata = {
13
+ title: 'Appraise | Tags',
14
+ description: 'Manage tags for categorizing test cases and test runs',
15
+ }
16
+
17
+ const Tags = async () => {
18
+ const { data: tags, error: tagsError } = await getAllTagsAction()
19
+
20
+ if (tagsError) {
21
+ return <div>Error: {tagsError}</div>
22
+ }
23
+
24
+ const tagsData = tags as TagModel[]
25
+
26
+ if (!tagsData || tagsData.length === 0) {
27
+ return (
28
+ <div className="flex min-h-[calc(100vh-20rem)] items-center justify-center">
29
+ <EmptyState
30
+ icon={<Tag className="h-8 w-8" />}
31
+ title="No tags found"
32
+ description="Get started by creating a tag to categorize your test cases and test runs"
33
+ createRoute="/tags/create"
34
+ createText="Create Tag"
35
+ />
36
+ </div>
37
+ )
38
+ }
39
+
40
+ return (
41
+ <>
42
+ <div className="mb-8">
43
+ <PageHeader>
44
+ <span className="flex items-center">
45
+ <Tag className="mr-2 h-8 w-8" />
46
+ Tags
47
+ </span>
48
+ </PageHeader>
49
+ <HeaderSubtitle>Tags are used to categorize test cases and test runs</HeaderSubtitle>
50
+ </div>
51
+ <Suspense fallback={<DataTableSkeleton />}>
52
+ <TagTable />
53
+ </Suspense>
54
+ </>
55
+ )
56
+ }
57
+
58
+ export default Tags
@@ -0,0 +1,147 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@/components/ui/button'
4
+ import { Input } from '@/components/ui/input'
5
+ import { Label } from '@/components/ui/label'
6
+ import { formOpts, type Tag } from '@/constants/form-opts/tag-form-opts'
7
+ import { toast } from '@/hooks/use-toast'
8
+ import { ActionResponse } from '@/types/form/actionHandler'
9
+
10
+ import { useForm } from '@tanstack/react-form'
11
+ import { useRouter } from 'next/navigation'
12
+ import React from 'react'
13
+ import { z } from 'zod'
14
+
15
+ const TagForm = ({
16
+ defaultValues,
17
+ successTitle,
18
+ successMessage,
19
+ id,
20
+ onSubmitAction,
21
+ }: {
22
+ defaultValues?: Tag
23
+ successTitle: string
24
+ successMessage: string
25
+ id?: string
26
+ onSubmitAction: (_prev: unknown, value: Tag, id?: string) => Promise<ActionResponse>
27
+ }) => {
28
+ const router = useRouter()
29
+ const form = useForm({
30
+ defaultValues: defaultValues ?? formOpts?.defaultValues,
31
+ validators: formOpts?.validators,
32
+ onSubmit: async ({ value }) => {
33
+ const res = await onSubmitAction(undefined, value, id)
34
+ if (res.status === 200) {
35
+ toast({
36
+ title: successTitle,
37
+ description: successMessage,
38
+ })
39
+ router.push('/tags')
40
+ }
41
+ if (res.status === 400) {
42
+ toast({
43
+ title: 'Error',
44
+ description: res.error,
45
+ variant: 'destructive',
46
+ })
47
+ }
48
+ if (res.status === 500) {
49
+ toast({
50
+ title: 'Error',
51
+ description: res.error,
52
+ variant: 'destructive',
53
+ })
54
+ }
55
+ },
56
+ })
57
+ return (
58
+ <form
59
+ onSubmit={e => {
60
+ e.preventDefault()
61
+ e.stopPropagation()
62
+ form.handleSubmit()
63
+ }}
64
+ >
65
+ <form.Field
66
+ name="name"
67
+ validators={{
68
+ onChange: z.string().min(1, { message: 'Name is required' }),
69
+ }}
70
+ >
71
+ {field => {
72
+ return (
73
+ <div className="mb-4 flex flex-col gap-2 lg:w-1/3">
74
+ <Label htmlFor={field.name}>Name</Label>
75
+ <Input id={field.name} value={field.state.value} onChange={e => field.handleChange(e.target.value)} />
76
+ {field.state.meta.isTouched &&
77
+ field.state.meta.errors.map((error, index) => (
78
+ <p key={index} className="text-xs text-pink-500">
79
+ {typeof error === 'string' ? error : error?.message || String(error)}
80
+ </p>
81
+ ))}
82
+ </div>
83
+ )
84
+ }}
85
+ </form.Field>
86
+ <form.Field
87
+ name="tagExpression"
88
+ validators={{
89
+ onChange: z
90
+ .string()
91
+ .min(1, { message: 'Tag expression is required' })
92
+ .refine(
93
+ value => {
94
+ // Check if the value follows Gherkin tag rules
95
+ // Only one tag allowed per entry - single word after @ symbol
96
+ const trimmedValue = value.trim()
97
+ if (!trimmedValue) return false
98
+
99
+ // Should not contain any spaces (only one tag allowed)
100
+ if (trimmedValue.includes(' ')) return false
101
+
102
+ // Tag should start with @
103
+ if (!trimmedValue.startsWith('@')) return false
104
+
105
+ // Tag should have at least one character after @
106
+ if (trimmedValue.length <= 1) return false
107
+
108
+ return true
109
+ },
110
+ {
111
+ message: 'Tag expression must be a single tag starting with @ and contain no spaces (e.g., "@smoke")',
112
+ },
113
+ ),
114
+ }}
115
+ >
116
+ {field => {
117
+ return (
118
+ <div className="mb-4 flex flex-col gap-2 lg:w-1/3">
119
+ <Label htmlFor={field.name}>Tag Expression</Label>
120
+ <Input
121
+ id={field.name}
122
+ value={field.state.value}
123
+ onChange={e => field.handleChange(e.target.value)}
124
+ placeholder="e.g. @smoke"
125
+ />
126
+ {field.state.meta.isTouched &&
127
+ field.state.meta.errors.map((error, index) => (
128
+ <p key={index} className="text-xs text-pink-500">
129
+ {typeof error === 'string' ? error : error?.message || String(error)}
130
+ </p>
131
+ ))}
132
+ </div>
133
+ )
134
+ }}
135
+ </form.Field>
136
+ <form.Subscribe selector={formState => [formState.canSubmit, formState.isSubmitting]}>
137
+ {([canSubmit, isSubmitting]) => (
138
+ <Button type="submit" disabled={!canSubmit}>
139
+ {isSubmitting ? '...' : 'Save'}
140
+ </Button>
141
+ )}
142
+ </form.Subscribe>
143
+ </form>
144
+ )
145
+ }
146
+
147
+ export default TagForm
@@ -0,0 +1,63 @@
1
+ 'use client'
2
+
3
+ import { DataTableColumnHeader } from '@/components/ui/data-table-column-header'
4
+ import { Tag } from '@prisma/client'
5
+ import { ColumnDef } from '@tanstack/react-table'
6
+
7
+ import { Checkbox } from '@/components/ui/checkbox'
8
+ import TableActions from '@/components/table/table-actions'
9
+ import { deleteTagAction } from '@/actions/tags/tag-actions'
10
+ import { formatDateTime } from '@/lib/utils'
11
+
12
+ export const tagTableCols: ColumnDef<Tag>[] = [
13
+ {
14
+ id: 'select',
15
+ header: ({ table }) => (
16
+ <Checkbox
17
+ checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate')}
18
+ onCheckedChange={value => table.toggleAllPageRowsSelected(!!value)}
19
+ aria-label="Select all"
20
+ className="mr-2"
21
+ />
22
+ ),
23
+ cell: ({ row }) => (
24
+ <Checkbox
25
+ checked={row.getIsSelected()}
26
+ onCheckedChange={value => row.toggleSelected(!!value)}
27
+ aria-label="Select row"
28
+ className="mr-2"
29
+ />
30
+ ),
31
+ enableSorting: false,
32
+ enableHiding: false,
33
+ },
34
+ {
35
+ accessorKey: 'name',
36
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Name" />,
37
+ },
38
+ {
39
+ accessorKey: 'tagExpression',
40
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Tag Expression" />,
41
+ },
42
+ {
43
+ accessorKey: 'createdAt',
44
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Created At" />,
45
+ cell: ({ row }) => {
46
+ return formatDateTime(row.original.createdAt)
47
+ },
48
+ },
49
+ {
50
+ accessorKey: 'updatedAt',
51
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Updated At" />,
52
+ cell: ({ row }) => {
53
+ return formatDateTime(row.original.updatedAt)
54
+ },
55
+ },
56
+ {
57
+ id: 'actions',
58
+ cell: ({ row }) => {
59
+ const tag = row.original
60
+ return <TableActions modifyLink={`/tags/modify/${tag.id}`} deleteHandler={() => deleteTagAction([tag.id])} />
61
+ },
62
+ },
63
+ ]
@@ -0,0 +1,29 @@
1
+ import { DataTable } from '@/components/ui/data-table'
2
+ import { deleteTagAction, getAllTagsAction } from '@/actions/tags/tag-actions'
3
+ import { Tag } from '@prisma/client'
4
+ import React from 'react'
5
+ import { tagTableCols } from './tag-table-columns'
6
+
7
+ const TagTable = async () => {
8
+ const { data: tags, error: tagsError } = await getAllTagsAction()
9
+
10
+ if (tagsError) {
11
+ return <div>Error: {tagsError}</div>
12
+ }
13
+
14
+ return (
15
+ <>
16
+ <DataTable
17
+ columns={tagTableCols}
18
+ data={tags as Tag[]}
19
+ filterColumn="name"
20
+ filterPlaceholder="Filter by name..."
21
+ createLink="/tags/create"
22
+ modifyLink="/tags/modify"
23
+ deleteAction={deleteTagAction}
24
+ />
25
+ </>
26
+ )
27
+ }
28
+
29
+ export default TagTable
@@ -0,0 +1,28 @@
1
+ import { createTemplateStepGroupAction } from '@/actions/template-step-group/template-step-group-actions'
2
+ import { TemplateStepGroupForm } from '../template-step-group-form'
3
+ import PageHeader from '@/components/typography/page-header'
4
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
5
+ import { Metadata } from 'next'
6
+
7
+ export const metadata: Metadata = {
8
+ title: 'Appraise | Create Template Step Group',
9
+ description: 'Create a new template step group to organize related template steps',
10
+ }
11
+
12
+ const CreateTemplateStepGroup = async () => {
13
+ return (
14
+ <>
15
+ <div className="mb-8">
16
+ <PageHeader>Create Template Step Group</PageHeader>
17
+ <HeaderSubtitle>Create a new template step group to organize related template steps</HeaderSubtitle>
18
+ </div>
19
+ <TemplateStepGroupForm
20
+ successTitle="Group created"
21
+ successMessage="Template step group created successfully"
22
+ onSubmitAction={createTemplateStepGroupAction}
23
+ />
24
+ </>
25
+ )
26
+ }
27
+
28
+ export default CreateTemplateStepGroup
@@ -0,0 +1,45 @@
1
+ import {
2
+ getTemplateStepGroupByIdAction,
3
+ updateTemplateStepGroupAction,
4
+ } from '@/actions/template-step-group/template-step-group-actions'
5
+ import { TemplateStepGroupForm } from '../../template-step-group-form'
6
+ import React from 'react'
7
+ import { TemplateStepGroup } from '@prisma/client'
8
+ import { Metadata } from 'next'
9
+
10
+ export const metadata: Metadata = {
11
+ title: 'Appraise | Modify Template Step Group',
12
+ description: 'Update template step group configuration',
13
+ }
14
+
15
+ const TemplateStepGroupType = {
16
+ ACTION: 'ACTION',
17
+ VALIDATION: 'VALIDATION',
18
+ } as const
19
+
20
+ const ModifyTemplateStepGroup = async ({ params }: { params: Promise<{ id: string }> }) => {
21
+ const { id } = await params
22
+ const { data: templateStepGroup, error } = await getTemplateStepGroupByIdAction(id)
23
+
24
+ if (error) {
25
+ return <div>Error: {error}</div>
26
+ }
27
+
28
+ const templateStepGroupData = templateStepGroup as TemplateStepGroup
29
+
30
+ return (
31
+ <TemplateStepGroupForm
32
+ defaultValues={{
33
+ name: templateStepGroupData.name ?? '',
34
+ description: templateStepGroupData.description ?? '',
35
+ type: (templateStepGroupData as { type?: 'ACTION' | 'VALIDATION' }).type ?? TemplateStepGroupType.ACTION,
36
+ }}
37
+ successTitle="Group updated"
38
+ successMessage="Template step group updated successfully"
39
+ onSubmitAction={updateTemplateStepGroupAction}
40
+ id={id}
41
+ />
42
+ )
43
+ }
44
+
45
+ export default ModifyTemplateStepGroup
@@ -0,0 +1,60 @@
1
+ import PageHeader from '@/components/typography/page-header'
2
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
3
+ import { Component } from 'lucide-react'
4
+ import TemplateStepGroupTable from './template-step-group-table'
5
+ import { Suspense } from 'react'
6
+ import DataTableSkeleton from '@/components/loading-skeleton/data-table/data-table-skeleton'
7
+ import { getAllTemplateStepGroupsAction } from '@/actions/template-step-group/template-step-group-actions'
8
+ import EmptyState from '@/components/data-state/empty-state'
9
+ import { TemplateStepGroup } from '@prisma/client'
10
+ import { Metadata } from 'next'
11
+
12
+ export const metadata: Metadata = {
13
+ title: 'Appraise | Template Step Groups',
14
+ description: 'Manage template step groups for organizing related template steps',
15
+ }
16
+
17
+ const TemplateStepGroups = async () => {
18
+ const { data: templateStepGroups, error: templateStepGroupsError } = await getAllTemplateStepGroupsAction()
19
+
20
+ if (templateStepGroupsError) {
21
+ return <div>Error: {templateStepGroupsError}</div>
22
+ }
23
+
24
+ const templateStepGroupsData = templateStepGroups as TemplateStepGroup[]
25
+
26
+ if (!templateStepGroupsData || templateStepGroupsData.length === 0) {
27
+ return (
28
+ <div className="flex min-h-[calc(100vh-20rem)] items-center justify-center">
29
+ <EmptyState
30
+ icon={<Component className="h-8 w-8" />}
31
+ title="No template step groups found"
32
+ description="Get started by creating a template step group to organize related template steps"
33
+ createRoute="/template-step-groups/create"
34
+ createText="Create Template Step Group"
35
+ />
36
+ </div>
37
+ )
38
+ }
39
+
40
+ return (
41
+ <>
42
+ <div className="mb-8">
43
+ <PageHeader>
44
+ <span className="flex items-center">
45
+ <Component className="mr-2 h-8 w-8" />
46
+ Template Step Groups
47
+ </span>
48
+ </PageHeader>
49
+ <HeaderSubtitle>
50
+ Template step groups organize related template steps for better management and reusability
51
+ </HeaderSubtitle>
52
+ </div>
53
+ <Suspense fallback={<DataTableSkeleton />}>
54
+ <TemplateStepGroupTable />
55
+ </Suspense>
56
+ </>
57
+ )
58
+ }
59
+
60
+ export default TemplateStepGroups
@@ -0,0 +1,167 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@/components/ui/button'
4
+ import { Input } from '@/components/ui/input'
5
+ import { Label } from '@/components/ui/label'
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
7
+ import { formOpts, TemplateStepGroup } from '@/constants/form-opts/template-step-group-form-opts'
8
+ import { toast } from '@/hooks/use-toast'
9
+ import { ActionResponse } from '@/types/form/actionHandler'
10
+ import { useForm } from '@tanstack/react-form'
11
+ import { useRouter } from 'next/navigation'
12
+ import { z } from 'zod'
13
+
14
+ // TemplateStepGroupType values
15
+ const TemplateStepGroupType = {
16
+ ACTION: 'ACTION',
17
+ VALIDATION: 'VALIDATION',
18
+ } as const
19
+
20
+ // Enum validator for type field
21
+ const TemplateStepGroupTypeEnum = z.enum(['ACTION', 'VALIDATION'])
22
+
23
+ export const TemplateStepGroupForm = ({
24
+ defaultValues,
25
+ successTitle,
26
+ successMessage,
27
+ id,
28
+ onSubmitAction,
29
+ }: {
30
+ defaultValues?: TemplateStepGroup
31
+ successTitle: string
32
+ successMessage: string
33
+ id?: string
34
+ onSubmitAction: (
35
+ _prev: unknown,
36
+ value: TemplateStepGroup,
37
+ id?: string,
38
+ ) => Promise<ActionResponse>
39
+ }) => {
40
+ const router = useRouter()
41
+ const form = useForm({
42
+ defaultValues: defaultValues ?? formOpts?.defaultValues,
43
+ validators: formOpts?.validators,
44
+ onSubmit: async ({ value }) => {
45
+ const res = await onSubmitAction(undefined, value, id)
46
+ if (res.status === 200) {
47
+ toast({
48
+ title: successTitle,
49
+ description: successMessage,
50
+ })
51
+ router.push('/template-step-groups')
52
+ }
53
+ if (res.status === 400) {
54
+ toast({
55
+ title: 'Error',
56
+ description: res.error,
57
+ variant: 'destructive',
58
+ })
59
+ }
60
+ if (res.status === 500) {
61
+ toast({
62
+ title: 'Error',
63
+ description: res.error,
64
+ variant: 'destructive',
65
+ })
66
+ }
67
+ },
68
+ })
69
+ return (
70
+ <form
71
+ onSubmit={e => {
72
+ e.preventDefault()
73
+ e.stopPropagation()
74
+ form.handleSubmit()
75
+ }}
76
+ >
77
+ <form.Field
78
+ name="name"
79
+ validators={{
80
+ onChange: z.string().min(3, { message: 'Name must be at least 3 characters' }),
81
+ }}
82
+ >
83
+ {field => {
84
+ return (
85
+ <div className="mb-4 flex flex-col gap-2 lg:w-1/3">
86
+ <Label htmlFor={field.name}>Name</Label>
87
+ <Input
88
+ id={field.name}
89
+ name={field.name}
90
+ value={field.state.value}
91
+ onChange={e => field.handleChange(e.target.value)}
92
+ />
93
+ {field.state.meta.errors.map((error, index) => (
94
+ <p key={index} className="text-xs text-pink-500">
95
+ {typeof error === 'string' ? error : error?.message || String(error)}
96
+ </p>
97
+ ))}
98
+ </div>
99
+ )
100
+ }}
101
+ </form.Field>
102
+ <form.Field name="description">
103
+ {field => {
104
+ return (
105
+ <div className="mb-4 flex flex-col gap-2 lg:w-1/3">
106
+ <Label htmlFor={field.name}>Description</Label>
107
+ <Input
108
+ id={field.name}
109
+ name={field.name}
110
+ value={field.state.value}
111
+ onChange={e => field.handleChange(e.target.value)}
112
+ />
113
+ {field.state.meta.errors.map((error, index) => (
114
+ <p key={index} className="text-xs text-pink-500">
115
+ {typeof error === 'string' ? error : error?.message || String(error)}
116
+ </p>
117
+ ))}
118
+ </div>
119
+ )
120
+ }}
121
+ </form.Field>
122
+ <form.Field
123
+ name="type"
124
+ validators={{
125
+ onChange: TemplateStepGroupTypeEnum,
126
+ }}
127
+ >
128
+ {field => {
129
+ return (
130
+ <div className="mb-4 flex flex-col gap-2 lg:w-1/3">
131
+ <Label htmlFor={field.name}>Type</Label>
132
+ <Select
133
+ onValueChange={value => {
134
+ field.handleChange(value as 'ACTION' | 'VALIDATION')
135
+ }}
136
+ value={field.state.value}
137
+ >
138
+ <SelectTrigger id={field.name}>
139
+ <SelectValue placeholder="Select a type" />
140
+ </SelectTrigger>
141
+ <SelectContent>
142
+ {Object.values(TemplateStepGroupType).map(type => (
143
+ <SelectItem key={type} value={type as string}>
144
+ {type}
145
+ </SelectItem>
146
+ ))}
147
+ </SelectContent>
148
+ </Select>
149
+ {field.state.meta.errors.map((error, index) => (
150
+ <p key={index} className="text-xs text-pink-500">
151
+ {typeof error === 'string' ? error : error?.message || String(error)}
152
+ </p>
153
+ ))}
154
+ </div>
155
+ )
156
+ }}
157
+ </form.Field>
158
+ <form.Subscribe selector={formState => [formState.canSubmit, formState.isSubmitting]}>
159
+ {([canSubmit, isSubmitting]) => (
160
+ <Button type="submit" disabled={!canSubmit}>
161
+ {isSubmitting ? '...' : 'Save'}
162
+ </Button>
163
+ )}
164
+ </form.Subscribe>
165
+ </form>
166
+ )
167
+ }