@nextsparkjs/core 0.1.0-beta.188 → 0.1.0-beta.189

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 (412) hide show
  1. package/dist/components/api/keys/ApiKeyDisplay.js +19 -19
  2. package/dist/components/api/keys/CreateApiKeyDialog.js +21 -21
  3. package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
  4. package/dist/components/app/layouts/PublicNavbar.js +34 -16
  5. package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
  6. package/dist/components/app/misc/ThemeToggle.js +3 -4
  7. package/dist/components/auth/SessionCookieRefresher.d.ts +5 -0
  8. package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -1
  9. package/dist/components/auth/SessionCookieRefresher.js +7 -1
  10. package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
  11. package/dist/components/auth/forms/LoginForm.js +49 -12
  12. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  13. package/dist/components/auth/forms/SignupForm.js +4 -3
  14. package/dist/components/dashboard/block-editor/block-settings-panel.js +1 -1
  15. package/dist/components/dashboard/block-editor/floating-block-toolbar.js +1 -1
  16. package/dist/components/dashboard/block-editor/sortable-block.js +1 -1
  17. package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
  18. package/dist/components/dashboard/layouts/Sidebar.js +8 -6
  19. package/dist/components/dashboard/layouts/TopNavbar.d.ts.map +1 -1
  20. package/dist/components/dashboard/layouts/TopNavbar.js +51 -54
  21. package/dist/components/dashboard/misc/NotificationsDropdown.d.ts.map +1 -1
  22. package/dist/components/dashboard/misc/NotificationsDropdown.js +17 -15
  23. package/dist/components/dashboard/misc/SearchDropdown.d.ts.map +1 -1
  24. package/dist/components/dashboard/misc/SearchDropdown.js +19 -22
  25. package/dist/components/dashboard/mobile/MobileBottomNav.js +1 -1
  26. package/dist/components/dashboard/mobile/MobileTopBar.js +2 -2
  27. package/dist/components/dashboard/navigation/DynamicNavigation.js +5 -5
  28. package/dist/components/devtools/BlockDetailViewer.js +1 -1
  29. package/dist/components/devtools/BlocksViewer.js +1 -1
  30. package/dist/components/entities/EntityChildManager.js +5 -5
  31. package/dist/components/entities/EntityDetailPage.d.ts.map +1 -1
  32. package/dist/components/entities/EntityDetailPage.js +16 -15
  33. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  34. package/dist/components/entities/EntityFieldRenderer.js +12 -7
  35. package/dist/components/entities/EntityPageHeader.js +5 -5
  36. package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
  37. package/dist/components/entities/ErrorBoundary.js +20 -21
  38. package/dist/components/entities/wrappers/EntityDetailWrapper.js +5 -5
  39. package/dist/components/entities/wrappers/EntityFormWrapper.js +2 -2
  40. package/dist/components/entities/wrappers/EntityListWrapper.js +3 -3
  41. package/dist/components/permissions/NoPermission.js +10 -10
  42. package/dist/components/public/pageBuilder/PageRenderer.d.ts.map +1 -1
  43. package/dist/components/public/pageBuilder/PageRenderer.js +2 -1
  44. package/dist/components/settings/SettingsPageSkeleton.js +4 -4
  45. package/dist/components/settings/layouts/SettingsSidebar.js +6 -6
  46. package/dist/components/shared/MultiSelectFilter.d.ts +3 -1
  47. package/dist/components/shared/MultiSelectFilter.d.ts.map +1 -1
  48. package/dist/components/shared/MultiSelectFilter.js +7 -3
  49. package/dist/components/shared/SearchInput.d.ts +2 -0
  50. package/dist/components/shared/SearchInput.d.ts.map +1 -1
  51. package/dist/components/shared/SearchInput.js +64 -6
  52. package/dist/components/ui/address-input.js +18 -18
  53. package/dist/components/ui/audio-upload.js +9 -9
  54. package/dist/components/ui/combobox.d.ts.map +1 -1
  55. package/dist/components/ui/combobox.js +4 -4
  56. package/dist/components/ui/country-select.d.ts.map +1 -1
  57. package/dist/components/ui/country-select.js +3 -3
  58. package/dist/components/ui/currency-select.d.ts.map +1 -1
  59. package/dist/components/ui/currency-select.js +3 -3
  60. package/dist/components/ui/dynamic-icon.d.ts.map +1 -1
  61. package/dist/components/ui/dynamic-icon.js +3 -2
  62. package/dist/components/ui/file-upload.js +7 -7
  63. package/dist/components/ui/image-upload.js +8 -8
  64. package/dist/components/ui/multi-select.d.ts +2 -1
  65. package/dist/components/ui/multi-select.d.ts.map +1 -1
  66. package/dist/components/ui/multi-select.js +4 -3
  67. package/dist/components/ui/phone-input.d.ts.map +1 -1
  68. package/dist/components/ui/phone-input.js +3 -3
  69. package/dist/components/ui/relation-display.js +1 -1
  70. package/dist/components/ui/simple-relation-select.d.ts +2 -1
  71. package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
  72. package/dist/components/ui/simple-relation-select.js +7 -6
  73. package/dist/components/ui/tags-input.d.ts.map +1 -1
  74. package/dist/components/ui/tags-input.js +1 -1
  75. package/dist/components/ui/timezone-select.d.ts.map +1 -1
  76. package/dist/components/ui/timezone-select.js +3 -3
  77. package/dist/components/ui/user-display.js +4 -4
  78. package/dist/components/ui/user-select.d.ts.map +1 -1
  79. package/dist/components/ui/user-select.js +9 -9
  80. package/dist/components/ui/video-upload.js +9 -9
  81. package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
  82. package/dist/contexts/SubscriptionContext.js +5 -1
  83. package/dist/contexts/TeamContext.d.ts.map +1 -1
  84. package/dist/contexts/TeamContext.js +83 -26
  85. package/dist/emails/otp-verification.d.ts.map +1 -1
  86. package/dist/emails/otp-verification.js +3 -2
  87. package/dist/hooks/useAccountPreferencesSync.d.ts +13 -0
  88. package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -0
  89. package/dist/hooks/useAccountPreferencesSync.js +95 -0
  90. package/dist/hooks/useAuth.d.ts +202 -15
  91. package/dist/hooks/useAuth.d.ts.map +1 -1
  92. package/dist/hooks/useAuth.js +49 -13
  93. package/dist/hooks/useEntityConfig.js +2 -2
  94. package/dist/hooks/useEntitySearch.js +10 -10
  95. package/dist/hooks/useNotifications.js +12 -12
  96. package/dist/hooks/usePrefetch.d.ts.map +1 -1
  97. package/dist/hooks/usePrefetch.js +6 -2
  98. package/dist/hooks/useSearch.js +15 -15
  99. package/dist/hooks/useSessionCookieRefresh.d.ts +18 -1
  100. package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -1
  101. package/dist/hooks/useSessionCookieRefresh.js +17 -4
  102. package/dist/hooks/useSessionHint.d.ts +12 -0
  103. package/dist/hooks/useSessionHint.d.ts.map +1 -0
  104. package/dist/hooks/useSessionHint.js +15 -0
  105. package/dist/hooks/useTranslationPreload.js +6 -6
  106. package/dist/i18n.d.ts +13 -0
  107. package/dist/i18n.d.ts.map +1 -1
  108. package/dist/i18n.js +2 -27
  109. package/dist/lib/actions/entity.actions.d.ts.map +1 -1
  110. package/dist/lib/actions/entity.actions.js +3 -2
  111. package/dist/lib/api/auth/dual-auth.d.ts +4 -1
  112. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  113. package/dist/lib/api/auth/dual-auth.js +5 -16
  114. package/dist/lib/api/entities.js +33 -33
  115. package/dist/lib/api/keys.d.ts +27 -27
  116. package/dist/lib/api/keys.js +27 -27
  117. package/dist/lib/auth/callback-url.d.ts +11 -0
  118. package/dist/lib/auth/callback-url.d.ts.map +1 -0
  119. package/dist/lib/auth/callback-url.js +14 -0
  120. package/dist/lib/auth/otp-config.d.ts +55 -0
  121. package/dist/lib/auth/otp-config.d.ts.map +1 -0
  122. package/dist/lib/auth/otp-config.js +48 -0
  123. package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
  124. package/dist/lib/auth/registration-helpers.js +3 -1
  125. package/dist/lib/auth/session-hint.d.ts +31 -0
  126. package/dist/lib/auth/session-hint.d.ts.map +1 -0
  127. package/dist/lib/auth/session-hint.js +37 -0
  128. package/dist/lib/auth.d.ts.map +1 -1
  129. package/dist/lib/auth.js +6 -4
  130. package/dist/lib/blocks/loader.d.ts +3 -11
  131. package/dist/lib/blocks/loader.d.ts.map +1 -1
  132. package/dist/lib/blocks/loader.js +1 -9
  133. package/dist/lib/blocks/loader.server.d.ts +18 -0
  134. package/dist/lib/blocks/loader.server.d.ts.map +1 -0
  135. package/dist/lib/blocks/loader.server.js +11 -0
  136. package/dist/lib/blocks/sanitize-html.d.ts +31 -0
  137. package/dist/lib/blocks/sanitize-html.d.ts.map +1 -0
  138. package/dist/lib/blocks/sanitize-html.js +60 -0
  139. package/dist/lib/config/config-sync.d.ts +1 -0
  140. package/dist/lib/config/config-sync.d.ts.map +1 -1
  141. package/dist/lib/config/config-sync.js +4 -1
  142. package/dist/lib/config/types.d.ts +48 -0
  143. package/dist/lib/config/types.d.ts.map +1 -1
  144. package/dist/lib/email/types.d.ts +6 -0
  145. package/dist/lib/email/types.d.ts.map +1 -1
  146. package/dist/lib/entities/serialization.d.ts.map +1 -1
  147. package/dist/lib/entities/serialization.js +3 -6
  148. package/dist/lib/icons.d.ts +12 -0
  149. package/dist/lib/icons.d.ts.map +1 -0
  150. package/dist/lib/icons.js +30 -0
  151. package/dist/lib/locale.d.ts +25 -1
  152. package/dist/lib/locale.d.ts.map +1 -1
  153. package/dist/lib/locale.js +49 -35
  154. package/dist/lib/mcp/engine.js +1 -1
  155. package/dist/lib/mcp/errors.js +17 -17
  156. package/dist/lib/mcp/schema-builder.js +16 -16
  157. package/dist/lib/mcp/tool-generator.js +18 -18
  158. package/dist/lib/search-highlighting.d.ts +7 -0
  159. package/dist/lib/search-highlighting.d.ts.map +1 -1
  160. package/dist/lib/search-highlighting.js +6 -2
  161. package/dist/lib/selectors/core-selectors.d.ts +2 -0
  162. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  163. package/dist/lib/selectors/domains/auth.selectors.d.ts +1 -0
  164. package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
  165. package/dist/lib/selectors/domains/auth.selectors.js +1 -0
  166. package/dist/lib/selectors/domains/entities.selectors.d.ts +2 -0
  167. package/dist/lib/selectors/domains/entities.selectors.d.ts.map +1 -1
  168. package/dist/lib/selectors/domains/entities.selectors.js +2 -0
  169. package/dist/lib/selectors/selectors.d.ts +4 -0
  170. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  171. package/dist/lib/services/block.service.js +1 -1
  172. package/dist/lib/teams/active-team-cookie.d.ts +24 -0
  173. package/dist/lib/teams/active-team-cookie.d.ts.map +1 -0
  174. package/dist/lib/teams/active-team-cookie.js +17 -0
  175. package/dist/lib/teams/dashboard-team.d.ts +20 -0
  176. package/dist/lib/teams/dashboard-team.d.ts.map +1 -0
  177. package/dist/lib/teams/dashboard-team.js +38 -0
  178. package/dist/lib/teams/schema.d.ts.map +1 -1
  179. package/dist/lib/teams/schema.js +1 -1
  180. package/dist/lib/template-resolver.d.ts.map +1 -1
  181. package/dist/lib/template-resolver.js +0 -1
  182. package/dist/lib/theme/get-default-theme-mode.d.ts +7 -4
  183. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  184. package/dist/lib/theme/get-default-theme-mode.js +2 -44
  185. package/dist/messages/de/auth.json +4 -2
  186. package/dist/messages/de/common.json +83 -3
  187. package/dist/messages/de/email.json +1 -1
  188. package/dist/messages/de/index.d.ts +87 -0
  189. package/dist/messages/de/index.d.ts.map +1 -1
  190. package/dist/messages/de/settings.json +5 -0
  191. package/dist/messages/en/auth.json +4 -2
  192. package/dist/messages/en/common.json +83 -3
  193. package/dist/messages/en/email.json +1 -1
  194. package/dist/messages/en/index.d.ts +86 -0
  195. package/dist/messages/en/index.d.ts.map +1 -1
  196. package/dist/messages/en/settings.json +4 -0
  197. package/dist/messages/es/auth.json +4 -2
  198. package/dist/messages/es/common.json +83 -3
  199. package/dist/messages/es/email.json +1 -1
  200. package/dist/messages/es/index.d.ts +86 -0
  201. package/dist/messages/es/index.d.ts.map +1 -1
  202. package/dist/messages/es/settings.json +4 -0
  203. package/dist/messages/fr/auth.json +4 -2
  204. package/dist/messages/fr/common.json +83 -3
  205. package/dist/messages/fr/email.json +1 -1
  206. package/dist/messages/fr/index.d.ts +87 -0
  207. package/dist/messages/fr/index.d.ts.map +1 -1
  208. package/dist/messages/fr/settings.json +5 -0
  209. package/dist/messages/it/auth.json +4 -2
  210. package/dist/messages/it/common.json +83 -3
  211. package/dist/messages/it/email.json +1 -1
  212. package/dist/messages/it/index.d.ts +87 -0
  213. package/dist/messages/it/index.d.ts.map +1 -1
  214. package/dist/messages/it/settings.json +5 -0
  215. package/dist/messages/pt/auth.json +4 -2
  216. package/dist/messages/pt/common.json +83 -3
  217. package/dist/messages/pt/email.json +1 -1
  218. package/dist/messages/pt/index.d.ts +87 -0
  219. package/dist/messages/pt/index.d.ts.map +1 -1
  220. package/dist/messages/pt/settings.json +5 -0
  221. package/dist/nextspark-registries.d.ts +54 -0
  222. package/dist/providers/DashboardProviders.d.ts +13 -10
  223. package/dist/providers/DashboardProviders.d.ts.map +1 -1
  224. package/dist/providers/DashboardProviders.js +7 -1
  225. package/dist/templates/app/(auth)/forgot-password/page.tsx +2 -2
  226. package/dist/templates/app/(auth)/reset-password/page.tsx +6 -6
  227. package/dist/templates/app/403/page.tsx +6 -6
  228. package/dist/templates/app/api/v1/teams/switch/route.ts +24 -5
  229. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  230. package/dist/templates/app/dashboard/(main)/layout.tsx +4 -3
  231. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  232. package/dist/templates/app/dashboard/settings/profile/page.tsx +7 -6
  233. package/dist/templates/app/dashboard/settings/security/page.tsx +5 -4
  234. package/dist/templates/app/layout.ppr.tsx +8 -8
  235. package/dist/templates/app/layout.tsx +12 -17
  236. package/dist/templates/blocks/cta-section/component.tsx +5 -2
  237. package/dist/templates/blocks/cta-section/config.ts +3 -2
  238. package/dist/templates/blocks/cta-section/fields.ts +2 -2
  239. package/dist/templates/blocks/cta-section/schema.ts +2 -2
  240. package/dist/templates/blocks/features-grid/component.tsx +10 -2
  241. package/dist/templates/blocks/features-grid/config.ts +3 -2
  242. package/dist/templates/blocks/features-grid/fields.ts +2 -2
  243. package/dist/templates/blocks/features-grid/schema.ts +2 -2
  244. package/dist/templates/blocks/hero/component.tsx +6 -3
  245. package/dist/templates/blocks/hero/config.ts +3 -2
  246. package/dist/templates/blocks/hero/fields.ts +2 -2
  247. package/dist/templates/blocks/hero/schema.ts +2 -2
  248. package/dist/templates/blocks/testimonials/component.tsx +6 -3
  249. package/dist/templates/blocks/testimonials/config.ts +3 -2
  250. package/dist/templates/blocks/testimonials/fields.ts +2 -2
  251. package/dist/templates/blocks/testimonials/schema.ts +2 -2
  252. package/dist/templates/blocks/text-content/component.tsx +7 -3
  253. package/dist/templates/blocks/text-content/config.ts +3 -2
  254. package/dist/templates/blocks/text-content/fields.ts +2 -2
  255. package/dist/templates/blocks/text-content/schema.ts +2 -2
  256. package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  257. package/dist/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  258. package/dist/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  259. package/dist/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  260. package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  261. package/dist/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  262. package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  263. package/dist/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  264. package/dist/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  265. package/dist/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  266. package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  267. package/dist/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  268. package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  269. package/dist/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  270. package/dist/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  271. package/dist/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  272. package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  273. package/dist/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  274. package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  275. package/dist/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  276. package/dist/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  277. package/dist/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  278. package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  279. package/dist/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  280. package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  281. package/dist/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  282. package/dist/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  283. package/dist/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  284. package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  285. package/dist/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  286. package/dist/templates/contents/themes/starter/config/app.config.ts +8 -0
  287. package/dist/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  288. package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  289. package/dist/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  290. package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  291. package/dist/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  292. package/dist/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  293. package/dist/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  294. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  295. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  296. package/dist/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  297. package/dist/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  298. package/dist/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  299. package/dist/templates/features/blog/blocks/post-content/component.tsx +5 -1
  300. package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
  301. package/dist/templates/features/pages/blocks/hero/component.tsx +3 -0
  302. package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
  303. package/dist/templates/proxy.ts +138 -53
  304. package/dist/types/blocks.d.ts.map +1 -1
  305. package/dist/types/blocks.js +1 -1
  306. package/dist/types/task.types.d.ts +1 -1
  307. package/dist/types/task.types.js +7 -7
  308. package/dist/utils/dev/TranslationDebugger.js +2 -2
  309. package/package.json +14 -5
  310. package/scripts/build/registry/__tests__/block-registry.test.mjs +73 -0
  311. package/scripts/build/registry/__tests__/icons-discovery.test.mjs +130 -0
  312. package/scripts/build/registry/__tests__/identifier.test.mjs +65 -0
  313. package/scripts/build/registry/__tests__/page-generator.test.mjs +430 -0
  314. package/scripts/build/registry/__tests__/scheduled-actions-registry.test.mjs +41 -0
  315. package/scripts/build/registry/__tests__/template-registry.test.mjs +89 -0
  316. package/scripts/build/registry/discovery/icons.mjs +275 -0
  317. package/scripts/build/registry/discovery/index.mjs +1 -0
  318. package/scripts/build/registry/generators/block-registry.mjs +203 -55
  319. package/scripts/build/registry/generators/feature-registry.mjs +9 -2
  320. package/scripts/build/registry/generators/icon-registry.mjs +56 -0
  321. package/scripts/build/registry/generators/identifier.mjs +28 -0
  322. package/scripts/build/registry/generators/index.mjs +1 -0
  323. package/scripts/build/registry/generators/plugin-registry.mjs +4 -3
  324. package/scripts/build/registry/generators/scheduled-actions-registry.mjs +4 -2
  325. package/scripts/build/registry/generators/template-registry.mjs +49 -41
  326. package/scripts/build/registry/generators/theme-registry.mjs +6 -7
  327. package/scripts/build/registry/post-build/page-generator.mjs +380 -59
  328. package/scripts/build/registry.mjs +9 -1
  329. package/templates/app/(auth)/forgot-password/page.tsx +2 -2
  330. package/templates/app/(auth)/reset-password/page.tsx +6 -6
  331. package/templates/app/403/page.tsx +6 -6
  332. package/templates/app/api/v1/teams/switch/route.ts +24 -5
  333. package/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  334. package/templates/app/dashboard/(main)/layout.tsx +4 -3
  335. package/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  336. package/templates/app/dashboard/settings/profile/page.tsx +7 -6
  337. package/templates/app/dashboard/settings/security/page.tsx +5 -4
  338. package/templates/app/layout.ppr.tsx +8 -8
  339. package/templates/app/layout.tsx +12 -17
  340. package/templates/blocks/cta-section/component.tsx +5 -2
  341. package/templates/blocks/cta-section/config.ts +3 -2
  342. package/templates/blocks/cta-section/fields.ts +2 -2
  343. package/templates/blocks/cta-section/schema.ts +2 -2
  344. package/templates/blocks/features-grid/component.tsx +10 -2
  345. package/templates/blocks/features-grid/config.ts +3 -2
  346. package/templates/blocks/features-grid/fields.ts +2 -2
  347. package/templates/blocks/features-grid/schema.ts +2 -2
  348. package/templates/blocks/hero/component.tsx +6 -3
  349. package/templates/blocks/hero/config.ts +3 -2
  350. package/templates/blocks/hero/fields.ts +2 -2
  351. package/templates/blocks/hero/schema.ts +2 -2
  352. package/templates/blocks/testimonials/component.tsx +6 -3
  353. package/templates/blocks/testimonials/config.ts +3 -2
  354. package/templates/blocks/testimonials/fields.ts +2 -2
  355. package/templates/blocks/testimonials/schema.ts +2 -2
  356. package/templates/blocks/text-content/component.tsx +7 -3
  357. package/templates/blocks/text-content/config.ts +3 -2
  358. package/templates/blocks/text-content/fields.ts +2 -2
  359. package/templates/blocks/text-content/schema.ts +2 -2
  360. package/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  361. package/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  362. package/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  363. package/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  364. package/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  365. package/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  366. package/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  367. package/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  368. package/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  369. package/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  370. package/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  371. package/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  372. package/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  373. package/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  374. package/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  375. package/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  376. package/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  377. package/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  378. package/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  379. package/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  380. package/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  381. package/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  382. package/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  383. package/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  384. package/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  385. package/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  386. package/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  387. package/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  388. package/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  389. package/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  390. package/templates/contents/themes/starter/config/app.config.ts +8 -0
  391. package/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  392. package/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  393. package/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  394. package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  395. package/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  396. package/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  397. package/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  398. package/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  399. package/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  400. package/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  401. package/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  402. package/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  403. package/templates/features/blog/blocks/post-content/component.tsx +5 -1
  404. package/templates/features/blog/blocks/post-content/schema.ts +1 -1
  405. package/templates/features/pages/blocks/hero/component.tsx +3 -0
  406. package/templates/features/pages/blocks/hero/schema.ts +1 -1
  407. package/templates/proxy.ts +138 -53
  408. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.client.ts +9 -0
  409. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.lazy.ts +5 -0
  410. package/tests/jest/__mocks__/@nextsparkjs/registries/icon-registry.ts +42 -0
  411. package/tests/jest/__mocks__/next-intl-server.js +85 -5
  412. package/tests/jest/__mocks__/next-server.js +32 -0
