@nextsparkjs/core 0.1.0-beta.84 → 0.1.0-beta.85

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 (356) hide show
  1. package/dist/styles/classes.json +1 -1
  2. package/dist/templates/app/(auth)/forgot-password/page.tsx +216 -0
  3. package/dist/templates/app/(auth)/layout.tsx +51 -0
  4. package/dist/templates/app/(auth)/login/page.tsx +21 -0
  5. package/dist/templates/app/(auth)/reset-password/page.tsx +212 -0
  6. package/dist/templates/app/(auth)/signup/page.tsx +21 -0
  7. package/dist/templates/app/(auth)/verify-email/page.tsx +190 -0
  8. package/dist/templates/app/(public)/[...slug]/page.tsx +378 -0
  9. package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
  10. package/dist/templates/app/(public)/docs/layout.tsx +25 -0
  11. package/dist/templates/app/(public)/docs/page.tsx +81 -0
  12. package/dist/templates/app/(public)/layout.tsx +41 -0
  13. package/dist/templates/app/(public)/page.tsx +19 -0
  14. package/dist/templates/app/403/page.tsx +89 -0
  15. package/dist/templates/app/api/auth/[...all]/route.ts +78 -0
  16. package/dist/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
  17. package/dist/templates/app/api/csp-report/route.ts +175 -0
  18. package/dist/templates/app/api/devtools/config/entities/route.ts +108 -0
  19. package/dist/templates/app/api/devtools/config/theme/route.ts +66 -0
  20. package/dist/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
  21. package/dist/templates/app/api/devtools/tests/route.ts +134 -0
  22. package/dist/templates/app/api/health/route.ts +29 -0
  23. package/dist/templates/app/api/internal/user-metadata/route.ts +36 -0
  24. package/dist/templates/app/api/superadmin/subscriptions/route.ts +310 -0
  25. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
  26. package/dist/templates/app/api/superadmin/teams/route.ts +188 -0
  27. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
  28. package/dist/templates/app/api/superadmin/users/route.ts +323 -0
  29. package/dist/templates/app/api/user/delete-account/route.ts +55 -0
  30. package/dist/templates/app/api/user/plan-flags/route.ts +283 -0
  31. package/dist/templates/app/api/user/profile/route.ts +133 -0
  32. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
  33. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
  34. package/dist/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
  35. package/dist/templates/app/api/v1/[entity]/docs.md +369 -0
  36. package/dist/templates/app/api/v1/[entity]/presets.ts +194 -0
  37. package/dist/templates/app/api/v1/[entity]/route.ts +31 -0
  38. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
  39. package/dist/templates/app/api/v1/api-keys/docs.md +101 -0
  40. package/dist/templates/app/api/v1/api-keys/presets.ts +31 -0
  41. package/dist/templates/app/api/v1/api-keys/route.ts +250 -0
  42. package/dist/templates/app/api/v1/auth/docs.md +184 -0
  43. package/dist/templates/app/api/v1/auth/presets.ts +44 -0
  44. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
  45. package/dist/templates/app/api/v1/billing/cancel/route.ts +206 -0
  46. package/dist/templates/app/api/v1/billing/change-plan/route.ts +97 -0
  47. package/dist/templates/app/api/v1/billing/check-action/route.ts +81 -0
  48. package/dist/templates/app/api/v1/billing/checkout/route.ts +124 -0
  49. package/dist/templates/app/api/v1/billing/docs.md +209 -0
  50. package/dist/templates/app/api/v1/billing/plans/route.ts +85 -0
  51. package/dist/templates/app/api/v1/billing/portal/route.ts +90 -0
  52. package/dist/templates/app/api/v1/billing/presets.ts +121 -0
  53. package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
  54. package/dist/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
  55. package/dist/templates/app/api/v1/blocks/docs.md +173 -0
  56. package/dist/templates/app/api/v1/blocks/presets.ts +121 -0
  57. package/dist/templates/app/api/v1/blocks/route.ts +45 -0
  58. package/dist/templates/app/api/v1/blocks/validate/route.ts +45 -0
  59. package/dist/templates/app/api/v1/cron/docs.md +116 -0
  60. package/dist/templates/app/api/v1/cron/presets.ts +26 -0
  61. package/dist/templates/app/api/v1/cron/process/route.ts +108 -0
  62. package/dist/templates/app/api/v1/devtools/blocks/route.ts +82 -0
  63. package/dist/templates/app/api/v1/devtools/docs/route.ts +150 -0
  64. package/dist/templates/app/api/v1/devtools/docs.md +204 -0
  65. package/dist/templates/app/api/v1/devtools/features/route.ts +61 -0
  66. package/dist/templates/app/api/v1/devtools/flows/route.ts +61 -0
  67. package/dist/templates/app/api/v1/devtools/presets.ts +113 -0
  68. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
  69. package/dist/templates/app/api/v1/devtools/testing/route.ts +82 -0
  70. package/dist/templates/app/api/v1/media/docs.md +117 -0
  71. package/dist/templates/app/api/v1/media/presets.ts +24 -0
  72. package/dist/templates/app/api/v1/media/upload/route.ts +150 -0
  73. package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
  74. package/dist/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
  75. package/dist/templates/app/api/v1/plugin/docs.md +79 -0
  76. package/dist/templates/app/api/v1/plugin/presets.ts +21 -0
  77. package/dist/templates/app/api/v1/plugin/route.ts +96 -0
  78. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
  79. package/dist/templates/app/api/v1/post-categories/docs.md +134 -0
  80. package/dist/templates/app/api/v1/post-categories/presets.ts +78 -0
  81. package/dist/templates/app/api/v1/post-categories/route.ts +119 -0
  82. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
  83. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
  84. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
  85. package/dist/templates/app/api/v1/team-invitations/docs.md +88 -0
  86. package/dist/templates/app/api/v1/team-invitations/presets.ts +43 -0
  87. package/dist/templates/app/api/v1/team-invitations/route.ts +114 -0
  88. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
  89. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
  90. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
  91. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
  92. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
  93. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
  94. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
  95. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
  96. package/dist/templates/app/api/v1/teams/docs.md +320 -0
  97. package/dist/templates/app/api/v1/teams/presets.ts +178 -0
  98. package/dist/templates/app/api/v1/teams/route.ts +293 -0
  99. package/dist/templates/app/api/v1/teams/switch/route.ts +88 -0
  100. package/dist/templates/app/api/v1/theme/[...path]/route.ts +361 -0
  101. package/dist/templates/app/api/v1/theme/docs.md +74 -0
  102. package/dist/templates/app/api/v1/theme/presets.ts +21 -0
  103. package/dist/templates/app/api/v1/theme/route.ts +96 -0
  104. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
  105. package/dist/templates/app/api/v1/users/[id]/route.ts +302 -0
  106. package/dist/templates/app/api/v1/users/docs.md +93 -0
  107. package/dist/templates/app/api/v1/users/presets.ts +59 -0
  108. package/dist/templates/app/api/v1/users/route.ts +197 -0
  109. package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
  110. package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
  111. package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
  112. package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
  113. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
  114. package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
  115. package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
  116. package/dist/templates/app/dashboard/(main)/layout.tsx +98 -0
  117. package/dist/templates/app/dashboard/(main)/loading.tsx +5 -0
  118. package/dist/templates/app/dashboard/(main)/page.tsx +201 -0
  119. package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
  120. package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
  121. package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
  122. package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
  123. package/dist/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
  124. package/dist/templates/app/dashboard/features/analytics/page.tsx +35 -0
  125. package/dist/templates/app/dashboard/features/automation/page.tsx +35 -0
  126. package/dist/templates/app/dashboard/features/layout.tsx +13 -0
  127. package/dist/templates/app/dashboard/features/loading.tsx +5 -0
  128. package/dist/templates/app/dashboard/features/webhooks/page.tsx +35 -0
  129. package/dist/templates/app/dashboard/layout.tsx +86 -0
  130. package/dist/templates/app/dashboard/permission-denied/page.tsx +29 -0
  131. package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
  132. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
  133. package/dist/templates/app/dashboard/settings/billing/loading.tsx +5 -0
  134. package/dist/templates/app/dashboard/settings/billing/page.tsx +284 -0
  135. package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
  136. package/dist/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
  137. package/dist/templates/app/dashboard/settings/invoices/page.tsx +82 -0
  138. package/dist/templates/app/dashboard/settings/layout.tsx +151 -0
  139. package/dist/templates/app/dashboard/settings/loading.tsx +5 -0
  140. package/dist/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
  141. package/dist/templates/app/dashboard/settings/notifications/page.tsx +462 -0
  142. package/dist/templates/app/dashboard/settings/page.tsx +92 -0
  143. package/dist/templates/app/dashboard/settings/password/loading.tsx +5 -0
  144. package/dist/templates/app/dashboard/settings/password/page.tsx +306 -0
  145. package/dist/templates/app/dashboard/settings/plans/loading.tsx +5 -0
  146. package/dist/templates/app/dashboard/settings/plans/page.tsx +40 -0
  147. package/dist/templates/app/dashboard/settings/profile/loading.tsx +5 -0
  148. package/dist/templates/app/dashboard/settings/profile/page.tsx +686 -0
  149. package/dist/templates/app/dashboard/settings/security/loading.tsx +5 -0
  150. package/dist/templates/app/dashboard/settings/security/page.tsx +505 -0
  151. package/dist/templates/app/dashboard/settings/teams/loading.tsx +5 -0
  152. package/dist/templates/app/dashboard/settings/teams/page.tsx +272 -0
  153. package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
  154. package/dist/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
  155. package/dist/templates/app/devtools/blocks/page.tsx +31 -0
  156. package/dist/templates/app/devtools/config/page.tsx +31 -0
  157. package/dist/templates/app/devtools/features/page.tsx +31 -0
  158. package/dist/templates/app/devtools/flows/page.tsx +31 -0
  159. package/dist/templates/app/devtools/layout.tsx +58 -0
  160. package/dist/templates/app/devtools/page.tsx +121 -0
  161. package/dist/templates/app/devtools/scheduled-actions/page.tsx +157 -0
  162. package/dist/templates/app/devtools/style/page.tsx +330 -0
  163. package/dist/templates/app/devtools/tags/page.tsx +31 -0
  164. package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
  165. package/dist/templates/app/favicon.ico +0 -0
  166. package/dist/templates/app/globals.css +12 -0
  167. package/dist/templates/app/layout.tsx +96 -0
  168. package/dist/templates/app/public/page.tsx +30 -0
  169. package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
  170. package/dist/templates/app/superadmin/docs/page.tsx +75 -0
  171. package/dist/templates/app/superadmin/layout.tsx +67 -0
  172. package/dist/templates/app/superadmin/page.tsx +149 -0
  173. package/dist/templates/app/superadmin/subscriptions/page.tsx +655 -0
  174. package/dist/templates/app/superadmin/team-roles/page.tsx +493 -0
  175. package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
  176. package/dist/templates/app/superadmin/teams/page.tsx +302 -0
  177. package/dist/templates/app/superadmin/users/[userId]/page.tsx +548 -0
  178. package/dist/templates/app/superadmin/users/page.tsx +528 -0
  179. package/package.json +2 -2
  180. package/templates/app/(auth)/forgot-password/page.tsx +216 -0
  181. package/templates/app/(auth)/layout.tsx +51 -0
  182. package/templates/app/(auth)/login/page.tsx +21 -0
  183. package/templates/app/(auth)/reset-password/page.tsx +212 -0
  184. package/templates/app/(auth)/signup/page.tsx +21 -0
  185. package/templates/app/(auth)/verify-email/page.tsx +190 -0
  186. package/templates/app/(public)/[...slug]/page.tsx +378 -0
  187. package/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
  188. package/templates/app/(public)/docs/layout.tsx +25 -0
  189. package/templates/app/(public)/docs/page.tsx +81 -0
  190. package/templates/app/(public)/layout.tsx +41 -0
  191. package/templates/app/(public)/page.tsx +19 -0
  192. package/templates/app/403/page.tsx +89 -0
  193. package/templates/app/api/auth/[...all]/route.ts +78 -0
  194. package/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
  195. package/templates/app/api/csp-report/route.ts +175 -0
  196. package/templates/app/api/devtools/config/entities/route.ts +108 -0
  197. package/templates/app/api/devtools/config/theme/route.ts +66 -0
  198. package/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
  199. package/templates/app/api/devtools/tests/route.ts +134 -0
  200. package/templates/app/api/health/route.ts +29 -0
  201. package/templates/app/api/internal/user-metadata/route.ts +36 -0
  202. package/templates/app/api/superadmin/subscriptions/route.ts +310 -0
  203. package/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
  204. package/templates/app/api/superadmin/teams/route.ts +188 -0
  205. package/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
  206. package/templates/app/api/superadmin/users/route.ts +323 -0
  207. package/templates/app/api/user/delete-account/route.ts +55 -0
  208. package/templates/app/api/user/plan-flags/route.ts +283 -0
  209. package/templates/app/api/user/profile/route.ts +133 -0
  210. package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
  211. package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
  212. package/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
  213. package/templates/app/api/v1/[entity]/docs.md +369 -0
  214. package/templates/app/api/v1/[entity]/presets.ts +194 -0
  215. package/templates/app/api/v1/[entity]/route.ts +31 -0
  216. package/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
  217. package/templates/app/api/v1/api-keys/docs.md +101 -0
  218. package/templates/app/api/v1/api-keys/presets.ts +31 -0
  219. package/templates/app/api/v1/api-keys/route.ts +250 -0
  220. package/templates/app/api/v1/auth/docs.md +184 -0
  221. package/templates/app/api/v1/auth/presets.ts +44 -0
  222. package/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
  223. package/templates/app/api/v1/billing/cancel/route.ts +206 -0
  224. package/templates/app/api/v1/billing/change-plan/route.ts +97 -0
  225. package/templates/app/api/v1/billing/check-action/route.ts +81 -0
  226. package/templates/app/api/v1/billing/checkout/route.ts +124 -0
  227. package/templates/app/api/v1/billing/docs.md +209 -0
  228. package/templates/app/api/v1/billing/plans/route.ts +85 -0
  229. package/templates/app/api/v1/billing/portal/route.ts +90 -0
  230. package/templates/app/api/v1/billing/presets.ts +121 -0
  231. package/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
  232. package/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
  233. package/templates/app/api/v1/blocks/docs.md +173 -0
  234. package/templates/app/api/v1/blocks/presets.ts +121 -0
  235. package/templates/app/api/v1/blocks/route.ts +45 -0
  236. package/templates/app/api/v1/blocks/validate/route.ts +45 -0
  237. package/templates/app/api/v1/cron/docs.md +116 -0
  238. package/templates/app/api/v1/cron/presets.ts +26 -0
  239. package/templates/app/api/v1/cron/process/route.ts +108 -0
  240. package/templates/app/api/v1/devtools/blocks/route.ts +82 -0
  241. package/templates/app/api/v1/devtools/docs/route.ts +150 -0
  242. package/templates/app/api/v1/devtools/docs.md +204 -0
  243. package/templates/app/api/v1/devtools/features/route.ts +61 -0
  244. package/templates/app/api/v1/devtools/flows/route.ts +61 -0
  245. package/templates/app/api/v1/devtools/presets.ts +113 -0
  246. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
  247. package/templates/app/api/v1/devtools/testing/route.ts +82 -0
  248. package/templates/app/api/v1/media/docs.md +117 -0
  249. package/templates/app/api/v1/media/presets.ts +24 -0
  250. package/templates/app/api/v1/media/upload/route.ts +150 -0
  251. package/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
  252. package/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
  253. package/templates/app/api/v1/plugin/docs.md +79 -0
  254. package/templates/app/api/v1/plugin/presets.ts +21 -0
  255. package/templates/app/api/v1/plugin/route.ts +96 -0
  256. package/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
  257. package/templates/app/api/v1/post-categories/docs.md +134 -0
  258. package/templates/app/api/v1/post-categories/presets.ts +78 -0
  259. package/templates/app/api/v1/post-categories/route.ts +119 -0
  260. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
  261. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
  262. package/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
  263. package/templates/app/api/v1/team-invitations/docs.md +88 -0
  264. package/templates/app/api/v1/team-invitations/presets.ts +43 -0
  265. package/templates/app/api/v1/team-invitations/route.ts +114 -0
  266. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
  267. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
  268. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
  269. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
  270. package/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
  271. package/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
  272. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
  273. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
  274. package/templates/app/api/v1/teams/docs.md +320 -0
  275. package/templates/app/api/v1/teams/presets.ts +178 -0
  276. package/templates/app/api/v1/teams/route.ts +293 -0
  277. package/templates/app/api/v1/teams/switch/route.ts +88 -0
  278. package/templates/app/api/v1/theme/[...path]/route.ts +361 -0
  279. package/templates/app/api/v1/theme/docs.md +74 -0
  280. package/templates/app/api/v1/theme/presets.ts +21 -0
  281. package/templates/app/api/v1/theme/route.ts +96 -0
  282. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
  283. package/templates/app/api/v1/users/[id]/route.ts +302 -0
  284. package/templates/app/api/v1/users/docs.md +93 -0
  285. package/templates/app/api/v1/users/presets.ts +59 -0
  286. package/templates/app/api/v1/users/route.ts +197 -0
  287. package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
  288. package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
  289. package/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
  290. package/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
  291. package/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
  292. package/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
  293. package/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
  294. package/templates/app/dashboard/(main)/layout.tsx +98 -0
  295. package/templates/app/dashboard/(main)/loading.tsx +5 -0
  296. package/templates/app/dashboard/(main)/page.tsx +201 -0
  297. package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
  298. package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
  299. package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
  300. package/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
  301. package/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
  302. package/templates/app/dashboard/features/analytics/page.tsx +35 -0
  303. package/templates/app/dashboard/features/automation/page.tsx +35 -0
  304. package/templates/app/dashboard/features/layout.tsx +13 -0
  305. package/templates/app/dashboard/features/loading.tsx +5 -0
  306. package/templates/app/dashboard/features/webhooks/page.tsx +35 -0
  307. package/templates/app/dashboard/layout.tsx +86 -0
  308. package/templates/app/dashboard/permission-denied/page.tsx +29 -0
  309. package/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
  310. package/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
  311. package/templates/app/dashboard/settings/billing/loading.tsx +5 -0
  312. package/templates/app/dashboard/settings/billing/page.tsx +284 -0
  313. package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
  314. package/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
  315. package/templates/app/dashboard/settings/invoices/page.tsx +82 -0
  316. package/templates/app/dashboard/settings/layout.tsx +151 -0
  317. package/templates/app/dashboard/settings/loading.tsx +5 -0
  318. package/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
  319. package/templates/app/dashboard/settings/notifications/page.tsx +462 -0
  320. package/templates/app/dashboard/settings/page.tsx +92 -0
  321. package/templates/app/dashboard/settings/password/loading.tsx +5 -0
  322. package/templates/app/dashboard/settings/password/page.tsx +306 -0
  323. package/templates/app/dashboard/settings/plans/loading.tsx +5 -0
  324. package/templates/app/dashboard/settings/plans/page.tsx +40 -0
  325. package/templates/app/dashboard/settings/profile/loading.tsx +5 -0
  326. package/templates/app/dashboard/settings/profile/page.tsx +686 -0
  327. package/templates/app/dashboard/settings/security/loading.tsx +5 -0
  328. package/templates/app/dashboard/settings/security/page.tsx +505 -0
  329. package/templates/app/dashboard/settings/teams/loading.tsx +5 -0
  330. package/templates/app/dashboard/settings/teams/page.tsx +272 -0
  331. package/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
  332. package/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
  333. package/templates/app/devtools/blocks/page.tsx +31 -0
  334. package/templates/app/devtools/config/page.tsx +31 -0
  335. package/templates/app/devtools/features/page.tsx +31 -0
  336. package/templates/app/devtools/flows/page.tsx +31 -0
  337. package/templates/app/devtools/layout.tsx +58 -0
  338. package/templates/app/devtools/page.tsx +121 -0
  339. package/templates/app/devtools/scheduled-actions/page.tsx +157 -0
  340. package/templates/app/devtools/style/page.tsx +330 -0
  341. package/templates/app/devtools/tags/page.tsx +31 -0
  342. package/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
  343. package/templates/app/favicon.ico +0 -0
  344. package/templates/app/globals.css +12 -0
  345. package/templates/app/layout.tsx +96 -0
  346. package/templates/app/public/page.tsx +30 -0
  347. package/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
  348. package/templates/app/superadmin/docs/page.tsx +75 -0
  349. package/templates/app/superadmin/layout.tsx +67 -0
  350. package/templates/app/superadmin/page.tsx +149 -0
  351. package/templates/app/superadmin/subscriptions/page.tsx +655 -0
  352. package/templates/app/superadmin/team-roles/page.tsx +493 -0
  353. package/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
  354. package/templates/app/superadmin/teams/page.tsx +302 -0
  355. package/templates/app/superadmin/users/[userId]/page.tsx +548 -0
  356. package/templates/app/superadmin/users/page.tsx +528 -0
