@tinyrack/issuary-server 0.22.0 → 0.24.0

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 (333) hide show
  1. package/dist/entities/oauth-client.entity.d.ts +0 -18
  2. package/dist/entities/oauth-client.entity.d.ts.map +1 -1
  3. package/dist/entities/oauth-code.entity.d.ts +0 -18
  4. package/dist/entities/oauth-code.entity.d.ts.map +1 -1
  5. package/dist/entities/oauth-device-code.entity.d.ts +0 -18
  6. package/dist/entities/oauth-device-code.entity.d.ts.map +1 -1
  7. package/dist/entities/revoked-token.entity.d.ts +0 -18
  8. package/dist/entities/revoked-token.entity.d.ts.map +1 -1
  9. package/dist/entities/terms-content.entity.d.ts +0 -9
  10. package/dist/entities/terms-content.entity.d.ts.map +1 -1
  11. package/dist/entities/terms.entity.d.ts +0 -9
  12. package/dist/entities/terms.entity.d.ts.map +1 -1
  13. package/dist/entities/user-consent.entity.d.ts +0 -18
  14. package/dist/entities/user-consent.entity.d.ts.map +1 -1
  15. package/dist/entities/user-terms-consent.entity.d.ts +0 -9
  16. package/dist/entities/user-terms-consent.entity.d.ts.map +1 -1
  17. package/dist/entities/user-totp-recovery-code.entity.d.ts +0 -9
  18. package/dist/entities/user-totp-recovery-code.entity.d.ts.map +1 -1
  19. package/dist/entities/user-totp.entity.d.ts +0 -9
  20. package/dist/entities/user-totp.entity.d.ts.map +1 -1
  21. package/dist/entities/user.entity.d.ts +0 -9
  22. package/dist/entities/user.entity.d.ts.map +1 -1
  23. package/dist/entities/user.entity.js +0 -4
  24. package/dist/entities/user.entity.js.map +1 -1
  25. package/dist/entrypoints/app.d.ts +0 -59
  26. package/dist/entrypoints/app.d.ts.map +1 -1
  27. package/dist/entrypoints/database/postgres/compiled-functions.d.ts +20 -23
  28. package/dist/entrypoints/database/postgres/compiled-functions.d.ts.map +1 -1
  29. package/dist/entrypoints/database/postgres/compiled-functions.js +104 -134
  30. package/dist/entrypoints/database/postgres/compiled-functions.js.map +1 -1
  31. package/dist/entrypoints/database/sqlite/compiled-functions.d.ts +20 -23
  32. package/dist/entrypoints/database/sqlite/compiled-functions.d.ts.map +1 -1
  33. package/dist/entrypoints/database/sqlite/compiled-functions.js +104 -134
  34. package/dist/entrypoints/database/sqlite/compiled-functions.js.map +1 -1
  35. package/dist/lib/config/branding.d.ts +0 -4
  36. package/dist/lib/config/branding.d.ts.map +1 -1
  37. package/dist/lib/config/branding.js +0 -14
  38. package/dist/lib/config/branding.js.map +1 -1
  39. package/dist/lib/config/resolved.d.ts +0 -3
  40. package/dist/lib/config/resolved.d.ts.map +1 -1
  41. package/dist/lib/config/security.d.ts +0 -2
  42. package/dist/lib/config/security.d.ts.map +1 -1
  43. package/dist/lib/config/security.js +0 -5
  44. package/dist/lib/config/security.js.map +1 -1
  45. package/dist/middleware/auth.d.ts +0 -8
  46. package/dist/middleware/auth.d.ts.map +1 -1
  47. package/dist/middleware/auth.js +0 -64
  48. package/dist/middleware/auth.js.map +1 -1
  49. package/dist/middleware/session.d.ts +0 -9
  50. package/dist/middleware/session.d.ts.map +1 -1
  51. package/dist/middleware/session.js +0 -18
  52. package/dist/middleware/session.js.map +1 -1
  53. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.d.ts +6 -0
  54. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.d.ts.map +1 -0
  55. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.js +19 -0
  56. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.js.map +1 -0
  57. package/dist/migrations/postgres/index.d.ts.map +1 -1
  58. package/dist/migrations/postgres/index.js +2 -0
  59. package/dist/migrations/postgres/index.js.map +1 -1
  60. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.d.ts +6 -0
  61. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.d.ts.map +1 -0
  62. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.js +19 -0
  63. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.js.map +1 -0
  64. package/dist/migrations/sqlite/index.d.ts.map +1 -1
  65. package/dist/migrations/sqlite/index.js +2 -0
  66. package/dist/migrations/sqlite/index.js.map +1 -1
  67. package/dist/repositories/oauth-code.repository.d.ts +2 -2
  68. package/dist/repositories/oauth-code.repository.d.ts.map +1 -1
  69. package/dist/repositories/oauth-code.repository.js +3 -7
  70. package/dist/repositories/oauth-code.repository.js.map +1 -1
  71. package/dist/repositories/oauth-device-code.repository.d.ts +4 -4
  72. package/dist/repositories/oauth-device-code.repository.d.ts.map +1 -1
  73. package/dist/repositories/oauth-device-code.repository.js +6 -18
  74. package/dist/repositories/oauth-device-code.repository.js.map +1 -1
  75. package/dist/repositories/user-totp-recovery-code.repository.d.ts +1 -7
  76. package/dist/repositories/user-totp-recovery-code.repository.d.ts.map +1 -1
  77. package/dist/repositories/user-totp-recovery-code.repository.js +1 -2
  78. package/dist/repositories/user-totp-recovery-code.repository.js.map +1 -1
  79. package/dist/repositories/user-totp.repository.d.ts +0 -9
  80. package/dist/repositories/user-totp.repository.d.ts.map +1 -1
  81. package/dist/routes/api/auth/index.d.ts +0 -52
  82. package/dist/routes/api/auth/index.d.ts.map +1 -1
  83. package/dist/routes/api/auth/index.js +0 -2
  84. package/dist/routes/api/auth/index.js.map +1 -1
  85. package/dist/routes/api/auth/passkey/verify/post.d.ts +0 -37
  86. package/dist/routes/api/auth/passkey/verify/post.d.ts.map +1 -1
  87. package/dist/routes/api/auth/passkey/verify/post.js +2 -8
  88. package/dist/routes/api/auth/passkey/verify/post.js.map +1 -1
  89. package/dist/routes/api/config/get.d.ts +0 -4
  90. package/dist/routes/api/config/get.d.ts.map +1 -1
  91. package/dist/routes/api/config/get.js +0 -2
  92. package/dist/routes/api/config/get.js.map +1 -1
  93. package/dist/routes/api/config/index.d.ts +0 -4
  94. package/dist/routes/api/config/index.d.ts.map +1 -1
  95. package/dist/routes/api/index.d.ts +0 -56
  96. package/dist/routes/api/index.d.ts.map +1 -1
  97. package/dist/routes/index.d.ts +0 -56
  98. package/dist/routes/index.d.ts.map +1 -1
  99. package/dist/routes/oauth/device/get-post.js +5 -5
  100. package/dist/routes/oauth/device/get-post.js.map +1 -1
  101. package/dist/schemas/error.d.ts +0 -25
  102. package/dist/schemas/error.d.ts.map +1 -1
  103. package/dist/schemas/error.js +0 -1
  104. package/dist/schemas/error.js.map +1 -1
  105. package/dist/schemas/response.d.ts +0 -24
  106. package/dist/schemas/response.d.ts.map +1 -1
  107. package/dist/schemas/response.js +0 -13
  108. package/dist/schemas/response.js.map +1 -1
  109. package/dist/seeders/config.seeder.js +0 -1
  110. package/dist/seeders/config.seeder.js.map +1 -1
  111. package/dist/services/container.d.ts +0 -3
  112. package/dist/services/container.d.ts.map +1 -1
  113. package/dist/services/container.js +0 -15
  114. package/dist/services/container.js.map +1 -1
  115. package/dist/services/oauth-client.service.d.ts +0 -1
  116. package/dist/services/oauth-client.service.d.ts.map +1 -1
  117. package/dist/services/oauth-client.service.js +2 -17
  118. package/dist/services/oauth-client.service.js.map +1 -1
  119. package/dist/services/oauth-connect.service.d.ts.map +1 -1
  120. package/dist/services/oauth-connect.service.js +0 -4
  121. package/dist/services/oauth-connect.service.js.map +1 -1
  122. package/dist/services/oauth-token.service.d.ts.map +1 -1
  123. package/dist/services/oauth-token.service.js +4 -4
  124. package/dist/services/oauth-token.service.js.map +1 -1
  125. package/dist/services/password-auth.service.d.ts.map +1 -1
  126. package/dist/services/password-auth.service.js +3 -12
  127. package/dist/services/password-auth.service.js.map +1 -1
  128. package/dist/services/security.service.d.ts +0 -8
  129. package/dist/services/security.service.d.ts.map +1 -1
  130. package/dist/services/security.service.js +21 -82
  131. package/dist/services/security.service.js.map +1 -1
  132. package/dist/services/totp.service.js +2 -2
  133. package/dist/services/totp.service.js.map +1 -1
  134. package/package.json +2 -1
  135. package/public/assets/2fa-6btYq1AL.js +2 -0
  136. package/public/assets/{2fa-C37MUkye.js.map → 2fa-6btYq1AL.js.map} +1 -1
  137. package/public/assets/2fa-D3ieXkuh.js +2 -0
  138. package/public/assets/{2fa-kiqzGSz2.js.map → 2fa-D3ieXkuh.js.map} +1 -1
  139. package/public/assets/2fa-Dxh4Kxy-.js +2 -0
  140. package/public/assets/{2fa-BgveHSB_.js.map → 2fa-Dxh4Kxy-.js.map} +1 -1
  141. package/public/assets/2fa-UcpXAI9J.js +2 -0
  142. package/public/assets/{2fa-UL_cNRnQ.js.map → 2fa-UcpXAI9J.js.map} +1 -1
  143. package/public/assets/{DialogHandle-Zvz8SgS0.js → DialogHandle-tiMUnjZQ.js} +2 -2
  144. package/public/assets/{DialogHandle-Zvz8SgS0.js.map → DialogHandle-tiMUnjZQ.js.map} +1 -1
  145. package/public/assets/admin-B8RQbf4_.js +2 -0
  146. package/public/assets/admin-B8RQbf4_.js.map +1 -0
  147. package/public/assets/admin-users-Dz1QNdy3.js +2 -0
  148. package/public/assets/{admin-users-CwTJdiih.js.map → admin-users-Dz1QNdy3.js.map} +1 -1
  149. package/public/assets/alert-leqyvHOp.js.map +1 -1
  150. package/public/assets/{auth-choice-row-BlSsUI7d.js → auth-choice-row-DWzfcZQY.js} +2 -2
  151. package/public/assets/{auth-choice-row-BlSsUI7d.js.map → auth-choice-row-DWzfcZQY.js.map} +1 -1
  152. package/public/assets/{auth-field-HwxENJnB.js → auth-field-DsLGh1U-.js} +2 -2
  153. package/public/assets/{auth-field-HwxENJnB.js.map → auth-field-DsLGh1U-.js.map} +1 -1
  154. package/public/assets/auth-layout-DGTylGtX.js +2 -0
  155. package/public/assets/auth-layout-DGTylGtX.js.map +1 -0
  156. package/public/assets/{auth-page-header-CIs08f5Y.js → auth-page-header-COvqWINf.js} +2 -2
  157. package/public/assets/auth-page-header-COvqWINf.js.map +1 -0
  158. package/public/assets/authorization-context-banner-DVx1TsPY.js +2 -0
  159. package/public/assets/authorization-context-banner-DVx1TsPY.js.map +1 -0
  160. package/public/assets/{avatar-CttWiIv3.js → avatar-BCLugfaZ.js} +2 -2
  161. package/public/assets/{avatar-CttWiIv3.js.map → avatar-BCLugfaZ.js.map} +1 -1
  162. package/public/assets/badge-CsOfUji0.js.map +1 -1
  163. package/public/assets/{checkbox-CJrRV8S1.js → checkbox-CFVDS3zJ.js} +2 -2
  164. package/public/assets/{checkbox-CJrRV8S1.js.map → checkbox-CFVDS3zJ.js.map} +1 -1
  165. package/public/assets/config-DTQCUlfZ.js +2 -0
  166. package/public/assets/config-DTQCUlfZ.js.map +1 -0
  167. package/public/assets/{consent-BIhxtXHx.js → consent-CKOohdYI.js} +2 -2
  168. package/public/assets/{consent-BIhxtXHx.js.map → consent-CKOohdYI.js.map} +1 -1
  169. package/public/assets/{consent-DRmENn26.js → consent-ChiWc7Ql.js} +2 -2
  170. package/public/assets/{consent-DRmENn26.js.map → consent-ChiWc7Ql.js.map} +1 -1
  171. package/public/assets/createLucideIcon-BPpAIXR_.js.map +1 -1
  172. package/public/assets/{email-CVRDeJ78.js → email-Crjg1dcr.js} +2 -2
  173. package/public/assets/{email-CVRDeJ78.js.map → email-Crjg1dcr.js.map} +1 -1
  174. package/public/assets/email-UcpXAI9J.js +2 -0
  175. package/public/assets/{email-UL_cNRnQ.js.map → email-UcpXAI9J.js.map} +1 -1
  176. package/public/assets/error-VfMv226c.js +2 -0
  177. package/public/assets/{error-2VPY5w8V.js.map → error-VfMv226c.js.map} +1 -1
  178. package/public/assets/{field-DDt6F9TZ.js → field-89zKYDVL.js} +2 -2
  179. package/public/assets/{field-DDt6F9TZ.js.map → field-89zKYDVL.js.map} +1 -1
  180. package/public/assets/forgot-Dr88fVMq.js +2 -0
  181. package/public/assets/{forgot-DzDcduho.js.map → forgot-Dr88fVMq.js.map} +1 -1
  182. package/public/assets/forgot-UcpXAI9J.js +2 -0
  183. package/public/assets/{forgot-UL_cNRnQ.js.map → forgot-UcpXAI9J.js.map} +1 -1
  184. package/public/assets/{index-DZyHRhbQ.css → index-CDD8owGK.css} +1 -1
  185. package/public/assets/index-CZ0rWM7l.js +11 -0
  186. package/public/assets/index-CZ0rWM7l.js.map +1 -0
  187. package/public/assets/{initial-avatar-CZzHAPm3.js → initial-avatar-ocwNWL38.js} +2 -2
  188. package/public/assets/{initial-avatar-CZzHAPm3.js.map → initial-avatar-ocwNWL38.js.map} +1 -1
  189. package/public/assets/{input-CNRpSR08.js → input-GPigtjgi.js} +2 -2
  190. package/public/assets/{input-CNRpSR08.js.map → input-GPigtjgi.js.map} +1 -1
  191. package/public/assets/{labeled-separator-fnKySKv2.js → labeled-separator-CV7nb0XW.js} +2 -2
  192. package/public/assets/{labeled-separator-fnKySKv2.js.map → labeled-separator-CV7nb0XW.js.map} +1 -1
  193. package/public/assets/link-C-WdTj3F.js.map +1 -1
  194. package/public/assets/login-CQeTLmIu.js +2 -0
  195. package/public/assets/login-CQeTLmIu.js.map +1 -0
  196. package/public/assets/login-UcpXAI9J.js +2 -0
  197. package/public/assets/login-UcpXAI9J.js.map +1 -0
  198. package/public/assets/{logout-CHdmF_16.js → logout-B5T52P8u.js} +2 -2
  199. package/public/assets/{logout-CHdmF_16.js.map → logout-B5T52P8u.js.map} +1 -1
  200. package/public/assets/{modal-sCM_zT82.js → modal-BV3gpI-p.js} +2 -2
  201. package/public/assets/{modal-sCM_zT82.js.map → modal-BV3gpI-p.js.map} +1 -1
  202. package/public/assets/{mutationOptions-CrswitPs.js → mutationOptions-DyYFUbyn.js} +2 -2
  203. package/public/assets/{mutationOptions-CrswitPs.js.map → mutationOptions-DyYFUbyn.js.map} +1 -1
  204. package/public/assets/passkey-Bf7n49Z7.js +2 -0
  205. package/public/assets/{passkey-CEx1VZ5R.js.map → passkey-Bf7n49Z7.js.map} +1 -1
  206. package/public/assets/passkey-CT3G7pCL.js +2 -0
  207. package/public/assets/{passkey-CU8x9J2w.js.map → passkey-CT3G7pCL.js.map} +1 -1
  208. package/public/assets/passkey-J1qnYp9A.js +2 -0
  209. package/public/assets/{passkey-B2Blw47e.js.map → passkey-J1qnYp9A.js.map} +1 -1
  210. package/public/assets/passkey-UcpXAI9J.js +2 -0
  211. package/public/assets/{passkey-UL_cNRnQ.js.map → passkey-UcpXAI9J.js.map} +1 -1
  212. package/public/assets/password-BKI5GTRs.js +2 -0
  213. package/public/assets/password-BKI5GTRs.js.map +1 -0
  214. package/public/assets/password-UcpXAI9J.js +2 -0
  215. package/public/assets/password-UcpXAI9J.js.map +1 -0
  216. package/public/assets/password-reset-sU5KkBKi.js +2 -0
  217. package/public/assets/{password-reset-CIacHnvA.js.map → password-reset-sU5KkBKi.js.map} +1 -1
  218. package/public/assets/{password-strength-C0Zkrrct.js → password-strength-DTWF07Px.js} +2 -2
  219. package/public/assets/{password-strength-C0Zkrrct.js.map → password-strength-DTWF07Px.js.map} +1 -1
  220. package/public/assets/{pin-input-BlD4tPZJ.js → pin-input-BGOLfkwL.js} +2 -2
  221. package/public/assets/{pin-input-BlD4tPZJ.js.map → pin-input-BGOLfkwL.js.map} +1 -1
  222. package/public/assets/{profile-D43Yq-6S.js → profile-DSTMtoUf.js} +2 -2
  223. package/public/assets/{profile-D43Yq-6S.js.map → profile-DSTMtoUf.js.map} +1 -1
  224. package/public/assets/profile-RpDmbWNA.js +2 -0
  225. package/public/assets/profile-RpDmbWNA.js.map +1 -0
  226. package/public/assets/provider-logos-u1YURWQr.js.map +1 -1
  227. package/public/assets/recovery-DbgpqG6f.js +2 -0
  228. package/public/assets/{recovery-CLExnfYX.js.map → recovery-DbgpqG6f.js.map} +1 -1
  229. package/public/assets/register-B2Lixapy.js +2 -0
  230. package/public/assets/{register-CZUyWXRO.js.map → register-B2Lixapy.js.map} +1 -1
  231. package/public/assets/register-UcpXAI9J.js +2 -0
  232. package/public/assets/{register-UL_cNRnQ.js.map → register-UcpXAI9J.js.map} +1 -1
  233. package/public/assets/reset-C-Qjl2Qy.js +2 -0
  234. package/public/assets/reset-C-Qjl2Qy.js.map +1 -0
  235. package/public/assets/reset-UcpXAI9J.js +2 -0
  236. package/public/assets/{reset-UL_cNRnQ.js.map → reset-UcpXAI9J.js.map} +1 -1
  237. package/public/assets/{select-CWdSzsVo.js → select-BKx4bF4A.js} +2 -2
  238. package/public/assets/{select-CWdSzsVo.js.map → select-BKx4bF4A.js.map} +1 -1
  239. package/public/assets/select-UcpXAI9J.js +2 -0
  240. package/public/assets/{select-UL_cNRnQ.js.map → select-UcpXAI9J.js.map} +1 -1
  241. package/public/assets/{separator-BWGym8uM.js → separator-B0Bvh1he.js} +2 -2
  242. package/public/assets/{separator-BWGym8uM.js.map → separator-B0Bvh1he.js.map} +1 -1
  243. package/public/assets/shell-header-bar-CgyNXbPS.js +2 -0
  244. package/public/assets/shell-header-bar-CgyNXbPS.js.map +1 -0
  245. package/public/assets/{terms-D43Yq-6S.js → terms-DSTMtoUf.js} +2 -2
  246. package/public/assets/{terms-D43Yq-6S.js.map → terms-DSTMtoUf.js.map} +1 -1
  247. package/public/assets/{terms-COVFmzyl.js → terms-Vgpg5IZK.js} +2 -2
  248. package/public/assets/{terms-COVFmzyl.js.map → terms-Vgpg5IZK.js.map} +1 -1
  249. package/public/assets/{terms-checkbox-list-Cw0zB-lO.js → terms-checkbox-list-DqbXU4qw.js} +2 -2
  250. package/public/assets/{terms-checkbox-list-Cw0zB-lO.js.map → terms-checkbox-list-DqbXU4qw.js.map} +1 -1
  251. package/public/assets/{totp-BfCm6IWl.js → totp-BNZ904Zm.js} +2 -2
  252. package/public/assets/{totp-BfCm6IWl.js.map → totp-BNZ904Zm.js.map} +1 -1
  253. package/public/assets/totp-Cjns-PoB.js +2 -0
  254. package/public/assets/{totp-BFJEXxk0.js.map → totp-Cjns-PoB.js.map} +1 -1
  255. package/public/assets/totp-g0-s50HZ.js +2 -0
  256. package/public/assets/{totp-MINhsVxT.js.map → totp-g0-s50HZ.js.map} +1 -1
  257. package/public/assets/use-totp-setup-CtTaklH-.js +3 -0
  258. package/public/assets/{use-totp-setup-C7Zk_2IY.js.map → use-totp-setup-CtTaklH-.js.map} +1 -1
  259. package/public/assets/{useAriaLabelledBy-CiOCg6X5.js → useAriaLabelledBy-7uIPXK-0.js} +2 -2
  260. package/public/assets/{useAriaLabelledBy-CiOCg6X5.js.map → useAriaLabelledBy-7uIPXK-0.js.map} +1 -1
  261. package/public/assets/{useMutation-ZLFeJg75.js → useMutation-BWZ0G4Td.js} +2 -2
  262. package/public/assets/{useMutation-ZLFeJg75.js.map → useMutation-BWZ0G4Td.js.map} +1 -1
  263. package/public/assets/useQuery-pOdkMV4B.js +2 -0
  264. package/public/assets/{useQuery-BvMz34BW.js.map → useQuery-pOdkMV4B.js.map} +1 -1
  265. package/public/assets/users-B1hvVo3B.js +2 -0
  266. package/public/assets/users-B1hvVo3B.js.map +1 -0
  267. package/public/index.html +4 -5
  268. package/dist/routes/api/auth/password/reset-required/post.d.ts +0 -18
  269. package/dist/routes/api/auth/password/reset-required/post.d.ts.map +0 -1
  270. package/dist/routes/api/auth/password/reset-required/post.js +0 -39
  271. package/dist/routes/api/auth/password/reset-required/post.js.map +0 -1
  272. package/dist/services/legacy-credential-retirement.service.d.ts +0 -20
  273. package/dist/services/legacy-credential-retirement.service.d.ts.map +0 -1
  274. package/dist/services/legacy-credential-retirement.service.js +0 -90
  275. package/dist/services/legacy-credential-retirement.service.js.map +0 -1
  276. package/public/assets/2fa-BgveHSB_.js +0 -2
  277. package/public/assets/2fa-C37MUkye.js +0 -2
  278. package/public/assets/2fa-UL_cNRnQ.js +0 -2
  279. package/public/assets/2fa-kiqzGSz2.js +0 -2
  280. package/public/assets/admin-DbDuu6AI.js +0 -2
  281. package/public/assets/admin-DbDuu6AI.js.map +0 -1
  282. package/public/assets/admin-users-CwTJdiih.js +0 -2
  283. package/public/assets/auth-layout-CoeqNhTT.js +0 -2
  284. package/public/assets/auth-layout-CoeqNhTT.js.map +0 -1
  285. package/public/assets/auth-page-header-CIs08f5Y.js.map +0 -1
  286. package/public/assets/authorization-context-banner-BbtQmMYH.js +0 -2
  287. package/public/assets/authorization-context-banner-BbtQmMYH.js.map +0 -1
  288. package/public/assets/config-v3-oy_ef.js +0 -2
  289. package/public/assets/config-v3-oy_ef.js.map +0 -1
  290. package/public/assets/email-UL_cNRnQ.js +0 -2
  291. package/public/assets/error-2VPY5w8V.js +0 -2
  292. package/public/assets/forgot-DzDcduho.js +0 -2
  293. package/public/assets/forgot-UL_cNRnQ.js +0 -2
  294. package/public/assets/index-gliHKncz.js +0 -11
  295. package/public/assets/index-gliHKncz.js.map +0 -1
  296. package/public/assets/link-BN90emXr.js +0 -2
  297. package/public/assets/link-BN90emXr.js.map +0 -1
  298. package/public/assets/lock-keyhole-Cz72xkCn.js +0 -2
  299. package/public/assets/lock-keyhole-Cz72xkCn.js.map +0 -1
  300. package/public/assets/login-C6m2qSUt.js +0 -2
  301. package/public/assets/login-C6m2qSUt.js.map +0 -1
  302. package/public/assets/login-UL_cNRnQ.js +0 -2
  303. package/public/assets/login-UL_cNRnQ.js.map +0 -1
  304. package/public/assets/passkey-B2Blw47e.js +0 -2
  305. package/public/assets/passkey-CEx1VZ5R.js +0 -2
  306. package/public/assets/passkey-CU8x9J2w.js +0 -2
  307. package/public/assets/passkey-UL_cNRnQ.js +0 -2
  308. package/public/assets/password-BhU_a0cR.js +0 -2
  309. package/public/assets/password-BhU_a0cR.js.map +0 -1
  310. package/public/assets/password-UL_cNRnQ.js +0 -2
  311. package/public/assets/password-UL_cNRnQ.js.map +0 -1
  312. package/public/assets/password-reset-CIacHnvA.js +0 -2
  313. package/public/assets/profile-D1cEjfs8.js +0 -2
  314. package/public/assets/profile-D1cEjfs8.js.map +0 -1
  315. package/public/assets/recovery-CLExnfYX.js +0 -2
  316. package/public/assets/register-CZUyWXRO.js +0 -2
  317. package/public/assets/register-UL_cNRnQ.js +0 -2
  318. package/public/assets/reset-N3RQTYEB.js +0 -2
  319. package/public/assets/reset-N3RQTYEB.js.map +0 -1
  320. package/public/assets/reset-UL_cNRnQ.js +0 -2
  321. package/public/assets/reset-required-Cv5edDS6.js +0 -2
  322. package/public/assets/reset-required-Cv5edDS6.js.map +0 -1
  323. package/public/assets/reset-required-UL_cNRnQ.js +0 -2
  324. package/public/assets/reset-required-UL_cNRnQ.js.map +0 -1
  325. package/public/assets/select-UL_cNRnQ.js +0 -2
  326. package/public/assets/shell-header-bar-C9AB5lT-.js +0 -2
  327. package/public/assets/shell-header-bar-C9AB5lT-.js.map +0 -1
  328. package/public/assets/totp-BFJEXxk0.js +0 -2
  329. package/public/assets/totp-MINhsVxT.js +0 -2
  330. package/public/assets/use-totp-setup-C7Zk_2IY.js +0 -3
  331. package/public/assets/useQuery-BvMz34BW.js +0 -2
  332. package/public/assets/users-COM7u3A-.js +0 -2
  333. package/public/assets/users-COM7u3A-.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"register-CZUyWXRO.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","Link","useNavigate","TRButton","LockIcon","MailIcon","useDeferredValue","useMemo","useForm","useTranslation","z","AuthField","AuthFooter","AuthFooterLink","AuthPageHeader","AuthorizationContextBanner","PasswordStrength","TermsCheckboxList","LabeledSeparator","AuthLayout","IssuaryError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","appConfigQueryOptions","registerMutationOptions","getSessionQueryOptions","getTermsQueryOptions","Route","Register","t","i18n","navigate","queryClient","search","useSearch","lang","language","deferredLang","data","configData","termsData","isPasswordAuthEnabled","auth","password","enabled","passwordPolicy","policy","hasTerms","terms","length","explicitTerms","filter","term","consentMode","hasExplicitTerms","implicitNotice","registration","signup_notice","fallback_language","registerSchema","object","email","string","min","pipe","min_length","count","max","max_length","termsConsents","Object","fromEntries","map","id","required","literal","message","boolean","RegisterFormValues","infer","registerMutation","onSuccess","params","user","email_verification_required","email_verified","to","setQueryData","queryKey","second_factor_required","available_2fa_methods","SecondFactorMethod","totp","push","passkey","method","passkey_name","window","location","href","onError","error","code","setError","type","onSettled","invalidateQueries","register","handleSubmit","control","setValue","watch","formState","errors","defaultValues","resolver","mode","passwordValue","onSubmit","values","consents","termsId","agreed","undefined","mutate","__html","isPending","component"],"sources":["../../../frontend/src/queries/register.ts","../../../frontend/src/routes/register/index.tsx?tsr-split=component"],"sourcesContent":["import { mutationOptions } from '@tanstack/react-query';\nimport type { InferResponseType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\nimport type { TermsConsentItem } from './terms';\n\nexport type RegisterParams = {\n email: string;\n password: string;\n consents?: TermsConsentItem[];\n};\n\nexport type RegisterResponse = InferResponseType<\n (typeof client.api.auth.register)['$post'],\n 200\n>;\n\nexport const registerMutationOptions = mutationOptions({\n mutationFn: async (values: RegisterParams) => {\n const res = await client.api.auth.register.$post({\n json: values,\n header: {},\n });\n return jsonOk(res);\n },\n});\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { LockIcon, MailIcon } from 'lucide-react';\nimport { useDeferredValue, useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { TermsCheckboxList } from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerMutationOptions } from '#frontend/queries/register.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { getTermsQueryOptions } from '#frontend/queries/terms.ts';\n\nexport const Route = createFileRoute('/register/')({\n component: Register,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n if (!config.registration.public_registration) {\n throw redirect({\n to: '/',\n replace: true,\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n search,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n const queries: Array<Promise<unknown>> = [\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n ];\n\n if (hasAuthorizationContext(deps.search)) {\n queries.push(\n context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n ),\n );\n }\n\n await Promise.all(queries);\n },\n});\n\nfunction Register() {\n const { t, i18n } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const deferredLang = useDeferredValue(lang);\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const { data: termsData } = useSuspenseQuery(\n getTermsQueryOptions(deferredLang),\n );\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const passwordPolicy = configData.auth.password.policy;\n\n // Check if there are any terms to display\n const hasTerms = termsData.terms.length > 0;\n const explicitTerms = useMemo(\n () => termsData.terms.filter((term) => term.consentMode === 'explicit'),\n [termsData.terms],\n );\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Get implicit notice from config\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n const registerSchema = useMemo(\n () =>\n z.object({\n email: z\n .string()\n .min(1, t('validation.email.required'))\n .pipe(z.email(t('validation.email.invalid'))),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [explicitTerms, passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n type RegisterFormValues = z.infer<typeof registerSchema>;\n\n const registerMutation = useMutation({\n ...registerMutationOptions,\n onSuccess: async (data, params) => {\n const user = data.user;\n\n if (user.email_verification_required && !user.email_verified) {\n return navigate({\n to: '/verify/email',\n search: {\n email: params.email,\n ...extractOAuthParams(search),\n },\n });\n }\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (configData.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (configData.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: {\n ...extractOAuthParams(search),\n passkey_name: 'default',\n },\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n if (error instanceof IssuaryError) {\n if (error.code === 'REGISTRATION_EMAIL_NOT_ALLOWED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailNotAllowed'),\n });\n } else if (error.code === 'REGISTRATION_DISABLED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.registrationDisabled'),\n });\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n },\n onSettled: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n control,\n setValue,\n watch,\n formState: { errors },\n } = useForm<RegisterFormValues>({\n defaultValues: {\n email: '',\n password: '',\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [term.id, false]),\n ),\n },\n resolver: standardSchemaResolver(registerSchema),\n mode: 'onChange',\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = (values: RegisterFormValues) => {\n // Build consents array for registration\n // Only send explicit consents - implicit ones are handled by backend\n const consents = hasTerms\n ? explicitTerms.map((term) => ({\n termsId: term.id,\n agreed: values.termsConsents[term.id] ?? false,\n }))\n : undefined;\n\n registerMutation.mutate({\n email: values.email,\n password: values.password,\n consents,\n });\n };\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('register.subtitle')}\n title={t('register.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {isPasswordAuthEnabled && (\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n autoComplete=\"email\"\n error={errors.email}\n icon={MailIcon}\n label={t('register.email.label')}\n placeholder={t('register.email.placeholder')}\n {...register('email')}\n type=\"email\"\n />\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n hint={t('register.password.hint', {\n count: passwordPolicy.min_length,\n })}\n icon={LockIcon}\n label={t('register.password.label')}\n placeholder={t('register.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength\n password={passwordValue}\n policy={passwordPolicy}\n />\n </div>\n\n <div className=\"flex flex-col gap-tinyrack-md\">\n {implicitNotice && (\n <div className=\"text-center text-tinyrack-text-muted text-tinyrack-xs\">\n <div\n className=\"prose prose-sm text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{\n __html: implicitNotice,\n }}\n data-testid=\"terms-implicit-notice\"\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <LabeledSeparator label={t('terms.additionalOptionalConsent')} />\n )}\n\n {hasTerms && hasExplicitTerms && (\n <TermsCheckboxList\n control={control}\n disabled={registerMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n )}\n </div>\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('register.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('register.submit')}\n </TRButton>\n </form>\n )}\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('register.link.login')}\n </Link>\n }\n text={t('register.footer.haveAccount')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"qkCAgBA,EAAA,EAAA,CAAA,WAAA,KAAA,0DAOE,CAAA,QC8DF,SAASiC,GAAW,CAClB,GAAM,CAAEC,IAAGC,QAASvB,EAAe,EAC7BwB,EAAW/B,GAAY,EACvBgC,EAAcnC,EAAe,EAC7BoC,EAASN,EAAMO,UAAU,EAEzBC,EAAOF,EAAOE,MAAQL,EAAKM,SAC3BC,GAAAA,EAAAA,EAAAA,iBAAAA,CAAgCF,CAAI,EAEpC,CAAEG,KAAMC,GAAezC,EAAiByB,CAAqB,EAC7D,CAAEe,KAAME,GAAc1C,EAC1B4B,EAAqBW,CAAY,CACnC,EAEMI,EAAwBF,EAAWG,KAAKC,SAASC,QACjDC,EAAiBN,EAAWG,KAAKC,SAASG,OAG1CC,EAAWP,EAAUQ,MAAMC,OAAS,EACpCC,GAAAA,EAAAA,EAAAA,QAAAA,KACEV,EAAUQ,MAAMG,OAAQC,GAASA,EAAKC,cAAgB,UAAU,EACtE,CAACb,EAAUQ,KAAK,CAClB,EACMM,EAAmBJ,EAAcD,OAAS,EAG1CM,EACJhB,EAAWiB,aAAaC,gBAAgBtB,IACxCI,EAAWiB,aAAaC,gBAAgBlB,EAAWT,KAAK4B,mBAEpDC,GAAAA,EAAAA,EAAAA,QAAAA,KAEFnD,EAAS,CACPqD,MAAOrD,EACG,CAAC,CACRuD,IAAI,EAAGlC,EAAE,2BAA2B,CAAC,CAAC,CACtCmC,KAAKxD,EAAQqB,EAAE,0BAA0B,CAAC,CAAC,EAC9Cc,SAAUnC,EACA,CAAC,CACRuD,IACClB,EAAeoB,WACfpC,EAAE,0BAA2B,CAC3BqC,MAAOrB,EAAeoB,UACxB,CAAC,CACH,CAAC,CACAE,IACCtB,EAAeuB,WACfvC,EAAE,0BAA2B,CAC3BqC,MAAOrB,EAAeuB,UACxB,CAAC,CACH,EACFC,cAAe7D,EACb8D,OAAOC,YACLrB,EAAcsB,IAAKpB,GAAS,CAC1BA,EAAKqB,GACLrB,EAAKsB,SACDlE,EAAU,GAAM,CACdoE,QAAS/C,EAAE,2BAA2B,CACxC,CAAC,EACDrB,EAAU,CAAC,CAChB,CACH,CACF,CACF,CAAC,EACH,CAAC0C,EAAeL,EAAeuB,WAAYvB,EAAeoB,WAAYpC,CAAC,CACzE,EAIMmD,EAAmBpF,EAAY,CACnC,GAAG4B,EACHyD,UAAW,MAAO3C,EAAM4C,IAAW,CACjC,IAAMC,EAAO7C,EAAK6C,KAElB,GAAIA,EAAKC,6BAA+B,CAACD,EAAKE,eAC5C,OAAOtD,EAAS,CACduD,GAAI,gBACJrD,OAAQ,CACN4B,MAAOqB,EAAOrB,MACd,GAAGzC,EAAmBa,CAAM,CAC9B,CACF,CAAC,EAQH,GALAD,EAAYuD,aAAa9D,EAAuB+D,SAAU,CAClDL,MACR,CAAC,EACD,MAAM7D,EAAK,EAEP6D,EAAKM,uBAAwB,CAC/B,IAAMC,EAA8C,CAAA,EAyBlD,OAxBEnD,EAAWG,KAAKC,SAASiD,KAAKhD,SAChC8C,EAAsBG,KAAK,MAAM,EAE/BtD,EAAWG,KAAKoD,QAAQlD,SAC1B8C,EAAsBG,KAAK,SAAS,EAGlCH,EAAsBzC,SAAW,EACpByC,EAAsB,KACtB,OACN3D,EAAS,CACduD,GAAI,cACJrD,OAAQb,EAAmBa,CAAM,CACnC,CAAC,EAEMF,EAAS,CACduD,GAAI,iBACJrD,OAAQ,CACN,GAAGb,EAAmBa,CAAM,EAC5B+D,aAAc,SAChB,CACF,CAAC,EAGIjE,EAAS,CACduD,GAAI,aACJrD,OAAQb,EAAmBa,CAAM,CACnC,CAAC,CAEL,CAEIZ,EAAYY,CAAM,EACpBgE,OAAOC,SAASC,KAAOhF,GAA+Bc,CAAM,EAE5DF,EAAS,CAAEuD,GAAI,UAAW,CAAC,CAE/B,EACAc,QAAUC,GAAU,CACdA,aAAiBnF,GACfmF,EAAMC,OAAS,iCACjBC,EAAS,QAAS,CAChBC,KAAM,SACN5B,QAAS/C,EAAE,gCAAgC,CAC7C,CAAC,EACQwE,EAAMC,OAAS,wBACxBC,EAAS,QAAS,CAChBC,KAAM,SACN5B,QAAS/C,EAAE,qCAAqC,CAClD,CAAC,EAED0E,EAAS,QAAS,CAChBC,KAAM,SACN5B,QAAS/C,EAAE,4BAA4B,CACzC,CAAC,EAGH0E,EAAS,QAAS,CAChBC,KAAM,SACN5B,QAAS/C,EAAE,4BAA4B,CACzC,CAAC,CAEL,EACA4E,UAAW,SAAY,CACrB,MAAMzE,EAAY0E,kBAAkB,CAClClB,SAAU/D,EAAuB+D,QACnC,CAAC,CACH,CACF,CAAC,EAEK,CACJmB,WACAJ,WACAK,gBACAC,WACAC,YACAC,SACAC,UAAW,CAAEC,WACX3G,EAA4B,CAC9B4G,cAAe,CACbrD,MAAO,GACPlB,SAAU,GACV0B,cAAeC,OAAOC,YACpBrB,EAAcsB,IAAKpB,GAAS,CAACA,EAAKqB,GAAI,EAAK,CAAC,CAC9C,CACF,EACA0C,SAAUxH,EAAuBgE,CAAc,EAC/CyD,KAAM,UACR,CAAC,EAEKC,GAAgBN,GAAM,UAAU,EAmBtC,OACE,EAAA,EAAA,KAAA,CAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,SAAUlF,EAAE,mBAAmB,EAC/B,MAAOA,EAAE,gBAAgB,CAAE,CAAA,GAG7B,EAAA,EAAA,IAAA,CAAC,EAAD,CAAoCI,QAAO,CAAA,EAE1CQ,IACC,EAAA,EAAA,KAAA,CAAC,OAAD,CACE,UAAU,gCACV,SAAUmE,GA7BAW,GAA+B,CAG/C,IAAMC,EAAWzE,EACbG,EAAcsB,IAAKpB,IAAU,CAC3BqE,QAASrE,EAAKqB,GACdiD,OAAQH,EAAOlD,cAAcjB,EAAKqB,KAAO,EAC3C,EAAE,EACFkD,IAAAA,GAEJ3C,EAAiB4C,OAAO,CACtB/D,MAAO0D,EAAO1D,MACdlB,SAAU4E,EAAO5E,SACjB6E,UACF,CAAC,CACH,CAcuC,WAFjC,EAIE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,aAAa,QACb,MAAOP,EAAOpD,MACd,KAAM1D,EACN,MAAO0B,EAAE,sBAAsB,EAC/B,YAAaA,EAAE,4BAA4B,EAC3C,GAAI8E,EAAS,OAAO,EACpB,KAAK,OAAO,CAAA,GAGd,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,yCAAf,EACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,aAAa,eACb,MAAOM,EAAOtE,SACd,KAAMd,EAAE,yBAA0B,CAChCqC,MAAOrB,EAAeoB,UACxB,CAAC,EACD,KAAM/D,EACN,MAAO2B,EAAE,yBAAyB,EAClC,YAAaA,EAAE,+BAA+B,EAC9C,GAAI8E,EAAS,UAAU,EACvB,KAAK,UAAU,CAAA,GAGjB,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,SAAUU,GACV,OAAQxE,CAAe,CAAA,CAEtB,KAEL,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,yCAAf,CACGU,IACC,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,kEACb,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,UAAU,wDACV,wBAAyB,CACvBsE,OAAQtE,CACV,EACA,cAAY,uBAAuB,CAAA,CAElC,CAAA,EAGNA,GAAkBD,IACjB,EAAA,EAAA,IAAA,CAAC,EAAD,CAAkB,MAAOzB,EAAE,iCAAiC,CAAE,CAAA,EAG/DkB,GAAYO,IACX,EAAA,EAAA,IAAA,CAAC,EAAD,CACWuD,WACT,SAAU7B,EAAiB8C,UACnBb,SACEH,YACV,MAAO5D,CAAc,CAAA,CAGtB,KAEL,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,UAAU,SACV,OAAO,UACP,QAAS8B,EAAiB8C,UAC1B,aAAcjG,EAAE,qBAAqB,EACrC,KAAK,SACL,OAAO,cAENA,EAAE,iBAAiB,CACZ,CAAA,CACN,KAGR,EAAA,EAAA,IAAA,CAAC,EAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,MACE,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,OAAQT,EAAmBa,CAAM,EAAG,GAAG,kBAC1CJ,EAAE,qBAAqB,CACpB,CAAA,EAER,KAAMA,EAAE,6BAA6B,CAAE,CAAA,CAE/B,CAAA,CACF,CAAA,CAAA,CAEhB"}
1
+ {"version":3,"file":"register-B2Lixapy.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","Link","useNavigate","TRButton","LockIcon","MailIcon","useDeferredValue","useMemo","useForm","useTranslation","z","AuthField","AuthFooter","AuthFooterLink","AuthPageHeader","AuthorizationContextBanner","PasswordStrength","TermsCheckboxList","LabeledSeparator","AuthLayout","IssuaryError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","appConfigQueryOptions","registerMutationOptions","getSessionQueryOptions","getTermsQueryOptions","Route","Register","t","i18n","navigate","queryClient","search","useSearch","lang","language","deferredLang","data","configData","termsData","isPasswordAuthEnabled","auth","password","enabled","passwordPolicy","policy","hasTerms","terms","length","explicitTerms","filter","term","consentMode","hasExplicitTerms","implicitNotice","registration","signup_notice","fallback_language","registerSchema","object","email","string","min","pipe","min_length","count","max","max_length","termsConsents","Object","fromEntries","map","id","required","literal","message","boolean","RegisterFormValues","infer","registerMutation","onSuccess","params","user","email_verification_required","email_verified","to","setQueryData","queryKey","second_factor_required","available_2fa_methods","SecondFactorMethod","totp","push","passkey","method","passkey_name","window","location","href","onError","error","code","setError","type","onSettled","invalidateQueries","register","handleSubmit","control","setValue","watch","formState","errors","defaultValues","resolver","mode","passwordValue","onSubmit","values","consents","termsId","agreed","undefined","mutate","__html","isPending","component"],"sources":["../../../frontend/src/queries/register.ts","../../../frontend/src/routes/register/index.tsx?tsr-split=component"],"sourcesContent":["import { mutationOptions } from '@tanstack/react-query';\nimport type { InferResponseType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\nimport type { TermsConsentItem } from './terms';\n\nexport type RegisterParams = {\n email: string;\n password: string;\n consents?: TermsConsentItem[];\n};\n\nexport type RegisterResponse = InferResponseType<\n (typeof client.api.auth.register)['$post'],\n 200\n>;\n\nexport const registerMutationOptions = mutationOptions({\n mutationFn: async (values: RegisterParams) => {\n const res = await client.api.auth.register.$post({\n json: values,\n header: {},\n });\n return jsonOk(res);\n },\n});\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { LockIcon, MailIcon } from 'lucide-react';\nimport { useDeferredValue, useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { TermsCheckboxList } from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerMutationOptions } from '#frontend/queries/register.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { getTermsQueryOptions } from '#frontend/queries/terms.ts';\n\nexport const Route = createFileRoute('/register/')({\n component: Register,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n if (!config.registration.public_registration) {\n throw redirect({\n to: '/',\n replace: true,\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n search,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n const queries: Array<Promise<unknown>> = [\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n ];\n\n if (hasAuthorizationContext(deps.search)) {\n queries.push(\n context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n ),\n );\n }\n\n await Promise.all(queries);\n },\n});\n\nfunction Register() {\n const { t, i18n } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const deferredLang = useDeferredValue(lang);\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const { data: termsData } = useSuspenseQuery(\n getTermsQueryOptions(deferredLang),\n );\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const passwordPolicy = configData.auth.password.policy;\n\n // Check if there are any terms to display\n const hasTerms = termsData.terms.length > 0;\n const explicitTerms = useMemo(\n () => termsData.terms.filter((term) => term.consentMode === 'explicit'),\n [termsData.terms],\n );\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Get implicit notice from config\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n const registerSchema = useMemo(\n () =>\n z.object({\n email: z\n .string()\n .min(1, t('validation.email.required'))\n .pipe(z.email(t('validation.email.invalid'))),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [explicitTerms, passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n type RegisterFormValues = z.infer<typeof registerSchema>;\n\n const registerMutation = useMutation({\n ...registerMutationOptions,\n onSuccess: async (data, params) => {\n const user = data.user;\n\n if (user.email_verification_required && !user.email_verified) {\n return navigate({\n to: '/verify/email',\n search: {\n email: params.email,\n ...extractOAuthParams(search),\n },\n });\n }\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (configData.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (configData.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: {\n ...extractOAuthParams(search),\n passkey_name: 'default',\n },\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n if (error instanceof IssuaryError) {\n if (error.code === 'REGISTRATION_EMAIL_NOT_ALLOWED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailNotAllowed'),\n });\n } else if (error.code === 'REGISTRATION_DISABLED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.registrationDisabled'),\n });\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n },\n onSettled: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n control,\n setValue,\n watch,\n formState: { errors },\n } = useForm<RegisterFormValues>({\n defaultValues: {\n email: '',\n password: '',\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [term.id, false]),\n ),\n },\n resolver: standardSchemaResolver(registerSchema),\n mode: 'onChange',\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = (values: RegisterFormValues) => {\n // Build consents array for registration\n // Only send explicit consents - implicit ones are handled by backend\n const consents = hasTerms\n ? explicitTerms.map((term) => ({\n termsId: term.id,\n agreed: values.termsConsents[term.id] ?? false,\n }))\n : undefined;\n\n registerMutation.mutate({\n email: values.email,\n password: values.password,\n consents,\n });\n };\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('register.subtitle')}\n title={t('register.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {isPasswordAuthEnabled && (\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n autoComplete=\"email\"\n error={errors.email}\n icon={MailIcon}\n label={t('register.email.label')}\n placeholder={t('register.email.placeholder')}\n {...register('email')}\n type=\"email\"\n />\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n hint={t('register.password.hint', {\n count: passwordPolicy.min_length,\n })}\n icon={LockIcon}\n label={t('register.password.label')}\n placeholder={t('register.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength\n password={passwordValue}\n policy={passwordPolicy}\n />\n </div>\n\n <div className=\"flex flex-col gap-tinyrack-md\">\n {implicitNotice && (\n <div className=\"text-center text-tinyrack-text-muted text-tinyrack-xs\">\n <div\n className=\"prose prose-sm text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{\n __html: implicitNotice,\n }}\n data-testid=\"terms-implicit-notice\"\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <LabeledSeparator label={t('terms.additionalOptionalConsent')} />\n )}\n\n {hasTerms && hasExplicitTerms && (\n <TermsCheckboxList\n control={control}\n disabled={registerMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n )}\n </div>\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('register.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('register.submit')}\n </TRButton>\n </form>\n )}\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('register.link.login')}\n </Link>\n }\n text={t('register.footer.haveAccount')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{I as e}from"./index-CZ0rWM7l.js";var t=e;export{t as errorComponent};
2
+ //# sourceMappingURL=register-UcpXAI9J.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-UL_cNRnQ.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/register/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { LockIcon, MailIcon } from 'lucide-react';\nimport { useDeferredValue, useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { TermsCheckboxList } from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerMutationOptions } from '#frontend/queries/register.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { getTermsQueryOptions } from '#frontend/queries/terms.ts';\n\nexport const Route = createFileRoute('/register/')({\n component: Register,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n if (!config.registration.public_registration) {\n throw redirect({\n to: '/',\n replace: true,\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n search,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n const queries: Array<Promise<unknown>> = [\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n ];\n\n if (hasAuthorizationContext(deps.search)) {\n queries.push(\n context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n ),\n );\n }\n\n await Promise.all(queries);\n },\n});\n\nfunction Register() {\n const { t, i18n } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const deferredLang = useDeferredValue(lang);\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const { data: termsData } = useSuspenseQuery(\n getTermsQueryOptions(deferredLang),\n );\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const passwordPolicy = configData.auth.password.policy;\n\n // Check if there are any terms to display\n const hasTerms = termsData.terms.length > 0;\n const explicitTerms = useMemo(\n () => termsData.terms.filter((term) => term.consentMode === 'explicit'),\n [termsData.terms],\n );\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Get implicit notice from config\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n const registerSchema = useMemo(\n () =>\n z.object({\n email: z\n .string()\n .min(1, t('validation.email.required'))\n .pipe(z.email(t('validation.email.invalid'))),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [explicitTerms, passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n type RegisterFormValues = z.infer<typeof registerSchema>;\n\n const registerMutation = useMutation({\n ...registerMutationOptions,\n onSuccess: async (data, params) => {\n const user = data.user;\n\n if (user.email_verification_required && !user.email_verified) {\n return navigate({\n to: '/verify/email',\n search: {\n email: params.email,\n ...extractOAuthParams(search),\n },\n });\n }\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (configData.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (configData.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: {\n ...extractOAuthParams(search),\n passkey_name: 'default',\n },\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n if (error instanceof IssuaryError) {\n if (error.code === 'REGISTRATION_EMAIL_NOT_ALLOWED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailNotAllowed'),\n });\n } else if (error.code === 'REGISTRATION_DISABLED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.registrationDisabled'),\n });\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n },\n onSettled: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n control,\n setValue,\n watch,\n formState: { errors },\n } = useForm<RegisterFormValues>({\n defaultValues: {\n email: '',\n password: '',\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [term.id, false]),\n ),\n },\n resolver: standardSchemaResolver(registerSchema),\n mode: 'onChange',\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = (values: RegisterFormValues) => {\n // Build consents array for registration\n // Only send explicit consents - implicit ones are handled by backend\n const consents = hasTerms\n ? explicitTerms.map((term) => ({\n termsId: term.id,\n agreed: values.termsConsents[term.id] ?? false,\n }))\n : undefined;\n\n registerMutation.mutate({\n email: values.email,\n password: values.password,\n consents,\n });\n };\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('register.subtitle')}\n title={t('register.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {isPasswordAuthEnabled && (\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n autoComplete=\"email\"\n error={errors.email}\n icon={MailIcon}\n label={t('register.email.label')}\n placeholder={t('register.email.placeholder')}\n {...register('email')}\n type=\"email\"\n />\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n hint={t('register.password.hint', {\n count: passwordPolicy.min_length,\n })}\n icon={LockIcon}\n label={t('register.password.label')}\n placeholder={t('register.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength\n password={passwordValue}\n policy={passwordPolicy}\n />\n </div>\n\n <div className=\"flex flex-col gap-tinyrack-md\">\n {implicitNotice && (\n <div className=\"text-center text-tinyrack-text-muted text-tinyrack-xs\">\n <div\n className=\"prose prose-sm text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{\n __html: implicitNotice,\n }}\n data-testid=\"terms-implicit-notice\"\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <LabeledSeparator label={t('terms.additionalOptionalConsent')} />\n )}\n\n {hasTerms && hasExplicitTerms && (\n <TermsCheckboxList\n control={control}\n disabled={registerMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n )}\n </div>\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('register.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('register.submit')}\n </TRButton>\n </form>\n )}\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('register.link.login')}\n </Link>\n }\n text={t('register.footer.haveAccount')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"wCA4BsF,IAAAC,EAA7ED"}
1
+ {"version":3,"file":"register-UcpXAI9J.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/register/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { LockIcon, MailIcon } from 'lucide-react';\nimport { useDeferredValue, useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { TermsCheckboxList } from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerMutationOptions } from '#frontend/queries/register.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { getTermsQueryOptions } from '#frontend/queries/terms.ts';\n\nexport const Route = createFileRoute('/register/')({\n component: Register,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n if (!config.registration.public_registration) {\n throw redirect({\n to: '/',\n replace: true,\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n search,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n const queries: Array<Promise<unknown>> = [\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n ];\n\n if (hasAuthorizationContext(deps.search)) {\n queries.push(\n context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n ),\n );\n }\n\n await Promise.all(queries);\n },\n});\n\nfunction Register() {\n const { t, i18n } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const deferredLang = useDeferredValue(lang);\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const { data: termsData } = useSuspenseQuery(\n getTermsQueryOptions(deferredLang),\n );\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const passwordPolicy = configData.auth.password.policy;\n\n // Check if there are any terms to display\n const hasTerms = termsData.terms.length > 0;\n const explicitTerms = useMemo(\n () => termsData.terms.filter((term) => term.consentMode === 'explicit'),\n [termsData.terms],\n );\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Get implicit notice from config\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n const registerSchema = useMemo(\n () =>\n z.object({\n email: z\n .string()\n .min(1, t('validation.email.required'))\n .pipe(z.email(t('validation.email.invalid'))),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [explicitTerms, passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n type RegisterFormValues = z.infer<typeof registerSchema>;\n\n const registerMutation = useMutation({\n ...registerMutationOptions,\n onSuccess: async (data, params) => {\n const user = data.user;\n\n if (user.email_verification_required && !user.email_verified) {\n return navigate({\n to: '/verify/email',\n search: {\n email: params.email,\n ...extractOAuthParams(search),\n },\n });\n }\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (configData.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (configData.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: {\n ...extractOAuthParams(search),\n passkey_name: 'default',\n },\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n if (error instanceof IssuaryError) {\n if (error.code === 'REGISTRATION_EMAIL_NOT_ALLOWED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailNotAllowed'),\n });\n } else if (error.code === 'REGISTRATION_DISABLED') {\n setError('email', {\n type: 'manual',\n message: t('register.error.registrationDisabled'),\n });\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n } else {\n setError('email', {\n type: 'manual',\n message: t('register.error.emailExists'),\n });\n }\n },\n onSettled: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n control,\n setValue,\n watch,\n formState: { errors },\n } = useForm<RegisterFormValues>({\n defaultValues: {\n email: '',\n password: '',\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [term.id, false]),\n ),\n },\n resolver: standardSchemaResolver(registerSchema),\n mode: 'onChange',\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = (values: RegisterFormValues) => {\n // Build consents array for registration\n // Only send explicit consents - implicit ones are handled by backend\n const consents = hasTerms\n ? explicitTerms.map((term) => ({\n termsId: term.id,\n agreed: values.termsConsents[term.id] ?? false,\n }))\n : undefined;\n\n registerMutation.mutate({\n email: values.email,\n password: values.password,\n consents,\n });\n };\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('register.subtitle')}\n title={t('register.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {isPasswordAuthEnabled && (\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n autoComplete=\"email\"\n error={errors.email}\n icon={MailIcon}\n label={t('register.email.label')}\n placeholder={t('register.email.placeholder')}\n {...register('email')}\n type=\"email\"\n />\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n hint={t('register.password.hint', {\n count: passwordPolicy.min_length,\n })}\n icon={LockIcon}\n label={t('register.password.label')}\n placeholder={t('register.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength\n password={passwordValue}\n policy={passwordPolicy}\n />\n </div>\n\n <div className=\"flex flex-col gap-tinyrack-md\">\n {implicitNotice && (\n <div className=\"text-center text-tinyrack-text-muted text-tinyrack-xs\">\n <div\n className=\"prose prose-sm text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{\n __html: implicitNotice,\n }}\n data-testid=\"terms-implicit-notice\"\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <LabeledSeparator label={t('terms.additionalOptionalConsent')} />\n )}\n\n {hasTerms && hasExplicitTerms && (\n <TermsCheckboxList\n control={control}\n disabled={registerMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n )}\n </div>\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('register.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('register.submit')}\n </TRButton>\n </form>\n )}\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('register.link.login')}\n </Link>\n }\n text={t('register.footer.haveAccount')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"wCA4BsF,IAAAC,EAA7ED"}
@@ -0,0 +1,2 @@
1
+ import{J as e,Z as t,i as n,q as r,s as i,t as a}from"./createLucideIcon-BPpAIXR_.js";import{Sn as o,t as s}from"./config-DTQCUlfZ.js";import{t as c}from"./useMutation-BWZ0G4Td.js";import{tt as l}from"./shell-header-bar-CgyNXbPS.js";import{t as u}from"./circle-check-6QTLhwM_.js";import{t as d}from"./auth-field-DsLGh1U-.js";import{t as f}from"./key-round-CdvHb8vT.js";import{t as p}from"./lock-7NRyvXBS.js";import{c as m,o as h}from"./zod-C87S6Tec.js";import{L as g,R as _,V as v,c as y}from"./index-CZ0rWM7l.js";import{t as b}from"./auth-page-header-COvqWINf.js";import{t as x}from"./alert-leqyvHOp.js";import{t as S}from"./auth-layout-DGTylGtX.js";import{n as C,t as w}from"./auth-footer-Cz5ivsDc.js";import{r as T,t as E}from"./standard-schema-e8gtSzGE.js";import{n as D}from"./password-reset-sU5KkBKi.js";import{t as O}from"./password-strength-DTWF07Px.js";var k=a(`lock-keyhole`,[[`circle`,{cx:`12`,cy:`16`,r:`1`,key:`1au0dj`}],[`rect`,{x:`3`,y:`10`,width:`18`,height:`12`,rx:`2`,key:`6s8ecr`}],[`path`,{d:`M7 10V7a5 5 0 0 1 10 0v3`,key:`1pqi11`}]]),A=t(e()),j=r();function M(){let{t:e}=n(),t=v(),{token:r}=y.useSearch(),[a,M]=(0,A.useState)(!1),{data:N}=l(s),P=N.auth.password.policy,F=(0,A.useMemo)(()=>h({token:m().min(1,e(`validation.token.required`)),password:m().min(P.min_length,e(`validation.password.min`,{count:P.min_length})).max(P.max_length,e(`validation.password.max`,{count:P.max_length})),confirmPassword:m().min(1,e(`validation.confirmPassword.required`))}).refine(e=>e.password===e.confirmPassword,{message:e(`validation.confirmPassword.mismatch`),path:[`confirmPassword`]}),[P.max_length,P.min_length,e]),I=c({...D,onSuccess:()=>{M(!0)}}),{register:L,setError:R,handleSubmit:z,watch:B,formState:{errors:V}}=T({defaultValues:{token:r||``,password:``,confirmPassword:``},resolver:E(F)}),H=B(`password`);return a?(0,j.jsx)(S,{children:(0,j.jsx)(`div`,{"data-testid":`alert-success`,children:(0,j.jsx)(g,{description:(0,j.jsxs)(j.Fragment,{children:[e(`resetPassword.success.subtitle`),(0,j.jsx)(`br`,{}),e(`resetPassword.success.description`)]}),icon:u,title:e(`resetPassword.success.title`),tone:`success`,children:(0,j.jsx)(i,{className:`w-full`,"data-testid":`reset-password-go-login`,intent:`primary`,onClick:()=>t({to:`/login`}),type:`button`,uiSize:`lg`,children:e(`resetPassword.success.goToLogin`)})})})}):(0,j.jsxs)(S,{children:[(0,j.jsx)(b,{subtitle:e(`resetPassword.subtitle`),title:e(`resetPassword.title`)}),(0,j.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:z(async t=>{try{await I.mutateAsync({token:t.token,password:t.password})}catch(t){t&&typeof t==`object`&&`code`in t?t.code===`INVALID_PASSWORD_RESET_TOKEN`?R(`token`,{type:`manual`,message:e(`resetPassword.error.invalidToken`)}):t.code===`USER_NOT_EDITABLE`&&R(`token`,{type:`manual`,message:e(`resetPassword.error.notEditable`)}):R(`token`,{type:`manual`,message:e(`resetPassword.error.invalidToken`)})}}),children:[!r&&(0,j.jsx)(d,{error:V.token,icon:f,label:e(`resetPassword.token.label`),placeholder:e(`resetPassword.token.placeholder`),...L(`token`),type:`text`}),r&&V.token&&(0,j.jsx)(x,{"data-testid":`reset-password-token-error`,icon:_,type:`error`,children:V.token.message}),(0,j.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[(0,j.jsx)(d,{autoComplete:`new-password`,error:V.password,icon:p,label:e(`resetPassword.password.label`),placeholder:e(`resetPassword.password.placeholder`),...L(`password`),type:`password`}),(0,j.jsx)(O,{password:H,policy:P})]}),(0,j.jsx)(d,{autoComplete:`new-password`,error:V.confirmPassword,icon:k,label:e(`resetPassword.confirmPassword.label`),placeholder:e(`resetPassword.confirmPassword.placeholder`),...L(`confirmPassword`),type:`password`}),(0,j.jsx)(i,{className:`w-full`,intent:`primary`,loading:I.isPending,loadingLabel:e(`resetPassword.submitting`),type:`submit`,uiSize:`lg`,children:e(`resetPassword.submit`)})]}),(0,j.jsx)(w,{children:(0,j.jsx)(C,{link:(0,j.jsx)(o,{"data-testid":`reset-password-back-to-login`,to:`/login`,children:e(`resetPassword.backToLogin`)})})})]})}export{M as component};
2
+ //# sourceMappingURL=reset-C-Qjl2Qy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset-C-Qjl2Qy.js","names":["standardSchemaResolver","useMutation","useSuspenseQuery","Link","useNavigate","TRButton","CircleAlertIcon","CircleCheckIcon","KeyRoundIcon","LockIcon","LockKeyholeIcon","useMemo","useState","useForm","useTranslation","z","AuthField","AuthFooter","AuthFooterLink","AuthOutcome","AuthPageHeader","PasswordStrength","Alert","AuthLayout","appConfigQueryOptions","resetPasswordMutationOptions","Route","ResetPasswordFormValues","token","password","confirmPassword","ResetPassword","t","navigate","search","useSearch","queryToken","resetSuccess","setResetSuccess","data","configData","passwordPolicy","auth","policy","resetPasswordSchema","object","string","min","min_length","count","max","max_length","refine","message","path","resetPasswordMutation","onSuccess","register","setError","handleSubmit","watch","formState","errors","defaultValues","resolver","passwordValue","onSubmit","values","mutateAsync","error","code","type","to","isPending","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/lock-keyhole.mjs","../../../frontend/src/routes/password/reset/index.tsx?tsr-split=component"],"sourcesContent":["/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"circle\", { cx: \"12\", cy: \"16\", r: \"1\", key: \"1au0dj\" }],\n [\"rect\", { x: \"3\", y: \"10\", width: \"18\", height: \"12\", rx: \"2\", key: \"6s8ecr\" }],\n [\"path\", { d: \"M7 10V7a5 5 0 0 1 10 0v3\", key: \"1pqi11\" }]\n];\nconst LockKeyhole = createLucideIcon(\"lock-keyhole\", __iconNode);\n\nexport { __iconNode, LockKeyhole as default };\n//# sourceMappingURL=lock-keyhole.mjs.map\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport {\n CircleAlertIcon,\n CircleCheckIcon,\n KeyRoundIcon,\n LockIcon,\n LockKeyholeIcon,\n} from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { resetPasswordMutationOptions } from '#frontend/queries/password-reset.ts';\n\nconst SearchSchema = z.object({\n token: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/password/reset/')({\n component: ResetPassword,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype ResetPasswordFormValues = {\n token: string;\n password: string;\n confirmPassword: string;\n};\n\nfunction ResetPassword() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const search = Route.useSearch();\n const { token: queryToken } = search;\n const [resetSuccess, setResetSuccess] = useState(false);\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const passwordPolicy = configData.auth.password.policy;\n\n const resetPasswordSchema = useMemo(\n () =>\n z\n .object({\n token: z.string().min(1, t('validation.token.required')),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n confirmPassword: z\n .string()\n .min(1, t('validation.confirmPassword.required')),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: t('validation.confirmPassword.mismatch'),\n path: ['confirmPassword'],\n }),\n [passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n const resetPasswordMutation = useMutation({\n ...resetPasswordMutationOptions,\n onSuccess: () => {\n setResetSuccess(true);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n watch,\n formState: { errors },\n } = useForm<ResetPasswordFormValues>({\n defaultValues: {\n token: queryToken || '',\n password: '',\n confirmPassword: '',\n },\n resolver: standardSchemaResolver(resetPasswordSchema),\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = async (values: ResetPasswordFormValues) => {\n try {\n await resetPasswordMutation.mutateAsync({\n token: values.token,\n password: values.password,\n });\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error) {\n if (error.code === 'INVALID_PASSWORD_RESET_TOKEN') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n } else if (error.code === 'USER_NOT_EDITABLE') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.notEditable'),\n });\n }\n } else {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n }\n }\n };\n\n if (resetSuccess) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('resetPassword.success.subtitle')}\n <br />\n {t('resetPassword.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('resetPassword.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"reset-password-go-login\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/login' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('resetPassword.success.goToLogin')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('resetPassword.subtitle')}\n title={t('resetPassword.title')}\n />\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n {!queryToken && (\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('resetPassword.token.label')}\n placeholder={t('resetPassword.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n )}\n\n {/*\n A token that arrived in the URL has no field to attach an error to,\n so a rejected link is reported as a banner above the form. The form\n stays put: the user can paste a fresh token without a round trip.\n */}\n {queryToken && errors.token && (\n <Alert\n data-testid=\"reset-password-token-error\"\n icon={CircleAlertIcon}\n type=\"error\"\n >\n {errors.token.message}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n icon={LockIcon}\n label={t('resetPassword.password.label')}\n placeholder={t('resetPassword.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength password={passwordValue} policy={passwordPolicy} />\n </div>\n\n <AuthField\n autoComplete=\"new-password\"\n error={errors.confirmPassword}\n icon={LockKeyholeIcon}\n label={t('resetPassword.confirmPassword.label')}\n placeholder={t('resetPassword.confirmPassword.placeholder')}\n {...register('confirmPassword')}\n type=\"password\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={resetPasswordMutation.isPending}\n loadingLabel={t('resetPassword.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('resetPassword.submit')}\n </TRButton>\n </form>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link data-testid=\"reset-password-back-to-login\" to=\"/login\">\n {t('resetPassword.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{I as e}from"./index-CZ0rWM7l.js";var t=e;export{t as errorComponent};
2
+ //# sourceMappingURL=reset-UcpXAI9J.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reset-UL_cNRnQ.js","names":["RouteErrorFallback","ResetPasswordFormValues","token","password","confirmPassword","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/password/reset/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport {\n CircleAlertIcon,\n CircleCheckIcon,\n KeyRoundIcon,\n LockIcon,\n LockKeyholeIcon,\n} from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { resetPasswordMutationOptions } from '#frontend/queries/password-reset.ts';\n\nconst SearchSchema = z.object({\n token: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/password/reset/')({\n component: ResetPassword,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype ResetPasswordFormValues = {\n token: string;\n password: string;\n confirmPassword: string;\n};\n\nfunction ResetPassword() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const search = Route.useSearch();\n const { token: queryToken } = search;\n const [resetSuccess, setResetSuccess] = useState(false);\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const passwordPolicy = configData.auth.password.policy;\n\n const resetPasswordSchema = useMemo(\n () =>\n z\n .object({\n token: z.string().min(1, t('validation.token.required')),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n confirmPassword: z\n .string()\n .min(1, t('validation.confirmPassword.required')),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: t('validation.confirmPassword.mismatch'),\n path: ['confirmPassword'],\n }),\n [passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n const resetPasswordMutation = useMutation({\n ...resetPasswordMutationOptions,\n onSuccess: () => {\n setResetSuccess(true);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n watch,\n formState: { errors },\n } = useForm<ResetPasswordFormValues>({\n defaultValues: {\n token: queryToken || '',\n password: '',\n confirmPassword: '',\n },\n resolver: standardSchemaResolver(resetPasswordSchema),\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = async (values: ResetPasswordFormValues) => {\n try {\n await resetPasswordMutation.mutateAsync({\n token: values.token,\n password: values.password,\n });\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error) {\n if (error.code === 'INVALID_PASSWORD_RESET_TOKEN') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n } else if (error.code === 'USER_NOT_EDITABLE') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.notEditable'),\n });\n }\n } else {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n }\n }\n };\n\n if (resetSuccess) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('resetPassword.success.subtitle')}\n <br />\n {t('resetPassword.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('resetPassword.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"reset-password-go-login\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/login' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('resetPassword.success.goToLogin')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('resetPassword.subtitle')}\n title={t('resetPassword.title')}\n />\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n {!queryToken && (\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('resetPassword.token.label')}\n placeholder={t('resetPassword.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n )}\n\n {/*\n A token that arrived in the URL has no field to attach an error to,\n so a rejected link is reported as a banner above the form. The form\n stays put: the user can paste a fresh token without a round trip.\n */}\n {queryToken && errors.token && (\n <Alert\n data-testid=\"reset-password-token-error\"\n icon={CircleAlertIcon}\n type=\"error\"\n >\n {errors.token.message}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n icon={LockIcon}\n label={t('resetPassword.password.label')}\n placeholder={t('resetPassword.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength password={passwordValue} policy={passwordPolicy} />\n </div>\n\n <AuthField\n autoComplete=\"new-password\"\n error={errors.confirmPassword}\n icon={LockKeyholeIcon}\n label={t('resetPassword.confirmPassword.label')}\n placeholder={t('resetPassword.confirmPassword.placeholder')}\n {...register('confirmPassword')}\n type=\"password\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={resetPasswordMutation.isPending}\n loadingLabel={t('resetPassword.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('resetPassword.submit')}\n </TRButton>\n </form>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link data-testid=\"reset-password-back-to-login\" to=\"/login\">\n {t('resetPassword.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"wCA6DE,IAAAK,EAhCOL"}
1
+ {"version":3,"file":"reset-UcpXAI9J.js","names":["RouteErrorFallback","ResetPasswordFormValues","token","password","confirmPassword","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/password/reset/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n Link,\n redirect,\n useNavigate,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport {\n CircleAlertIcon,\n CircleCheckIcon,\n KeyRoundIcon,\n LockIcon,\n LockKeyholeIcon,\n} from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { PasswordStrength } from '#frontend/components/auth/password-strength.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { resetPasswordMutationOptions } from '#frontend/queries/password-reset.ts';\n\nconst SearchSchema = z.object({\n token: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/password/reset/')({\n component: ResetPassword,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype ResetPasswordFormValues = {\n token: string;\n password: string;\n confirmPassword: string;\n};\n\nfunction ResetPassword() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const search = Route.useSearch();\n const { token: queryToken } = search;\n const [resetSuccess, setResetSuccess] = useState(false);\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const passwordPolicy = configData.auth.password.policy;\n\n const resetPasswordSchema = useMemo(\n () =>\n z\n .object({\n token: z.string().min(1, t('validation.token.required')),\n password: z\n .string()\n .min(\n passwordPolicy.min_length,\n t('validation.password.min', {\n count: passwordPolicy.min_length,\n }),\n )\n .max(\n passwordPolicy.max_length,\n t('validation.password.max', {\n count: passwordPolicy.max_length,\n }),\n ),\n confirmPassword: z\n .string()\n .min(1, t('validation.confirmPassword.required')),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: t('validation.confirmPassword.mismatch'),\n path: ['confirmPassword'],\n }),\n [passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n const resetPasswordMutation = useMutation({\n ...resetPasswordMutationOptions,\n onSuccess: () => {\n setResetSuccess(true);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n watch,\n formState: { errors },\n } = useForm<ResetPasswordFormValues>({\n defaultValues: {\n token: queryToken || '',\n password: '',\n confirmPassword: '',\n },\n resolver: standardSchemaResolver(resetPasswordSchema),\n });\n\n const passwordValue = watch('password');\n\n const onSubmit = async (values: ResetPasswordFormValues) => {\n try {\n await resetPasswordMutation.mutateAsync({\n token: values.token,\n password: values.password,\n });\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error) {\n if (error.code === 'INVALID_PASSWORD_RESET_TOKEN') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n } else if (error.code === 'USER_NOT_EDITABLE') {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.notEditable'),\n });\n }\n } else {\n setError('token', {\n type: 'manual',\n message: t('resetPassword.error.invalidToken'),\n });\n }\n }\n };\n\n if (resetSuccess) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('resetPassword.success.subtitle')}\n <br />\n {t('resetPassword.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('resetPassword.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"reset-password-go-login\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/login' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('resetPassword.success.goToLogin')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('resetPassword.subtitle')}\n title={t('resetPassword.title')}\n />\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n {!queryToken && (\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('resetPassword.token.label')}\n placeholder={t('resetPassword.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n )}\n\n {/*\n A token that arrived in the URL has no field to attach an error to,\n so a rejected link is reported as a banner above the form. The form\n stays put: the user can paste a fresh token without a round trip.\n */}\n {queryToken && errors.token && (\n <Alert\n data-testid=\"reset-password-token-error\"\n icon={CircleAlertIcon}\n type=\"error\"\n >\n {errors.token.message}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <AuthField\n autoComplete=\"new-password\"\n error={errors.password}\n icon={LockIcon}\n label={t('resetPassword.password.label')}\n placeholder={t('resetPassword.password.placeholder')}\n {...register('password')}\n type=\"password\"\n />\n\n <PasswordStrength password={passwordValue} policy={passwordPolicy} />\n </div>\n\n <AuthField\n autoComplete=\"new-password\"\n error={errors.confirmPassword}\n icon={LockKeyholeIcon}\n label={t('resetPassword.confirmPassword.label')}\n placeholder={t('resetPassword.confirmPassword.placeholder')}\n {...register('confirmPassword')}\n type=\"password\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={resetPasswordMutation.isPending}\n loadingLabel={t('resetPassword.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('resetPassword.submit')}\n </TRButton>\n </form>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link data-testid=\"reset-password-back-to-login\" to=\"/login\">\n {t('resetPassword.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"wCA6DE,IAAAK,EAhCOL"}
@@ -1,2 +1,2 @@
1
- import{i as e,q as t,s as n,t as r}from"./createLucideIcon-BPpAIXR_.js";import{n as i,sr as a}from"./config-v3-oy_ef.js";import{t as o}from"./useMutation-ZLFeJg75.js";import{M as s,tt as c}from"./shell-header-bar-C9AB5lT-.js";import{t as l}from"./plus-DSBBSrgj.js";import{t as u}from"./trash-2-DZdKBLvC.js";import{M as d,N as f,a as p,i as m,n as h,r as g,z as _}from"./index-gliHKncz.js";import{t as v}from"./badge-CsOfUji0.js";import{t as y}from"./auth-choice-row-BlSsUI7d.js";import{t as b}from"./auth-page-header-CIs08f5Y.js";import{t as x}from"./alert-leqyvHOp.js";import{t as S}from"./initial-avatar-CZzHAPm3.js";import{t as C}from"./auth-layout-CoeqNhTT.js";var w=r(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),T=r(`circle-user-round`,[[`path`,{d:`M17.925 20.056a6 6 0 0 0-11.851.001`,key:`z69sun`}],[`circle`,{cx:`12`,cy:`11`,r:`4`,key:`1gt34v`}],[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),E=t();function D(e){let t=e?.split(` `).filter(Boolean)??[];return t.includes(`login`)||t.push(`login`),t.join(` `)}function O(e){let t=f(e),n=new URLSearchParams;for(let[e,r]of Object.entries({...t,prompt:D(t.prompt),account_selected:1}))r!==void 0&&n.set(e,String(r));return`/login?${n.toString()}`}function k(){let{t}=e(),r=h.useSearch(),f=a(),{data:D}=c(g(r.client_id)),k=D.accounts.length===0&&!D.allow_add_account,A=()=>{window.location.href=d({...r,account_selected:1})},j=o({...p,onSuccess:A}),M=o({...m,onSuccess:()=>{f.invalidateQueries({queryKey:i.accounts(r.client_id)})}});return(0,E.jsxs)(C,{width:`wide`,children:[(0,E.jsx)(b,{subtitle:t(`accountSelect.subtitle`),title:t(`accountSelect.title`)}),D.accounts.length===0?(0,E.jsx)(x,{icon:T,type:`info`,children:t(k?`accountSelect.unavailable`:`accountSelect.noRememberedAccounts`)}):(0,E.jsx)(`div`,{className:`flex flex-col gap-tinyrack-xs`,"data-testid":`account-list`,children:D.accounts.map(e=>(0,E.jsx)(`div`,{"data-testid":`remembered-account`,children:(0,E.jsx)(y,{description:e.current?t(`accountSelect.currentAccount`):t(`accountSelect.rememberedAccount`),label:e.email,leading:(0,E.jsx)(S,{email:e.email,size:`sm`}),render:(0,E.jsx)(n,{"data-testid":`select-account-${e.sub}`,disabled:j.isPending,onClick:()=>j.mutate({sub:e.sub}),type:`button`}),trailing:(0,E.jsxs)(E.Fragment,{children:[e.current?(0,E.jsx)(v,{uiSize:`md`,variant:`info`,children:t(`accountSelect.current`)}):null,D.allow_remove_account&&!e.current?(0,E.jsx)(s,{appearance:`ghost`,"aria-label":t(`accountSelect.removeAccount`,{email:e.email}),"data-testid":`remove-account-${e.sub}`,disabled:M.isPending,intent:`neutral`,onClick:()=>M.mutate({sub:e.sub}),type:`button`,uiSize:`sm`,children:(0,E.jsx)(u,{"aria-hidden":!0,className:`size-tinyrack-lg`})}):null]})})},e.sub))}),D.allow_add_account?(0,E.jsxs)(_,{appearance:`outline`,className:`w-full justify-between`,intent:`neutral`,render:(0,E.jsx)(`a`,{href:O(r)}),children:[(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-tinyrack-sm`,children:[(0,E.jsx)(l,{"aria-hidden":!0,className:`size-tinyrack-lg`}),t(`accountSelect.useAnotherAccount`)]}),(0,E.jsx)(w,{"aria-hidden":!0,className:`size-tinyrack-lg`})]}):null]})}export{k as component};
2
- //# sourceMappingURL=select-CWdSzsVo.js.map
1
+ import{i as e,q as t,s as n,t as r}from"./createLucideIcon-BPpAIXR_.js";import{Sr as i,n as a}from"./config-DTQCUlfZ.js";import{t as o}from"./useMutation-BWZ0G4Td.js";import{M as s,tt as c}from"./shell-header-bar-CgyNXbPS.js";import{t as l}from"./plus-DSBBSrgj.js";import{t as u}from"./trash-2-DZdKBLvC.js";import{M as d,N as f,a as p,i as m,n as h,r as g,z as _}from"./index-CZ0rWM7l.js";import{t as v}from"./badge-CsOfUji0.js";import{t as y}from"./auth-choice-row-DWzfcZQY.js";import{t as b}from"./auth-page-header-COvqWINf.js";import{t as x}from"./alert-leqyvHOp.js";import{t as S}from"./initial-avatar-ocwNWL38.js";import{t as C}from"./auth-layout-DGTylGtX.js";var w=r(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),T=r(`circle-user-round`,[[`path`,{d:`M17.925 20.056a6 6 0 0 0-11.851.001`,key:`z69sun`}],[`circle`,{cx:`12`,cy:`11`,r:`4`,key:`1gt34v`}],[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),E=t();function D(e){let t=e?.split(` `).filter(Boolean)??[];return t.includes(`login`)||t.push(`login`),t.join(` `)}function O(e){let t=f(e),n=new URLSearchParams;for(let[e,r]of Object.entries({...t,prompt:D(t.prompt),account_selected:1}))r!==void 0&&n.set(e,String(r));return`/login?${n.toString()}`}function k(){let{t}=e(),r=h.useSearch(),f=i(),{data:D}=c(g(r.client_id)),k=D.accounts.length===0&&!D.allow_add_account,A=()=>{window.location.href=d({...r,account_selected:1})},j=o({...p,onSuccess:A}),M=o({...m,onSuccess:()=>{f.invalidateQueries({queryKey:a.accounts(r.client_id)})}});return(0,E.jsxs)(C,{width:`wide`,children:[(0,E.jsx)(b,{subtitle:t(`accountSelect.subtitle`),title:t(`accountSelect.title`)}),D.accounts.length===0?(0,E.jsx)(x,{icon:T,type:`info`,children:t(k?`accountSelect.unavailable`:`accountSelect.noRememberedAccounts`)}):(0,E.jsx)(`div`,{className:`flex flex-col gap-tinyrack-xs`,"data-testid":`account-list`,children:D.accounts.map(e=>(0,E.jsx)(`div`,{"data-testid":`remembered-account`,children:(0,E.jsx)(y,{description:e.current?t(`accountSelect.currentAccount`):t(`accountSelect.rememberedAccount`),label:e.email,leading:(0,E.jsx)(S,{email:e.email,size:`sm`}),render:(0,E.jsx)(n,{"data-testid":`select-account-${e.sub}`,disabled:j.isPending,onClick:()=>j.mutate({sub:e.sub}),type:`button`}),trailing:(0,E.jsxs)(E.Fragment,{children:[e.current?(0,E.jsx)(v,{uiSize:`md`,variant:`info`,children:t(`accountSelect.current`)}):null,D.allow_remove_account&&!e.current?(0,E.jsx)(s,{appearance:`ghost`,"aria-label":t(`accountSelect.removeAccount`,{email:e.email}),"data-testid":`remove-account-${e.sub}`,disabled:M.isPending,intent:`neutral`,onClick:()=>M.mutate({sub:e.sub}),type:`button`,uiSize:`sm`,children:(0,E.jsx)(u,{"aria-hidden":!0,className:`size-tinyrack-lg`})}):null]})})},e.sub))}),D.allow_add_account?(0,E.jsxs)(_,{appearance:`outline`,className:`w-full justify-between`,intent:`neutral`,render:(0,E.jsx)(`a`,{href:O(r)}),children:[(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-tinyrack-sm`,children:[(0,E.jsx)(l,{"aria-hidden":!0,className:`size-tinyrack-lg`}),t(`accountSelect.useAnotherAccount`)]}),(0,E.jsx)(w,{"aria-hidden":!0,className:`size-tinyrack-lg`})]}):null]})}export{k as component};
2
+ //# sourceMappingURL=select-BKx4bF4A.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select-CWdSzsVo.js","names":["__iconNode","useMutation","useQueryClient","useSuspenseQuery","TRBadge","TRButton","TRIconButton","TRLinkButton","ArrowRightIcon","CircleUserRoundIcon","PlusIcon","Trash2Icon","useTranslation","AuthChoiceRow","AuthPageHeader","Alert","InitialAvatar","AuthLayout","buildAuthorizeUrl","extractOAuthParams","accountsQueryOptions","removeAccountMutationOptions","selectAccountMutationOptions","queryKeys","Route","appendLoginPrompt","prompt","values","split","filter","Boolean","includes","push","join","buildLoginHref","search","ReturnType","useSearch","oauthParams","params","URLSearchParams","key","value","Object","entries","account_selected","undefined","set","String","toString","AccountSelect","t","queryClient","data","client_id","accountSelectionUnavailable","accounts","length","allow_add_account","continueWithSelectedAccount","window","location","href","selectMutation","onSuccess","removeMutation","invalidateQueries","queryKey","map","account","sub","current","email","isPending","mutate","allow_remove_account","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/arrow-right.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/circle-user-round.mjs","../../../frontend/src/routes/account/select/index.tsx?tsr-split=component"],"sourcesContent":["/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M5 12h14\", key: \"1ays0h\" }],\n [\"path\", { d: \"m12 5 7 7-7 7\", key: \"xquz4c\" }]\n];\nconst ArrowRight = createLucideIcon(\"arrow-right\", __iconNode);\n\nexport { __iconNode, ArrowRight as default };\n//# sourceMappingURL=arrow-right.mjs.map\n","/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M17.925 20.056a6 6 0 0 0-11.851.001\", key: \"z69sun\" }],\n [\"circle\", { cx: \"12\", cy: \"11\", r: \"4\", key: \"1gt34v\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }]\n];\nconst CircleUserRound = createLucideIcon(\"circle-user-round\", __iconNode);\n\nexport { __iconNode, CircleUserRound as default };\n//# sourceMappingURL=circle-user-round.mjs.map\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { TRBadge } from '@tinyrack/ui/components/badge';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRIconButton } from '@tinyrack/ui/components/icon-button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport {\n ArrowRightIcon,\n CircleUserRoundIcon,\n PlusIcon,\n Trash2Icon,\n} from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { AuthChoiceRow } from '#frontend/components/auth/auth-choice-row.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: 1,\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n const accountSelectionUnavailable =\n data.accounts.length === 0 && !data.allow_add_account;\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: 1,\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('accountSelect.subtitle')}\n title={t('accountSelect.title')}\n />\n\n {data.accounts.length === 0 ? (\n <Alert icon={CircleUserRoundIcon} type=\"info\">\n {accountSelectionUnavailable\n ? t('accountSelect.unavailable')\n : t('accountSelect.noRememberedAccounts')}\n </Alert>\n ) : (\n <div\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"account-list\"\n >\n {data.accounts.map((account) => (\n <div data-testid=\"remembered-account\" key={account.sub}>\n <AuthChoiceRow\n description={\n account.current\n ? t('accountSelect.currentAccount')\n : t('accountSelect.rememberedAccount')\n }\n label={account.email}\n leading={<InitialAvatar email={account.email} size=\"sm\" />}\n /*\n Picking an account is a mutation, not navigation, so this\n one is a button. No children — the row supplies them.\n */\n render={\n <TRButton\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n />\n }\n trailing={\n <>\n {account.current ? (\n <TRBadge uiSize=\"md\" variant=\"info\">\n {t('accountSelect.current')}\n </TRBadge>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <TRIconButton\n appearance=\"ghost\"\n aria-label={t('accountSelect.removeAccount', {\n email: account.email,\n })}\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n intent=\"neutral\"\n onClick={() =>\n removeMutation.mutate({ sub: account.sub })\n }\n type=\"button\"\n uiSize=\"sm\"\n >\n <Trash2Icon aria-hidden className=\"size-tinyrack-lg\" />\n </TRIconButton>\n ) : null}\n </>\n }\n />\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <TRLinkButton\n appearance=\"outline\"\n className=\"w-full justify-between\"\n intent=\"neutral\"\n render={<a href={buildLoginHref(search)} />}\n >\n <span className=\"inline-flex items-center gap-tinyrack-sm\">\n <PlusIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('accountSelect.useAnotherAccount')}\n </span>\n <ArrowRightIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRLinkButton>\n ) : null}\n </AuthLayout>\n );\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
1
+ {"version":3,"file":"select-BKx4bF4A.js","names":["__iconNode","useMutation","useQueryClient","useSuspenseQuery","TRBadge","TRButton","TRIconButton","TRLinkButton","ArrowRightIcon","CircleUserRoundIcon","PlusIcon","Trash2Icon","useTranslation","AuthChoiceRow","AuthPageHeader","Alert","InitialAvatar","AuthLayout","buildAuthorizeUrl","extractOAuthParams","accountsQueryOptions","removeAccountMutationOptions","selectAccountMutationOptions","queryKeys","Route","appendLoginPrompt","prompt","values","split","filter","Boolean","includes","push","join","buildLoginHref","search","ReturnType","useSearch","oauthParams","params","URLSearchParams","key","value","Object","entries","account_selected","undefined","set","String","toString","AccountSelect","t","queryClient","data","client_id","accountSelectionUnavailable","accounts","length","allow_add_account","continueWithSelectedAccount","window","location","href","selectMutation","onSuccess","removeMutation","invalidateQueries","queryKey","map","account","sub","current","email","isPending","mutate","allow_remove_account","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/arrow-right.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/circle-user-round.mjs","../../../frontend/src/routes/account/select/index.tsx?tsr-split=component"],"sourcesContent":["/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M5 12h14\", key: \"1ays0h\" }],\n [\"path\", { d: \"m12 5 7 7-7 7\", key: \"xquz4c\" }]\n];\nconst ArrowRight = createLucideIcon(\"arrow-right\", __iconNode);\n\nexport { __iconNode, ArrowRight as default };\n//# sourceMappingURL=arrow-right.mjs.map\n","/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M17.925 20.056a6 6 0 0 0-11.851.001\", key: \"z69sun\" }],\n [\"circle\", { cx: \"12\", cy: \"11\", r: \"4\", key: \"1gt34v\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }]\n];\nconst CircleUserRound = createLucideIcon(\"circle-user-round\", __iconNode);\n\nexport { __iconNode, CircleUserRound as default };\n//# sourceMappingURL=circle-user-round.mjs.map\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { TRBadge } from '@tinyrack/ui/components/badge';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRIconButton } from '@tinyrack/ui/components/icon-button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport {\n ArrowRightIcon,\n CircleUserRoundIcon,\n PlusIcon,\n Trash2Icon,\n} from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { AuthChoiceRow } from '#frontend/components/auth/auth-choice-row.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: 1,\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n const accountSelectionUnavailable =\n data.accounts.length === 0 && !data.allow_add_account;\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: 1,\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('accountSelect.subtitle')}\n title={t('accountSelect.title')}\n />\n\n {data.accounts.length === 0 ? (\n <Alert icon={CircleUserRoundIcon} type=\"info\">\n {accountSelectionUnavailable\n ? t('accountSelect.unavailable')\n : t('accountSelect.noRememberedAccounts')}\n </Alert>\n ) : (\n <div\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"account-list\"\n >\n {data.accounts.map((account) => (\n <div data-testid=\"remembered-account\" key={account.sub}>\n <AuthChoiceRow\n description={\n account.current\n ? t('accountSelect.currentAccount')\n : t('accountSelect.rememberedAccount')\n }\n label={account.email}\n leading={<InitialAvatar email={account.email} size=\"sm\" />}\n /*\n Picking an account is a mutation, not navigation, so this\n one is a button. No children — the row supplies them.\n */\n render={\n <TRButton\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n />\n }\n trailing={\n <>\n {account.current ? (\n <TRBadge uiSize=\"md\" variant=\"info\">\n {t('accountSelect.current')}\n </TRBadge>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <TRIconButton\n appearance=\"ghost\"\n aria-label={t('accountSelect.removeAccount', {\n email: account.email,\n })}\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n intent=\"neutral\"\n onClick={() =>\n removeMutation.mutate({ sub: account.sub })\n }\n type=\"button\"\n uiSize=\"sm\"\n >\n <Trash2Icon aria-hidden className=\"size-tinyrack-lg\" />\n </TRIconButton>\n ) : null}\n </>\n }\n />\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <TRLinkButton\n appearance=\"outline\"\n className=\"w-full justify-between\"\n intent=\"neutral\"\n render={<a href={buildLoginHref(search)} />}\n >\n <span className=\"inline-flex items-center gap-tinyrack-sm\">\n <PlusIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('accountSelect.useAnotherAccount')}\n </span>\n <ArrowRightIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRLinkButton>\n ) : null}\n </AuthLayout>\n );\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{I as e}from"./index-CZ0rWM7l.js";var t=e;export{t as errorComponent};
2
+ //# sourceMappingURL=select-UcpXAI9J.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select-UL_cNRnQ.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/account/select/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { TRBadge } from '@tinyrack/ui/components/badge';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRIconButton } from '@tinyrack/ui/components/icon-button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport {\n ArrowRightIcon,\n CircleUserRoundIcon,\n PlusIcon,\n Trash2Icon,\n} from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { AuthChoiceRow } from '#frontend/components/auth/auth-choice-row.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: 1,\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n const accountSelectionUnavailable =\n data.accounts.length === 0 && !data.allow_add_account;\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: 1,\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('accountSelect.subtitle')}\n title={t('accountSelect.title')}\n />\n\n {data.accounts.length === 0 ? (\n <Alert icon={CircleUserRoundIcon} type=\"info\">\n {accountSelectionUnavailable\n ? t('accountSelect.unavailable')\n : t('accountSelect.noRememberedAccounts')}\n </Alert>\n ) : (\n <div\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"account-list\"\n >\n {data.accounts.map((account) => (\n <div data-testid=\"remembered-account\" key={account.sub}>\n <AuthChoiceRow\n description={\n account.current\n ? t('accountSelect.currentAccount')\n : t('accountSelect.rememberedAccount')\n }\n label={account.email}\n leading={<InitialAvatar email={account.email} size=\"sm\" />}\n /*\n Picking an account is a mutation, not navigation, so this\n one is a button. No children — the row supplies them.\n */\n render={\n <TRButton\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n />\n }\n trailing={\n <>\n {account.current ? (\n <TRBadge uiSize=\"md\" variant=\"info\">\n {t('accountSelect.current')}\n </TRBadge>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <TRIconButton\n appearance=\"ghost\"\n aria-label={t('accountSelect.removeAccount', {\n email: account.email,\n })}\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n intent=\"neutral\"\n onClick={() =>\n removeMutation.mutate({ sub: account.sub })\n }\n type=\"button\"\n uiSize=\"sm\"\n >\n <Trash2Icon aria-hidden className=\"size-tinyrack-lg\" />\n </TRIconButton>\n ) : null}\n </>\n }\n />\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <TRLinkButton\n appearance=\"outline\"\n className=\"w-full justify-between\"\n intent=\"neutral\"\n render={<a href={buildLoginHref(search)} />}\n >\n <span className=\"inline-flex items-center gap-tinyrack-sm\">\n <PlusIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('accountSelect.useAnotherAccount')}\n </span>\n <ArrowRightIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRLinkButton>\n ) : null}\n </AuthLayout>\n );\n}\n"],"mappings":"wCAqBsF,IAAAC,EAA7ED"}
1
+ {"version":3,"file":"select-UcpXAI9J.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/account/select/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { TRBadge } from '@tinyrack/ui/components/badge';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRIconButton } from '@tinyrack/ui/components/icon-button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport {\n ArrowRightIcon,\n CircleUserRoundIcon,\n PlusIcon,\n Trash2Icon,\n} from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { AuthChoiceRow } from '#frontend/components/auth/auth-choice-row.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: 1,\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n const accountSelectionUnavailable =\n data.accounts.length === 0 && !data.allow_add_account;\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: 1,\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('accountSelect.subtitle')}\n title={t('accountSelect.title')}\n />\n\n {data.accounts.length === 0 ? (\n <Alert icon={CircleUserRoundIcon} type=\"info\">\n {accountSelectionUnavailable\n ? t('accountSelect.unavailable')\n : t('accountSelect.noRememberedAccounts')}\n </Alert>\n ) : (\n <div\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"account-list\"\n >\n {data.accounts.map((account) => (\n <div data-testid=\"remembered-account\" key={account.sub}>\n <AuthChoiceRow\n description={\n account.current\n ? t('accountSelect.currentAccount')\n : t('accountSelect.rememberedAccount')\n }\n label={account.email}\n leading={<InitialAvatar email={account.email} size=\"sm\" />}\n /*\n Picking an account is a mutation, not navigation, so this\n one is a button. No children — the row supplies them.\n */\n render={\n <TRButton\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n />\n }\n trailing={\n <>\n {account.current ? (\n <TRBadge uiSize=\"md\" variant=\"info\">\n {t('accountSelect.current')}\n </TRBadge>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <TRIconButton\n appearance=\"ghost\"\n aria-label={t('accountSelect.removeAccount', {\n email: account.email,\n })}\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n intent=\"neutral\"\n onClick={() =>\n removeMutation.mutate({ sub: account.sub })\n }\n type=\"button\"\n uiSize=\"sm\"\n >\n <Trash2Icon aria-hidden className=\"size-tinyrack-lg\" />\n </TRIconButton>\n ) : null}\n </>\n }\n />\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <TRLinkButton\n appearance=\"outline\"\n className=\"w-full justify-between\"\n intent=\"neutral\"\n render={<a href={buildLoginHref(search)} />}\n >\n <span className=\"inline-flex items-center gap-tinyrack-sm\">\n <PlusIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('accountSelect.useAnotherAccount')}\n </span>\n <ArrowRightIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRLinkButton>\n ) : null}\n </AuthLayout>\n );\n}\n"],"mappings":"wCAqBsF,IAAAC,EAA7ED"}
@@ -1,2 +1,2 @@
1
- import{J as e,Z as t,c as n,q as r}from"./createLucideIcon-BPpAIXR_.js";import{bn as i}from"./config-v3-oy_ef.js";var a=t(e(),1).forwardRef(function(e,t){let{className:r,render:i,orientation:a=`horizontal`,style:o,...s}=e;return n(`div`,e,{state:{orientation:a},ref:t,props:[{role:`separator`,"aria-orientation":a},s]})}),o=r(),s=i(a,`tr-separator`);function c({"aria-orientation":e,orientation:t=`horizontal`,role:n=`separator`,...r}){return(0,o.jsx)(s,{...r,"aria-orientation":n===`separator`?e??t:void 0,orientation:t,role:n})}export{c as t};
2
- //# sourceMappingURL=separator-BWGym8uM.js.map
1
+ import{J as e,Z as t,c as n,q as r}from"./createLucideIcon-BPpAIXR_.js";import{bn as i}from"./config-DTQCUlfZ.js";var a=t(e(),1).forwardRef(function(e,t){let{className:r,render:i,orientation:a=`horizontal`,style:o,...s}=e;return n(`div`,e,{state:{orientation:a},ref:t,props:[{role:`separator`,"aria-orientation":a},s]})}),o=r(),s=i(a,`tr-separator`);function c({"aria-orientation":e,orientation:t=`horizontal`,role:n=`separator`,...r}){return(0,o.jsx)(s,{...r,"aria-orientation":n===`separator`?e??t:void 0,orientation:t,role:n})}export{c as t};
2
+ //# sourceMappingURL=separator-B0Bvh1he.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"separator-BWGym8uM.js","names":[],"sources":["../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/separator/Separator.mjs","../../../../node_modules/.pnpm/@tinyrack+ui@0.25.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.3.1_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/separator/separator.js"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useRenderElement } from \"../internals/useRenderElement.mjs\";\n\n/**\n * A separator element accessible to screen readers.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Separator](https://base-ui.com/react/components/separator)\n */\nexport const Separator = /*#__PURE__*/React.forwardRef(function SeparatorComponent(componentProps, forwardedRef) {\n const {\n className,\n render,\n orientation = 'horizontal',\n style,\n ...elementProps\n } = componentProps;\n const state = {\n orientation\n };\n const element = useRenderElement('div', componentProps, {\n state,\n ref: forwardedRef,\n props: [{\n role: 'separator',\n 'aria-orientation': orientation\n }, elementProps]\n });\n return element;\n});\nif (process.env.NODE_ENV !== \"production\") Separator.displayName = \"Separator\";","import { createComponentPart } from \"../../internal/component-part.js\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Separator } from \"@base-ui/react/separator\";\n//#region src/components/separator/separator.tsx\nconst SeparatorPart = createComponentPart(Separator, \"tr-separator\");\nfunction TRSeparator({ \"aria-orientation\": ariaOrientation, orientation = \"horizontal\", role = \"separator\", ...props }) {\n\treturn /* @__PURE__ */ jsx(SeparatorPart, {\n\t\t...props,\n\t\t\"aria-orientation\": role === \"separator\" ? ariaOrientation ?? orientation : void 0,\n\t\torientation,\n\t\trole\n\t});\n}\n//#endregion\nexport { TRSeparator };\n\n//# sourceMappingURL=separator.js.map"],"x_google_ignoreList":[0,1],"mappings":"kHAWA,IAAa,SAAyB,CAAA,CAAM,WAAW,SAA4B,EAAgB,EAAc,CAC/G,GAAM,CACJ,YACA,SACA,cAAc,aACd,QACA,GAAG,GACD,EAYJ,OARgB,EAAiB,MAAO,EAAgB,CACtD,MAAA,CAHA,aAGI,EACJ,IAAK,EACL,MAAO,CAAC,CACN,KAAM,YACN,mBAAoB,CACtB,EAAG,CAAY,CACjB,CACa,CACf,CAAC,QC3BK,EAAgB,EAAoB,EAAW,cAAc,EACnE,SAAS,EAAY,CAAE,mBAAoB,EAAiB,cAAc,aAAc,OAAO,YAAa,GAAG,GAAS,CACvH,OAAuB,EAAA,EAAA,IAAA,CAAI,EAAe,CACzC,GAAG,EACH,mBAAoB,IAAS,YAAc,GAAmB,EAAc,IAAK,GACjF,cACA,MACD,CAAC,CACF"}
1
+ {"version":3,"file":"separator-B0Bvh1he.js","names":[],"sources":["../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/separator/Separator.mjs","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/separator/separator.js"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useRenderElement } from \"../internals/useRenderElement.mjs\";\n\n/**\n * A separator element accessible to screen readers.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Separator](https://base-ui.com/react/components/separator)\n */\nexport const Separator = /*#__PURE__*/React.forwardRef(function SeparatorComponent(componentProps, forwardedRef) {\n const {\n className,\n render,\n orientation = 'horizontal',\n style,\n ...elementProps\n } = componentProps;\n const state = {\n orientation\n };\n const element = useRenderElement('div', componentProps, {\n state,\n ref: forwardedRef,\n props: [{\n role: 'separator',\n 'aria-orientation': orientation\n }, elementProps]\n });\n return element;\n});\nif (process.env.NODE_ENV !== \"production\") Separator.displayName = \"Separator\";","import { createComponentPart } from \"../../internal/component-part.js\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Separator } from \"@base-ui/react/separator\";\n//#region src/components/separator/separator.tsx\nconst SeparatorPart = createComponentPart(Separator, \"tr-separator\");\nfunction TRSeparator({ \"aria-orientation\": ariaOrientation, orientation = \"horizontal\", role = \"separator\", ...props }) {\n\treturn /* @__PURE__ */ jsx(SeparatorPart, {\n\t\t...props,\n\t\t\"aria-orientation\": role === \"separator\" ? ariaOrientation ?? orientation : void 0,\n\t\torientation,\n\t\trole\n\t});\n}\n//#endregion\nexport { TRSeparator };\n\n//# sourceMappingURL=separator.js.map"],"x_google_ignoreList":[0,1],"mappings":"kHAWA,IAAa,SAAyB,CAAA,CAAM,WAAW,SAA4B,EAAgB,EAAc,CAC/G,GAAM,CACJ,YACA,SACA,cAAc,aACd,QACA,GAAG,GACD,EAYJ,OARgB,EAAiB,MAAO,EAAgB,CACtD,MAAA,CAHA,aAGI,EACJ,IAAK,EACL,MAAO,CAAC,CACN,KAAM,YACN,mBAAoB,CACtB,EAAG,CAAY,CACjB,CACa,CACf,CAAC,QC3BK,EAAgB,EAAoB,EAAW,cAAc,EACnE,SAAS,EAAY,CAAE,mBAAoB,EAAiB,cAAc,aAAc,OAAO,YAAa,GAAG,GAAS,CACvH,OAAuB,EAAA,EAAA,IAAA,CAAI,EAAe,CACzC,GAAG,EACH,mBAAoB,IAAS,YAAc,GAAmB,EAAc,IAAK,GACjF,cACA,MACD,CAAC,CACF"}