@@ -0,0 +1,100 @@
1
+ import React from 'react'
2
+ import { cn } from '@nextsparkjs/core/lib/utils'
3
+ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
4
+ import { sanitizeBlockHtml } from '@nextsparkjs/core/lib/blocks/sanitize-html'
5
+ import type { TextContentBlockProps } from './schema'
6
+
7
+ /**
8
+ * Text Content Block Component
9
+ *
10
+ * Props from 3-tab structure:
11
+ * - Content: title, content (rich-text), cta
12
+ * - Design: backgroundColor, maxWidth, alignment
13
+ * - Advanced: className, id
14
+ *
15
+ * Note: `content` is the main rich-text body of this block
16
+ */
17
+ export function TextContentBlock({
18
+ // Base content props
19
+ title,
20
+ content,
21
+ cta,
22
+ // Base design props
23
+ backgroundColor,
24
+ // Text-content-specific design
25
+ maxWidth = 'lg',
26
+ alignment = 'left',
27
+ // Base advanced props
28
+ className,
29
+ id,
30
+ }: TextContentBlockProps) {
31
+ const maxWidthClasses: Record<string, string> = {
32
+ sm: 'max-w-2xl',
33
+ md: 'max-w-3xl',
34
+ lg: 'max-w-4xl',
35
+ xl: 'max-w-5xl',
36
+ full: 'max-w-none',
37
+ }
38
+
39
+ const alignmentClasses: Record<string, string> = {
40
+ left: 'text-left',
41
+ center: 'text-center mx-auto',
42
+ right: 'text-right ml-auto',
43
+ }
44
+
45
+ // Build section classes with background and custom className
46
+ const sectionClasses = buildSectionClasses(
47
+ 'py-16 px-4 @md:py-24',
48
+ { backgroundColor, className }
49
+ )
50
+
51
+ return (
52
+ <section id={id} className={sectionClasses} data-cy="block-text-content">
53
+ <div className="container mx-auto">
54
+ {/* Optional Section Title */}
55
+ {title && (
56
+ <div className={cn(
57
+ 'mb-8',
58
+ maxWidthClasses[maxWidth],
59
+ alignmentClasses[alignment]
60
+ )}>
61
+ <h2 className="text-3xl font-bold @md:text-4xl">
62
+ {title}
63
+ </h2>
64
+ </div>
65
+ )}
66
+
67
+ {/* Rich Text Content */}
68
+ <div
69
+ className={cn(
70
+ 'prose prose-lg dark:prose-invert',
71
+ maxWidthClasses[maxWidth],
72
+ alignmentClasses[alignment]
73
+ )}
74
+ dangerouslySetInnerHTML={{ __html: sanitizeBlockHtml(content) }}
75
+ />
76
+
77
+ {/* Optional CTA */}
78
+ {cta && (
79
+ <div className={cn(
80
+ 'mt-8',
81
+ maxWidthClasses[maxWidth],
82
+ alignmentClasses[alignment]
83
+ )}>
84
+ <a
85
+ href={cta.link}
86
+ target={cta.target}
87
+ rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
88
+ className="inline-flex items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
89
+ >
90
+ {cta.text}
91
+ </a>
92
+ </div>
93
+ )}
94
+ </div>
95
+ </section>
96
+ )
97
+ }
98
+
99
+ // The generated block registry imports this as the module's default.
100
+ export default TextContentBlock
@@ -0,0 +1,11 @@
1
+ import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
4
+ slug: 'text-content',
5
+ name: 'Text Content',
6
+ description: 'Rich text content block for paragraphs, lists, and formatted text',
7
+ category: 'content',
8
+ icon: 'FileText',
9
+ thumbnail: '/theme/blocks/text-content/thumbnail.png',
10
+ scope: ['pages']
11
+ }
@@ -0,0 +1,88 @@
1
+ import type { FieldDefinition } from '@nextsparkjs/core/types/blocks'
2
+ import {
3
+ baseContentFields,
4
+ baseDesignFields,
5
+ baseAdvancedFields,
6
+ } from '@nextsparkjs/core/types/blocks'
7
+
8
+ /**
9
+ * Text Content Block Field Definitions
10
+ *
11
+ * Organized into 3 tabs:
12
+ * - Content: title, cta (from base) + content (rich-text, replaces base content)
13
+ * - Design: backgroundColor (from base) + maxWidth, alignment
14
+ * - Advanced: className, id (from base)
15
+ *
16
+ * Note: The `content` field is overridden with a rich-text version
17
+ */
18
+
19
+ // Filter out base content field (we'll replace it with rich-text version)
20
+ const baseContentFieldsWithoutContent = baseContentFields.filter(
21
+ (field) => field.name !== 'content'
22
+ )
23
+
24
+ // Text-content-specific content fields (rich-text content)
25
+ const textContentFields: FieldDefinition[] = [
26
+ {
27
+ name: 'content',
28
+ label: 'Content',
29
+ type: 'rich-text',
30
+ tab: 'content',
31
+ required: true,
32
+ placeholder: 'Enter your content here...',
33
+ helpText: 'Rich text content with formatting support',
34
+ },
35
+ ]
36
+
37
+ // Text-content-specific design fields
38
+ const textDesignFields: FieldDefinition[] = [
39
+ {
40
+ name: 'maxWidth',
41
+ label: 'Maximum Width',
42
+ type: 'select',
43
+ tab: 'design',
44
+ required: false,
45
+ default: 'lg',
46
+ helpText: 'Maximum content width',
47
+ options: [
48
+ { label: 'Small (640px)', value: 'sm' },
49
+ { label: 'Medium (768px)', value: 'md' },
50
+ { label: 'Large (1024px)', value: 'lg' },
51
+ { label: 'Extra Large (1280px)', value: 'xl' },
52
+ { label: 'Full Width', value: 'full' },
53
+ ],
54
+ },
55
+ {
56
+ name: 'alignment',
57
+ label: 'Text Alignment',
58
+ type: 'select',
59
+ tab: 'design',
60
+ required: false,
61
+ default: 'left',
62
+ helpText: 'Text alignment within the content area',
63
+ options: [
64
+ { label: 'Left', value: 'left' },
65
+ { label: 'Center', value: 'center' },
66
+ { label: 'Right', value: 'right' },
67
+ ],
68
+ },
69
+ ]
70
+
71
+ /**
72
+ * Complete field definitions organized by tab
73
+ */
74
+ export const fieldDefinitions: FieldDefinition[] = [
75
+ // Content tab: base fields (without content) + rich-text content
76
+ ...baseContentFieldsWithoutContent,
77
+ ...textContentFields,
78
+
79
+ // Design tab: base fields + text-specific
80
+ ...baseDesignFields,
81
+ ...textDesignFields,
82
+
83
+ // Advanced tab: base fields only
84
+ ...baseAdvancedFields,
85
+ ]
86
+
87
+ // Alias for compatibility
88
+ export const fields = fieldDefinitions
@@ -0,0 +1,6 @@
1
+ export { config } from './config'
2
+ export { schema } from './schema'
3
+ export { fields } from './fields'
4
+ export { TextContentBlock as Component } from './component'
5
+
6
+ export type { TextContentBlockProps } from './schema'
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod'
2
+ import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
3
+
4
+ /**
5
+ * Text Content Block Schema
6
+ *
7
+ * Extends base schema with:
8
+ * - content: Rich text/HTML content (overrides base content with required)
9
+ * - maxWidth: Content width constraint
10
+ * - alignment: Text alignment
11
+ *
12
+ * Note: Uses base schema title, cta, backgroundColor, className, id
13
+ * The `content` field is overridden to be required and serves as rich-text body
14
+ */
15
+ export const textContentSpecificSchema = z.object({
16
+ // Content: rich text body (overrides base optional content)
17
+ content: z.string().min(1, 'Content is required'),
18
+
19
+ // Design: layout options
20
+ maxWidth: z.enum(['sm', 'md', 'lg', 'xl', 'full']).default('lg'),
21
+ alignment: z.enum(['left', 'center', 'right']).default('left'),
22
+ })
23
+
24
+ /**
25
+ * Complete Text Content Block Schema
26
+ * Note: textContentSpecificSchema.content overrides baseBlockSchema.content
27
+ */
28
+ export const schema = baseBlockSchema.extend(textContentSpecificSchema.shape)
29
+
30
+ export type TextContentBlockProps = z.infer<typeof schema>
@@ -134,6 +134,14 @@ export const APP_CONFIG_OVERRIDES = {
134
134
  // expiresIn: 60 * 60 * 24 * 90, // 90 days
135
135
  // updateAge: 60 * 60 * 24 * 7, // renew weekly
136
136
  // },
137
+
138
+ /**
139
+ * Email OTP code lifetime (seconds) and length (digits) — defaults: 300
140
+ * (5 minutes) / 6. Drives the `emailOTP` plugin, the login form's
141
+ * countdown, and the OTP email's "expires in" notice together, so they
142
+ * can't drift apart:
143
+ */
144
+ // otp: { expiresIn: 60 * 10, otpLength: 6 },
137
145
  /**
138
146
  * Whether Better Auth automatically sends the verification email on signup.
139
147
  *
@@ -38,6 +38,13 @@ export const taskEntityConfig: EntityConfig = {
38
38
  dashboard: {
39
39
  showInMenu: true,
40
40
  showInTopbar: true,
41
+ // `filterable` below turns the feature on; this says what to filter by.
42
+ // Without it the list renders no filter controls at all, which is what
43
+ // the UAT specs (and the cypress entities fixture) already expect.
44
+ filters: [
45
+ { field: 'status', type: 'multiSelect' },
46
+ { field: 'priority', type: 'multiSelect' },
47
+ ],
41
48
  },
42
49
  public: {
43
50
  hasArchivePage: false,
@@ -6,6 +6,7 @@ import {
6
6
  loginAsDefaultDeveloper,
7
7
  loginAsDefaultSuperadmin,
8
8
  loginAsDefaultMember,
9
+ hasSeededTeamRoleUser,
9
10
  CORE_USERS
10
11
  } from '../../../src/session-helpers'
11
12
 
@@ -145,7 +146,14 @@ describe('DevTools - Access Control', {
145
146
  })
146
147
  })
147
148
 
148
- describe('ACCESS-007: Member is BLOCKED from /devtools', { tags: '@smoke' }, () => {
149
+ // Needs a user with a plain team role, which the starter does not seed — it
150
+ // creates superadmin and developer only. ACCESS-006 above already covers a
151
+ // non-developer being blocked, using a user that does exist, so skipping this
152
+ // leaves no path untested. A theme that seeds team members sets MEMBER_EMAIL
153
+ // and it runs.
154
+ const memberSuite = hasSeededTeamRoleUser('MEMBER') ? describe : describe.skip
155
+
156
+ memberSuite('ACCESS-007: Member is BLOCKED from /devtools', { tags: '@smoke' }, () => {
149
157
  it('should redirect member to /dashboard when attempting to access /devtools', { tags: '@smoke' }, () => {
150
158
  allure.severity('critical')
151
159
 
@@ -6,8 +6,8 @@
6
6
  * - API Key with tasks:read, tasks:write scopes (or superadmin with *)
7
7
  * - x-team-id header for team context
8
8
  */
9
- const BaseAPIController = require('../../src/controllers/BaseAPIController')
10
- const entitiesConfig = require('../../fixtures/entities.json')
9
+ const BaseAPIController = require('../../../../src/controllers/BaseAPIController')
10
+ const entitiesConfig = require('../../../../fixtures/entities.json')
11
11
 
12
12
  const { slug } = entitiesConfig.entities.tasks
13
13
 
@@ -38,24 +38,13 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
38
38
  })