@@ -0,0 +1,151 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { usePathname } from 'next/navigation'
5
+ import { notFound } from 'next/navigation'
6
+ import { Button } from '@nextsparkjs/core/components/ui/button'
7
+ import { SettingsSidebar } from '@nextsparkjs/core/components/settings/layouts/SettingsSidebar'
8
+ import { MobileTopBar } from '@nextsparkjs/core/components/dashboard/mobile/MobileTopBar'
9
+ import { MobileBottomNav } from '@nextsparkjs/core/components/dashboard/mobile/MobileBottomNav'
10
+ import { ArrowLeft } from 'lucide-react'
11
+ import { useState, useCallback, useEffect } from 'react'
12
+ import { sel } from '@nextsparkjs/core/selectors'
13
+ import { useTranslations } from 'next-intl'
14
+ import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
15
+ import { isSettingsPageEnabled } from '@nextsparkjs/core/lib/config/config-sync'
16
+
17
+ /**
18
+ * Map of URL paths to settings page keys for validation
19
+ */
20
+ const PATH_TO_PAGE_KEY: Record<string, string> = {
21
+ '/dashboard/settings/profile': 'profile',
22
+ '/dashboard/settings/security': 'security',
23
+ '/dashboard/settings/password': 'password',
24
+ '/dashboard/settings/notifications': 'notifications',
25
+ '/dashboard/settings/api-keys': 'api-keys',
26
+ '/dashboard/settings/billing': 'billing',
27
+ '/dashboard/settings/teams': 'teams',
28
+ '/dashboard/settings/teams/permissions': 'teams',
29
+ '/dashboard/settings/invoices': 'billing',
30
+ }
31
+
32
+ function SettingsLayout({
33
+ children,
34
+ }: {
35
+ children: React.ReactNode
36
+ }) {
37
+ const [statusMessage, setStatusMessage] = useState('')
38
+ const pathname = usePathname()
39
+ const t = useTranslations('settings')
40
+
41
+ // Validate if the current settings page is enabled
42
+ // This prevents access to disabled pages via direct URL
43
+ useEffect(() => {
44
+ const pageKey = PATH_TO_PAGE_KEY[pathname]
45
+
46
+ // If we have a page key mapping, check if it's enabled
47
+ if (pageKey) {
48
+ const enabled = isSettingsPageEnabled(pageKey)
49
+ if (!enabled) {
50
+ notFound()
51
+ }
52
+ }
53
+ // For paths not in the mapping (like /dashboard/settings itself), allow access
54
+ }, [pathname])
55
+
56
+ const handleBackToDashboard = useCallback(() => {
57
+ setStatusMessage(t('navigation.backToDashboard'))
58
+ }, [t])
59
+
60
+ return (
61
+ <>
62
+ {/* MANDATORY: Screen reader announcements */}
63
+ <div
64
+ aria-live="polite"
65
+ aria-atomic="true"
66
+ className="sr-only"
67
+ >
68
+ {statusMessage}
69
+ </div>
70
+
71
+ {/* Mobile only - TopBar */}
72
+ <MobileTopBar />
73
+
74
+ {/* Mobile only - BottomNav */}
75
+ <MobileBottomNav />
76
+
77
+ <div
78
+ className="min-h-screen bg-gradient-to-b from-background to-muted/20"
79
+ data-cy={sel('settings.sidebar.layout.main')}
80
+ >
81
+ <div className="max-w-7xl mx-auto p-4 lg:p-6 pt-16 pb-24 lg:pt-6 lg:pb-6">
82
+ {/* Back Button - Desktop only */}
83
+ <nav
84
+ className="mb-6 hidden lg:block"
85
+ aria-label={t('navigation.ariaLabel')}
86
+ data-cy={sel('settings.sidebar.nav.container')}
87
+ >
88
+ <Link href="/dashboard">
89
+ <Button
90
+ variant="ghost"
91
+ size="sm"
92
+ className="gap-2"
93
+ onClick={handleBackToDashboard}
94
+ aria-label={t('navigation.backToDashboard')}
95
+ data-cy={sel('settings.sidebar.backButton')}
96
+ >
97
+ <ArrowLeft className="h-4 w-4" aria-hidden="true" />
98
+ {t('navigation.backButton')}
99
+ </Button>
100
+ </Link>
101
+ </nav>
102
+
103
+ {/* Header - Desktop only */}
104
+ <header
105
+ className="mb-8 hidden lg:block"
106
+ data-cy={sel('settings.sidebar.layout.header')}
107
+ >
108
+ <h1
109
+ className="text-3xl font-bold"
110
+ id="settings-main-heading"
111
+ >
112
+ {t('layout.title')}
113
+ </h1>
114
+ <p
115
+ className="text-muted-foreground text-sm mt-1"
116
+ >
117
+ {t('layout.description')}
118
+ </p>
119
+ </header>
120
+
121
+ {/* Main Content */}
122
+ <main
123
+ className="flex flex-col lg:flex-row gap-0 lg:gap-8"
124
+ aria-labelledby="settings-main-heading"
125
+ data-cy={sel('settings.sidebar.layout.contentArea')}
126
+ >
127
+ {/* Settings Sidebar - Desktop only */}
128
+ <aside
129
+ className="w-64 hidden lg:block"
130
+ aria-label={t('layout.sidebarLabel')}
131
+ data-cy={sel('settings.sidebar.container')}
132
+ >
133
+ <SettingsSidebar />
134
+ </aside>
135
+
136
+ {/* Content Area */}
137
+ <section
138
+ className="flex-1 w-full"
139
+ aria-label={t('layout.contentLabel')}
140
+ data-cy={sel('settings.sidebar.layout.pageContent')}
141
+ >
142
+ {children}
143
+ </section>
144
+ </main>
145
+ </div>
146
+ </div>
147
+ </>
148
+ )
149
+ }
150
+
151
+ export default getTemplateOrDefaultClient('app/dashboard/settings/layout.tsx', SettingsLayout)
@@ -0,0 +1,5 @@
1
+ import { SkeletonSettingsOverview } from '@nextsparkjs/core/components/ui/skeleton-settings'
2
+
3
+ export default function SettingsLoading() {
4
+ return <SkeletonSettingsOverview />
5
+ }
@@ -0,0 +1,5 @@
1
+ import { SkeletonNotificationsPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
2
+
3
+ export default function NotificationsLoading() {
4
+ return <SkeletonNotificationsPage />
5
+ }
@@ -0,0 +1,462 @@
1
+ 'use client'
2
+
3
+
4
+ import { useState, useCallback, useEffect } from 'react'
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@nextsparkjs/core/components/ui/card'
6
+ import { Badge } from '@nextsparkjs/core/components/ui/badge'
7
+ import { Switch } from '@nextsparkjs/core/components/ui/switch'
8
+ import { Button } from '@nextsparkjs/core/components/ui/button'
9
+ import { toast } from 'sonner'
10
+ import {
11
+ Mail,
12
+ Smartphone,
13
+ MessageSquare,
14
+ Shield,
15
+ Zap,
16
+ Save,
17
+ Loader2
18
+ } from 'lucide-react'
19
+ import { sel } from '@nextsparkjs/core/selectors'
20
+ import { useTranslations } from 'next-intl'
21
+ import { useUserWithMetaSettings } from '@nextsparkjs/core/hooks/useUserSettings'
22
+ import { NotificationsPageSkeleton } from '@nextsparkjs/core/components/settings/SettingsPageSkeleton'
23
+ import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
24
+
25
+ function NotificationsPage() {
26
+ const t = useTranslations('settings')
27
+
28
+ // Hook para manejar user metadata con autenticación de sesión
29
+ const {
30
+ data: userData,
31
+ isLoading: isLoadingUser,
32
+ updateEntity: updateUserMeta,
33
+ isUpdating
34
+ } = useUserWithMetaSettings()
35
+
36
+ // Estado para el master switch de push notifications
37
+ const [pushNotificationsEnabled, setPushNotificationsEnabled] = useState(true)
38
+
39
+ // Estados individuales para cada notificación
40
+ const [notificationSettings, setNotificationSettings] = useState({
41
+ login_alerts: { email: true, push: true },
42
+ password_changes: { email: true, push: true },
43
+ suspicious_activity: { email: true, push: true },
44
+ mentions: { email: true, push: true },
45
+ project_updates: { email: true, push: false },
46
+ team_invites: { email: true, push: true },
47
+ newsletter: { email: false, push: false },
48
+ promotions: { email: false, push: false },
49
+ feature_announcements: { email: true, push: false },
50
+ })
51
+
52
+ const [statusMessage, setStatusMessage] = useState('')
53
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
54
+
55
+ // Cargar configuraciones desde metadata al montar el componente
56
+ useEffect(() => {
57
+ if (userData?.meta?.notificationsPreferences) {
58
+ const notifPrefs = userData.meta.notificationsPreferences as Record<string, unknown>
59
+
60
+ // Cargar configuración de push notifications
61
+ if (notifPrefs.pushEnabled !== undefined) {
62
+ setPushNotificationsEnabled(notifPrefs.pushEnabled as boolean)
63
+ }
64
+
65
+ // Cargar configuraciones individuales de notificaciones
66
+ setNotificationSettings({
67
+ login_alerts: {
68
+ email: (notifPrefs.loginAlertsEmail as boolean) ?? true,
69
+ push: (notifPrefs.loginAlertsPush as boolean) ?? true
70
+ },
71
+ password_changes: {
72
+ email: (notifPrefs.passwordChangesEmail as boolean) ?? true,
73
+ push: (notifPrefs.passwordChangesPush as boolean) ?? true
74
+ },
75
+ suspicious_activity: {
76
+ email: (notifPrefs.suspiciousActivityEmail as boolean) ?? true,
77
+ push: (notifPrefs.suspiciousActivityPush as boolean) ?? true
78
+ },
79
+ mentions: {
80
+ email: (notifPrefs.mentionsEmail as boolean) ?? true,
81
+ push: (notifPrefs.mentionsPush as boolean) ?? true
82
+ },
83
+ project_updates: {
84
+ email: (notifPrefs.projectUpdatesEmail as boolean) ?? true,
85
+ push: (notifPrefs.projectUpdatesPush as boolean) ?? false
86
+ },
87
+ team_invites: {
88
+ email: (notifPrefs.teamInvitesEmail as boolean) ?? true,
89
+ push: (notifPrefs.teamInvitesPush as boolean) ?? true
90
+ },
91
+ newsletter: {
92
+ email: (notifPrefs.newsletterEmail as boolean) ?? false,
93
+ push: (notifPrefs.newsletterPush as boolean) ?? false
94
+ },
95
+ promotions: {
96
+ email: (notifPrefs.promotionsEmail as boolean) ?? false,
97
+ push: (notifPrefs.promotionsPush as boolean) ?? false
98
+ },
99
+ feature_announcements: {
100
+ email: (notifPrefs.featureAnnouncementsEmail as boolean) ?? true,
101
+ push: (notifPrefs.featureAnnouncementsPush as boolean) ?? false
102
+ }
103
+ })
104
+ setHasUnsavedChanges(false)
105
+ }
106
+ }, [userData?.meta])
107
+
108
+ const notificationTypes = [
109
+ {
110
+ category: t('notifications.categories.security.title'),
111
+ icon: <Shield className="h-5 w-5 text-red-500" />,
112
+ description: t('notifications.categories.security.description'),
113
+ notifications: [
114
+ {
115
+ id: 'login_alerts',
116
+ title: t('notifications.types.loginAlerts.title'),
117
+ description: t('notifications.types.loginAlerts.description'),
118
+ email: notificationSettings.login_alerts.email,
119
+ push: notificationSettings.login_alerts.push,
120
+ required: true,
121
+ },
122
+ {
123
+ id: 'password_changes',
124
+ title: t('notifications.types.passwordChanges.title'),
125
+ description: t('notifications.types.passwordChanges.description'),
126
+ email: notificationSettings.password_changes.email,
127
+ push: notificationSettings.password_changes.push,
128
+ required: true,
129
+ },
130
+ {
131
+ id: 'suspicious_activity',
132
+ title: t('notifications.types.suspiciousActivity.title'),
133
+ description: t('notifications.types.suspiciousActivity.description'),
134
+ email: notificationSettings.suspicious_activity.email,
135
+ push: notificationSettings.suspicious_activity.push,
136
+ required: true,
137
+ },
138
+ ]
139
+ },
140
+ {
141
+ category: t('notifications.categories.activity.title'),
142
+ icon: <Zap className="h-5 w-5 text-blue-500" />,
143
+ description: t('notifications.categories.activity.description'),
144
+ notifications: [
145
+ {
146
+ id: 'mentions',
147
+ title: t('notifications.types.mentions.title'),
148
+ description: t('notifications.types.mentions.description'),
149
+ email: notificationSettings.mentions.email,
150
+ push: notificationSettings.mentions.push,
151
+ required: false,
152
+ },
153
+ {
154
+ id: 'project_updates',
155
+ title: t('notifications.types.projectUpdates.title'),
156
+ description: t('notifications.types.projectUpdates.description'),
157
+ email: notificationSettings.project_updates.email,
158
+ push: notificationSettings.project_updates.push,
159
+ required: false,
160
+ },
161
+ {
162
+ id: 'team_invites',
163
+ title: t('notifications.types.teamInvites.title'),
164
+ description: t('notifications.types.teamInvites.description'),
165
+ email: notificationSettings.team_invites.email,
166
+ push: notificationSettings.team_invites.push,
167
+ required: false,
168
+ },
169
+ ]
170
+ },
171
+ {
172
+ category: t('notifications.categories.marketing.title'),
173
+ icon: <MessageSquare className="h-5 w-5 text-green-500" />,
174
+ description: t('notifications.categories.marketing.description'),
175
+ notifications: [
176
+ {
177
+ id: 'newsletter',
178
+ title: t('notifications.types.newsletter.title'),
179
+ description: t('notifications.types.newsletter.description'),
180
+ email: notificationSettings.newsletter.email,
181
+ push: notificationSettings.newsletter.push,
182
+ required: false,
183
+ },
184
+ {
185
+ id: 'promotions',
186
+ title: t('notifications.types.promotions.title'),
187
+ description: t('notifications.types.promotions.description'),
188
+ email: notificationSettings.promotions.email,
189
+ push: notificationSettings.promotions.push,
190
+ required: false,
191
+ },
192
+ {
193
+ id: 'feature_announcements',
194
+ title: t('notifications.types.featureAnnouncements.title'),
195
+ description: t('notifications.types.featureAnnouncements.description'),
196
+ email: notificationSettings.feature_announcements.email,
197
+ push: notificationSettings.feature_announcements.push,
198
+ required: false,
199
+ },
200
+ ]
201
+ }
202
+ ]
203
+
204
+ const handleNotificationToggle = useCallback((notificationId: string, type: 'email' | 'push') => {
205
+ setNotificationSettings(prev => ({
206
+ ...prev,
207
+ [notificationId]: {
208
+ ...prev[notificationId as keyof typeof prev],
209
+ [type]: !prev[notificationId as keyof typeof prev][type]
210
+ }
211
+ }))
212
+
213
+ const currentValue = notificationSettings[notificationId as keyof typeof notificationSettings][type]
214
+ const status = currentValue ? t('notifications.labels.disabled') : t('notifications.labels.enabled')
215
+ setStatusMessage(t('notifications.messages.notificationToggled', { type: type, notification: notificationId, status: status }))
216
+ setHasUnsavedChanges(true)
217
+ }, [notificationSettings, t])
218
+
219
+ // Función para guardar configuraciones como metadata
220
+ const handleSaveSettings = useCallback(async () => {
221
+ try {
222
+ // Crear metadata en formato anidado
223
+ const notificationsPreferences = {
224
+ pushEnabled: pushNotificationsEnabled,
225
+ loginAlertsEmail: notificationSettings.login_alerts.email,
226
+ loginAlertsPush: notificationSettings.login_alerts.push,
227
+ passwordChangesEmail: notificationSettings.password_changes.email,
228
+ passwordChangesPush: notificationSettings.password_changes.push,
229
+ suspiciousActivityEmail: notificationSettings.suspicious_activity.email,
230
+ suspiciousActivityPush: notificationSettings.suspicious_activity.push,
231
+ mentionsEmail: notificationSettings.mentions.email,
232
+ mentionsPush: notificationSettings.mentions.push,
233
+ projectUpdatesEmail: notificationSettings.project_updates.email,
234
+ projectUpdatesPush: notificationSettings.project_updates.push,
235
+ teamInvitesEmail: notificationSettings.team_invites.email,
236
+ teamInvitesPush: notificationSettings.team_invites.push,
237
+ newsletterEmail: notificationSettings.newsletter.email,
238
+ newsletterPush: notificationSettings.newsletter.push,
239
+ promotionsEmail: notificationSettings.promotions.email,
240
+ promotionsPush: notificationSettings.promotions.push,
241
+ featureAnnouncementsEmail: notificationSettings.feature_announcements.email,
242
+ featureAnnouncementsPush: notificationSettings.feature_announcements.push,
243
+ }
244
+
245
+ await updateUserMeta({
246
+ meta: {
247
+ notificationsPreferences
248
+ }
249
+ })
250
+
251
+ setHasUnsavedChanges(false)
252
+ toast.success(t('notifications.messages.saveSuccess'), {
253
+ description: t('notifications.messages.saveSuccessDescription'),
254
+ })
255
+ } catch (error) {
256
+ console.error('Error saving notification settings:', error)
257
+ toast.error(t('notifications.messages.saveError'), {
258
+ description: t('notifications.messages.saveErrorDescription'),
259
+ })
260
+ }
261
+ }, [pushNotificationsEnabled, notificationSettings, updateUserMeta, t])
262
+
263
+ // Mostrar skeleton mientras cargan los datos
264
+ if (isLoadingUser) {
265
+ return <NotificationsPageSkeleton />
266
+ }
267
+
268
+ return (
269
+ <>
270
+ {/* MANDATORY: Screen reader announcements */}
271
+ <div
272
+ aria-live="polite"
273
+ aria-atomic="true"
274
+ className="sr-only"
275
+ >
276
+ {statusMessage}
277
+ </div>
278
+
279
+ <div
280
+ className="max-w-4xl"
281
+ data-cy={sel('settings.notifications.container')}
282
+ >
283
+ <div className="space-y-6">
284
+ {/* Header */}
285
+ <header>
286
+ <h1
287
+ className="text-2xl font-bold"
288
+ id="notifications-heading"
289
+ >
290
+ {t('notifications.title')}
291
+ </h1>
292
+ <p className="text-muted-foreground mt-1">
293
+ {t('notifications.description')}
294
+ </p>
295
+ </header>
296
+
297
+ {/* Main Configuration Card */}
298
+ <Card>
299
+ <CardHeader>
300
+ <CardTitle>{t('notifications.main.title')}</CardTitle>
301
+ <CardDescription>
302
+ {t('notifications.main.description')}
303
+ </CardDescription>
304
+ </CardHeader>
305
+ <CardContent className="space-y-8">
306
+ {/* Canales de Notificación */}
307
+ <div className="space-y-6">
308
+ <h3 className="text-lg font-semibold flex items-center gap-2">
309
+ <Smartphone className="h-5 w-5" />
310
+ {t('notifications.channels.title')}
311
+ </h3>
312
+
313
+ {/* Push Notifications Master Switch */}
314
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-center">
315
+ <div className="space-y-1">
316
+ <div className="flex items-center gap-3">
317
+ <Smartphone className="h-5 w-5" />
318
+ <h4 className="font-medium">{t('notifications.channels.push.title')}</h4>
319
+ </div>
320
+ <p className="text-sm text-muted-foreground">
321
+ {t('notifications.channels.push.description')}
322
+ </p>
323
+ </div>
324
+ <div className="flex justify-end">
325
+ <div className="flex items-center gap-3">
326
+ <Badge variant={pushNotificationsEnabled ? 'default' : 'secondary'}>
327
+ {pushNotificationsEnabled ? t('notifications.channels.push.enabled') : t('notifications.channels.push.disabled')}
328
+ </Badge>
329
+ <Switch
330
+ checked={pushNotificationsEnabled}
331
+ onCheckedChange={(checked: boolean) => {
332
+ setPushNotificationsEnabled(checked)
333
+ setStatusMessage(checked ? t('notifications.messages.pushEnabled') : t('notifications.messages.pushDisabled'))
334
+ setHasUnsavedChanges(true)
335
+ }}
336
+ aria-label={pushNotificationsEnabled ? t('notifications.channels.push.ariaEnabled') : t('notifications.channels.push.ariaDisabled')}
337
+ data-cy={sel('settings.notifications.pushToggle')}
338
+ />
339
+ </div>
340
+ </div>
341
+ </div>
342
+
343
+ {/* Email Notifications Info */}
344
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-center">
345
+ <div className="space-y-1">
346
+ <div className="flex items-center gap-3">
347
+ <Mail className="h-5 w-5" />
348
+ <h4 className="font-medium">{t('notifications.channels.email.title')}</h4>
349
+ </div>
350
+ <p className="text-sm text-muted-foreground">
351
+ {t('notifications.channels.email.description')}
352
+ </p>
353
+ </div>
354
+ <div className="flex justify-end">
355
+ <Badge variant="outline">{t('notifications.channels.email.alwaysEnabled')}</Badge>
356
+ </div>
357
+ </div>
358
+ </div>
359
+
360
+ {/* Divisor */}
361
+ <hr className="border-muted" />
362
+
363
+ {/* Tipos de Notificaciones */}
364
+ {notificationTypes.map((type, index) => (
365
+ <div
366
+ key={index}
367
+ className="space-y-6"
368
+ data-cy={sel('settings.notifications.category.container', { category: type.category.toLowerCase().replace(/\s+/g, '-') })}
369
+ >
370
+ <h3 className="text-lg font-semibold flex items-center gap-2">
371
+ {type.icon}
372
+ {type.category}
373
+ </h3>
374
+
375
+ <div className="space-y-6">
376
+ {type.notifications.map((notification) => (
377
+ <div
378
+ key={notification.id}
379
+ className="grid grid-cols-1 md:grid-cols-2 gap-6 items-center"
380
+ >
381
+ <div className="space-y-1">
382
+ <div className="flex items-center gap-2">
383
+ <h4 className="font-medium">{notification.title}</h4>
384
+ {notification.required && (
385
+ <Badge variant="outline" className="text-xs">
386
+ {t('notifications.labels.required')}
387
+ </Badge>
388
+ )}
389
+ </div>
390
+ <p className="text-sm text-muted-foreground">
391
+ {notification.description}
392
+ </p>
393
+ </div>
394
+
395
+ <div className="flex justify-end">
396
+ <div className="flex items-center gap-6">
397
+ {/* Email Toggle */}
398
+ <div className="flex items-center gap-2">
399
+ <Mail className="h-4 w-4 text-muted-foreground" />
400
+ <span className="text-sm">{t('notifications.labels.email')}</span>
401
+ <Switch
402
+ checked={notification.email}
403
+ disabled={notification.required}
404
+ onCheckedChange={() => handleNotificationToggle(notification.id, 'email')}
405
+ aria-label={t('notifications.labels.emailAriaLabel', { title: notification.title, status: notification.email ? t('notifications.labels.enabled') : t('notifications.labels.disabled') })}
406
+ />
407
+ </div>
408
+
409
+ {/* Push Toggle */}
410
+ <div className="flex items-center gap-2">
411
+ <Smartphone className="h-4 w-4 text-muted-foreground" />
412
+ <span className="text-sm">{t('notifications.labels.push')}</span>
413
+ <Switch
414
+ checked={notification.push && pushNotificationsEnabled}
415
+ disabled={notification.required || !pushNotificationsEnabled}
416
+ onCheckedChange={() => handleNotificationToggle(notification.id, 'push')}
417
+ aria-label={t('notifications.labels.pushAriaLabel', { title: notification.title, status: (notification.push && pushNotificationsEnabled) ? t('notifications.labels.enabled') : t('notifications.labels.disabled') })}
418
+ />
419
+ </div>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ ))}
424
+ </div>
425
+
426
+ {/* Divisor entre secciones (excepto la última) */}
427
+ {index < notificationTypes.length - 1 && (
428
+ <hr className="border-muted" />
429
+ )}
430
+ </div>
431
+ ))}
432
+
433
+ {/* Botón de Guardar */}
434
+ <div className="flex justify-end pt-6 border-t border-muted">
435
+ <Button
436
+ onClick={handleSaveSettings}
437
+ disabled={!hasUnsavedChanges || isUpdating || isLoadingUser}
438
+ className="min-w-[120px]"
439
+ data-cy={sel('settings.notifications.submitButton')}
440
+ >
441
+ {isUpdating ? (
442
+ <>
443
+ <Loader2 className="h-4 w-4 mr-2 animate-spin" />
444
+ {t('notifications.buttons.saving')}
445
+ </>
446
+ ) : (
447
+ <>
448
+ <Save className="h-4 w-4 mr-2" />
449
+ {t('notifications.buttons.save')}
450
+ </>
451
+ )}
452
+ </Button>
453
+ </div>
454
+ </CardContent>
455
+ </Card>
456
+ </div>
457
+ </div>
458
+ </>
459
+ )
460
+ }
461
+
462
+ export default getTemplateOrDefaultClient('app/dashboard/settings/notifications/page.tsx', NotificationsPage)