@nextsparkjs/theme-default 0.1.0-beta.17 → 0.1.0-beta.171

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 (332) hide show
  1. package/api/ai/chat/stream/route.ts +4 -1
  2. package/api/ai/orchestrator/route.ts +10 -3
  3. package/api/ai/single-agent/route.ts +10 -3
  4. package/api/ai/usage/route.ts +4 -1
  5. package/blocks/benefits/component.tsx +4 -4
  6. package/blocks/cta-section/component.tsx +4 -4
  7. package/blocks/faq-accordion/component.tsx +2 -2
  8. package/blocks/features-grid/component.tsx +5 -5
  9. package/blocks/hero/component.tsx +2 -2
  10. package/blocks/hero/fields.ts +1 -1
  11. package/blocks/hero-with-form/component.tsx +7 -7
  12. package/blocks/hero-with-form/fields.ts +1 -1
  13. package/blocks/jumbotron/component.tsx +7 -7
  14. package/blocks/jumbotron/fields.ts +1 -1
  15. package/blocks/logo-cloud/component.tsx +6 -6
  16. package/blocks/logo-cloud/fields.ts +1 -1
  17. package/blocks/post-content/component.tsx +2 -2
  18. package/blocks/pricing-table/component.tsx +5 -5
  19. package/blocks/split-content/component.tsx +5 -5
  20. package/blocks/split-content/fields.ts +1 -1
  21. package/blocks/stats-counter/component.tsx +9 -9
  22. package/blocks/testimonials/component.tsx +4 -4
  23. package/blocks/testimonials/fields.ts +1 -1
  24. package/blocks/text-content/component.tsx +12 -10
  25. package/blocks/timeline/component.tsx +12 -12
  26. package/blocks/video-hero/component.tsx +7 -7
  27. package/blocks/video-hero/fields.ts +1 -1
  28. package/components/ai-chat/ChatPanel.tsx +7 -7
  29. package/components/ai-chat/Message.tsx +2 -2
  30. package/components/ai-chat/MessageInput.tsx +3 -3
  31. package/components/ai-chat/MessageList.tsx +3 -3
  32. package/components/ai-chat/TypingIndicator.tsx +2 -2
  33. package/config/app.config.ts +90 -62
  34. package/config/billing.config.ts +5 -10
  35. package/config/dashboard.config.ts +14 -0
  36. package/config/features.config.ts +10 -0
  37. package/config/permissions.config.ts +22 -23
  38. package/docs/{01-overview → public/01-overview}/01-introduction.md +5 -0
  39. package/docs/{01-overview → public/01-overview}/02-customization.md +5 -0
  40. package/docs/{02-features → public/02-features}/03-tasks-entity.md +5 -0
  41. package/docs/{03-ai → public/03-ai}/01-overview.md +5 -0
  42. package/docs/{03-ai → public/03-ai}/02-customization.md +5 -0
  43. package/docs/superadmin/01-setup/01-configuration.md +79 -0
  44. package/docs/superadmin/01-setup/02-deployment.md +82 -0
  45. package/docs/superadmin/02-management/01-users.md +83 -0
  46. package/docs/superadmin/03-integrations/01-langchain.md +139 -0
  47. package/emails/verify-email.ts +33 -0
  48. package/entities/customers/api/docs.md +107 -0
  49. package/entities/customers/api/presets.ts +80 -0
  50. package/entities/pages/api/docs.md +114 -0
  51. package/entities/pages/api/presets.ts +72 -0
  52. package/entities/posts/api/docs.md +120 -0
  53. package/entities/posts/api/presets.ts +74 -0
  54. package/entities/tasks/api/docs.md +126 -0
  55. package/entities/tasks/api/presets.ts +84 -0
  56. package/lib/selectors.ts +7 -4
  57. package/messages/de/admin.json +45 -0
  58. package/messages/en/admin.json +56 -0
  59. package/messages/en/navigation.json +2 -1
  60. package/messages/es/admin.json +56 -0
  61. package/messages/es/navigation.json +2 -1
  62. package/messages/fr/admin.json +45 -0
  63. package/messages/it/admin.json +45 -0
  64. package/messages/pt/admin.json +45 -0
  65. package/migrations/089_add_editor_team_role.sql +15 -23
  66. package/migrations/090_demo_users_teams.sql +11 -11
  67. package/migrations/091_greek_teams_billing.sql +15 -15
  68. package/migrations/093_pages_sample_data.sql +7 -7
  69. package/migrations/098_patterns_sample_data.sql +234 -0
  70. package/nextsparkjs-theme-default-0.1.0-beta.137.tgz +0 -0
  71. package/package.json +4 -3
  72. package/styles/globals.css +42 -0
  73. package/templates/(public)/page.tsx +1 -1
  74. package/tests/cypress/e2e/_utils/devtools/access.bdd.md +262 -0
  75. package/tests/cypress/e2e/_utils/devtools/access.cy.ts +171 -0
  76. package/tests/cypress/e2e/_utils/devtools/navigation.bdd.md +261 -0
  77. package/tests/cypress/e2e/_utils/devtools/navigation.cy.ts +157 -0
  78. package/tests/cypress/e2e/_utils/devtools/pages.bdd.md +303 -0
  79. package/tests/cypress/e2e/_utils/devtools/pages.cy.ts +184 -0
  80. package/tests/cypress/e2e/_utils/docs/README.md +215 -0
  81. package/tests/cypress/e2e/_utils/selectors/auth.bdd.md +354 -0
  82. package/tests/cypress/e2e/_utils/selectors/auth.cy.ts +310 -0
  83. package/tests/cypress/e2e/_utils/selectors/billing.bdd.md +276 -0
  84. package/tests/cypress/e2e/_utils/selectors/billing.cy.ts +182 -0
  85. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +615 -0
  86. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +783 -0
  87. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  88. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.bdd.md +205 -0
  89. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +137 -0
  90. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.bdd.md +147 -0
  91. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +114 -0
  92. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +76 -0
  93. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +68 -0
  94. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +326 -0
  95. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +177 -0
  96. package/tests/cypress/e2e/_utils/selectors/devtools.bdd.md +306 -0
  97. package/tests/cypress/e2e/_utils/selectors/devtools.cy.ts +273 -0
  98. package/tests/cypress/e2e/_utils/selectors/global-search.bdd.md +115 -0
  99. package/tests/cypress/e2e/_utils/selectors/global-search.cy.ts +93 -0
  100. package/tests/cypress/e2e/_utils/selectors/patterns.bdd.md +388 -0
  101. package/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +559 -0
  102. package/tests/cypress/e2e/_utils/selectors/public.cy.ts +112 -0
  103. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.bdd.md +266 -0
  104. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.cy.ts +233 -0
  105. package/tests/cypress/e2e/_utils/selectors/settings-billing.bdd.md +78 -0
  106. package/tests/cypress/e2e/_utils/selectors/settings-billing.cy.ts +108 -0
  107. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +129 -0
  108. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +115 -0
  109. package/tests/cypress/e2e/_utils/selectors/settings-password.bdd.md +82 -0
  110. package/tests/cypress/e2e/_utils/selectors/settings-password.cy.ts +74 -0
  111. package/tests/cypress/e2e/_utils/selectors/settings-profile.bdd.md +77 -0
  112. package/tests/cypress/e2e/_utils/selectors/settings-profile.cy.ts +79 -0
  113. package/tests/cypress/e2e/_utils/selectors/settings-teams.bdd.md +130 -0
  114. package/tests/cypress/e2e/_utils/selectors/settings-teams.cy.ts +86 -0
  115. package/tests/cypress/e2e/_utils/selectors/superadmin.bdd.md +261 -0
  116. package/tests/cypress/e2e/_utils/selectors/superadmin.cy.ts +193 -0
  117. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +593 -0
  118. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +864 -0
  119. package/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +126 -0
  120. package/tests/cypress/e2e/_utils/selectors/teams.bdd.md +278 -0
  121. package/tests/cypress/e2e/_utils/selectors/teams.cy.ts +195 -0
  122. package/tests/cypress/e2e/_utils/superadmin/all-teams.bdd.md +261 -0
  123. package/tests/cypress/e2e/_utils/superadmin/all-teams.cy.ts +177 -0
  124. package/tests/cypress/e2e/_utils/superadmin/all-users.bdd.md +406 -0
  125. package/tests/cypress/e2e/_utils/superadmin/all-users.cy.ts +294 -0
  126. package/tests/cypress/e2e/_utils/superadmin/dashboard.bdd.md +235 -0
  127. package/tests/cypress/e2e/_utils/superadmin/dashboard.cy.ts +149 -0
  128. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.bdd.md +290 -0
  129. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.cy.ts +194 -0
  130. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  131. package/tests/cypress/e2e/ai/chat-api.cy.ts +119 -0
  132. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  133. package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +319 -0
  134. package/tests/cypress/e2e/api/_core/billing/check-action.cy.ts +326 -0
  135. package/tests/cypress/e2e/api/_core/billing/checkout.cy.ts +358 -0
  136. package/tests/cypress/e2e/api/_core/billing/lifecycle.cy.ts +423 -0
  137. package/tests/cypress/e2e/api/_core/billing/plans/README.md +345 -0
  138. package/tests/cypress/e2e/api/_core/billing/plans/business.cy.ts +412 -0
  139. package/tests/cypress/e2e/api/_core/billing/plans/downgrade.cy.ts +510 -0
  140. package/tests/cypress/e2e/api/_core/billing/plans/fixtures/billing-plans.json +163 -0
  141. package/tests/cypress/e2e/api/_core/billing/plans/free.cy.ts +500 -0
  142. package/tests/cypress/e2e/api/_core/billing/plans/pro.cy.ts +497 -0
  143. package/tests/cypress/e2e/api/_core/billing/plans/starter.cy.ts +342 -0
  144. package/tests/cypress/e2e/api/_core/billing/portal.cy.ts +313 -0
  145. package/tests/cypress/e2e/api/_core/devtools/registries.bdd.md +300 -0
  146. package/tests/cypress/e2e/api/_core/devtools/registries.cy.ts +368 -0
  147. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.bdd.md +375 -0
  148. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.cy.ts +346 -0
  149. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  150. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  151. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.bdd.md +649 -0
  152. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.cy.ts +333 -0
  153. package/tests/cypress/e2e/api/_core/security/security-headers.cy.ts +601 -0
  154. package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +923 -0
  155. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  156. package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +469 -0
  157. package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +913 -0
  158. package/tests/cypress/e2e/api/_core/users/users-security.cy.ts +375 -0
  159. package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +648 -0
  160. package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +839 -0
  161. package/tests/cypress/e2e/api/entities/media/media-crud.cy.ts +600 -0
  162. package/tests/cypress/e2e/api/entities/media/media-role-permissions.cy.ts +617 -0
  163. package/tests/cypress/e2e/api/entities/media/media-team-isolation.cy.ts +464 -0
  164. package/tests/cypress/e2e/api/entities/pages/blocks-scope.cy.ts +396 -0
  165. package/tests/cypress/e2e/api/entities/pages/pages-crud.cy.ts +425 -0
  166. package/tests/cypress/e2e/api/entities/pages/pages-status.cy.ts +335 -0
  167. package/tests/cypress/e2e/api/entities/posts/post-categories-crud.cy.ts +610 -0
  168. package/tests/cypress/e2e/api/entities/posts/posts-crud.cy.ts +709 -0
  169. package/tests/cypress/e2e/api/entities/posts/posts-status.cy.ts +396 -0
  170. package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +602 -0
  171. package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +878 -0
  172. package/tests/cypress/e2e/patterns/patterns-in-pages.cy.ts +367 -0
  173. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.bdd.md +231 -0
  174. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.cy.ts +144 -0
  175. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.bdd.md +118 -0
  176. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.cy.ts +84 -0
  177. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.bdd.md +288 -0
  178. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.cy.ts +188 -0
  179. package/tests/cypress/e2e/uat/_core/auth/login-logout.bdd.md +160 -0
  180. package/tests/cypress/e2e/uat/_core/auth/login-logout.cy.ts +116 -0
  181. package/tests/cypress/e2e/uat/_core/auth/password-reset.bdd.md +289 -0
  182. package/tests/cypress/e2e/uat/_core/auth/password-reset.cy.ts +200 -0
  183. package/tests/cypress/e2e/uat/_core/auth/registration-control-invitation.cy.ts +176 -0
  184. package/tests/cypress/e2e/uat/_core/auth/registration-control-open.cy.ts +131 -0
  185. package/tests/cypress/e2e/uat/_core/auth/registration-control.cy.ts +140 -0
  186. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.bdd.md +225 -0
  187. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.cy.ts +148 -0
  188. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.bdd.md +251 -0
  189. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.cy.ts +163 -0
  190. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.bdd.md +231 -0
  191. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.cy.ts +141 -0
  192. package/tests/cypress/e2e/uat/_core/billing/extended.bdd.md +273 -0
  193. package/tests/cypress/e2e/uat/_core/billing/extended.cy.ts +209 -0
  194. package/tests/cypress/e2e/uat/_core/billing/feature-gates.bdd.md +407 -0
  195. package/tests/cypress/e2e/uat/_core/billing/feature-gates.cy.ts +307 -0
  196. package/tests/cypress/e2e/uat/_core/billing/page.bdd.md +329 -0
  197. package/tests/cypress/e2e/uat/_core/billing/page.cy.ts +250 -0
  198. package/tests/cypress/e2e/uat/_core/billing/status.bdd.md +190 -0
  199. package/tests/cypress/e2e/uat/_core/billing/status.cy.ts +145 -0
  200. package/tests/cypress/e2e/uat/_core/billing/team-switch.bdd.md +156 -0
  201. package/tests/cypress/e2e/uat/_core/billing/team-switch.cy.ts +122 -0
  202. package/tests/cypress/e2e/uat/_core/billing/usage.bdd.md +218 -0
  203. package/tests/cypress/e2e/uat/_core/billing/usage.cy.ts +176 -0
  204. package/tests/cypress/e2e/uat/_core/blocks/hero.bdd.md +124 -0
  205. package/tests/cypress/e2e/uat/_core/blocks/hero.cy.ts +56 -0
  206. package/tests/cypress/e2e/uat/_core/devtools/api-tester.cy.ts +390 -0
  207. package/tests/cypress/e2e/uat/_core/performance/suspense-loading.cy.ts +134 -0
  208. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.bdd.md +736 -0
  209. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.cy.ts +740 -0
  210. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  211. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.bdd.md +553 -0
  212. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.cy.ts +185 -0
  213. package/tests/cypress/e2e/uat/_core/teams/switcher.bdd.md +1151 -0
  214. package/tests/cypress/e2e/uat/_core/teams/switcher.cy.ts +497 -0
  215. package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +198 -0
  216. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  217. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  218. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  219. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  220. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  221. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  222. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  223. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  224. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  225. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  226. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  227. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  228. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  229. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  230. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  231. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  232. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  233. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  234. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  235. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  236. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  237. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  238. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  239. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  240. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  241. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  242. package/tests/cypress/e2e/uat/features/roles/editor-role.bdd.md +552 -0
  243. package/tests/cypress/e2e/uat/features/roles/editor-role.cy.ts +210 -0
  244. package/tests/cypress/e2e/uat/features/roles/member-restrictions.bdd.md +450 -0
  245. package/tests/cypress/e2e/uat/features/roles/member-restrictions.cy.ts +189 -0
  246. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.bdd.md +530 -0
  247. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.cy.ts +247 -0
  248. package/tests/cypress/fixtures/blocks.json +218 -0
  249. package/tests/cypress/fixtures/entities.json +87 -0
  250. package/tests/cypress/fixtures/page-builder.json +21 -0
  251. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  252. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  253. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  254. package/tests/cypress/src/components/EntityForm.ts +375 -0
  255. package/tests/cypress/src/components/EntityList.ts +389 -0
  256. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  257. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  258. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  259. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  260. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  261. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  262. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  263. package/tests/cypress/src/components/index.ts +21 -0
  264. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  265. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  266. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  267. package/tests/cypress/src/controllers/MediaAPIController.js +231 -0
  268. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  269. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  270. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  271. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  272. package/tests/cypress/src/controllers/index.js +25 -0
  273. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  274. package/tests/cypress/src/core/BasePOM.ts +33 -0
  275. package/tests/cypress/src/core/BlockEditorBasePOM.ts +874 -0
  276. package/tests/cypress/src/core/DashboardEntityPOM.ts +41 -0
  277. package/tests/cypress/src/core/index.ts +14 -0
  278. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  279. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  280. package/tests/cypress/src/entities/PatternsPOM.ts +329 -0
  281. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  282. package/tests/cypress/src/entities/TasksPOM.ts +246 -0
  283. package/tests/cypress/src/entities/index.ts +16 -0
  284. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  285. package/tests/cypress/src/features/DashboardPOM.ts +271 -0
  286. package/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
  287. package/tests/cypress/src/features/PageBuilderPOM.ts +283 -0
  288. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  289. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  290. package/tests/cypress/src/features/SettingsPOM.ts +707 -0
  291. package/tests/cypress/src/features/SuperadminPOM.ts +851 -0
  292. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  293. package/tests/cypress/src/features/index.ts +28 -0
  294. package/tests/cypress/src/helpers/ApiInterceptor.ts +20 -0
  295. package/tests/cypress/src/index.ts +101 -0
  296. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  297. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  298. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  299. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  300. package/tests/cypress/src/pages/index.js +5 -0
  301. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  302. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  303. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  304. package/tests/cypress/src/pages/public/index.js +6 -0
  305. package/tests/cypress/src/selectors.ts +46 -0
  306. package/tests/cypress/src/session-helpers.ts +518 -0
  307. package/tests/cypress/support/doc-commands.ts +260 -0
  308. package/tests/cypress/support/e2e.ts +90 -0
  309. package/tests/cypress.config.ts +178 -0
  310. package/tests/jest/__mocks__/@nextsparkjs/core/components/ui/badge.js +16 -0
  311. package/tests/jest/__mocks__/@nextsparkjs/core/lib/db.js +11 -0
  312. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +160 -0
  313. package/tests/jest/__mocks__/@nextsparkjs/registries/theme-registry.ts +68 -0
  314. package/tests/jest/__mocks__/jose.js +22 -0
  315. package/tests/jest/__mocks__/next/image.js +15 -0
  316. package/tests/jest/__mocks__/next-server.js +56 -0
  317. package/tests/jest/components/post-header.test.tsx +377 -0
  318. package/tests/jest/jest.config.cjs +154 -0
  319. package/tests/jest/langchain/COVERAGE.md +372 -0
  320. package/tests/jest/langchain/guardrails.test.ts +465 -0
  321. package/tests/jest/langchain/streaming.test.ts +370 -0
  322. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  323. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  324. package/tests/jest/langchain/tracer.test.ts +823 -0
  325. package/tests/jest/services/tasks.service.test.ts +707 -0
  326. package/tests/jest/setup.ts +170 -0
  327. package/tests/jest/tsconfig.jest.json +6 -0
  328. package/tests/jest/validation/categories.test.ts +429 -0
  329. package/tests/jest/validation/posts.test.ts +546 -0
  330. package/tests/tsconfig.json +21 -0
  331. /package/docs/{02-features → public/02-features}/01-components.md +0 -0
  332. /package/docs/{02-features → public/02-features}/02-styling.md +0 -0