39
39
 
40
40
  afterEach(() => {
41
- // Cleanup: Delete any tasks created during tests
42
- if (createdTaskIds.length > 0) {
43
- cy.window().then((win) => {
44
- const teamId = win.localStorage.getItem('activeTeamId')
45
- createdTaskIds.forEach((id) => {
46
- cy.request({
47
- method: 'DELETE',
48
- url: `/api/v1/tasks/${id}`,
49
- headers: {
50
- 'Content-Type': 'application/json',
51
- 'x-team-id': teamId || ''
52
- },
53
- failOnStatusCode: false
54
- })
55
- })
56
- createdTaskIds = []
57
- })
58
- }
41
+ // Through the command rather than a request built here: it resolves the
42
+ // team the API requires even before a dashboard visit has put it in
43
+ // localStorage. Sending an empty `x-team-id` gets a 400 that nothing
44
+ // reports, and the tasks survive into the next test, where a count of rows
45
+ // then measures the leftovers instead of what that test created.
46
+ createdTaskIds.forEach((id) => cy.deleteTask(id))
47
+ createdTaskIds = []
59
48
  })
60
49
 
61
50
  // ============================================================
@@ -75,7 +64,9 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
75
64
  tasks.fillTaskForm(taskData)
76
65
  tasks.submitForm()
