@nextsparkjs/core 0.1.0-beta.187 → 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 (414) 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.d.ts +10 -1
  27. package/dist/components/dashboard/mobile/MobileTopBar.d.ts.map +1 -1
  28. package/dist/components/dashboard/mobile/MobileTopBar.js +4 -3
  29. package/dist/components/dashboard/navigation/DynamicNavigation.js +5 -5
  30. package/dist/components/devtools/BlockDetailViewer.js +1 -1
  31. package/dist/components/devtools/BlocksViewer.js +1 -1
  32. package/dist/components/entities/EntityChildManager.js +5 -5
  33. package/dist/components/entities/EntityDetailPage.d.ts.map +1 -1
  34. package/dist/components/entities/EntityDetailPage.js +16 -15
  35. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  36. package/dist/components/entities/EntityFieldRenderer.js +12 -7
  37. package/dist/components/entities/EntityPageHeader.js +5 -5
  38. package/dist/components/entities/ErrorBoundary.d.ts.map +1 -1
  39. package/dist/components/entities/ErrorBoundary.js +20 -21
  40. package/dist/components/entities/wrappers/EntityDetailWrapper.js +5 -5
  41. package/dist/components/entities/wrappers/EntityFormWrapper.js +2 -2
  42. package/dist/components/entities/wrappers/EntityListWrapper.js +3 -3
  43. package/dist/components/permissions/NoPermission.js +10 -10
  44. package/dist/components/public/pageBuilder/PageRenderer.d.ts.map +1 -1
  45. package/dist/components/public/pageBuilder/PageRenderer.js +2 -1
  46. package/dist/components/settings/SettingsPageSkeleton.js +4 -4
  47. package/dist/components/settings/layouts/SettingsSidebar.js +6 -6
  48. package/dist/components/shared/MultiSelectFilter.d.ts +3 -1
  49. package/dist/components/shared/MultiSelectFilter.d.ts.map +1 -1
  50. package/dist/components/shared/MultiSelectFilter.js +7 -3
  51. package/dist/components/shared/SearchInput.d.ts +2 -0
  52. package/dist/components/shared/SearchInput.d.ts.map +1 -1
  53. package/dist/components/shared/SearchInput.js +64 -6
  54. package/dist/components/ui/address-input.js +18 -18
  55. package/dist/components/ui/audio-upload.js +9 -9
  56. package/dist/components/ui/combobox.d.ts.map +1 -1
  57. package/dist/components/ui/combobox.js +4 -4
  58. package/dist/components/ui/country-select.d.ts.map +1 -1
  59. package/dist/components/ui/country-select.js +3 -3
  60. package/dist/components/ui/currency-select.d.ts.map +1 -1
  61. package/dist/components/ui/currency-select.js +3 -3
  62. package/dist/components/ui/dynamic-icon.d.ts.map +1 -1
  63. package/dist/components/ui/dynamic-icon.js +3 -2
  64. package/dist/components/ui/file-upload.js +7 -7
  65. package/dist/components/ui/image-upload.js +8 -8
  66. package/dist/components/ui/multi-select.d.ts +2 -1
  67. package/dist/components/ui/multi-select.d.ts.map +1 -1
  68. package/dist/components/ui/multi-select.js +4 -3
  69. package/dist/components/ui/phone-input.d.ts.map +1 -1
  70. package/dist/components/ui/phone-input.js +3 -3
  71. package/dist/components/ui/relation-display.js +1 -1
  72. package/dist/components/ui/simple-relation-select.d.ts +2 -1
  73. package/dist/components/ui/simple-relation-select.d.ts.map +1 -1
  74. package/dist/components/ui/simple-relation-select.js +7 -6
  75. package/dist/components/ui/tags-input.d.ts.map +1 -1
  76. package/dist/components/ui/tags-input.js +1 -1
  77. package/dist/components/ui/timezone-select.d.ts.map +1 -1
  78. package/dist/components/ui/timezone-select.js +3 -3
  79. package/dist/components/ui/user-display.js +4 -4
  80. package/dist/components/ui/user-select.d.ts.map +1 -1
  81. package/dist/components/ui/user-select.js +9 -9
  82. package/dist/components/ui/video-upload.js +9 -9
  83. package/dist/contexts/SubscriptionContext.d.ts.map +1 -1
  84. package/dist/contexts/SubscriptionContext.js +5 -1
  85. package/dist/contexts/TeamContext.d.ts.map +1 -1
  86. package/dist/contexts/TeamContext.js +83 -26
  87. package/dist/emails/otp-verification.d.ts.map +1 -1
  88. package/dist/emails/otp-verification.js +3 -2
  89. package/dist/hooks/useAccountPreferencesSync.d.ts +13 -0
  90. package/dist/hooks/useAccountPreferencesSync.d.ts.map +1 -0
  91. package/dist/hooks/useAccountPreferencesSync.js +95 -0
  92. package/dist/hooks/useAuth.d.ts +202 -15
  93. package/dist/hooks/useAuth.d.ts.map +1 -1
  94. package/dist/hooks/useAuth.js +49 -13
  95. package/dist/hooks/useEntityConfig.js +2 -2
  96. package/dist/hooks/useEntitySearch.js +10 -10
  97. package/dist/hooks/useNotifications.js +12 -12
  98. package/dist/hooks/usePrefetch.d.ts.map +1 -1
  99. package/dist/hooks/usePrefetch.js +6 -2
  100. package/dist/hooks/useSearch.js +15 -15
  101. package/dist/hooks/useSessionCookieRefresh.d.ts +18 -1
  102. package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -1
  103. package/dist/hooks/useSessionCookieRefresh.js +17 -4
  104. package/dist/hooks/useSessionHint.d.ts +12 -0
  105. package/dist/hooks/useSessionHint.d.ts.map +1 -0
  106. package/dist/hooks/useSessionHint.js +15 -0
  107. package/dist/hooks/useTranslationPreload.js +6 -6
  108. package/dist/i18n.d.ts +13 -0
  109. package/dist/i18n.d.ts.map +1 -1
  110. package/dist/i18n.js +2 -27
  111. package/dist/lib/actions/entity.actions.d.ts.map +1 -1
  112. package/dist/lib/actions/entity.actions.js +3 -2
  113. package/dist/lib/api/auth/dual-auth.d.ts +4 -1
  114. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  115. package/dist/lib/api/auth/dual-auth.js +5 -16
  116. package/dist/lib/api/entities.js +33 -33
  117. package/dist/lib/api/keys.d.ts +27 -27
  118. package/dist/lib/api/keys.js +27 -27
  119. package/dist/lib/auth/callback-url.d.ts +11 -0
  120. package/dist/lib/auth/callback-url.d.ts.map +1 -0
  121. package/dist/lib/auth/callback-url.js +14 -0
  122. package/dist/lib/auth/otp-config.d.ts +55 -0
  123. package/dist/lib/auth/otp-config.d.ts.map +1 -0
  124. package/dist/lib/auth/otp-config.js +48 -0
  125. package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
  126. package/dist/lib/auth/registration-helpers.js +3 -1
  127. package/dist/lib/auth/session-hint.d.ts +31 -0
  128. package/dist/lib/auth/session-hint.d.ts.map +1 -0
  129. package/dist/lib/auth/session-hint.js +37 -0
  130. package/dist/lib/auth.d.ts.map +1 -1
  131. package/dist/lib/auth.js +6 -4
  132. package/dist/lib/blocks/loader.d.ts +3 -11
  133. package/dist/lib/blocks/loader.d.ts.map +1 -1
  134. package/dist/lib/blocks/loader.js +1 -9
  135. package/dist/lib/blocks/loader.server.d.ts +18 -0
  136. package/dist/lib/blocks/loader.server.d.ts.map +1 -0
  137. package/dist/lib/blocks/loader.server.js +11 -0
  138. package/dist/lib/blocks/sanitize-html.d.ts +31 -0
  139. package/dist/lib/blocks/sanitize-html.d.ts.map +1 -0
  140. package/dist/lib/blocks/sanitize-html.js +60 -0
  141. package/dist/lib/config/config-sync.d.ts +1 -0
  142. package/dist/lib/config/config-sync.d.ts.map +1 -1
  143. package/dist/lib/config/config-sync.js +4 -1
  144. package/dist/lib/config/types.d.ts +48 -0
  145. package/dist/lib/config/types.d.ts.map +1 -1
  146. package/dist/lib/email/types.d.ts +6 -0
  147. package/dist/lib/email/types.d.ts.map +1 -1
  148. package/dist/lib/entities/serialization.d.ts.map +1 -1
  149. package/dist/lib/entities/serialization.js +3 -6
  150. package/dist/lib/icons.d.ts +12 -0
  151. package/dist/lib/icons.d.ts.map +1 -0
  152. package/dist/lib/icons.js +30 -0
  153. package/dist/lib/locale.d.ts +25 -1
  154. package/dist/lib/locale.d.ts.map +1 -1
  155. package/dist/lib/locale.js +49 -35
  156. package/dist/lib/mcp/engine.js +1 -1
  157. package/dist/lib/mcp/errors.js +17 -17
  158. package/dist/lib/mcp/schema-builder.js +16 -16
  159. package/dist/lib/mcp/tool-generator.js +18 -18
  160. package/dist/lib/search-highlighting.d.ts +7 -0
  161. package/dist/lib/search-highlighting.d.ts.map +1 -1
  162. package/dist/lib/search-highlighting.js +6 -2
  163. package/dist/lib/selectors/core-selectors.d.ts +2 -0
  164. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  165. package/dist/lib/selectors/domains/auth.selectors.d.ts +1 -0
  166. package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
  167. package/dist/lib/selectors/domains/auth.selectors.js +1 -0
  168. package/dist/lib/selectors/domains/entities.selectors.d.ts +2 -0
  169. package/dist/lib/selectors/domains/entities.selectors.d.ts.map +1 -1
  170. package/dist/lib/selectors/domains/entities.selectors.js +2 -0
  171. package/dist/lib/selectors/selectors.d.ts +4 -0
  172. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  173. package/dist/lib/services/block.service.js +1 -1
  174. package/dist/lib/teams/active-team-cookie.d.ts +24 -0
  175. package/dist/lib/teams/active-team-cookie.d.ts.map +1 -0
  176. package/dist/lib/teams/active-team-cookie.js +17 -0
  177. package/dist/lib/teams/dashboard-team.d.ts +20 -0
  178. package/dist/lib/teams/dashboard-team.d.ts.map +1 -0
  179. package/dist/lib/teams/dashboard-team.js +38 -0
  180. package/dist/lib/teams/schema.d.ts.map +1 -1
  181. package/dist/lib/teams/schema.js +1 -1
  182. package/dist/lib/template-resolver.d.ts.map +1 -1
  183. package/dist/lib/template-resolver.js +0 -1
  184. package/dist/lib/theme/get-default-theme-mode.d.ts +7 -4
  185. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  186. package/dist/lib/theme/get-default-theme-mode.js +2 -44
  187. package/dist/messages/de/auth.json +4 -2
  188. package/dist/messages/de/common.json +83 -3
  189. package/dist/messages/de/email.json +1 -1
  190. package/dist/messages/de/index.d.ts +87 -0
  191. package/dist/messages/de/index.d.ts.map +1 -1
  192. package/dist/messages/de/settings.json +5 -0
  193. package/dist/messages/en/auth.json +4 -2
  194. package/dist/messages/en/common.json +83 -3
  195. package/dist/messages/en/email.json +1 -1
  196. package/dist/messages/en/index.d.ts +86 -0
  197. package/dist/messages/en/index.d.ts.map +1 -1
  198. package/dist/messages/en/settings.json +4 -0
  199. package/dist/messages/es/auth.json +4 -2
  200. package/dist/messages/es/common.json +83 -3
  201. package/dist/messages/es/email.json +1 -1
  202. package/dist/messages/es/index.d.ts +86 -0
  203. package/dist/messages/es/index.d.ts.map +1 -1
  204. package/dist/messages/es/settings.json +4 -0
  205. package/dist/messages/fr/auth.json +4 -2
  206. package/dist/messages/fr/common.json +83 -3
  207. package/dist/messages/fr/email.json +1 -1
  208. package/dist/messages/fr/index.d.ts +87 -0
  209. package/dist/messages/fr/index.d.ts.map +1 -1
  210. package/dist/messages/fr/settings.json +5 -0
  211. package/dist/messages/it/auth.json +4 -2
  212. package/dist/messages/it/common.json +83 -3
  213. package/dist/messages/it/email.json +1 -1
  214. package/dist/messages/it/index.d.ts +87 -0
  215. package/dist/messages/it/index.d.ts.map +1 -1
  216. package/dist/messages/it/settings.json +5 -0
  217. package/dist/messages/pt/auth.json +4 -2
  218. package/dist/messages/pt/common.json +83 -3
  219. package/dist/messages/pt/email.json +1 -1
  220. package/dist/messages/pt/index.d.ts +87 -0
  221. package/dist/messages/pt/index.d.ts.map +1 -1
  222. package/dist/messages/pt/settings.json +5 -0
  223. package/dist/nextspark-registries.d.ts +54 -0
  224. package/dist/providers/DashboardProviders.d.ts +13 -10
  225. package/dist/providers/DashboardProviders.d.ts.map +1 -1
  226. package/dist/providers/DashboardProviders.js +7 -1
  227. package/dist/templates/app/(auth)/forgot-password/page.tsx +2 -2
  228. package/dist/templates/app/(auth)/reset-password/page.tsx +6 -6
  229. package/dist/templates/app/403/page.tsx +6 -6
  230. package/dist/templates/app/api/v1/teams/switch/route.ts +24 -5
  231. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  232. package/dist/templates/app/dashboard/(main)/layout.tsx +4 -3
  233. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  234. package/dist/templates/app/dashboard/settings/profile/page.tsx +7 -6
  235. package/dist/templates/app/dashboard/settings/security/page.tsx +5 -4
  236. package/dist/templates/app/layout.ppr.tsx +8 -8
  237. package/dist/templates/app/layout.tsx +12 -17
  238. package/dist/templates/blocks/cta-section/component.tsx +5 -2
  239. package/dist/templates/blocks/cta-section/config.ts +3 -2
  240. package/dist/templates/blocks/cta-section/fields.ts +2 -2
  241. package/dist/templates/blocks/cta-section/schema.ts +2 -2
  242. package/dist/templates/blocks/features-grid/component.tsx +10 -2
  243. package/dist/templates/blocks/features-grid/config.ts +3 -2
  244. package/dist/templates/blocks/features-grid/fields.ts +2 -2
  245. package/dist/templates/blocks/features-grid/schema.ts +2 -2
  246. package/dist/templates/blocks/hero/component.tsx +6 -3
  247. package/dist/templates/blocks/hero/config.ts +3 -2
  248. package/dist/templates/blocks/hero/fields.ts +2 -2
  249. package/dist/templates/blocks/hero/schema.ts +2 -2
  250. package/dist/templates/blocks/testimonials/component.tsx +6 -3
  251. package/dist/templates/blocks/testimonials/config.ts +3 -2
  252. package/dist/templates/blocks/testimonials/fields.ts +2 -2
  253. package/dist/templates/blocks/testimonials/schema.ts +2 -2
  254. package/dist/templates/blocks/text-content/component.tsx +7 -3
  255. package/dist/templates/blocks/text-content/config.ts +3 -2
  256. package/dist/templates/blocks/text-content/fields.ts +2 -2
  257. package/dist/templates/blocks/text-content/schema.ts +2 -2
  258. package/dist/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  259. package/dist/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  260. package/dist/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  261. package/dist/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  262. package/dist/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  263. package/dist/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  264. package/dist/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  265. package/dist/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  266. package/dist/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  267. package/dist/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  268. package/dist/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  269. package/dist/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  270. package/dist/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  271. package/dist/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  272. package/dist/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  273. package/dist/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  274. package/dist/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  275. package/dist/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  276. package/dist/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  277. package/dist/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  278. package/dist/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  279. package/dist/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  280. package/dist/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  281. package/dist/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  282. package/dist/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  283. package/dist/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  284. package/dist/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  285. package/dist/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  286. package/dist/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  287. package/dist/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  288. package/dist/templates/contents/themes/starter/config/app.config.ts +8 -0
  289. package/dist/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  290. package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  291. package/dist/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  292. package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  293. package/dist/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  294. package/dist/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  295. package/dist/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  296. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  297. package/dist/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  298. package/dist/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  299. package/dist/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  300. package/dist/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  301. package/dist/templates/features/blog/blocks/post-content/component.tsx +5 -1
  302. package/dist/templates/features/blog/blocks/post-content/schema.ts +1 -1
  303. package/dist/templates/features/pages/blocks/hero/component.tsx +3 -0
  304. package/dist/templates/features/pages/blocks/hero/schema.ts +1 -1
  305. package/dist/templates/proxy.ts +138 -53
  306. package/dist/types/blocks.d.ts.map +1 -1
  307. package/dist/types/blocks.js +1 -1
  308. package/dist/types/task.types.d.ts +1 -1
  309. package/dist/types/task.types.js +7 -7
  310. package/dist/utils/dev/TranslationDebugger.js +2 -2
  311. package/package.json +14 -5
  312. package/scripts/build/registry/__tests__/block-registry.test.mjs +73 -0
  313. package/scripts/build/registry/__tests__/icons-discovery.test.mjs +130 -0
  314. package/scripts/build/registry/__tests__/identifier.test.mjs +65 -0
  315. package/scripts/build/registry/__tests__/page-generator.test.mjs +430 -0
  316. package/scripts/build/registry/__tests__/scheduled-actions-registry.test.mjs +41 -0
  317. package/scripts/build/registry/__tests__/template-registry.test.mjs +89 -0
  318. package/scripts/build/registry/discovery/icons.mjs +275 -0
  319. package/scripts/build/registry/discovery/index.mjs +1 -0
  320. package/scripts/build/registry/generators/block-registry.mjs +203 -55
  321. package/scripts/build/registry/generators/feature-registry.mjs +9 -2
  322. package/scripts/build/registry/generators/icon-registry.mjs +56 -0
  323. package/scripts/build/registry/generators/identifier.mjs +28 -0
  324. package/scripts/build/registry/generators/index.mjs +1 -0
  325. package/scripts/build/registry/generators/plugin-registry.mjs +4 -3
  326. package/scripts/build/registry/generators/scheduled-actions-registry.mjs +4 -2
  327. package/scripts/build/registry/generators/template-registry.mjs +49 -41
  328. package/scripts/build/registry/generators/theme-registry.mjs +6 -7
  329. package/scripts/build/registry/post-build/page-generator.mjs +380 -59
  330. package/scripts/build/registry.mjs +9 -1
  331. package/templates/app/(auth)/forgot-password/page.tsx +2 -2
  332. package/templates/app/(auth)/reset-password/page.tsx +6 -6
  333. package/templates/app/403/page.tsx +6 -6
  334. package/templates/app/api/v1/teams/switch/route.ts +24 -5
  335. package/templates/app/dashboard/(main)/[entity]/layout.tsx +9 -7
  336. package/templates/app/dashboard/(main)/layout.tsx +4 -3
  337. package/templates/app/dashboard/settings/api-keys/page.tsx +36 -34
  338. package/templates/app/dashboard/settings/profile/page.tsx +7 -6
  339. package/templates/app/dashboard/settings/security/page.tsx +5 -4
  340. package/templates/app/layout.ppr.tsx +8 -8
  341. package/templates/app/layout.tsx +12 -17
  342. package/templates/blocks/cta-section/component.tsx +5 -2
  343. package/templates/blocks/cta-section/config.ts +3 -2
  344. package/templates/blocks/cta-section/fields.ts +2 -2
  345. package/templates/blocks/cta-section/schema.ts +2 -2
  346. package/templates/blocks/features-grid/component.tsx +10 -2
  347. package/templates/blocks/features-grid/config.ts +3 -2
  348. package/templates/blocks/features-grid/fields.ts +2 -2
  349. package/templates/blocks/features-grid/schema.ts +2 -2
  350. package/templates/blocks/hero/component.tsx +6 -3
  351. package/templates/blocks/hero/config.ts +3 -2
  352. package/templates/blocks/hero/fields.ts +2 -2
  353. package/templates/blocks/hero/schema.ts +2 -2
  354. package/templates/blocks/testimonials/component.tsx +6 -3
  355. package/templates/blocks/testimonials/config.ts +3 -2
  356. package/templates/blocks/testimonials/fields.ts +2 -2
  357. package/templates/blocks/testimonials/schema.ts +2 -2
  358. package/templates/blocks/text-content/component.tsx +7 -3
  359. package/templates/blocks/text-content/config.ts +3 -2
  360. package/templates/blocks/text-content/fields.ts +2 -2
  361. package/templates/blocks/text-content/schema.ts +2 -2
  362. package/templates/contents/themes/starter/blocks/cta-section/component.tsx +102 -0
  363. package/templates/contents/themes/starter/blocks/cta-section/config.ts +11 -0
  364. package/templates/contents/themes/starter/blocks/cta-section/fields.ts +89 -0
  365. package/templates/contents/themes/starter/blocks/cta-section/index.ts +6 -0
  366. package/templates/contents/themes/starter/blocks/cta-section/schema.ts +32 -0
  367. package/templates/contents/themes/starter/blocks/cta-section/thumbnail.png +0 -0
  368. package/templates/contents/themes/starter/blocks/features-grid/component.tsx +119 -0
  369. package/templates/contents/themes/starter/blocks/features-grid/config.ts +11 -0
  370. package/templates/contents/themes/starter/blocks/features-grid/fields.ts +97 -0
  371. package/templates/contents/themes/starter/blocks/features-grid/index.ts +6 -0
  372. package/templates/contents/themes/starter/blocks/features-grid/schema.ts +40 -0
  373. package/templates/contents/themes/starter/blocks/features-grid/thumbnail.png +0 -0
  374. package/templates/contents/themes/starter/blocks/hero/component.tsx +105 -0
  375. package/templates/contents/themes/starter/blocks/hero/config.ts +11 -0
  376. package/templates/contents/themes/starter/blocks/hero/fields.ts +60 -0
  377. package/templates/contents/themes/starter/blocks/hero/index.ts +6 -0
  378. package/templates/contents/themes/starter/blocks/hero/schema.ts +33 -0
  379. package/templates/contents/themes/starter/blocks/hero/thumbnail.png +0 -0
  380. package/templates/contents/themes/starter/blocks/testimonials/component.tsx +116 -0
  381. package/templates/contents/themes/starter/blocks/testimonials/config.ts +11 -0
  382. package/templates/contents/themes/starter/blocks/testimonials/fields.ts +105 -0
  383. package/templates/contents/themes/starter/blocks/testimonials/index.ts +6 -0
  384. package/templates/contents/themes/starter/blocks/testimonials/schema.ts +41 -0
  385. package/templates/contents/themes/starter/blocks/testimonials/thumbnail.png +0 -0
  386. package/templates/contents/themes/starter/blocks/text-content/component.tsx +100 -0
  387. package/templates/contents/themes/starter/blocks/text-content/config.ts +11 -0
  388. package/templates/contents/themes/starter/blocks/text-content/fields.ts +88 -0
  389. package/templates/contents/themes/starter/blocks/text-content/index.ts +6 -0
  390. package/templates/contents/themes/starter/blocks/text-content/schema.ts +30 -0
  391. package/templates/contents/themes/starter/blocks/text-content/thumbnail.png +0 -0
  392. package/templates/contents/themes/starter/config/app.config.ts +8 -0
  393. package/templates/contents/themes/starter/entities/tasks/tasks.config.ts +7 -0
  394. package/templates/contents/themes/starter/tests/cypress/e2e/_utils/devtools/access.cy.ts +9 -1
  395. package/templates/contents/themes/starter/tests/cypress/e2e/api/entities/tasks/TaskAPIController.js +2 -2
  396. package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +45 -38
  397. package/templates/contents/themes/starter/tests/cypress/fixtures/entities.json +16 -0
  398. package/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +3 -1
  399. package/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +3 -3
  400. package/templates/contents/themes/starter/tests/cypress/src/entities/PagesPOM.ts +137 -0
  401. package/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +2 -0
  402. package/templates/contents/themes/starter/tests/cypress/src/index.ts +1 -0
  403. package/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +42 -11
  404. package/templates/contents/themes/starter/tests/cypress/support/commands.ts +29 -18
  405. package/templates/features/blog/blocks/post-content/component.tsx +5 -1
  406. package/templates/features/blog/blocks/post-content/schema.ts +1 -1
  407. package/templates/features/pages/blocks/hero/component.tsx +3 -0
  408. package/templates/features/pages/blocks/hero/schema.ts +1 -1
  409. package/templates/proxy.ts +138 -53
  410. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.client.ts +9 -0
  411. package/tests/jest/__mocks__/@nextsparkjs/registries/block-registry.lazy.ts +5 -0
  412. package/tests/jest/__mocks__/@nextsparkjs/registries/icon-registry.ts +42 -0
  413. package/tests/jest/__mocks__/next-intl-server.js +85 -5
  414. package/tests/jest/__mocks__/next-server.js +32 -0
