@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,686 @@
1
+ 'use client'
2
+
3
+ import { useUserProfile } from '@nextsparkjs/core/hooks/useUserProfile'
4
+ import { useAuth } from '@nextsparkjs/core/hooks/useAuth'
5
+ import { setUserLocaleClient } from '@nextsparkjs/core/lib/locale-client'
6
+ import { I18N_CONFIG } from '@nextsparkjs/core/lib/config'
7
+ import { useEffect, useState, useCallback } from 'react'
8
+ import { useForm } from 'react-hook-form'
9
+ import { zodResolver } from '@hookform/resolvers/zod'
10
+ import { useMutation, useQueryClient } from '@tanstack/react-query'
11
+ import { Button } from '@nextsparkjs/core/components/ui/button'
12
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@nextsparkjs/core/components/ui/card'
13
+ import { Input } from '@nextsparkjs/core/components/ui/input'
14
+ import { Label } from '@nextsparkjs/core/components/ui/label'
15
+ import {
16
+ Select,
17
+ SelectContent,
18
+ SelectItem,
19
+ SelectTrigger,
20
+ SelectValue,
21
+ } from '@nextsparkjs/core/components/ui/select'
22
+ import {
23
+ Popover,
24
+ PopoverContent,
25
+ PopoverTrigger,
26
+ } from '@nextsparkjs/core/components/ui/popover'
27
+ import {
28
+ Command,
29
+ CommandEmpty,
30
+ CommandGroup,
31
+ CommandInput,
32
+ CommandItem,
33
+ CommandList,
34
+ } from '@nextsparkjs/core/components/ui/command'
35
+ import { Separator } from '@nextsparkjs/core/components/ui/separator'
36
+ import { Alert, AlertDescription } from '@nextsparkjs/core/components/ui/alert'
37
+ import {
38
+ Dialog,
39
+ DialogContent,
40
+ DialogDescription,
41
+ DialogFooter,
42
+ DialogHeader,
43
+ DialogTitle,
44
+ DialogTrigger,
45
+ } from '@nextsparkjs/core/components/ui/dialog'
46
+ import {
47
+ Loader2,
48
+ User,
49
+ Mail,
50
+ Calendar,
51
+ Globe,
52
+ Clock,
53
+ Languages,
54
+ CheckCircle2,
55
+ AlertCircle,
56
+ ChevronDown,
57
+ ChevronRight,
58
+ Trash2,
59
+ Check,
60
+ ChevronsUpDown
61
+ } from 'lucide-react'
62
+ import { profileSchema, ProfileFormData } from '@nextsparkjs/core/lib/validation'
63
+ import { countries, timezones } from '@nextsparkjs/core/lib/countries-timezones'
64
+ import { sel } from '@nextsparkjs/core/selectors'
65
+ import { useTranslations } from 'next-intl'
66
+ import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
67
+
68
+ // Language options
69
+ const languages = [
70
+ { value: 'en', label: 'English' },
71
+ { value: 'es', label: 'Español' },
72
+ ] as const
73
+
74
+ function ProfilePage() {
75
+ const { profile, isLoading, error } = useUserProfile()
76
+ const { signOut } = useAuth()
77
+ const queryClient = useQueryClient()
78
+ const [updateSuccess, setUpdateSuccess] = useState(false)
79
+ const [advancedOpen, setAdvancedOpen] = useState(false)
80
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
81
+ const [statusMessage, setStatusMessage] = useState('')
82
+ const [timezoneOpen, setTimezoneOpen] = useState(false)
83
+ const [countryOpen, setCountryOpen] = useState(false)
84
+ const t = useTranslations('settings')
85
+
86
+ // Form setup
87
+ const {
88
+ register,
89
+ handleSubmit,
90
+ formState: { errors, isSubmitting },
91
+ setValue,
92
+ watch,
93
+ reset
94
+ } = useForm<ProfileFormData>({
95
+ resolver: zodResolver(profileSchema),
96
+ defaultValues: {
97
+ firstName: '',
98
+ lastName: '',
99
+ country: '',
100
+ timezone: '',
101
+ language: I18N_CONFIG.defaultLocale,
102
+ },
103
+ })
104
+
105
+ // Update form when profile data loads
106
+ useEffect(() => {
107
+ if (profile) {
108
+ reset({
109
+ firstName: profile.firstName || '',
110
+ lastName: profile.lastName || '',
111
+ country: profile.country || '',
112
+ timezone: profile.timezone || '',
113
+ language: profile.language || I18N_CONFIG.defaultLocale,
114
+ })
115
+ }
116
+ }, [profile, reset])
117
+
118
+ // Update profile mutation
119
+ const updateProfileMutation = useMutation({
120
+ mutationFn: async (data: ProfileFormData) => {
121
+ const response = await fetch('/api/user/profile', {
122
+ method: 'PATCH',
123
+ headers: {
124
+ 'Content-Type': 'application/json',
125
+ },
126
+ body: JSON.stringify(data),
127
+ })
128
+
129
+ if (!response.ok) {
130
+ const errorData = await response.json()
131
+ throw new Error(errorData.error || 'Failed to update profile')
132
+ }
133
+
134
+ return response.json()
135
+ },
136
+ onSuccess: () => {
137
+ queryClient.invalidateQueries({ queryKey: ['user-profile'] })
138
+ setUpdateSuccess(true)
139
+ setStatusMessage(t('profile.messages.updateSuccess'))
140
+ setTimeout(() => {
141
+ setUpdateSuccess(false)
142
+ setStatusMessage('')
143
+ }, 3000)
144
+ },
145
+ onError: (error) => {
146
+ setStatusMessage(t('profile.messages.updateError', { error: error.message }))
147
+ },
148
+ })
149
+
150
+ // Delete account mutation
151
+ const deleteAccountMutation = useMutation({
152
+ mutationFn: async () => {
153
+ const response = await fetch('/api/user/delete-account', {
154
+ method: 'DELETE',
155
+ headers: {
156
+ 'Content-Type': 'application/json',
157
+ },
158
+ })
159
+
160
+ if (!response.ok) {
161
+ const errorData = await response.json()
162
+ throw new Error(errorData.error || 'Failed to delete account')
163
+ }
164
+
165
+ return response.json()
166
+ },
167
+ onSuccess: async () => {
168
+ setStatusMessage(t('profile.messages.deleteSuccess'))
169
+ // Clear all query data and sign out the user
170
+ queryClient.clear()
171
+ await signOut()
172
+ // Redirect will happen automatically after signOut
173
+ },
174
+ onError: (error) => {
175
+ setStatusMessage(t('profile.messages.deleteError', { error: error.message }))
176
+ },
177
+ })
178
+
179
+ const onSubmit = useCallback(async (data: ProfileFormData) => {
180
+ setStatusMessage(t('profile.messages.updating'))
181
+
182
+ // Check if language changed
183
+ const languageChanged = profile?.language !== data.language
184
+
185
+ try {
186
+ // Update profile with ALL fields (including language)
187
+ await updateProfileMutation.mutateAsync(data)
188
+
189
+ // If language changed, update app language after successful profile update
190
+ if (languageChanged) {
191
+ try {
192
+ // Update cookie and reload to apply new language
193
+ setUserLocaleClient(data.language)
194
+ window.location.reload()
195
+ } catch (error) {
196
+ console.error('Error updating app language:', error)
197
+ }
198
+ }
199
+ } catch {
200
+ // Error handled by mutation onError
201
+ }
202
+ }, [updateProfileMutation, t, profile?.language])
203
+
204
+ const handleDeleteAccount = useCallback(() => {
205
+ setStatusMessage(t('profile.messages.deleting'))
206
+ deleteAccountMutation.mutate()
207
+ setDeleteDialogOpen(false)
208
+ }, [deleteAccountMutation, t])
209
+
210
+ const handleAdvancedToggle = useCallback(() => {
211
+ const newState = !advancedOpen
212
+ setAdvancedOpen(newState)
213
+ setStatusMessage(newState ? t('profile.messages.advancedOpen') : t('profile.messages.advancedClosed'))
214
+ }, [advancedOpen, t])
215
+
216
+
217
+
218
+ const countryValue = watch('country')
219
+ const timezoneValue = watch('timezone')
220
+ const languageValue = watch('language')
221
+
222
+ if (isLoading) {
223
+ return (
224
+ <div
225
+ className="flex items-center justify-center py-12"
226
+ role="status"
227
+ aria-label={t('profile.loading.aria')}
228
+ >
229
+ <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" aria-hidden="true" />
230
+ <span className="sr-only">{t('profile.loading.text')}</span>
231
+ </div>
232
+ )
233
+ }
234
+
235
+ if (error) {
236
+ return (
237
+ <Alert
238
+ className="max-w-md"
239
+ variant="destructive"
240
+ role="alert"
241
+ >
242
+ <AlertCircle className="h-4 w-4" aria-hidden="true" />
243
+ <AlertDescription>
244
+ {t('profile.error.message')}
245
+ </AlertDescription>
246
+ </Alert>
247
+ )
248
+ }
249
+
250
+ return (
251
+ <>
252
+ {/* MANDATORY: Screen reader announcements */}
253
+ <div
254
+ aria-live="polite"
255
+ aria-atomic="true"
256
+ className="sr-only"
257
+ >
258
+ {statusMessage}
259
+ </div>
260
+
261
+ <div
262
+ className="max-w-4xl space-y-6"
263
+ data-cy={sel('settings.profile.container')}
264
+ >
265
+ {/* Header */}
266
+ <header>
267
+ <h1
268
+ className="text-2xl font-bold"
269
+ id="profile-heading"
270
+ >
271
+ {t('profile.title')}
272
+ </h1>
273
+ <p className="text-muted-foreground mt-1">
274
+ {t('profile.description')}
275
+ </p>
276
+ </header>
277
+
278
+ {/* Success Alert */}
279
+ {updateSuccess && (
280
+ <Alert
281
+ className="border-green-200 bg-green-50 text-green-800"
282
+ role="status"
283
+ aria-live="polite"
284
+ data-cy={sel('settings.profile.successMessage')}
285
+ >
286
+ <CheckCircle2 className="h-4 w-4 text-green-600" aria-hidden="true" />
287
+ <AlertDescription>
288
+ {t('profile.success.message')}
289
+ </AlertDescription>
290
+ </Alert>
291
+ )}
292
+
293
+ {/* Error Alert */}
294
+ {updateProfileMutation.error && (
295
+ <Alert
296
+ variant="destructive"
297
+ role="alert"
298
+ aria-live="assertive"
299
+ >
300
+ <AlertCircle className="h-4 w-4" aria-hidden="true" />
301
+ <AlertDescription>
302
+ {updateProfileMutation.error.message}
303
+ </AlertDescription>
304
+ </Alert>
305
+ )}
306
+
307
+ {/* Single Column Layout */}
308
+ <Card
309
+ className="max-w-4xl"
310
+ data-cy={sel('settings.profile.form')}
311
+ >
312
+ <CardHeader>
313
+ <CardTitle
314
+ className="flex items-center gap-2"
315
+ id="profile-form-title"
316
+ >
317
+ <User className="h-5 w-5" aria-hidden="true" />
318
+ {t('profile.form.title')}
319
+ </CardTitle>
320
+ <CardDescription>
321
+ {t('profile.form.description')}
322
+ </CardDescription>
323
+ </CardHeader>
324
+ <CardContent>
325
+ <form
326
+ onSubmit={handleSubmit(onSubmit)}
327
+ className="space-y-6"
328
+ aria-labelledby="profile-form-title"
329
+ >
330
+ <div className="grid gap-4 sm:grid-cols-2">
331
+ <div className="space-y-2">
332
+ <Label htmlFor="firstName">{t('profile.form.firstName')}</Label>
333
+ <Input
334
+ id="firstName"
335
+ {...register('firstName')}
336
+ placeholder={t('profile.form.firstNamePlaceholder')}
337
+ />
338
+ {errors.firstName && (
339
+ <p className="text-sm text-destructive">{errors.firstName.message}</p>
340
+ )}
341
+ </div>
342
+
343
+ <div className="space-y-2">
344
+ <Label htmlFor="lastName">{t('profile.form.lastName')}</Label>
345
+ <Input
346
+ id="lastName"
347
+ {...register('lastName')}
348
+ placeholder={t('profile.form.lastNamePlaceholder')}
349
+ />
350
+ {errors.lastName && (
351
+ <p className="text-sm text-destructive">{errors.lastName.message}</p>
352
+ )}
353
+ </div>
354
+ </div>
355
+
356
+ {/* Email, Auth and Verification - 50%, 25%, 25% Layout */}
357
+ <div className="grid gap-4 grid-cols-1 md:grid-cols-4">
358
+ <div className="space-y-2 md:col-span-2">
359
+ <Label>{t('profile.form.email')}</Label>
360
+ <div className="relative">
361
+ <Mail className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
362
+ <Input
363
+ value={profile?.email || ''}
364
+ disabled
365
+ className="pl-10 bg-muted"
366
+ />
367
+ </div>
368
+ <p className="text-xs text-muted-foreground">
369
+ {t('profile.form.emailNote')}
370
+ </p>
371
+ </div>
372
+
373
+ <div className="space-y-2">
374
+ <Label>{t('profile.form.authMethod')}</Label>
375
+ <div className="flex items-center gap-3 py-2">
376
+ {profile?.authMethod === 'Google' ? (
377
+ <svg className="h-4 w-4" viewBox="0 0 24 24">
378
+ <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
379
+ <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
380
+ <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
381
+ <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
382
+ </svg>
383
+ ) : (
384
+ <Mail className="h-4 w-4 text-muted-foreground" />
385
+ )}
386
+ <span className="text-sm font-medium">{profile?.authMethod}</span>
387
+ </div>
388
+ </div>
389
+
390
+ <div className="space-y-2">
391
+ <Label>{t('profile.form.verificationStatus')}</Label>
392
+ <div className="flex items-center gap-3 py-2">
393
+ <CheckCircle2 className={`h-4 w-4 ${profile?.emailVerified ? 'text-green-600' : 'text-muted-foreground'}`} />
394
+ <span className="text-sm font-medium">
395
+ {profile?.emailVerified ? t('profile.form.verified') : t('profile.form.notVerified')}
396
+ </span>
397
+ </div>
398
+ </div>
399
+ </div>
400
+
401
+ {/* Location and Preferences - 20%, 30%, 50% Layout */}
402
+ <div className="grid gap-4 grid-cols-1 md:grid-cols-10">
403
+ <div className="space-y-2 md:col-span-2">
404
+ <Label htmlFor="language">{t('profile.form.language')}</Label>
405
+ <Select
406
+ value={languageValue || ''}
407
+ onValueChange={(value: string) => {
408
+ if (value) {
409
+ setValue('language', value)
410
+ }
411
+ }}
412
+ >
413
+ <SelectTrigger>
414
+ <div className="flex items-center gap-2">
415
+ <Languages className="h-4 w-4 text-muted-foreground" aria-hidden="true" />
416
+ <SelectValue placeholder={t('profile.form.languagePlaceholder')} />
417
+ </div>
418
+ </SelectTrigger>
419
+ <SelectContent>
420
+ {languages.map((language) => (
421
+ <SelectItem key={language.value} value={language.value}>
422
+ {language.label}
423
+ </SelectItem>
424
+ ))}
425
+ </SelectContent>
426
+ </Select>
427
+ {errors.language && (
428
+ <p className="text-sm text-destructive">{errors.language.message}</p>
429
+ )}
430
+ </div>
431
+
432
+ <div className="space-y-2 md:col-span-3">
433
+ <Label htmlFor="country">{t('profile.form.country')}</Label>
434
+ <Popover open={countryOpen} onOpenChange={setCountryOpen}>
435
+ <PopoverTrigger asChild>
436
+ <Button
437
+ variant="outline"
438
+ role="combobox"
439
+ aria-expanded={countryOpen}
440
+ className="w-full justify-between"
441
+ >
442
+ <div className="flex items-center gap-2">
443
+ <Globe className="h-4 w-4 text-muted-foreground" aria-hidden="true" />
444
+ {countryValue
445
+ ? countries.find((country) => country.value === countryValue)?.label
446
+ : t('profile.form.countryPlaceholder')}
447
+ </div>
448
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
449
+ </Button>
450
+ </PopoverTrigger>
451
+ <PopoverContent className="w-[400px] p-0">
452
+ <Command>
453
+ <CommandInput placeholder="Buscar país..." className="h-9" />
454
+ <CommandList>
455
+ <CommandEmpty>No se encontraron países.</CommandEmpty>
456
+ <CommandGroup>
457
+ {countries.map((country) => (
458
+ <CommandItem
459
+ key={country.value}
460
+ value={country.label}
461
+ onSelect={() => {
462
+ setValue('country', country.value === countryValue ? '' : country.value)
463
+ setCountryOpen(false)
464
+ }}
465
+ >
466
+ {country.label}
467
+ <Check
468
+ className={`ml-auto h-4 w-4 ${
469
+ countryValue === country.value ? "opacity-100" : "opacity-0"
470
+ }`}
471
+ />
472
+ </CommandItem>
473
+ ))}
474
+ </CommandGroup>
475
+ </CommandList>
476
+ </Command>
477
+ </PopoverContent>
478
+ </Popover>
479
+ {errors.country && (
480
+ <p className="text-sm text-destructive">{errors.country.message}</p>
481
+ )}
482
+ </div>
483
+
484
+ <div className="space-y-2 md:col-span-5">
485
+ <Label htmlFor="timezone">{t('profile.form.timezone')}</Label>
486
+ <Popover open={timezoneOpen} onOpenChange={setTimezoneOpen}>
487
+ <PopoverTrigger asChild>
488
+ <Button
489
+ variant="outline"
490
+ role="combobox"
491
+ aria-expanded={timezoneOpen}
492
+ className="w-full justify-between"
493
+ >
494
+ <div className="flex items-center gap-2">
495
+ <Clock className="h-4 w-4 text-muted-foreground" aria-hidden="true" />
496
+ {timezoneValue
497
+ ? timezones.find((timezone) => timezone.value === timezoneValue)?.label
498
+ : t('profile.form.timezonePlaceholder')}
499
+ </div>
500
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
501
+ </Button>
502
+ </PopoverTrigger>
503
+ <PopoverContent className="w-[400px] p-0">
504
+ <Command>
505
+ <CommandInput placeholder="Buscar timezone..." className="h-9" />
506
+ <CommandList>
507
+ <CommandEmpty>No se encontraron timezones.</CommandEmpty>
508
+ <CommandGroup>
509
+ {timezones.map((timezone) => (
510
+ <CommandItem
511
+ key={timezone.value}
512
+ value={timezone.label}
513
+ onSelect={() => {
514
+ setValue('timezone', timezone.value === timezoneValue ? '' : timezone.value)
515
+ setTimezoneOpen(false)
516
+ }}
517
+ >
518
+ {timezone.label}
519
+ <Check
520
+ className={`ml-auto h-4 w-4 ${
521
+ timezoneValue === timezone.value ? "opacity-100" : "opacity-0"
522
+ }`}
523
+ />
524
+ </CommandItem>
525
+ ))}
526
+ </CommandGroup>
527
+ </CommandList>
528
+ </Command>
529
+ </PopoverContent>
530
+ </Popover>
531
+ {errors.timezone && (
532
+ <p className="text-sm text-destructive">{errors.timezone.message}</p>
533
+ )}
534
+ </div>
535
+ </div>
536
+
537
+ {/* Action Section - Button and Member Info */}
538
+ <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 pt-2">
539
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
540
+ <Calendar className="h-4 w-4" />
541
+ <span className="font-medium">{t('profile.form.memberSince')}:</span>
542
+ <span>
543
+ {profile && new Date(profile.createdAt).toLocaleDateString('es-ES', {
544
+ month: 'long',
545
+ day: 'numeric',
546
+ year: 'numeric'
547
+ })}
548
+ </span>
549
+ </div>
550
+
551
+ <Button
552
+ type="submit"
553
+ disabled={isSubmitting || updateProfileMutation.isPending}
554
+ aria-describedby="submit-help"
555
+ className="w-full md:w-auto"
556
+ data-cy={sel('settings.profile.submitButton')}
557
+ >
558
+ {(isSubmitting || updateProfileMutation.isPending) ? (
559
+ <>
560
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
561
+ {t('profile.form.updating')}
562
+ </>
563
+ ) : (
564
+ t('profile.form.updateButton')
565
+ )}
566
+ </Button>
567
+ <div id="submit-help" className="sr-only">
568
+ {(isSubmitting || updateProfileMutation.isPending)
569
+ ? t('profile.form.submitHelpUpdating')
570
+ : t('profile.form.submitHelp')
571
+ }
572
+ </div>
573
+ </div>
574
+
575
+ {/* Advanced Section */}
576
+ <div className="mt-6">
577
+ <Separator />
578
+ <div className="mt-4">
579
+ <Button
580
+ type="button"
581
+ variant="ghost"
582
+ onClick={handleAdvancedToggle}
583
+ className="w-full justify-between text-muted-foreground hover:text-foreground"
584
+ aria-expanded={advancedOpen}
585
+ aria-controls="advanced-options"
586
+ aria-label={advancedOpen ? t('profile.advanced.closeLabel') : t('profile.advanced.openLabel')}
587
+ >
588
+ {t('profile.advanced.title')}
589
+ {advancedOpen ? (
590
+ <ChevronDown className="h-4 w-4" aria-hidden="true" />
591
+ ) : (
592
+ <ChevronRight className="h-4 w-4" aria-hidden="true" />
593
+ )}
594
+ </Button>
595
+
596
+ {advancedOpen && (
597
+ <div
598
+ id="advanced-options"
599
+ className="mt-4 space-y-4 border-t pt-4"
600
+ >
601
+ <div className="space-y-2">
602
+ <h4
603
+ className="text-sm font-medium text-destructive"
604
+ id="danger-zone-heading"
605
+ >
606
+ {t('profile.danger.title')}
607
+ </h4>
608
+ <p className="text-xs text-muted-foreground">
609
+ {t('profile.danger.warning')}
610
+ </p>
611
+
612
+ <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
613
+ <DialogTrigger asChild>
614
+ <Button
615
+ variant="destructive"
616
+ className="w-full gap-2"
617
+ disabled={deleteAccountMutation.isPending}
618
+ aria-describedby="danger-zone-heading"
619
+ >
620
+ <Trash2 className="h-4 w-4" aria-hidden="true" />
621
+ {t('profile.danger.deleteButton')}
622
+ </Button>
623
+ </DialogTrigger>
624
+ <DialogContent>
625
+ <DialogHeader>
626
+ <DialogTitle className="flex items-center gap-2 text-destructive">
627
+ <AlertCircle className="h-5 w-5" />
628
+ {t('profile.danger.dialogTitle')}
629
+ </DialogTitle>
630
+ <DialogDescription asChild>
631
+ <div className="space-y-2">
632
+ <div>
633
+ {t('profile.danger.dialogDescription')}
634
+ </div>
635
+ <div className="font-medium text-foreground">
636
+ {t('profile.danger.dialogWarning')}
637
+ </div>
638
+ <div>
639
+ {t('profile.danger.dialogDetail')}
640
+ </div>
641
+ </div>
642
+ </DialogDescription>
643
+ </DialogHeader>
644
+ <DialogFooter className="gap-2">
645
+ <Button
646
+ variant="outline"
647
+ onClick={() => setDeleteDialogOpen(false)}
648
+ disabled={deleteAccountMutation.isPending}
649
+ >
650
+ {t('profile.actions.cancel')}
651
+ </Button>
652
+ <Button
653
+ variant="destructive"
654
+ onClick={handleDeleteAccount}
655
+ disabled={deleteAccountMutation.isPending}
656
+ className="gap-2"
657
+ >
658
+ {deleteAccountMutation.isPending ? (
659
+ <>
660
+ <Loader2 className="h-4 w-4 animate-spin" />
661
+ {t('profile.danger.deleting')}
662
+ </>
663
+ ) : (
664
+ <>
665
+ <Trash2 className="h-4 w-4" />
666
+ {t('profile.danger.confirmDelete')}
667
+ </>
668
+ )}
669
+ </Button>
670
+ </DialogFooter>
671
+ </DialogContent>
672
+ </Dialog>
673
+ </div>
674
+ </div>
675
+ )}
676
+ </div>
677
+ </div>
678
+ </form>
679
+ </CardContent>
680
+ </Card>
681
+ </div>
682
+ </>
683
+ )
684
+ }
685
+
686
+ export default getTemplateOrDefaultClient('app/dashboard/settings/profile/page.tsx', ProfilePage)
@@ -0,0 +1,5 @@
1
+ import { SkeletonSecurityPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
2
+
3
+ export default function SecurityLoading() {
4
+ return <SkeletonSecurityPage />
5
+ }