77
66
 
78
- // Should redirect to list and show new task
67
+ // Saving lands on the detail page, so reaching the list is a separate
68
+ // visit. What this test is about is the task existing and showing there.
69
+ tasks.visitList()
79
70
  tasks.waitForList()
80
71
  tasks.assertTaskInList(taskData.title)
81
72
  })
@@ -93,6 +84,7 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
93
84
  tasks.fillTaskForm(taskData)
94
85
  tasks.submitForm()
95
86
 
87
+ tasks.visitList()
96
88
  tasks.waitForList()
97
89
  tasks.assertTaskInList(taskData.title)
98
90
  })
@@ -141,6 +133,9 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
141
133
 
142
134
  it('should navigate to task detail page', () => {
143
135
  cy.then(() => {
136
+ tasks.visitList()
137
+ tasks.waitForList()
138
+
144
139
  tasks.clickRow(testTaskId)
145
140
  tasks.waitForDetail()
146
141
 
@@ -195,7 +190,10 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
195
190
  tasks.fillTextField('title', newTitle)
196
191
  tasks.submitForm()
197
192
 
193
+ tasks.waitForDetail()
194
+
198
195
  // Verify update
196
+ tasks.visitList()
199
197
  tasks.waitForList()
200
198
  tasks.assertTaskInList(newTitle)
201
199
  tasks.assertTaskNotInList(originalTitle)
@@ -211,6 +209,9 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
211
209
  tasks.selectOption('status', 'done')
212
210
  tasks.submitForm()
213
211
 
212
+ tasks.waitForDetail()
213
+
214
+ tasks.visitList()
214
215
  tasks.waitForList()
215
216
  // Task should now show as done
216
217
  tasks.assertTaskInList(originalTitle)
@@ -226,6 +227,9 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
226
227
  tasks.selectOption('priority', 'urgent')
227
228
  tasks.submitForm()
228
229
 
230
+ tasks.waitForDetail()
231
+
232
+ tasks.visitList()
229
233
  tasks.waitForList()
230
234
  tasks.assertTaskInList(originalTitle)
231
235
  })
@@ -312,47 +316,49 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
312
316
  })