@@ -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'
@@ -85,4 +85,5 @@ export {
85
85
  loginAsViewer,
86
86
  loginAsDefaultDeveloper,
87
87
  loginAsDefaultSuperadmin,
88
+ hasSeededTeamRoleUser,
88
89
  } from './session-helpers'
@@ -39,19 +39,50 @@ const SUPERADMIN_PASSWORD = Cypress.env('SUPERADMIN_PASSWORD') || 'Pandora1234'
39
39
  const TEST_PASSWORD = Cypress.env('TEST_PASSWORD') || 'Test1234'
40
40
 
41
41
  /**
42
- * Default Theme Test Users
43
- * Teams: Everpoint Labs, Ironvale Global, Riverstone Ventures
42
+ * Team-role test users.
44
43
  *
45
- * Note: These are fallback demo users. For selector tests, use CORE_USER (developer).
44
+ * The starter seeds exactly two users (core migration 090): superadmin and
45
+ * developer. The names below come from the default theme's richer sample data,
46
+ * and every one of them has to be pointed at a real user through the matching
47
+ * env var before the specs that use it can run.
48
+ *
49
+ * OWNER is the exception, and it matters: it is what the CRUD specs log in as,
50
+ * so its fallback is the user the starter actually creates — superadmin owns
51
+ * the initial team. Leaving it on a name with no row made the whole UAT suite
52
+ * fail in session setup, before a single assertion.
46
53
  */