@@ -0,0 +1,874 @@
1
+ /**
2
+ * BlockEditorBasePOM - Base class for Page and Post builder POMs
3
+ *
4
+ * Provides common functionality for the block-based editor:
5
+ * - Editor navigation and setup
6
+ * - Title and slug management
7
+ * - Block picker interactions
8
+ * - Block manipulation (add, remove, reorder)
9
+ * - Settings panel interactions
10
+ * - Status management
11
+ * - Save operations
12
+ *
13
+ * @example
14
+ * class PageBuilderPOM extends BlockEditorBasePOM {
15
+ * protected entitySlug = 'pages'
16
+ *
17
+ * publishPage() {
18
+ * this.setStatus('published')
19
+ * this.save()
20
+ * return this
21
+ * }
22
+ * }
23
+ */
24
+
25
+ import { BasePOM } from './BasePOM'
26
+ import { cySelector } from '../selectors'
27
+ import { ApiInterceptor } from '../helpers/ApiInterceptor'
28
+
29
+ export abstract class BlockEditorBasePOM extends BasePOM {
30
+ /**
31
+ * Entity slug - must be defined by subclass
32
+ */
33
+ protected abstract entitySlug: string
34
+
35
+ protected _api: ApiInterceptor | null = null
36
+
37
+ // ============================================
38
+ // API INTERCEPTOR
39
+ // ============================================
40
+
41
+ get api(): ApiInterceptor {
42
+ if (!this._api) {
43
+ this._api = new ApiInterceptor(this.entitySlug)
44
+ }
45
+ return this._api
46
+ }
47
+
48
+ setupApiIntercepts() {
49
+ this.api.setupCrudIntercepts()
50
+ return this
51
+ }
52
+
53
+ // ============================================
54
+ // SELECTORS (from centralized selectors.ts)
55
+ // ============================================
56
+
57
+ get editorSelectors() {
58
+ return {
59
+ // =========================================================================
60
+ // HEADER - Top bar with title, slug, toggles, and actions
61
+ // =========================================================================
62
+ container: cySelector('blockEditor.header.container'),
63
+ backButton: cySelector('blockEditor.header.backButton'),
64
+ titleInput: cySelector('blockEditor.header.titleInput'),
65
+ slugInput: cySelector('blockEditor.header.slugInput'),
66
+ externalLink: cySelector('blockEditor.header.externalLink'),
67
+ viewModeToggle: cySelector('blockEditor.header.viewToggle'),
68
+ viewPreview: cySelector('blockEditor.header.viewPreview'),
69
+ viewSettings: cySelector('blockEditor.header.viewSettings'),
70
+ /** @deprecated Use viewSettings instead - v2.0 renamed viewEditor to viewSettings */
71
+ viewEditor: cySelector('blockEditor.header.viewSettings'),
72
+ saveButton: cySelector('blockEditor.header.saveButton'),
73
+ publishButton: cySelector('blockEditor.header.publishButton'),
74
+ settingsButton: cySelector('blockEditor.header.settingsButton'),
75
+ // Status selector (in header)
76
+ statusSelector: cySelector('blockEditor.header.statusSelector'),
77
+ statusOption: (value: string) =>
78
+ cySelector('blockEditor.header.statusOption', { value }),
79
+ statusDot: cySelector('blockEditor.header.statusDot'),
80
+ statusLabel: cySelector('blockEditor.header.statusLabel'),
81
+ // Locale selector (in header)
82
+ localeSelector: cySelector('blockEditor.header.localeSelector'),
83
+ localeOption: (locale: string) =>
84
+ cySelector('blockEditor.header.localeOption', { locale }),
85
+
86
+ // =========================================================================
87
+ // BLOCK PICKER - Left column "Bloques" tab
88
+ // =========================================================================
89
+ blockPicker: cySelector('blockEditor.blockPicker.container'),
90
+ // Tabs (v2.0: Blocks | Patterns | Layout)
91
+ tabBlocks: cySelector('blockEditor.blockPicker.tabBlocks'),
92
+ tabIndicator: cySelector('blockEditor.blockPicker.tabIndicator'),
93
+ /**
94
+ * @deprecated tabConfig no longer exists in v2.0.
95
+ * Entity fields moved to Settings mode (viewSettings) in center column.
96
+ * Use tabLayout for tree view, or viewSettings for entity config.
97
+ */
98
+ tabConfig: cySelector('blockEditor.blockPicker.tabLayout'),
99
+ // Search
100
+ searchWrapper: cySelector('blockEditor.blockPicker.searchWrapper'),
101
+ searchIcon: cySelector('blockEditor.blockPicker.searchIcon'),
102
+ blockSearch: cySelector('blockEditor.blockPicker.searchInput'),
103
+ blockSearchClear: cySelector('blockEditor.blockPicker.searchClear'),
104
+ // Categories
105
+ categoryChips: cySelector('blockEditor.blockPicker.categoryChips'),
106
+ category: (category: string) =>
107
+ cySelector('blockEditor.blockPicker.categoryChip', { category }),
108
+ // Block cards
109
+ blockItem: (slug: string) => cySelector('blockEditor.blockPicker.blockCard', { slug }),
110
+ blockIcon: (slug: string) => cySelector('blockEditor.blockPicker.blockIcon', { slug }),
111
+ blockName: (slug: string) => cySelector('blockEditor.blockPicker.blockName', { slug }),
112
+ addBlock: (slug: string) => cySelector('blockEditor.blockPicker.addButton', { slug }),
113
+ blockPickerEmpty: cySelector('blockEditor.blockPicker.empty'),
114
+ // Generic selector for counting all categories
115
+ categoryGeneric: '[data-cy^="block-picker-category-"]',
116
+
117
+ // =========================================================================
118
+ // PATTERNS TAB (in block picker)
119
+ // =========================================================================
120
+ tabPatterns: cySelector('blockEditor.blockPicker.tabPatterns'),
121
+ patternsSearch: cySelector('blockEditor.blockPicker.patternsSearch'),
122
+ patternsList: cySelector('blockEditor.blockPicker.patternsList'),
123
+ patternsEmpty: cySelector('blockEditor.blockPicker.patternsEmpty'),
124
+ // Pattern cards (dynamic)
125
+ patternCard: (id: string) =>
126
+ cySelector('blockEditor.blockPicker.patternCard', { id }),
127
+ patternCardIcon: (id: string) =>
128
+ cySelector('blockEditor.blockPicker.patternCardIcon', { id }),
129
+ patternCardTitle: (id: string) =>
130
+ cySelector('blockEditor.blockPicker.patternCardTitle', { id }),
131
+ patternCardDescription: (id: string) =>
132
+ cySelector('blockEditor.blockPicker.patternCardDescription', { id }),
133
+ patternCardInsertButton: (id: string) =>
134
+ cySelector('blockEditor.blockPicker.patternCardInsertButton', { id }),
135
+ // Generic selector for counting patterns
136
+ patternCardGeneric: '[data-cy^="block-picker-pattern-card-"]',
137
+
138
+ // =========================================================================
139
+ // TREE VIEW (Layout tab)
140
+ // =========================================================================
141
+ tabLayout: cySelector('blockEditor.blockPicker.tabLayout'),
142
+ treeView: cySelector('blockEditor.treeView.container'),
143
+ treeViewEmpty: cySelector('blockEditor.treeView.empty'),
144
+ treeNode: (id: string) => cySelector('blockEditor.treeView.node', { id }),
145
+ treeNodeIcon: (id: string) =>
146
+ cySelector('blockEditor.treeView.nodeIcon', { id }),
147
+ treeNodeName: (id: string) =>
148
+ cySelector('blockEditor.treeView.nodeName', { id }),
149
+
150
+ // =========================================================================
151
+ // CONFIG PANEL - Center column - Settings mode (v2.0)
152
+ // =========================================================================
153
+ configPanel: cySelector('blockEditor.configPanel.container'),
154
+ configPanelScroll: cySelector('blockEditor.configPanel.scroll'),
155
+ // Entity Fields Section (collapsible)
156
+ configEntitySection: cySelector('blockEditor.configPanel.entityFieldsSection.container'),
157
+ configEntityTrigger: cySelector('blockEditor.configPanel.entityFieldsSection.trigger'),
158
+ configEntityContent: cySelector('blockEditor.configPanel.entityFieldsSection.content'),
159
+ configEntityField: (name: string) =>
160
+ cySelector('blockEditor.configPanel.entityFieldsSection.field', { name }),
161
+ // SEO Section (collapsible) - v2.0 moved from entityMetaPanel
162
+ configSeoSection: cySelector('blockEditor.configPanel.seoMetaSection.container'),
163
+ configSeoTrigger: cySelector('blockEditor.configPanel.seoMetaSection.trigger'),
164
+ configSeoContent: cySelector('blockEditor.configPanel.seoMetaSection.content'),
165
+ configMetaTitle: cySelector('blockEditor.configPanel.seoMetaSection.metaTitle'),
166
+ configMetaDescription: cySelector('blockEditor.configPanel.seoMetaSection.metaDescription'),
167
+
168
+ // =========================================================================
169
+ // CONFIG PANEL - SEO SECTION (standalone box - v2.1)
170
+ // =========================================================================
171
+ configSeoSectionContainer: cySelector('blockEditor.configPanel.seoSection.container'),
172
+ configSeoSectionTrigger: cySelector('blockEditor.configPanel.seoSection.trigger'),
173
+ configSeoSectionContent: cySelector('blockEditor.configPanel.seoSection.content'),
174
+ configSeoMetaTitle: cySelector('blockEditor.configPanel.seoSection.metaTitle'),
175
+ configSeoMetaDescription: cySelector('blockEditor.configPanel.seoSection.metaDescription'),
176
+ configSeoMetaKeywords: cySelector('blockEditor.configPanel.seoSection.metaKeywords'),
177
+ configSeoOgImage: cySelector('blockEditor.configPanel.seoSection.ogImage'),
178
+
179
+ // =========================================================================
180
+ // CONFIG PANEL - CUSTOM FIELDS SECTION (standalone box - v2.1)
181
+ // =========================================================================
182
+ configCustomFieldsContainer: cySelector('blockEditor.configPanel.customFieldsSection.container'),
183
+ configCustomFieldsTrigger: cySelector('blockEditor.configPanel.customFieldsSection.trigger'),
184
+ configCustomFieldsContent: cySelector('blockEditor.configPanel.customFieldsSection.content'),
185
+ configCustomFieldsAddBtn: cySelector('blockEditor.configPanel.customFieldsSection.addButton'),
186
+ configCustomFieldKey: (index: number) =>
187
+ cySelector('blockEditor.configPanel.customFieldsSection.fieldKey', { index: String(index) }),
188
+ configCustomFieldValue: (index: number) =>
189
+ cySelector('blockEditor.configPanel.customFieldsSection.fieldValue', { index: String(index) }),
190
+ configCustomFieldRemove: (index: number) =>
191
+ cySelector('blockEditor.configPanel.customFieldsSection.fieldRemove', { index: String(index) }),
192
+ // Generic selectors for counting
193
+ configCustomFieldKeyGeneric: '[data-cy^="builder-config-custom-key-"]',
194
+ configCustomFieldValueGeneric: '[data-cy^="builder-config-custom-value-"]',
195
+ configCustomFieldRemoveGeneric: '[data-cy^="builder-config-custom-remove-"]',
196
+
197
+ // =========================================================================
198
+ // ENTITY FIELDS PANEL - DEPRECATED (moved to configPanel in v2.0)
199
+ // =========================================================================
200
+ /** @deprecated Use configEntitySection in Settings mode instead */
201
+ entityFieldsPanel: cySelector('blockEditor.configPanel.entityFieldsSection.container'),
202
+ /** @deprecated Use configEntityField instead */
203
+ entityField: (name: string) =>
204
+ cySelector('blockEditor.configPanel.entityFieldsSection.field', { name }),
205
+ entityCategoryList: cySelector('blockEditor.entityFieldsPanel.categoryList'),
206
+ entityCategory: (slug: string) =>
207
+ cySelector('blockEditor.entityFieldsPanel.categoryItem', { slug }),
208
+ entityCategoryCheckbox: (slug: string) =>
209
+ cySelector('blockEditor.entityFieldsPanel.categoryCheckbox', { slug }),
210
+
211
+ // =========================================================================
212
+ // LAYOUT CANVAS - Center column - Layout mode (draggable cards)
213
+ // =========================================================================
214
+ layoutCanvas: cySelector('blockEditor.layoutCanvas.container'),
215
+ layoutCanvasEmpty: cySelector('blockEditor.layoutCanvas.empty'),
216
+ layoutDropZone: cySelector('blockEditor.layoutCanvas.dropZone'),
217
+ // Sortable block cards
218
+ sortableBlock: (id: string) =>
219
+ cySelector('blockEditor.layoutCanvas.sortableBlock.container', { id }),
220
+ sortableBlockCard: (id: string) =>
221
+ cySelector('blockEditor.layoutCanvas.sortableBlock.card', { id }),
222
+ dragHandle: (id: string) =>
223
+ cySelector('blockEditor.layoutCanvas.sortableBlock.dragHandle', { id }),
224
+ sortableBlockName: (id: string) =>
225
+ cySelector('blockEditor.layoutCanvas.sortableBlock.name', { id }),
226
+ duplicateBlock: (id: string) =>
227
+ cySelector('blockEditor.layoutCanvas.sortableBlock.duplicateBtn', { id }),
228
+ removeBlock: (id: string) =>
229
+ cySelector('blockEditor.layoutCanvas.sortableBlock.removeBtn', { id }),
230
+ blockError: (id: string) =>
231
+ cySelector('blockEditor.layoutCanvas.sortableBlock.error', { id }),
232
+ // Generic selector for counting all sortable blocks
233
+ sortableBlockGeneric: '[data-cy^="sortable-block-"]',
234
+
235
+ // =========================================================================
236
+ // PREVIEW CANVAS - Center column - Preview mode (real blocks)
237
+ // =========================================================================
238
+ previewCanvas: cySelector('blockEditor.previewCanvas.container'),
239
+ previewWrapper: cySelector('blockEditor.previewCanvas.wrapper'),
240
+ previewCanvasEmpty: cySelector('blockEditor.previewCanvas.empty'),
241
+ previewBlock: (id: string) => cySelector('blockEditor.previewCanvas.block', { id }),
242
+ previewBlockWrapper: (id: string) =>
243
+ cySelector('blockEditor.previewCanvas.blockWrapper', { id }),
244
+ previewBlockSelected: (id: string) =>
245
+ cySelector('blockEditor.previewCanvas.blockSelected', { id }),
246
+ // Generic selector for counting all preview blocks
247
+ previewBlockGeneric: '[data-cy^="preview-block-"]',
248
+ // Move buttons
249
+ moveUpBtn: (id: string) => cySelector('blockEditor.previewCanvas.moveUp', { id }),
250
+ moveDownBtn: (id: string) => cySelector('blockEditor.previewCanvas.moveDown', { id }),
251
+ // Floating toolbar
252
+ floatingToolbar: (id: string) =>
253
+ cySelector('blockEditor.previewCanvas.floatingToolbar.container', { id }),
254
+ floatingToolbarDrag: (id: string) =>
255
+ cySelector('blockEditor.previewCanvas.floatingToolbar.dragHandle', { id }),
256
+ floatingToolbarName: (id: string) =>
257
+ cySelector('blockEditor.previewCanvas.floatingToolbar.blockName', { id }),
258
+ floatingToolbarDuplicate: (id: string) =>
259
+ cySelector('blockEditor.previewCanvas.floatingToolbar.duplicateBtn', { id }),
260
+ floatingToolbarDelete: (id: string) =>
261
+ cySelector('blockEditor.previewCanvas.floatingToolbar.deleteBtn', { id }),
262
+
263
+ // =========================================================================
264
+ // PATTERN REFERENCE (in canvas)
265
+ // =========================================================================
266
+ patternReference: (ref: string) =>
267
+ cySelector('blockEditor.patternReference.container', { ref }),
268
+ patternReferenceBadge: (ref: string) =>
269
+ cySelector('blockEditor.patternReference.badge', { ref }),
270
+ patternReferenceRemove: (ref: string) =>
271
+ cySelector('blockEditor.patternReference.remove', { ref }),
272
+ patternReferenceLocked: (ref: string) =>
273
+ cySelector('blockEditor.patternReference.locked', { ref }),
274
+ patternReferenceEditLink: (ref: string) =>
275
+ cySelector('blockEditor.patternReference.editLink', { ref }),
276
+ // Generic selector for counting pattern references
277
+ patternReferenceGeneric: '[data-cy^="pattern-reference-"]',
278
+
279
+ // =========================================================================
280
+ // ENTITY META PANEL - SEO and custom fields
281
+ // =========================================================================
282
+ entityMetaPanel: cySelector('blockEditor.entityMetaPanel.container'),
283
+ // SEO section
284
+ seoTrigger: cySelector('blockEditor.entityMetaPanel.seoSection.trigger'),
285
+ seoContent: cySelector('blockEditor.entityMetaPanel.seoSection.content'),
286
+ metaTitle: cySelector('blockEditor.entityMetaPanel.seoSection.metaTitle'),
287
+ metaDescription: cySelector('blockEditor.entityMetaPanel.seoSection.metaDescription'),
288
+ metaKeywords: cySelector('blockEditor.entityMetaPanel.seoSection.metaKeywords'),
289
+ ogImage: cySelector('blockEditor.entityMetaPanel.seoSection.ogImage'),
290
+ // Custom fields section
291
+ customFieldsTrigger: cySelector('blockEditor.entityMetaPanel.customFields.trigger'),
292
+ customFieldsContent: cySelector('blockEditor.entityMetaPanel.customFields.content'),
293
+ customFieldKey: (index: number) =>
294
+ cySelector('blockEditor.entityMetaPanel.customFields.fieldKey', { index }),
295
+ customFieldValue: (index: number) =>
296
+ cySelector('blockEditor.entityMetaPanel.customFields.fieldValue', { index }),
297
+ customFieldRemove: (index: number) =>
298
+ cySelector('blockEditor.entityMetaPanel.customFields.fieldRemove', { index }),
299
+ addCustomField: cySelector('blockEditor.entityMetaPanel.customFields.addButton'),
300
+
301
+ // =========================================================================
302
+ // BLOCK PROPERTIES PANEL - Right column
303
+ // =========================================================================
304
+ blockPropertiesPanel: cySelector('blockEditor.blockPropertiesPanel.container'),
305
+ blockPropertiesHeader: cySelector('blockEditor.blockPropertiesPanel.header'),
306
+ blockPropertiesClose: cySelector('blockEditor.blockPropertiesPanel.closeBtn'),
307
+ blockPropertiesIcon: cySelector('blockEditor.blockPropertiesPanel.blockIcon'),
308
+ blockPropertiesName: cySelector('blockEditor.blockPropertiesPanel.blockName'),
309
+ blockPropertiesTabs: cySelector('blockEditor.blockPropertiesPanel.tabs'),
310
+ tabContent: cySelector('blockEditor.blockPropertiesPanel.tabContent'),
311
+ tabDesign: cySelector('blockEditor.blockPropertiesPanel.tabDesign'),
312
+ tabAdvanced: cySelector('blockEditor.blockPropertiesPanel.tabAdvanced'),
313
+ blockPropertiesEmpty: cySelector('blockEditor.blockPropertiesPanel.empty'),
314
+ blockPropertiesError: cySelector('blockEditor.blockPropertiesPanel.error'),
315
+ // Dynamic form
316
+ dynamicForm: cySelector('blockEditor.blockPropertiesPanel.form.container'),
317
+ dynamicField: (name: string) =>
318
+ cySelector('blockEditor.blockPropertiesPanel.form.field', { name }),
319
+ /** @deprecated Use dynamicField instead */
320
+ fieldInput: (name: string) =>
321
+ cySelector('blockEditor.blockPropertiesPanel.form.field', { name }),
322
+ fieldGroup: (id: string) =>
323
+ cySelector('blockEditor.blockPropertiesPanel.form.fieldGroup', { id }),
324
+ // Reset props button
325
+ resetPropsBtn: cySelector('blockEditor.blockPropertiesPanel.resetPropsBtn'),
326
+ // Array field
327
+ arrayFieldContainer: (name: string) =>
328
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.container', { name }),
329
+ arrayFieldItem: (name: string, index: number) =>
330
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.item', { name, index }),
331
+ arrayFieldItemField: (name: string, index: number, field: string) =>
332
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.itemField', { name, index, field }),
333
+ arrayFieldMoveUp: (name: string, index: number) =>
334
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.itemMoveUp', { name, index }),
335
+ arrayFieldMoveDown: (name: string, index: number) =>
336
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.itemMoveDown', { name, index }),
337
+ arrayFieldRemove: (name: string, index: number) =>
338
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.itemRemove', { name, index }),
339
+ arrayFieldAdd: (name: string) =>
340
+ cySelector('blockEditor.blockPropertiesPanel.form.arrayField.addButton', { name }),
341
+ // Generic selectors for array fields (for counting/prefix matching)
342
+ arrayFieldGeneric: '[data-cy^="block-array-"]',
343
+ arrayFieldAddGeneric: '[data-cy$="-add"][data-cy^="block-array-"]',
344
+ arrayFieldRemoveGeneric: '[data-cy$="-remove"][data-cy^="block-array-"]',
345
+ arrayFieldUpGeneric: '[data-cy$="-up"][data-cy^="block-array-"]',
346
+ arrayFieldDownGeneric: '[data-cy$="-down"][data-cy^="block-array-"]',
347
+
348
+ // =========================================================================
349
+ // LEGACY ALIASES (for backward compatibility - will be removed)
350
+ // =========================================================================
351
+ /** @deprecated Use layoutCanvas instead */
352
+ blockCanvas: cySelector('blockEditor.layoutCanvas.container'),
353
+ /** @deprecated Use layoutCanvasEmpty instead */
354
+ blockCanvasEmpty: cySelector('blockEditor.layoutCanvas.empty'),
355
+ /** @deprecated Use blockPropertiesPanel instead */
356
+ settingsPanel: cySelector('blockEditor.blockPropertiesPanel.container'),
357
+ /** @deprecated Use blockPropertiesEmpty instead */
358
+ settingsPanelEmpty: cySelector('blockEditor.blockPropertiesPanel.empty'),
359
+ /** @deprecated Use blockPropertiesError instead */
360
+ settingsPanelError: cySelector('blockEditor.blockPropertiesPanel.error'),
361
+ /** @deprecated Use entityMetaPanel instead */
362
+ pageSettings: cySelector('blockEditor.entityMetaPanel.container'),
363
+ /** @deprecated Use entityFieldsPanel instead */
364
+ entityFieldsSidebar: cySelector('blockEditor.entityFieldsPanel.container'),
365
+ }
366
+ }
367
+
368
+ // ============================================
369
+ // NAVIGATION
370
+ // ============================================
371
+
372
+ visitList() {
373
+ cy.visit(`/dashboard/${this.entitySlug}`)
374
+ return this
375
+ }
376
+
377
+ visitCreate() {
378
+ cy.visit(`/dashboard/${this.entitySlug}/create`)
379
+ return this
380
+ }
381
+
382
+ visitEdit(id: string) {
383
+ cy.visit(`/dashboard/${this.entitySlug}/${id}/edit`)
384
+ return this
385
+ }
386
+
387
+ // ============================================
388
+ // WAITS
389
+ // ============================================
390
+
391
+ waitForEditor() {
392
+ cy.get(this.editorSelectors.container, { timeout: 15000 }).should('be.visible')
393
+ return this
394
+ }
395
+
396
+ /**
397
+ * Alias for waitForEditor (legacy compatibility)
398
+ */
399
+ waitForEditorLoad() {
400
+ return this.waitForEditor()
401
+ }
402
+
403
+ waitForSave() {
404
+ this.api.waitForUpdate()
405
+ return this
406
+ }
407
+
408
+ // ============================================
409
+ // EDITOR ACTIONS
410
+ // ============================================
411
+
412
+ /**
413
+ * Set the page/post title
414
+ */
415
+ setTitle(title: string) {
416
+ cy.get(this.editorSelectors.titleInput).clear().type(title)
417
+ return this
418
+ }
419
+
420
+ /**
421
+ * Set the page/post slug
422
+ */
423
+ setSlug(slug: string) {
424
+ cy.get(this.editorSelectors.slugInput).clear().type(slug)
425
+ return this
426
+ }
427
+
428
+ /**
429
+ * Save the page/post
430
+ */
431
+ save() {
432
+ cy.get(this.editorSelectors.saveButton).click()
433
+ return this
434
+ }
435
+
436
+ /**
437
+ * Save with API wait
438
+ */
439
+ saveWithWait() {
440
+ this.setupApiIntercepts()
441
+ this.save()
442
+ this.api.waitForUpdate()
443
+ return this
444
+ }
445
+
446
+ /**
447
+ * Set the status using the status selector
448
+ */
449
+ setStatus(status: string) {
450
+ cy.get(this.editorSelectors.statusSelector).click()
451
+ cy.get(this.editorSelectors.statusOption(status)).click()
452
+ return this
453
+ }
454
+
455
+ /**
456
+ * Toggle view mode (edit/preview)
457
+ */
458
+ toggleViewMode() {
459
+ cy.get(this.editorSelectors.viewModeToggle).click()
460
+ return this
461
+ }
462
+
463
+ /**
464
+ * Switch to Settings mode (v2.0 - entity fields, SEO, etc.)
465
+ * In v2.0, this shows the configPanel in the center column.
466
+ */
467
+ switchToSettingsMode() {
468
+ cy.get(this.editorSelectors.viewSettings).click()
469
+ return this
470
+ }
471
+
472
+ /**
473
+ * Switch to Layout/Editor mode
474
+ * @deprecated In v2.0, Layout is now a tab in BlockPicker (tabLayout), not a view mode.
475
+ * Use selectLayoutTab() for tree view, or switchToSettingsMode() for entity config.
476
+ */
477
+ switchToLayoutMode() {
478
+ // v2.0: "Layout" mode is now accessed via the Layout tab in left sidebar
479
+ // This method now clicks the Layout tab for backward compatibility
480
+ cy.get(this.editorSelectors.tabLayout).click()
481
+ return this
482
+ }
483
+
484
+ /**
485
+ * Switch to Preview mode
486
+ */
487
+ switchToPreviewMode() {
488
+ cy.get(this.editorSelectors.viewPreview).click()
489
+ return this
490
+ }
491
+
492
+ /**
493
+ * Select Blocks tab in left sidebar
494
+ */
495
+ selectBlocksTab() {
496
+ cy.get(this.editorSelectors.tabBlocks).click()
497
+ return this
498
+ }
499
+
500
+ /**
501
+ * Select Config/Fields tab in left sidebar
502
+ * @deprecated In v2.0, entity fields moved to Settings mode (center column).
503
+ * Use switchToSettingsMode() for entity config/fields.
504
+ */
505
+ selectConfigTab() {
506
+ // v2.0: Config is now accessed via Settings mode, not a tab
507
+ cy.get(this.editorSelectors.viewSettings).click()
508
+ return this
509
+ }
510
+
511
+ /**
512
+ * Select Layout tab in left sidebar (tree view)
513
+ */
514
+ selectLayoutTab() {
515
+ cy.get(this.editorSelectors.tabLayout).click()
516
+ return this
517
+ }
518
+
519
+ // ============================================
520
+ // BLOCK PICKER ACTIONS
521
+ // ============================================
522
+
523
+ /**
524
+ * Search for a block in the picker
525
+ */
526
+ searchBlock(term: string) {
527
+ cy.get(this.editorSelectors.blockSearch).clear().type(term)
528
+ return this
529
+ }
530
+
531
+ /**
532
+ * Clear block search
533
+ */
534
+ clearBlockSearch() {
535
+ cy.get(this.editorSelectors.blockSearch).clear()
536
+ return this
537
+ }
538
+
539
+ /**
540
+ * Select a block category
541
+ */
542
+ selectCategory(category: string) {
543
+ cy.get(this.editorSelectors.category(category)).click()
544
+ return this
545
+ }
546
+
547
+ /**
548
+ * Select "All" category
549
+ */
550
+ selectAllCategories() {
551
+ cy.get(this.editorSelectors.category('all')).click()
552
+ return this
553
+ }
554
+
555
+ /**
556
+ * Add a block by clicking its add button
557
+ */
558
+ addBlock(blockSlug: string) {
559
+ cy.get(this.editorSelectors.addBlock(blockSlug)).click()
560
+ return this
561
+ }
562
+
563
+ /**
564
+ * Click on a block item in the picker
565
+ */
566
+ clickBlockItem(blockSlug: string) {
567
+ cy.get(this.editorSelectors.blockItem(blockSlug)).click()
568
+ return this
569
+ }
570
+
571
+ // ============================================
572
+ // BLOCK MANIPULATION
573
+ // ============================================
574
+
575
+ /**
576
+ * Remove a block from the canvas
577
+ */
578
+ removeBlock(blockId: string) {
579
+ cy.get(this.editorSelectors.removeBlock(blockId)).click()
580
+ return this
581
+ }
582
+
583
+ /**
584
+ * Duplicate a block
585
+ */
586
+ duplicateBlock(blockId: string) {
587
+ cy.get(this.editorSelectors.duplicateBlock(blockId)).click()
588
+ return this
589
+ }
590
+
591
+ /**
592
+ * Click on a sortable block to select it
593
+ */
594
+ selectBlock(blockId: string) {
595
+ cy.get(this.editorSelectors.sortableBlock(blockId)).click()
596
+ return this
597
+ }
598
+
599
+ /**
600
+ * Move block up in preview
601
+ */
602
+ moveBlockUp(blockId: string) {
603
+ cy.get(this.editorSelectors.moveUpBtn(blockId)).click()
604
+ return this
605
+ }
606
+
607
+ /**
608
+ * Move block down in preview
609
+ */
610
+ moveBlockDown(blockId: string) {
611
+ cy.get(this.editorSelectors.moveDownBtn(blockId)).click()
612
+ return this
613
+ }
614
+
615
+ // ============================================
616
+ // SETTINGS PANEL ACTIONS
617
+ // ============================================
618
+
619
+ /**
620
+ * Switch to Content tab in settings panel
621
+ */
622
+ openContentTab() {
623
+ cy.get(this.editorSelectors.tabContent).click()
624
+ return this
625
+ }
626
+
627
+ /**
628
+ * Switch to Design tab in settings panel
629
+ */
630
+ openDesignTab() {
631
+ cy.get(this.editorSelectors.tabDesign).click()
632
+ return this
633
+ }
634
+
635
+ /**
636
+ * Switch to Advanced tab in settings panel
637
+ */
638
+ openAdvancedTab() {
639
+ cy.get(this.editorSelectors.tabAdvanced).click()
640
+ return this
641
+ }
642
+
643
+ /**
644
+ * Reset block props to defaults
645
+ */
646
+ resetBlockProps() {
647
+ cy.get(this.editorSelectors.resetPropsBtn).click()
648
+ return this
649
+ }
650
+
651
+ /**
652
+ * Fill a dynamic form field
653
+ */
654
+ fillDynamicField(name: string, value: string) {
655
+ cy.get(this.editorSelectors.dynamicField(name)).find('input, textarea').clear().type(value)
656
+ return this
657
+ }
658
+
659
+ /**
660
+ * Alias for fillDynamicField (legacy compatibility)
661
+ */
662
+ fillField(name: string, value: string) {
663
+ return this.fillDynamicField(name, value)
664
+ }
665
+
666
+ // ============================================
667
+ // SEO/PAGE SETTINGS
668
+ // ============================================
669
+
670
+ /**
671
+ * Open SEO settings section
672
+ */
673
+ openSeoSettings() {
674
+ cy.get(this.editorSelectors.seoTrigger).click()
675
+ return this
676
+ }
677
+
678
+ /**
679
+ * Set meta title
680
+ */
681
+ setMetaTitle(title: string) {
682
+ cy.get(this.editorSelectors.metaTitle).clear().type(title)
683
+ return this
684
+ }
685
+
686
+ /**
687
+ * Set meta description
688
+ */
689
+ setMetaDescription(description: string) {
690
+ cy.get(this.editorSelectors.metaDescription).clear().type(description)
691
+ return this
692
+ }
693
+
694
+ /**
695
+ * Set meta keywords
696
+ */
697
+ setMetaKeywords(keywords: string) {
698
+ cy.get(this.editorSelectors.metaKeywords).clear().type(keywords)
699
+ return this
700
+ }
701
+
702
+ /**
703
+ * Open custom fields section
704
+ */
705
+ openCustomFields() {
706
+ cy.get(this.editorSelectors.customFieldsTrigger).click()
707
+ return this
708
+ }
709
+
710
+ /**
711
+ * Add a custom field
712
+ */
713
+ addCustomField() {
714
+ cy.get(this.editorSelectors.addCustomField).click()
715
+ return this
716
+ }
717
+
718
+ /**
719
+ * Fill a custom field
720
+ */
721
+ fillCustomField(index: number, key: string, value: string) {
722
+ cy.get(this.editorSelectors.customFieldKey(index)).clear().type(key)
723
+ cy.get(this.editorSelectors.customFieldValue(index)).clear().type(value)
724
+ return this
725
+ }
726
+
727
+ /**
728
+ * Remove a custom field
729
+ */
730
+ removeCustomField(index: number) {
731
+ cy.get(this.editorSelectors.customFieldRemove(index)).click()
732
+ return this
733
+ }
734
+
735
+ // ============================================
736
+ // ARRAY FIELDS
737
+ // ============================================
738
+
739
+ /**
740
+ * Add an item to an array field
741
+ */
742
+ addArrayItem(name: string) {
743
+ cy.get(this.editorSelectors.arrayFieldAdd(name)).click()
744
+ return this
745
+ }
746
+
747
+ /**
748
+ * Remove an item from an array field
749
+ */
750
+ removeArrayItem(name: string, index: number) {
751
+ cy.get(this.editorSelectors.arrayFieldRemove(name, index)).click()
752
+ return this
753
+ }
754
+
755
+ /**
756
+ * Fill an array field item
757
+ */
758
+ fillArrayItem(name: string, index: number, field: string, value: string) {
759
+ cy.get(this.editorSelectors.arrayFieldItem(name, index, field)).clear().type(value)
760
+ return this
761
+ }
762
+
763
+ /**
764
+ * Move array item up
765
+ */
766
+ moveArrayItemUp(name: string, index: number) {
767
+ cy.get(this.editorSelectors.arrayFieldMoveUp(name, index)).click()
768
+ return this
769
+ }
770
+
771
+ /**
772
+ * Move array item down
773
+ */
774
+ moveArrayItemDown(name: string, index: number) {
775
+ cy.get(this.editorSelectors.arrayFieldMoveDown(name, index)).click()
776
+ return this
777
+ }
778
+
779
+ // ============================================
780
+ // ASSERTIONS
781
+ // ============================================
782
+
783
+ /**
784
+ * Assert editor is visible
785
+ */
786
+ assertEditorVisible() {
787
+ cy.get(this.editorSelectors.container).should('be.visible')
788
+ return this
789
+ }
790
+
791
+ /**
792
+ * Assert canvas is empty
793
+ */
794
+ assertCanvasEmpty() {
795
+ cy.get(this.editorSelectors.blockCanvasEmpty).should('be.visible')
796
+ return this
797
+ }
798
+
799
+ /**
800
+ * Assert block exists in canvas
801
+ */
802
+ assertBlockExists(blockId: string) {
803
+ cy.get(this.editorSelectors.sortableBlock(blockId)).should('exist')
804
+ return this
805
+ }
806
+
807
+ /**
808
+ * Assert block does not exist
809
+ */
810
+ assertBlockNotExists(blockId: string) {
811
+ cy.get(this.editorSelectors.sortableBlock(blockId)).should('not.exist')
812
+ return this
813
+ }
814
+
815
+ /**
816
+ * Assert status label shows specific status
817
+ */
818
+ assertStatus(status: string) {
819
+ cy.get(this.editorSelectors.statusLabel).should('contain.text', status)
820
+ return this
821
+ }
822
+
823
+ /**
824
+ * Assert settings panel is visible
825
+ */
826
+ assertSettingsPanelVisible() {
827
+ cy.get(this.editorSelectors.settingsPanel).should('be.visible')
828
+ return this
829
+ }
830
+
831
+ /**
832
+ * Assert settings panel shows empty state
833
+ */
834
+ assertSettingsPanelEmpty() {
835
+ cy.get(this.editorSelectors.settingsPanelEmpty).should('be.visible')
836
+ return this
837
+ }
838
+
839
+ /**
840
+ * Assert block count in canvas
841
+ */
842
+ assertBlockCount(count: number) {
843
+ cy.get(this.editorSelectors.sortableBlockGeneric).should('have.length', count)
844
+ return this
845
+ }
846
+
847
+ /**
848
+ * Assert preview block count
849
+ */
850
+ assertPreviewBlockCount(count: number) {
851
+ cy.get(this.editorSelectors.previewBlockGeneric).should('have.length', count)
852
+ return this
853
+ }
854
+
855
+ /**
856
+ * Assert block is visible in picker
857
+ */
858
+ assertBlockInPicker(blockSlug: string) {
859
+ cy.get(this.editorSelectors.blockItem(blockSlug))
860
+ .scrollIntoView()
861
+ .should('be.visible')
862
+ return this
863
+ }
864
+
865
+ /**
866
+ * Assert block picker is visible
867
+ */
868
+ assertBlockPickerVisible() {
869
+ cy.get(this.editorSelectors.blockPicker).should('be.visible')
870
+ return this
871
+ }
872
+ }
873
+
874
+ export default BlockEditorBasePOM