313
317
  })
314
318
 
315
- // Refresh list to see new tasks
316
- cy.reload()
319
+ // The list, not a reload: `cy.reload()` keeps the current URL, so a filter
320
+ // the previous test left in the query string is still applied here.
321
+ tasks.visitList()
317
322
  tasks.waitForList()
318
323
  })
319
324
 
320
325
  it('should filter tasks by status', () => {
321
- // Filter by "todo" status
322
326
  tasks.filterByStatus('todo')
323
327
 
324
- // Wait for filter to apply
325
- cy.wait(500)
328
+ // The filter travels in the URL, and waiting on that is what settles the
329
+ // list: asserting row by row while the table is still refetching detaches
330
+ // the very rows being checked.
331
+ cy.url().should('include', 'status=todo')
332
+ cy.get(tasks.selectors.rowGeneric).should('have.length.greaterThan', 0)
326
333
 
327
- // Should show only todo tasks
334
+ // A row shows the option's label, never the stored value.
328
335
  cy.get(tasks.selectors.rowGeneric).each(($row) => {
329
- cy.wrap($row).should('contain.text', 'todo')
336
+ cy.wrap($row).should('contain.text', 'To Do')
330
337
  })
331
338
  })
332
339
 
333
340
  it('should filter tasks by priority', () => {
334
- // Filter by "high" priority
335
341
  tasks.filterByPriority('high')
336
342
 
337
- // Wait for filter to apply
338
- cy.wait(500)
343
+ cy.url().should('include', 'priority=high')
344
+ cy.get(tasks.selectors.rowGeneric).should('have.length.greaterThan', 0)
339
345
 
340
- // Should show only high priority tasks
341
346
  cy.get(tasks.selectors.rowGeneric).each(($row) => {
342
- cy.wrap($row).should('contain.text', 'high')
347
+ cy.wrap($row).should('contain.text', 'High')
343
348
  })
344
349
  })