47
54
  export const DEFAULT_THEME_USERS = {
48
- OWNER: Cypress.env('OWNER_EMAIL') || 'carlos.mendoza@nextspark.dev',
55
+ OWNER: Cypress.env('OWNER_EMAIL') || 'superadmin@nextspark.dev',
49
56
  ADMIN: Cypress.env('ADMIN_EMAIL') || 'james.wilson@nextspark.dev',
50
57
  MEMBER: Cypress.env('MEMBER_EMAIL') || 'emily.johnson@nextspark.dev',
51
58
  EDITOR: Cypress.env('EDITOR_EMAIL') || 'diego.ramirez@nextspark.dev',
52
59
  VIEWER: Cypress.env('VIEWER_EMAIL') || 'sarah.davis@nextspark.dev',
53
60
  } as const
54
61
 
62
+ /**
63
+ * The password that goes with OWNER.
64
+ *
65
+ * OWNER falls back to the seeded superadmin, and that user's password is the
66
+ * superadmin one, not the demo-user one. A theme that points OWNER at a user of
67
+ * its own sets OWNER_EMAIL, and then the demo password is the right default.
68
+ */
69
+ const OWNER_PASSWORD =
70
+ Cypress.env('OWNER_PASSWORD') ||
71
+ (Cypress.env('OWNER_EMAIL') ? TEST_PASSWORD : SUPERADMIN_PASSWORD)
72
+
73
+ /**
74
+ * Whether a non-owner team role points at a user that exists.
75
+ *
76
+ * Only OWNER has a seeded fallback. The other four keep the default theme's
77
+ * names, which resolve to nothing here until the theme seeds them and sets the
78
+ * matching env var. A spec that needs one guards on this instead of failing in
79
+ * session setup, where the error is a ten-second timeout on the Dev Keyring
80
+ * rather than "this user does not exist".
81
+ */
82
+ export function hasSeededTeamRoleUser(role: 'ADMIN' | 'MEMBER' | 'EDITOR' | 'VIEWER'): boolean {
83
+ return Boolean(Cypress.env(`${role}_EMAIL`))
84
+ }
85
+
55
86
  /**
56
87
  * Core System Users (from core/migrations/090_sample_data.sql)
57
88
  * These users have special global roles, not team-based roles
@@ -164,7 +195,7 @@ function apiLogin(email: string, password: string = TEST_PASSWORD): Cypress.Chai
164
195
  */