345
350
 
346
351
  it('should clear filters', () => {
347
- // Apply filter
348
352
  tasks.filterByStatus('todo')
349
- cy.wait(500)
353
+ // The filter travels in the URL, and waiting on that is what settles the
354
+ // list — a fixed wait counts rows mid-refetch and sees a partial table.
355
+ cy.url().should('include', 'status=todo')
350
356
 
351
- // Clear filter
352
357
  tasks.clearStatusFilter()
353
- cy.wait(500)
358
+ cy.url().should('not.include', 'status=')
354
359
 
355
- // Should show all tasks again
360
+ // The three the beforeEach created: the starter seeds no tasks, so any
361
+ // higher number would be counting on a previous run's leftovers.
356
362
  cy.get(tasks.selectors.rowGeneric).should('have.length.at.least', 3)
357
363
  })
358
364
  })
@@ -388,8 +394,9 @@ describe('Tasks CRUD', { tags: ['@uat', '@tasks', '@crud'] }, () => {
388
394
  }
389
395
  })
390
396
 
391
- // Refresh list
392
- cy.reload()
397
+ // The list, not a reload: a search or filter left in the query string by
398
+ // the previous test would still be applied.
399
+ tasks.visitList()
393
400
  tasks.waitForList()
394
401
  })
395
402
 
@@ -15,6 +15,22 @@
15
15
  "status",
16
16
  "priority"
17
17
  ]
18
+ },
19
+ "pages": {
20
+ "slug": "pages",
21
+ "singular": "page",
22
+ "plural": "Pages",
23
+ "tableName": "pages",
24
+ "fields": [
25
+ "title",
26
+ "slug",
27
+ "status",
28
+ "locale"
29
+ ],
30
+ "filters": [
31
+ "status"
32
+ ],
33
+ "source": "theme"
18
34
  }
19
35
  },
20
36
  "tasks": {
@@ -118,7 +118,9 @@ export class DevKeyringPOM extends BasePOM {
118
118
  this.submitLogin()
119
119
 
120
120
  // 3. Wait for login to complete
121
- cy.url().should('include', '/dashboard', { timeout: 10000 })
121
+ // No explicit timeout: an inline one wins over the run's configuration, so a
122
+ // slow dev server could not be waited out with `--config defaultCommandTimeout`.
123
+ cy.url().should('include', '/dashboard')
122
124
 
123
125
  return this
124
126
  }
@@ -58,9 +58,9 @@ export abstract class BlockEditorBasePOM extends BasePOM {
58
58
  return {
59
59
  // Editor main
60
60
  container: cySelector('blockEditor.container'),
61
- titleInput: cySelector('blockEditor.titleInput'),
62
- slugInput: cySelector('blockEditor.slugInput'),
63
- saveButton: cySelector('blockEditor.saveButton'),
61
+ titleInput: cySelector('blockEditor.header.titleInput'),
62
+ slugInput: cySelector('blockEditor.header.slugInput'),
63
+ saveButton: cySelector('blockEditor.header.saveButton'),
64
64
  statusBadge: cySelector('blockEditor.statusBadge'),
65
65
  leftSidebarToggle: cySelector('blockEditor.leftSidebarToggle'),
66
66
  viewModeToggle: cySelector('blockEditor.viewModeToggle'),
@@ -0,0 +1,137 @@
1
+ /**
2
+ * PagesPOM - Page Object Model for Pages entity (List operations)
3
+ *
4
+ * Extends DashboardEntityPOM for list page operations.
5
+ * For editing pages with the block editor, use PageBuilderPOM from features/.
6
+ *
7
+ * @example
8
+ * // List operations
9
+ * PagesPOM.create()
10
+ * .visitList()
11
+ * .waitForList()
12
+ * .search('About Us')
13
+ * .selectFilter('status', 'published')
14
+ *
15
+ * // For editing pages, use PageBuilderPOM
16
+ * import { PageBuilderPOM } from '../features/PageBuilderPOM'
17
+ * PageBuilderPOM.create().visitEdit(pageId).setTitle('New Title').save()
18
+ */
19
+
20
+ import { DashboardEntityPOM } from '../core/DashboardEntityPOM'
21
+ import entitiesConfig from '../../fixtures/entities.json'
22
+
23
+ export interface PageListFilters {
24
+ status?: 'draft' | 'published' | 'archived'
25
+ locale?: 'en' | 'es'
26
+ }
27
+
28
+ export class PagesPOM extends DashboardEntityPOM {
29
+ constructor() {
30
+ super(entitiesConfig.entities.pages.slug)
31
+ }
32
+
33
+ // ============================================
34
+ // FACTORY METHOD
35
+ // ============================================
36
+
37
+ static create(): PagesPOM {
38
+ return new PagesPOM()
39
+ }
40
+
41
+ // ============================================
42
+ // PAGE-SPECIFIC LIST METHODS
43
+ // ============================================
44
+
45
+ /**
46
+ * Filter pages by status
47
+ */
48
+ filterByStatus(status: 'draft' | 'published' | 'archived') {
49
+ return this.selectFilter('status', status)
50
+ }
51
+
52
+ /**
53
+ * Apply multiple filters at once
54
+ */
55
+ applyFilters(filters: PageListFilters) {
56
+ if (filters.status) {
57
+ this.filterByStatus(filters.status)
58
+ }
59
+ if (filters.locale) {
60
+ this.selectFilter('locale', filters.locale)
61
+ }
62
+ return this
63
+ }
64
+
65
+ // ============================================
66
+ // WORKFLOW METHODS
67
+ // ============================================
68
+
69
+ /**
70
+ * Navigate to create page
71
+ * Note: Pages use block editor, so this redirects to the editor
72
+ */
73
+ navigateToCreate() {
74
+ this.clickAdd()
75
+ return this
76
+ }
77
+
78
+ /**
79
+ * Navigate to edit page for a page
80
+ * Note: Pages use block editor, so this redirects to the editor
81
+ */
82
+ navigateToEdit(id: string) {
83
+ this.clickRowAction('edit', id)
84
+ return this
85
+ }
86
+
87
+ /**
88
+ * Delete page from list with API waits
89
+ */
90
+ deletePageWithApiWait(id: string) {
91
+ this.visitDetailWithApiWait(id)
92
+ this.clickDelete()
93
+ this.confirmDelete()
94
+ this.api.waitForDelete()
95
+ return this
96
+ }
97
+
98
+ /**
99
+ * Delete page by finding it in the list by title
100
+ */
101
+ deletePageByTitleWithApiWait(title: string) {
102
+ this.clickRowByText(title)
103
+ this.waitForDetail()
104
+ this.clickDelete()
105
+ this.confirmDelete()
106
+ this.api.waitForDelete()
107
+ return this
108
+ }
109
+
110
+ // ============================================
111
+ // ENTITY-SPECIFIC ASSERTIONS
112
+ // ============================================
113
+
114
+ /**
115
+ * Assert page appears in list
116
+ */
117
+ assertPageInList(title: string) {
118
+ return this.assertInList(title)
119
+ }
120
+
121
+ /**
122
+ * Assert page does not appear in list
123
+ */
124
+ assertPageNotInList(title: string) {
125
+ return this.assertNotInList(title)
126
+ }
127
+
128
+ /**
129
+ * Assert page has specific status badge
130
+ */
131
+ assertPageStatus(id: string, status: string) {
132
+ cy.get(this.selectors.row(id)).should('contain.text', status)
133
+ return this
134
+ }
135
+ }
136
+
137
+ export default PagesPOM
@@ -4,9 +4,11 @@
4
4
  * Page Object Models for specific entities:
5
5
  * - TasksPOM: Tasks CRUD operations
6
6
  * - PatternsPOM: Patterns CRUD operations with usage tracking (core entity)
7
+ * - PagesPOM: Pages CRUD operations (the block editor specs build on it)
7
8
  *
8
9
  * Add more entity POMs as you create new entities.
9
10
  */
10
11
 
11
12
  export { TasksPOM, type TaskFormData } from './TasksPOM'
12
13
  export { PatternsPOM } from './PatternsPOM'
14
+ export { PagesPOM } from './PagesPOM'