165
196
  export function loginAsDefaultOwner() {
166
197
  cy.session('default-owner-session', () => {
167
- apiLogin(DEFAULT_THEME_USERS.OWNER).then((apiLoginSucceeded) => {
198
+ apiLogin(DEFAULT_THEME_USERS.OWNER, OWNER_PASSWORD).then((apiLoginSucceeded) => {
168
199
  // If API login succeeded, we need to visit a page before setting localStorage
169
200
  if (apiLoginSucceeded) {
170
201
  cy.visit('/dashboard', { timeout: 60000 })
@@ -389,22 +420,22 @@ export function getThemeUsers() {
389
420
  */
390
421
  export const BILLING_TEAMS = {
391
422
  FREE: {
392
- teamId: 'team-personal-carlos-001',
423
+ teamId: Cypress.env('BILLING_FREE_TEAM_ID') || 'team-personal-carlos-001',
393
424
  name: 'Carlos Personal',
394
425
  planSlug: 'free',
395
- owner: 'carlos.mendoza@nextspark.dev'
426
+ owner: Cypress.env('BILLING_FREE_OWNER') || 'carlos.mendoza@nextspark.dev'
396
427
  },
397
428
  PRO: {
398
- teamId: 'team-everpoint-001',
429
+ teamId: Cypress.env('BILLING_PRO_TEAM_ID') || 'team-everpoint-001',
399
430
  name: 'Everpoint Labs',
400
431
  planSlug: 'pro',
401
- owner: 'carlos.mendoza@nextspark.dev'
432
+ owner: Cypress.env('BILLING_PRO_OWNER') || 'carlos.mendoza@nextspark.dev'
402
433
  },
403
434
  ENTERPRISE: {
404
- teamId: 'team-ironvale-002',
435
+ teamId: Cypress.env('BILLING_ENTERPRISE_TEAM_ID') || 'team-ironvale-002',
405
436
  name: 'Ironvale Global',
406
437
  planSlug: 'enterprise',
407
- owner: 'ana.garcia@nextspark.dev'
438
+ owner: Cypress.env('BILLING_ENTERPRISE_OWNER') || 'ana.garcia@nextspark.dev'
408
439
  }
409
440
  } as const
410
441
 
@@ -140,18 +140,37 @@ Cypress.Commands.add('logout', () => {
140
140
  })
141
141
 
142
142
  /**
143
- * Create a task via API
143
+ * The headers an entity API call needs, `x-team-id` included.
144
+ *
145
+ * The app writes `activeTeamId` into localStorage while the dashboard loads, so
146
+ * a command that runs before any dashboard visit — a `beforeEach` seeding data,
147
+ * typically — finds nothing there. Without the header the API answers 400
148
+ * TEAM_CONTEXT_REQUIRED, and since these requests do not fail on status the
149
+ * seed silently does nothing: the failure only surfaces later, as an assertion
150
+ * about data that was never created. Asking the API for the team is what makes
151
+ * the command work from any starting point.
144
152
  */
145
- Cypress.Commands.add('createTask', (data) => {
153
+ function apiHeaders(): Cypress.Chainable<Record<string, string>> {
154
+ const base: Record<string, string> = { 'Content-Type': 'application/json' }
155
+
146
156
  return cy.window().then((win) => {
147
- const teamId = win.localStorage.getItem('activeTeamId')
148
- const headers: Record<string, string> = {
149
- 'Content-Type': 'application/json',
150
- }
151
- if (teamId) {
152
- headers['x-team-id'] = teamId
153
- }
157
+ const stored = win.localStorage.getItem('activeTeamId')
158
+ if (stored) return cy.wrap({ ...base, 'x-team-id': stored })
159
+
160
+ return cy
161
+ .request({ method: 'GET', url: '/api/v1/teams', failOnStatusCode: false })
162
+ .then((response) => {
163
+ const teamId = response.body?.data?.[0]?.id
164
+ return cy.wrap(teamId ? { ...base, 'x-team-id': teamId } : base)
165
+ })
166
+ })
167
+ }
154
168
 
169
+ /**
170
+ * Create a task via API
171
+ */
172
+ Cypress.Commands.add('createTask', (data) => {
173
+ return apiHeaders().then((headers) => {
155
174
  return cy.request({
156
175
  method: 'POST',
157
176
  url: '/api/v1/tasks',
@@ -171,15 +190,7 @@ Cypress.Commands.add('createTask', (data) => {
171
190
  * Delete a task via API
172
191
  */
173
192
  Cypress.Commands.add('deleteTask', (id: string) => {
174
- return cy.window().then((win) => {
175
- const teamId = win.localStorage.getItem('activeTeamId')
176
- const headers: Record<string, string> = {
177
- 'Content-Type': 'application/json',
178
- }
179
- if (teamId) {
180
- headers['x-team-id'] = teamId
181
- }
182
-
193
+ return apiHeaders().then((headers) => {
183
194
  return cy.request({
184
195
  method: 'DELETE',
185
196
  url: `/api/v1/tasks/${id}`,
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { cn } from '@nextsparkjs/core/lib/utils'
3
3
  import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
4
+ import { sanitizeBlockHtml } from '@nextsparkjs/core/lib/blocks/sanitize-html'
4
5
  import { sel } from '../../lib/selectors'
5
6
  import type { PostContentBlockProps } from './schema'
6
7
 
@@ -171,7 +172,7 @@ export function PostContentBlock({
171
172
  // Em/italic
172
173
  'prose-em:italic'
173
174
  )}
174
- dangerouslySetInnerHTML={{ __html: content }}
175
+ dangerouslySetInnerHTML={{ __html: sanitizeBlockHtml(content) }}
175
176
  />
176
177
 
177
178
  {/* Optional Divider after content */}
@@ -195,3 +196,6 @@ export function PostContentBlock({
195
196
  </section>
196
197
  )
197
198
  }
199
+
200
+ // The generated block registry imports this as the module's default.
201
+ export default PostContentBlock
@@ -41,6 +41,6 @@ export const postContentSpecificSchema = z.object({
41
41
  * Complete Post Content Block Schema
42
42
  * Note: postContentSpecificSchema.content overrides baseBlockSchema.content
43
43
  */
44
- export const schema = baseBlockSchema.merge(postContentSpecificSchema)
44
+ export const schema = baseBlockSchema.extend(postContentSpecificSchema.shape)
45
45
 
46
46
  export type PostContentBlockProps = z.infer<typeof schema>
@@ -113,3 +113,6 @@ export function HeroBlock({
113
113
  </section>
114
114
  )
115
115
  }
116
+
117
+ // The generated block registry imports this as the module's default.
118
+ export default HeroBlock
@@ -27,7 +27,7 @@ export const heroSpecificSchema = z.object({
27
27
  * Complete Hero Block Schema
28
28
  * Combines base fields + hero-specific fields
29
29
  */
30
- export const schema = baseBlockSchema.merge(heroSpecificSchema)
30
+ export const schema = baseBlockSchema.extend(heroSpecificSchema.shape)
31
31
 
32
32
  export type HeroBlockProps = z.infer<typeof schema>
33
33