@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-RpDmbWNA.js","names":["__iconNode","__iconNode","__iconNode","__iconNode","useMutation","useQueryClient","useSuspenseQuery","useRouter","TRButton","TRCard","TRText","CircleAlertIcon","LogOutIcon","useState","useTranslation","AuthPageHeader","DangerZoneSection","LinkedAccountsSection","PasskeySection","PasswordSection","TotpSection","UnlinkOAuthModal","UserInfoSection","Alert","InitialAvatar","AppLayout","ChangePasswordModal","DeleteAccountModal","DisableTotpModal","ManagePasskeysModal","RegenerateTotpRecoveryCodesModal","RemovePasswordModal","SetPasswordModal","SetupPasskeyModal","SetupTotpModal","tick","appConfigQueryOptions","logoutMutationOptions","getOAuthAuthorizeUrl","oauthAccountsQueryOptions","unlinkOAuthMutationOptions","getSessionQueryOptions","PasswordModalType","TotpModalType","PasskeyModalType","OAUTH_ERROR_I18N_MAP","Record","access_denied","temporarily_unavailable","server_error","Route","Profile","t","router","queryClient","search","useSearch","oauthError","oauth_error","oauthErrorMessage","undefined","unlinkingProvider","setUnlinkingProvider","unlinkModal","setUnlinkModal","id","name","passwordModal","setPasswordModal","totpModal","setTotpModal","passkeyModal","setPasskeyModal","showDeleteModal","setShowDeleteModal","data","session","oauthAccountsData","appConfig","logoutMutation","onSuccess","setQueryData","queryKey","user","navigate","to","onSettled","invalidateQueries","unlinkMutation","handleUnlinkConfirm","mutateAsync","availableProviders","available_providers","hasLinkedOAuth","some","p","linked","isConfigManaged","managed_by","passwordAuthMethod","auth","password","passkeyAuthMethod","passkey","totpEnabled","totp","enabled","passkeyEnabled","accountDeletionEnabled","account_deletion","retentionPeriod","retention","retentionDays","match","value","parseInt","unit","showPasswordSection","showTotpSection","showPasskeySection","showLinkedAccounts","length","hasSecurityOptions","isPending","mutate","email","totp_recovery_codes_missing","has_password","totp_registered","passkey_count","provider","display_name","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/cloud.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/link-2-off.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/pencil.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/settings.mjs","../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/smartphone.mjs","../../../frontend/src/components/profile/danger-zone-section.tsx","../../../frontend/src/components/profile/security-row.tsx","../../../frontend/src/components/profile/linked-accounts-section.tsx","../../../frontend/src/components/profile/passkey-section.tsx","../../../frontend/src/components/profile/password-section.tsx","../../../frontend/src/components/profile/totp-section.tsx","../../../frontend/src/components/ui/alert-banner.tsx","../../../frontend/src/components/profile/unlink-oauth-modal.tsx","../../../frontend/src/components/profile/user-info-section.tsx","../../../frontend/src/features/layout/app-layout.tsx","../../../frontend/src/queries/password.ts","../../../frontend/src/features/profile/change-password-modal.tsx","../../../frontend/src/queries/account.ts","../../../frontend/src/features/profile/delete-account-modal.tsx","../../../frontend/src/features/profile/disable-totp-modal.tsx","../../../frontend/src/features/profile/manage-passkeys-modal.tsx","../../../frontend/src/features/profile/regenerate-totp-recovery-codes-modal.tsx","../../../frontend/src/features/profile/remove-password-modal.tsx","../../../frontend/src/features/profile/set-password-modal.tsx","../../../frontend/src/features/profile/setup-passkey-modal.tsx","../../../frontend/src/features/profile/setup-totp-modal.tsx","../../../frontend/src/routes/profile/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: \"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z\", key: \"p7xjir\" }]\n];\nconst Cloud = createLucideIcon(\"cloud\", __iconNode);\n\nexport { __iconNode, Cloud as default };\n//# sourceMappingURL=cloud.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: \"M9 17H7A5 5 0 0 1 7 7\", key: \"10o201\" }],\n [\"path\", { d: \"M15 7h2a5 5 0 0 1 4 8\", key: \"1d3206\" }],\n [\"line\", { x1: \"8\", x2: \"12\", y1: \"12\", y2: \"12\", key: \"rvw6j4\" }],\n [\"line\", { x1: \"2\", x2: \"22\", y1: \"2\", y2: \"22\", key: \"a6p6uj\" }]\n];\nconst Link2Off = createLucideIcon(\"link-2-off\", __iconNode);\n\nexport { __iconNode, Link2Off as default };\n//# sourceMappingURL=link-2-off.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 [\n \"path\",\n {\n d: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\",\n key: \"1a8usu\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\", key: \"1mk7zo\" }]\n];\nconst Pencil = createLucideIcon(\"pencil\", __iconNode);\n\nexport { __iconNode, Pencil as default };\n//# sourceMappingURL=pencil.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 [\n \"path\",\n {\n d: \"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\",\n key: \"1i5ecw\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Settings = createLucideIcon(\"settings\", __iconNode);\n\nexport { __iconNode, Settings as default };\n//# sourceMappingURL=settings.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 [\"rect\", { width: \"14\", height: \"20\", x: \"5\", y: \"2\", rx: \"2\", ry: \"2\", key: \"1yt0o3\" }],\n [\"path\", { d: \"M12 18h.01\", key: \"mhygvu\" }]\n];\nconst Smartphone = createLucideIcon(\"smartphone\", __iconNode);\n\nexport { __iconNode, Smartphone as default };\n//# sourceMappingURL=smartphone.mjs.map\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { Trash2Icon, TriangleAlertIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\n\ninterface DangerZoneSectionProps {\n isConfigManaged: boolean;\n isDeletionEnabled: boolean;\n onDeleteClick: () => void;\n}\n\nexport function DangerZoneSection({\n isConfigManaged,\n isDeletionEnabled,\n onDeleteClick,\n}: DangerZoneSectionProps) {\n const { t } = useTranslation();\n\n if (!isDeletionEnabled) {\n return null;\n }\n\n return (\n <TRCard.Root\n className=\"border-tinyrack-danger-border bg-tinyrack-danger-surface\"\n variant=\"outlined\"\n >\n <TRCard.Header className=\"border-tinyrack-danger-border border-b-tinyrack-default px-tinyrack-lg py-tinyrack-md\">\n <div className=\"flex items-center gap-tinyrack-sm\">\n <TriangleAlertIcon\n aria-hidden\n className=\"size-tinyrack-lg text-tinyrack-danger-foreground\"\n />\n <TRCard.Title className=\"text-tinyrack-danger-foreground\">\n {t('profile.dangerZone.title')}\n </TRCard.Title>\n </div>\n <TRCard.Description>\n {t('profile.dangerZone.description')}\n </TRCard.Description>\n </TRCard.Header>\n <TRCard.Content className=\"px-tinyrack-lg py-tinyrack-md\">\n <div className=\"flex items-center justify-between gap-tinyrack-lg\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-tinyrack-3xs\">\n <TRText as=\"p\" variant=\"bodySm\" weight=\"medium\">\n {t('profile.deleteAccount.title')}\n </TRText>\n <TRText as=\"p\" color=\"muted\" variant=\"caption\">\n {isConfigManaged\n ? t('profile.deleteAccount.configManaged')\n : t('profile.deleteAccount.description')}\n </TRText>\n </div>\n <TRButton\n appearance=\"outline\"\n data-testid=\"profile-delete-account\"\n disabled={isConfigManaged}\n intent=\"danger\"\n onClick={onDeleteClick}\n type=\"button\"\n uiSize=\"sm\"\n >\n <Trash2Icon aria-hidden className=\"size-tinyrack-lg\" />\n {t('profile.deleteAccount.button')}\n </TRButton>\n </div>\n </TRCard.Content>\n </TRCard.Root>\n );\n}\n","import { TRText } from '@tinyrack/ui/components/text';\nimport type { LucideIcon } from 'lucide-react';\nimport { isValidElement, type ReactNode } from 'react';\n\ntype SecurityRowProps = {\n /**\n * A lucide glyph, which gets tinted with the row's state, or a ready-made\n * node for marks that carry their own colour — a provider's brand logo.\n */\n icon: LucideIcon | ReactNode;\n /** Tints the icon tile. \"Configured\" rather than \"enabled\" — a linked OAuth\n * account and a set password are both active states. */\n active: boolean;\n title: string;\n status: string;\n /** Secondary line under the status, e.g. a config-managed caveat. */\n note?: ReactNode;\n /** Trailing controls. */\n actions?: ReactNode;\n};\n\n/**\n * One credential the account has, or does not.\n *\n * Password, TOTP, passkeys, and each linked provider were four copies of the\n * same row — a tinted icon tile, a title, a status line, a trailing action\n * cluster — which is how they drifted into three different paddings.\n */\nexport function SecurityRow({\n icon,\n active,\n title,\n status,\n note,\n actions,\n}: SecurityRowProps) {\n /*\n Discriminated by `isValidElement`, not `typeof … === 'function'`: lucide\n icons are `forwardRef` objects, so a typeof check reads them as data and\n React then throws on rendering `{$$typeof, render}` as a child. An\n already-rendered mark is an element; a component is not.\n */\n let mark: ReactNode;\n if (isValidElement(icon)) {\n mark = icon;\n } else {\n const Icon = icon as LucideIcon;\n mark = (\n <Icon\n aria-hidden\n className={`size-tinyrack-lg ${\n active\n ? 'text-tinyrack-success-foreground'\n : 'text-tinyrack-text-muted'\n }`}\n />\n );\n }\n\n return (\n <div className=\"flex items-center justify-between gap-tinyrack-lg p-tinyrack-lg\">\n <div className=\"flex min-w-0 flex-1 items-center gap-tinyrack-md\">\n <div\n className={`flex size-9 shrink-0 items-center justify-center rounded-tinyrack-md ${\n active ? 'bg-tinyrack-success-surface' : 'bg-tinyrack-surface-muted'\n }`}\n >\n {mark}\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col gap-tinyrack-3xs\">\n <TRText as=\"div\" variant=\"bodySm\" weight=\"medium\">\n {title}\n </TRText>\n <TRText as=\"div\" color=\"muted\" variant=\"caption\">\n {status}\n </TRText>\n {note}\n </div>\n </div>\n {actions && (\n <div className=\"flex shrink-0 flex-wrap justify-end gap-tinyrack-xs\">\n {actions}\n </div>\n )}\n </div>\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { useTranslation } from 'react-i18next';\nimport { ProviderMark } from '#frontend/components/auth/provider-logos.tsx';\nimport { SecurityRow } from '#frontend/components/profile/security-row.tsx';\n\ninterface OAuthProvider {\n id: string;\n display_name: string;\n icon_url?: string;\n linked: boolean;\n}\n\ninterface LinkedAccountsSectionProps {\n providers: OAuthProvider[];\n unlinkingProvider: string | null;\n getAuthorizeUrl: (\n providerId: string,\n mode?: 'login' | 'register' | 'link',\n returnUrl?: string,\n ) => string;\n onUnlinkRequest: (provider: OAuthProvider) => void;\n}\n\nexport function LinkedAccountsSection({\n providers,\n unlinkingProvider,\n getAuthorizeUrl,\n onUnlinkRequest,\n}: LinkedAccountsSectionProps) {\n const { t } = useTranslation();\n\n if (providers.length === 0) {\n return null;\n }\n\n return (\n <TRCard.Root variant=\"outlined\">\n <TRCard.Header className=\"border-tinyrack-border border-b-tinyrack-default px-tinyrack-lg py-tinyrack-md\">\n <TRCard.Title>{t('profile.linkedAccounts.title')}</TRCard.Title>\n <TRCard.Description>\n {t('profile.linkedAccounts.description')}\n </TRCard.Description>\n </TRCard.Header>\n <TRCard.Content className=\"divide-y divide-tinyrack-border p-0\">\n {providers.map((provider) => (\n <SecurityRow\n actions={\n provider.linked ? (\n <TRButton\n appearance=\"ghost\"\n intent=\"danger\"\n loading={unlinkingProvider === provider.id}\n loadingLabel={t('profile.linkedAccounts.unlinking')}\n onClick={() => onUnlinkRequest(provider)}\n type=\"button\"\n uiSize=\"sm\"\n >\n {unlinkingProvider !== provider.id\n ? t('profile.linkedAccounts.unlink')\n : undefined}\n </TRButton>\n ) : (\n <TRLinkButton\n appearance=\"outline\"\n intent=\"neutral\"\n render={\n <a\n href={getAuthorizeUrl(provider.id, 'link', '/profile')}\n />\n }\n uiSize=\"sm\"\n >\n {t('profile.linkedAccounts.link')}\n </TRLinkButton>\n )\n }\n active={provider.linked}\n /*\n The real brand mark, not a generic link glyph. The sign-in screen\n shows the Google logo; showing a chain link for the same provider\n on a screen the user reaches minutes later reads as a different\n product.\n */\n icon={\n <ProviderMark\n className=\"size-tinyrack-lg\"\n iconUrl={provider.icon_url}\n providerId={provider.id}\n />\n }\n key={provider.id}\n status={\n provider.linked\n ? t('profile.linkedAccounts.connected')\n : t('profile.linkedAccounts.notConnected')\n }\n title={provider.display_name}\n />\n ))}\n </TRCard.Content>\n </TRCard.Root>\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { FingerprintIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { SecurityRow } from '#frontend/components/profile/security-row.tsx';\n\ntype PasskeyModalType = 'setup' | 'manage' | null;\n\ninterface PasskeySectionProps {\n passkeyCount: number;\n onOpenModal: (type: PasskeyModalType) => void;\n}\n\nexport function PasskeySection({\n passkeyCount,\n onOpenModal,\n}: PasskeySectionProps) {\n const { t } = useTranslation();\n const hasPasskeys = passkeyCount > 0;\n\n return (\n <SecurityRow\n actions={\n <TRButton\n appearance=\"ghost\"\n intent=\"primary\"\n onClick={() => onOpenModal(hasPasskeys ? 'manage' : 'setup')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {hasPasskeys ? t('profile.passkey.manage') : t('profile.passkey.add')}\n </TRButton>\n }\n active={hasPasskeys}\n icon={FingerprintIcon}\n status={\n hasPasskeys\n ? t('profile.passkey.status.enabled', { count: passkeyCount })\n : t('profile.passkey.status.disabled')\n }\n title={t('profile.passkey.title')}\n />\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { TRTooltip } from '@tinyrack/ui/components/tooltip';\nimport { InfoIcon, KeyRoundIcon, SettingsIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { SecurityRow } from '#frontend/components/profile/security-row.tsx';\n\ntype PasswordModalType = 'set' | 'change' | 'remove' | null;\n\ninterface PasswordSectionProps {\n hasPassword: boolean;\n hasLinkedOAuth: boolean;\n isConfigManaged: boolean;\n hasSecondFactorOnly: boolean;\n onOpenModal: (type: PasswordModalType) => void;\n}\n\nexport function PasswordSection({\n hasPassword,\n hasLinkedOAuth,\n isConfigManaged,\n hasSecondFactorOnly,\n onOpenModal,\n}: PasswordSectionProps) {\n const { t } = useTranslation();\n\n return (\n <SecurityRow\n actions={\n isConfigManaged ? undefined : hasPassword ? (\n <>\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-password-change\"\n intent=\"primary\"\n onClick={() => onOpenModal('change')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.change')}\n </TRButton>\n {hasLinkedOAuth ? (\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-password-remove\"\n intent=\"danger\"\n onClick={() => onOpenModal('remove')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.remove')}\n </TRButton>\n ) : null}\n {!hasLinkedOAuth && hasSecondFactorOnly && (\n <TRTooltip.Root>\n <TRTooltip.Trigger\n render={\n <TRButton\n appearance=\"ghost\"\n disabled\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.remove')}\n </TRButton>\n }\n />\n <TRTooltip.Portal>\n <TRTooltip.Positioner>\n <TRTooltip.Popup className=\"max-w-tinyrack-measure-lg rounded-tinyrack-md border border-tinyrack-border bg-tinyrack-surface p-tinyrack-md text-tinyrack-text text-tinyrack-xs shadow-tinyrack-overlay\">\n <div className=\"flex items-start gap-tinyrack-sm\">\n <InfoIcon\n aria-hidden\n className=\"size-tinyrack-lg shrink-0 text-tinyrack-warning-foreground\"\n />\n <div>{t('profile.password.removeDisabledReason')}</div>\n </div>\n </TRTooltip.Popup>\n </TRTooltip.Positioner>\n </TRTooltip.Portal>\n </TRTooltip.Root>\n )}\n </>\n ) : (\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-password-set\"\n intent=\"primary\"\n onClick={() => onOpenModal('set')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.set')}\n </TRButton>\n )\n }\n active={hasPassword}\n icon={KeyRoundIcon}\n note={\n isConfigManaged ? (\n <TRText\n as=\"div\"\n className=\"flex items-center gap-tinyrack-xs\"\n color=\"muted\"\n variant=\"caption\"\n >\n <SettingsIcon aria-hidden className=\"size-tinyrack-md shrink-0\" />\n <span>{t('profile.password.configManaged')}</span>\n </TRText>\n ) : undefined\n }\n status={\n hasPassword\n ? t('profile.password.status.set')\n : t('profile.password.status.notSet')\n }\n title={t('profile.password.title')}\n />\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { ShieldCheckIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { SecurityRow } from '#frontend/components/profile/security-row.tsx';\n\ntype TotpModalType = 'setup' | 'disable' | 'regenerate' | null;\n\ninterface TotpSectionProps {\n totpEnabled: boolean;\n recoveryCodesMissing: boolean;\n onOpenModal: (type: TotpModalType) => void;\n}\n\nexport function TotpSection({\n totpEnabled,\n recoveryCodesMissing,\n onOpenModal,\n}: TotpSectionProps) {\n const { t } = useTranslation();\n\n return (\n <SecurityRow\n actions={\n totpEnabled ? (\n <>\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-totp-regenerate\"\n intent=\"primary\"\n onClick={() => onOpenModal('regenerate')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.regenerate')}\n </TRButton>\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-totp-disable\"\n intent=\"danger\"\n onClick={() => onOpenModal('disable')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.disable')}\n </TRButton>\n </>\n ) : (\n <TRButton\n appearance=\"ghost\"\n data-testid=\"profile-totp-enable\"\n intent=\"primary\"\n onClick={() => onOpenModal('setup')}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.enable')}\n </TRButton>\n )\n }\n active={totpEnabled}\n icon={ShieldCheckIcon}\n status={\n totpEnabled\n ? recoveryCodesMissing\n ? t('profile.totp.status.recoveryCodesMissing')\n : t('profile.totp.status.enabled')\n : t('profile.totp.status.disabled')\n }\n title={t('profile.totp.title')}\n />\n );\n}\n","import { CircleAlertIcon, InfoIcon, TriangleAlertIcon } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\n\ntype AlertBannerVariant = 'error' | 'warning' | 'info';\n\ninterface AlertBannerProps {\n variant: AlertBannerVariant;\n children: ReactNode;\n}\n\nconst variantIcons: Record<AlertBannerVariant, typeof TriangleAlertIcon> = {\n error: CircleAlertIcon,\n warning: TriangleAlertIcon,\n info: InfoIcon,\n};\n\n/**\n * Status banner built on the design system's {@link Alert} (which wraps\n * `TRAlert`). Kept as a thin alias so existing call sites and the\n * `alert-banner-*` test ids stay stable while the visuals come from\n * `@tinyrack/ui`.\n */\nexport function AlertBanner({ variant, children }: AlertBannerProps) {\n return (\n <Alert\n data-testid={`alert-banner-${variant}`}\n icon={variantIcons[variant]}\n type={variant}\n >\n {children}\n </Alert>\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { Link2OffIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\n\ninterface UnlinkOAuthModalProps {\n isOpen: boolean;\n onClose: () => void;\n providerName: string;\n isPending: boolean;\n onConfirm: () => Promise<void>;\n}\n\nexport function UnlinkOAuthModal({\n isOpen,\n onClose,\n providerName,\n isPending,\n onConfirm,\n}: UnlinkOAuthModalProps) {\n const { t } = useTranslation();\n const [error, setError] = useState<string | null>(null);\n\n const handleClose = () => {\n if (isPending) return;\n setError(null);\n onClose();\n };\n\n const handleConfirm = async () => {\n setError(null);\n try {\n await onConfirm();\n handleClose();\n } catch {\n setError(t('profile.linkedAccounts.unlinkError'));\n }\n };\n\n return (\n <Modal\n description={t('profile.linkedAccounts.unlinkModal.description', {\n provider: providerName,\n })}\n icon={Link2OffIcon}\n isOpen={isOpen}\n onClose={handleClose}\n preventClose={isPending}\n title={t('profile.linkedAccounts.unlinkModal.title')}\n variant=\"destructive\"\n >\n <div className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\">\n <AlertBanner variant=\"warning\">\n {t('profile.linkedAccounts.unlinkModal.warning', {\n provider: providerName,\n })}\n </AlertBanner>\n\n {error && <AlertBanner variant=\"error\">{error}</AlertBanner>}\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"unlink-oauth-cancel\"\n disabled={isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.linkedAccounts.unlinkModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"unlink-oauth-unlink\"\n disabled={isPending}\n intent=\"danger\"\n loading={isPending}\n loadingLabel={t('profile.linkedAccounts.unlinking')}\n onClick={handleConfirm}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.linkedAccounts.unlink')}\n </TRButton>\n </ModalActions>\n </div>\n </Modal>\n );\n}\n","import { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { useTranslation } from 'react-i18next';\n\nfunction InfoRow({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between gap-tinyrack-lg px-tinyrack-lg py-tinyrack-md\">\n <TRText className=\"shrink-0\" color=\"muted\" variant=\"caption\">\n {label}\n </TRText>\n <TRText truncate variant=\"bodySm\" weight=\"medium\">\n {value}\n </TRText>\n </div>\n );\n}\n\ninterface UserInfoSectionProps {\n user: {\n sub: string;\n email: string;\n };\n}\n\nexport function UserInfoSection({ user }: UserInfoSectionProps) {\n const { t } = useTranslation();\n\n return (\n <TRCard.Root variant=\"outlined\">\n <TRCard.Header className=\"border-tinyrack-border border-b-tinyrack-default px-tinyrack-lg py-tinyrack-md\">\n <TRCard.Title>{t('profile.account.title')}</TRCard.Title>\n <TRCard.Description>\n {t('profile.account.description')}\n </TRCard.Description>\n </TRCard.Header>\n <TRCard.Content className=\"divide-y divide-tinyrack-border p-0\">\n <InfoRow label={t('profile.id.label')} value={user.sub} />\n <InfoRow label={t('profile.email.label')} value={user.email} />\n </TRCard.Content>\n </TRCard.Root>\n );\n}\n","import { Toaster } from '#frontend/components/ui/toaster.tsx';\nimport { ShellHeaderBar } from '#frontend/features/layout/shell-header-bar.tsx';\n\ntype AppLayoutProps = {\n children: React.ReactNode;\n /** Trailing header action — the profile's sign-out. */\n headerActions?: React.ReactNode;\n};\n\n/**\n * The shell for signed-in screens that are not the admin console.\n *\n * Shares the auth surface's vocabulary — the canvas, the header bar, the\n * entrance, the token-owned rhythm — but drops the brand panel and runs full\n * width. A settings page is a list of cards to work through, not a single task\n * to focus on, so surrendering the leading columns to a permanent brand column\n * would be paying prime space for decoration.\n *\n * Deliberately not extracted into a wrapper shared with `AuthLayout`: that one\n * is a twelve-column grid because it has a panel to place, and its content\n * stack carries a mobile-only card that would be a box inside a box here.\n * The genuinely shared piece is `ShellHeaderBar`.\n */\nexport function AppLayout({ children, headerActions }: AppLayoutProps) {\n return (\n <div className=\"flex min-h-dvh flex-col bg-tinyrack-surface\">\n {/*\n Profile hosts the TOTP recovery-codes step, which confirms a copy with\n a toast. Without a viewport here that confirmation would go nowhere on\n the one screen where losing the codes is unrecoverable.\n */}\n <Toaster />\n <ShellHeaderBar actions={headerActions} brand />\n <main className=\"flex flex-1 justify-center px-tinyrack-lg py-tinyrack-xl\">\n {/*\n The stack owns vertical rhythm so screens compose as a flat list of\n blocks instead of each one hand-rolling margins that drift apart.\n */}\n <div className=\"auth-enter flex w-full max-w-tinyrack-overlay-md flex-col gap-tinyrack-xl\">\n {children}\n </div>\n </main>\n </div>\n );\n}\n","import { mutationOptions } from '@tanstack/react-query';\nimport type { InferRequestType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\n\n/**\n * Set password for OAuth-only users\n */\nexport type SetPasswordParams = InferRequestType<\n (typeof client.api.user.password)['$post']\n>['json'];\n\nexport const setPasswordMutationOptions = mutationOptions({\n mutationFn: async (values: SetPasswordParams) => {\n const res = await client.api.user.password.$post({\n json: values,\n });\n return jsonOk(res);\n },\n});\n\n/**\n * Change password for users who already have a password\n */\nexport type ChangePasswordParams = InferRequestType<\n (typeof client.api.user.password)['$put']\n>['json'];\n\nexport const changePasswordMutationOptions = mutationOptions({\n mutationFn: async (values: ChangePasswordParams) => {\n const res = await client.api.user.password.$put({\n json: values,\n });\n return jsonOk(res);\n },\n});\n\n/**\n * Remove password (only if user has at least one\n * OAuth account linked)\n */\nexport type RemovePasswordParams = InferRequestType<\n (typeof client.api.user.password)['$delete']\n>['json'];\n\nexport const removePasswordMutationOptions = mutationOptions({\n mutationFn: async (values: RemovePasswordParams) => {\n const res = await client.api.user.password.$delete({\n json: values,\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 { TRButton } from '@tinyrack/ui/components/button';\nimport { KeyRoundIcon } from 'lucide-react';\nimport { 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { changePasswordMutationOptions } from '#frontend/queries/password.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface ChangePasswordModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport function ChangePasswordModal({\n isOpen,\n onClose,\n}: ChangePasswordModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const passwordPolicy = configData.auth.password.policy;\n\n const schema = useMemo(\n () =>\n z\n .object({\n currentPassword: z.string().min(1, t('validation.password.required')),\n newPassword: 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.string(),\n })\n .refine((data) => data.newPassword === data.confirmPassword, {\n message: t('validation.confirmPassword.mismatch'),\n path: ['confirmPassword'],\n }),\n [passwordPolicy.max_length, passwordPolicy.min_length, t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: {\n currentPassword: '',\n newPassword: '',\n confirmPassword: '',\n },\n });\n\n const mutation = useMutation({\n ...changePasswordMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n },\n });\n\n const handleClose = () => {\n form.reset();\n onClose();\n };\n\n const handleSubmit = form.handleSubmit(async (data) => {\n try {\n await mutation.mutateAsync({\n current_password: data.currentPassword,\n new_password: data.newPassword,\n });\n } catch (err) {\n if (\n err instanceof IssuaryError &&\n err.code === 'INVALID_CURRENT_PASSWORD'\n ) {\n form.setError('currentPassword', {\n message: t('profile.password.changeModal.invalidCurrent'),\n });\n } else {\n form.setError('root', {\n message: t('profile.password.changeModal.error'),\n });\n }\n }\n });\n\n return (\n <Modal\n description={t('profile.password.changeModal.description')}\n icon={KeyRoundIcon}\n isOpen={isOpen}\n onClose={handleClose}\n title={t('profile.password.changeModal.title')}\n >\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleSubmit}\n >\n <AuthField\n error={form.formState.errors.currentPassword}\n errorTestId=\"change-password-error-currentPassword\"\n id=\"current-password\"\n label={t('profile.password.changeModal.currentPassword')}\n placeholder={t(\n 'profile.password.changeModal.currentPasswordPlaceholder',\n )}\n {...form.register('currentPassword')}\n type=\"password\"\n />\n <AuthField\n error={form.formState.errors.newPassword}\n errorTestId=\"change-password-error-newPassword\"\n id=\"new-password-change\"\n label={t('profile.password.changeModal.newPassword')}\n placeholder={t('profile.password.changeModal.newPasswordPlaceholder')}\n {...form.register('newPassword')}\n type=\"password\"\n />\n <AuthField\n error={form.formState.errors.confirmPassword}\n errorTestId=\"change-password-error-confirmPassword\"\n id=\"confirm-password-change\"\n label={t('profile.password.changeModal.confirmPassword')}\n placeholder={t(\n 'profile.password.changeModal.confirmPasswordPlaceholder',\n )}\n {...form.register('confirmPassword')}\n type=\"password\"\n />\n {form.formState.errors.root && (\n <AlertBanner variant=\"error\">\n {form.formState.errors.root.message}\n </AlertBanner>\n )}\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"change-password-cancel\"\n disabled={mutation.isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.changeModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"change-password-submit\"\n disabled={mutation.isPending}\n intent=\"primary\"\n loading={mutation.isPending}\n loadingLabel={t('profile.password.changeModal.submitting')}\n type=\"submit\"\n uiSize=\"sm\"\n >\n {mutation.isPending\n ? undefined\n : t('profile.password.changeModal.submit')}\n </TRButton>\n </ModalActions>\n </form>\n </Modal>\n );\n}\n","import { mutationOptions } from '@tanstack/react-query';\nimport type { InferResponseType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\n\nexport type AccountDeletionResponse = InferResponseType<\n (typeof client.api.user)['$delete'],\n 200\n>;\n\nexport const deleteAccountMutationOptions = mutationOptions({\n mutationFn: async () => {\n const res = await client.api.user.$delete();\n return jsonOk(res);\n },\n});\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { useRouter } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { Trash2Icon } from 'lucide-react';\nimport { 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { deleteAccountMutationOptions } from '#frontend/queries/account.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface DeleteAccountModalProps {\n isOpen: boolean;\n onClose: () => void;\n retentionDays: number;\n}\n\nexport function DeleteAccountModal({\n isOpen,\n onClose,\n retentionDays,\n}: DeleteAccountModalProps) {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n\n const schema = useMemo(\n () =>\n z.object({\n confirmation: z\n .string()\n .refine(\n (val) =>\n val.toLowerCase() ===\n t('profile.deleteAccount.modal.confirmPlaceholder').toLowerCase(),\n { message: t('validation.confirmText.mismatch') },\n ),\n }),\n [t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { confirmation: '' },\n });\n\n const mutation = useMutation({\n ...deleteAccountMutationOptions,\n onSuccess: async () => {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: null,\n });\n await tick();\n router.navigate({ to: '/login' });\n },\n });\n\n const handleClose = () => {\n if (mutation.isPending) return;\n form.reset();\n onClose();\n };\n\n const handleSubmit = form.handleSubmit(async () => {\n try {\n await mutation.mutateAsync();\n } catch {\n form.setError('root', {\n message: t('profile.deleteAccount.error'),\n });\n }\n });\n\n return (\n <Modal\n icon={Trash2Icon}\n isOpen={isOpen}\n onClose={handleClose}\n preventClose={mutation.isPending}\n title={t('profile.deleteAccount.modal.title')}\n variant=\"destructive\"\n >\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleSubmit}\n >\n <AlertBanner variant=\"error\">\n <div className=\"flex flex-col gap-tinyrack-xs\">\n <p>\n {t('profile.deleteAccount.modal.description', {\n days: retentionDays,\n })}\n </p>\n <p className=\"text-tinyrack-xs opacity-tinyrack-hover\">\n {t('profile.deleteAccount.modal.warning')}\n </p>\n </div>\n </AlertBanner>\n\n <AuthField\n autoComplete=\"off\"\n error={form.formState.errors.confirmation}\n errorTestId=\"delete-account-error\"\n id=\"delete-confirmation\"\n label={t('profile.deleteAccount.modal.confirmLabel')}\n placeholder={t('profile.deleteAccount.modal.confirmPlaceholder')}\n {...form.register('confirmation')}\n type=\"text\"\n />\n\n {form.formState.errors.root && (\n <AlertBanner variant=\"error\">\n {form.formState.errors.root.message}\n </AlertBanner>\n )}\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"delete-account-cancel\"\n disabled={mutation.isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.deleteAccount.modal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"delete-account-submit\"\n disabled={mutation.isPending}\n intent=\"danger\"\n loading={mutation.isPending}\n loadingLabel={t('profile.deleteAccount.modal.confirming')}\n type=\"submit\"\n uiSize=\"sm\"\n >\n {mutation.isPending\n ? undefined\n : t('profile.deleteAccount.modal.confirm')}\n </TRButton>\n </ModalActions>\n </form>\n </Modal>\n );\n}\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { ShieldCheckIcon } from 'lucide-react';\nimport { 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { disableTotpMutationOptions } from '#frontend/queries/totp.ts';\n\ninterface DisableTotpModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport function DisableTotpModal({ isOpen, onClose }: DisableTotpModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n\n const schema = useMemo(\n () =>\n z.object({\n code: z\n .string()\n .length(6, t('validation.totp.length'))\n .regex(/^\\d{6}$/, t('validation.totp.digits')),\n }),\n [t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { code: '' },\n });\n\n const mutation = useMutation({\n ...disableTotpMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n },\n });\n\n const handleClose = () => {\n form.reset();\n onClose();\n };\n\n const handleSubmit = form.handleSubmit(async (data) => {\n try {\n await mutation.mutateAsync({ code: data.code });\n } catch (error) {\n if (error instanceof IssuaryError) {\n if (error.code === 'CANNOT_REMOVE_LAST_SECOND_FACTOR') {\n form.setError('code', {\n message: t(\n 'profile.totp.disableModal.cannotRemoveLastSecondFactor',\n ),\n });\n return;\n }\n }\n form.setError('code', {\n message: t('profile.totp.disableModal.error'),\n });\n }\n });\n\n return (\n <Modal\n description={t('profile.totp.disableModal.description')}\n icon={ShieldCheckIcon}\n isOpen={isOpen}\n onClose={handleClose}\n title={t('profile.totp.disableModal.title')}\n variant=\"destructive\"\n >\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleSubmit}\n >\n <AlertBanner variant=\"warning\">\n {t('profile.totp.disableModal.warning')}\n </AlertBanner>\n\n <AuthField\n autoComplete=\"one-time-code\"\n error={form.formState.errors.code}\n errorTestId=\"disable-totp-error\"\n id=\"disable-totp-code\"\n inputMode=\"numeric\"\n label={t('profile.totp.disableModal.codeLabel')}\n maxLength={6}\n pattern=\"[0-9]*\"\n placeholder=\"000000\"\n {...form.register('code')}\n type=\"text\"\n />\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"disable-totp-cancel\"\n disabled={mutation.isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.disableModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"disable-totp-submit\"\n disabled={mutation.isPending}\n intent=\"danger\"\n loading={mutation.isPending}\n loadingLabel={t('profile.totp.disableModal.disabling')}\n type=\"submit\"\n uiSize=\"sm\"\n >\n {mutation.isPending\n ? undefined\n : t('profile.totp.disableModal.disable')}\n </TRButton>\n </ModalActions>\n </form>\n </Modal>\n );\n}\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { TRBadge } from '@tinyrack/ui/components/badge';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport {\n CloudIcon,\n FingerprintIcon,\n PencilIcon,\n SmartphoneIcon,\n Trash2Icon,\n} from 'lucide-react';\nimport { useCallback, 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\nimport {\n deletePasskeyMutationOptions,\n getPasskeysQueryOptions,\n type PasskeyInfo,\n renamePasskeyMutationOptions,\n} from '#frontend/queries/passkey.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface ManagePasskeysModalProps {\n isOpen: boolean;\n onClose: () => void;\n onAddNew: () => void;\n}\n\nexport function ManagePasskeysModal({\n isOpen,\n onClose,\n onAddNew,\n}: ManagePasskeysModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n const [editingPasskey, setEditingPasskey] = useState<PasskeyInfo | null>(\n null,\n );\n const [deletingId, setDeletingId] = useState<string | null>(null);\n const [confirmingDeleteId, setConfirmingDeleteId] = useState<string | null>(\n null,\n );\n const [deleteError, setDeleteError] = useState<string | null>(null);\n\n const { data: passkeysData, isLoading } = useQuery({\n ...getPasskeysQueryOptions,\n enabled: isOpen,\n });\n\n const deleteMutation = useMutation({\n ...deletePasskeyMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: queryKeys.passkeys() });\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n onSettled: () => {\n setDeletingId(null);\n },\n });\n\n const handleClose = useCallback(() => {\n setEditingPasskey(null);\n setDeletingId(null);\n setConfirmingDeleteId(null);\n setDeleteError(null);\n onClose();\n }, [onClose]);\n\n const handleRequestDelete = (passkey: PasskeyInfo) => {\n setConfirmingDeleteId(passkey.id);\n setDeleteError(null);\n };\n\n const handleCancelDelete = () => {\n setConfirmingDeleteId(null);\n setDeleteError(null);\n };\n\n const handleConfirmDelete = async (passkey: PasskeyInfo) => {\n setDeletingId(passkey.id);\n setConfirmingDeleteId(null);\n try {\n await deleteMutation.mutateAsync({ id: passkey.id });\n } catch (error) {\n if (error instanceof IssuaryError) {\n if (error.code === 'CANNOT_REMOVE_LAST_SECOND_FACTOR') {\n setDeleteError(\n t('profile.passkey.manageModal.cannotRemoveLastSecondFactor'),\n );\n return;\n }\n }\n setDeleteError(t('profile.passkey.manageModal.deleteError'));\n }\n };\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n const passkeys = passkeysData?.passkeys || [];\n\n return (\n <Modal\n icon={FingerprintIcon}\n isOpen={isOpen}\n onClose={handleClose}\n size=\"lg\"\n title={t('profile.passkey.manageModal.title')}\n >\n <div className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-sm\">\n {deleteError && (\n <AlertBanner variant=\"error\">{deleteError}</AlertBanner>\n )}\n\n {isLoading && (\n <div className=\"flex justify-center py-tinyrack-xl\">\n <TRSpinner uiSize=\"md\" />\n </div>\n )}\n\n {!isLoading && passkeys.length === 0 && (\n <div\n className=\"py-tinyrack-xl text-center text-tinyrack-sm text-tinyrack-text-muted\"\n data-testid=\"passkeys-empty\"\n >\n <p>{t('profile.passkey.manageModal.noPasskeys')}</p>\n </div>\n )}\n\n {!isLoading && passkeys.length > 0 && (\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {passkeys.map((passkey) => (\n <PasskeyItem\n formatDate={formatDate}\n isConfirmingDelete={confirmingDeleteId === passkey.id}\n isDeleting={deletingId === passkey.id}\n isEditing={editingPasskey?.id === passkey.id}\n key={passkey.id}\n onCancelDelete={handleCancelDelete}\n onCancelEdit={() => setEditingPasskey(null)}\n onConfirmDelete={() => handleConfirmDelete(passkey)}\n onEdit={() => setEditingPasskey(passkey)}\n onRequestDelete={() => handleRequestDelete(passkey)}\n passkey={passkey}\n />\n ))}\n </div>\n )}\n </div>\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"manage-passkeys-close\"\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.manageModal.close')}\n </TRButton>\n <TRButton\n data-testid=\"manage-passkeys-add-new\"\n intent=\"primary\"\n onClick={() => {\n handleClose();\n onAddNew();\n }}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.manageModal.addNew')}\n </TRButton>\n </ModalActions>\n </Modal>\n );\n}\n\ninterface PasskeyItemProps {\n passkey: PasskeyInfo;\n isDeleting: boolean;\n isEditing: boolean;\n isConfirmingDelete: boolean;\n onEdit: () => void;\n onCancelEdit: () => void;\n onRequestDelete: () => void;\n onConfirmDelete: () => void;\n onCancelDelete: () => void;\n formatDate: (dateString: string) => string;\n}\n\nfunction PasskeyItem({\n passkey,\n isDeleting,\n isEditing,\n isConfirmingDelete,\n onEdit,\n onCancelEdit,\n onRequestDelete,\n onConfirmDelete,\n onCancelDelete,\n formatDate,\n}: PasskeyItemProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n\n const schema = useMemo(\n () =>\n z.object({\n name: z\n .string()\n .min(1, t('validation.passkey.name.required'))\n .max(100, t('validation.passkey.name.max')),\n }),\n [t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { name: passkey.name || '' },\n });\n\n const renameMutation = useMutation({\n ...renamePasskeyMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: queryKeys.passkeys() });\n onCancelEdit();\n },\n });\n\n const handleRename = form.handleSubmit(async (data) => {\n try {\n await renameMutation.mutateAsync({ id: passkey.id, name: data.name });\n } catch {\n form.setError('name', {\n message: t('profile.passkey.manageModal.renameError'),\n });\n }\n });\n\n if (isEditing) {\n return (\n <form\n className=\"flex flex-col gap-tinyrack-xs rounded-tinyrack-md bg-tinyrack-surface-muted p-tinyrack-sm\"\n onSubmit={handleRename}\n >\n <AuthField\n data-testid=\"passkey-rename-input\"\n error={form.formState.errors.name}\n errorTestId=\"passkey-rename-error\"\n hideLabel\n label={t('profile.passkey.manageModal.nameLabel')}\n placeholder={t('profile.passkey.manageModal.namePlaceholder')}\n {...form.register('name')}\n type=\"text\"\n />\n <div className=\"flex justify-end gap-tinyrack-xs\">\n <TRButton\n appearance=\"ghost\"\n disabled={renameMutation.isPending}\n onClick={() => {\n form.reset();\n onCancelEdit();\n }}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.manageModal.cancelEdit')}\n </TRButton>\n <TRButton\n disabled={renameMutation.isPending}\n intent=\"primary\"\n type=\"submit\"\n uiSize=\"sm\"\n >\n {renameMutation.isPending\n ? t('profile.passkey.manageModal.saving')\n : t('profile.passkey.manageModal.save')}\n </TRButton>\n </div>\n </form>\n );\n }\n\n if (isConfirmingDelete) {\n return (\n <div className=\"flex items-center justify-between rounded-tinyrack-md border border-tinyrack-danger-border bg-tinyrack-danger-surface p-tinyrack-sm text-tinyrack-danger-foreground\">\n <div className=\"flex items-center gap-tinyrack-sm\">\n <Trash2Icon className=\"size-tinyrack-lg\" />\n <span className=\"text-tinyrack-xs\">\n {t('profile.passkey.manageModal.deleteConfirmInline')}\n </span>\n </div>\n <div className=\"flex gap-tinyrack-xs\">\n <TRButton\n appearance=\"ghost\"\n onClick={onCancelDelete}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.manageModal.cancelEdit')}\n </TRButton>\n <TRButton\n intent=\"danger\"\n onClick={onConfirmDelete}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.manageModal.delete')}\n </TRButton>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className=\"flex items-center justify-between rounded-tinyrack-md bg-tinyrack-surface-muted p-tinyrack-sm\"\n data-testid=\"passkey-item\"\n >\n <div className=\"flex items-center gap-tinyrack-sm\">\n {passkey.device_type === 'multiDevice' ? (\n <CloudIcon className=\"size-tinyrack-lg text-tinyrack-primary-foreground\" />\n ) : (\n <SmartphoneIcon className=\"size-tinyrack-lg text-tinyrack-primary-foreground\" />\n )}\n <div>\n <div className=\"font-tinyrack-medium text-tinyrack-xs\">\n {passkey.name || t('profile.passkey.manageModal.unnamedPasskey')}\n </div>\n <div className=\"flex items-center gap-tinyrack-xs text-tinyrack-text-muted text-tinyrack-xs\">\n <span>\n {t('profile.passkey.manageModal.createdAt', {\n date: formatDate(passkey.created_at),\n })}\n </span>\n {passkey.backed_up && (\n <TRBadge uiSize=\"md\" variant=\"success\">\n {t('profile.passkey.manageModal.backedUp')}\n </TRBadge>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex gap-tinyrack-xs\">\n <TRButton\n appearance=\"ghost\"\n aria-label={t('profile.passkey.manageModal.rename')}\n onClick={onEdit}\n type=\"button\"\n uiSize=\"sm\"\n >\n <PencilIcon className=\"size-tinyrack-lg\" />\n </TRButton>\n <TRButton\n appearance=\"ghost\"\n aria-label={t('profile.passkey.manageModal.delete')}\n disabled={isDeleting}\n intent=\"danger\"\n loading={isDeleting}\n onClick={onRequestDelete}\n type=\"button\"\n uiSize=\"sm\"\n >\n {isDeleting ? undefined : <Trash2Icon className=\"size-tinyrack-lg\" />}\n </TRButton>\n </div>\n </div>\n );\n}\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { useRouter } from '@tanstack/react-router';\nimport { ShieldCheckIcon } from 'lucide-react';\nimport { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { RecoveryCodesStep } from '#frontend/components/totp/recovery-codes-step.tsx';\nimport { VerifyStep } from '#frontend/components/totp/verify-step.tsx';\nimport { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal } from '#frontend/components/ui/modal.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n type RegenerateTotpRecoveryCodesResponse,\n regenerateTotpRecoveryCodesMutationOptions,\n} from '#frontend/queries/totp.ts';\n\ninterface RegenerateTotpRecoveryCodesModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\ntype RegenerateStep = 'verify' | 'recovery';\n\nexport function RegenerateTotpRecoveryCodesModal({\n isOpen,\n onClose,\n}: RegenerateTotpRecoveryCodesModalProps) {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n const [step, setStep] = useState<RegenerateStep>('verify');\n const [response, setResponse] =\n useState<RegenerateTotpRecoveryCodesResponse | null>(null);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const mutation = useMutation({\n ...regenerateTotpRecoveryCodesMutationOptions,\n onSuccess: async (data) => {\n setResponse(data);\n setStep('recovery');\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const reset = useCallback(() => {\n setStep('verify');\n setResponse(null);\n setErrorMessage(null);\n mutation.reset();\n }, [mutation]);\n\n const handleClose = useCallback(() => {\n reset();\n onClose();\n }, [onClose, reset]);\n\n const handleVerify = useCallback(\n async (code: string) => {\n setErrorMessage(null);\n\n try {\n await mutation.mutateAsync({ code });\n } catch (error) {\n if (error instanceof IssuaryError) {\n if (error.code === 'INVALID_TOTP_CODE') {\n throw error;\n }\n\n if (error.code === 'UNAUTHORIZED') {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: null,\n });\n await tick();\n router.navigate({ to: '/login' });\n return;\n }\n\n if (\n error.code === 'TOTP_NOT_ENABLED' ||\n error.code === 'VALIDATION_ERROR'\n ) {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n return;\n }\n }\n\n setErrorMessage(t('profile.totp.regenerateModal.unexpectedError'));\n }\n },\n [handleClose, mutation, queryClient, router, t],\n );\n\n if (!appConfig.auth.password.totp.enabled) {\n return null;\n }\n\n return (\n <Modal\n description={\n step === 'verify'\n ? t('profile.totp.regenerateModal.description')\n : t('profile.totp.regenerateModal.recoveryDescription')\n }\n icon={ShieldCheckIcon}\n isOpen={isOpen}\n onClose={handleClose}\n preventClose={step === 'recovery'}\n size=\"sm\"\n title={\n step === 'verify'\n ? t('profile.totp.regenerateModal.title')\n : t('profile.totp.regenerateModal.recoveryTitle')\n }\n >\n {step === 'verify' ? (\n <div className=\"mt-tinyrack-lg\">\n {errorMessage && (\n <div className=\"mb-tinyrack-md\">\n <AlertBanner variant=\"error\">{errorMessage}</AlertBanner>\n </div>\n )}\n <VerifyStep\n invalidMessage={t('profile.totp.regenerateModal.error')}\n isPending={mutation.isPending}\n onSubmit={handleVerify}\n pendingText={t('profile.totp.regenerateModal.submitting')}\n submitLabel={t('profile.totp.regenerateModal.submit')}\n />\n </div>\n ) : (\n <div className=\"mt-tinyrack-lg\">\n <RecoveryCodesStep\n onConfirm={handleClose}\n recoveryCodes={response?.recovery_codes ?? []}\n />\n </div>\n )}\n </Modal>\n );\n}\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { KeyRoundIcon } from 'lucide-react';\nimport { 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport { removePasswordMutationOptions } from '#frontend/queries/password.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface RemovePasswordModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport function RemovePasswordModal({\n isOpen,\n onClose,\n}: RemovePasswordModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n\n const schema = useMemo(\n () =>\n z.object({\n currentPassword: z.string().min(1, t('validation.password.required')),\n }),\n [t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { currentPassword: '' },\n });\n\n const mutation = useMutation({\n ...removePasswordMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n },\n });\n\n const handleClose = () => {\n form.reset();\n onClose();\n };\n\n const handleSubmit = form.handleSubmit(async (data) => {\n try {\n await mutation.mutateAsync({\n current_password: data.currentPassword,\n });\n } catch (err) {\n if (err instanceof IssuaryError) {\n if (err.code === 'INVALID_CURRENT_PASSWORD') {\n form.setError('currentPassword', {\n message: t('profile.password.removeModal.invalidCurrent'),\n });\n return;\n }\n if (err.code === 'CANNOT_REMOVE_LAST_AUTH_METHOD') {\n form.setError('root', {\n message: t('profile.password.removeModal.noOAuth'),\n });\n return;\n }\n if (err.code === 'CANNOT_REMOVE_PASSWORD_WITH_SECOND_FACTOR_ONLY') {\n form.setError('root', {\n message: t('profile.password.removeModal.noOAuthWith2FA'),\n });\n return;\n }\n }\n form.setError('root', {\n message: t('profile.password.removeModal.error'),\n });\n }\n });\n\n return (\n <Modal\n description={t('profile.password.removeModal.description')}\n icon={KeyRoundIcon}\n isOpen={isOpen}\n onClose={handleClose}\n title={t('profile.password.removeModal.title')}\n variant=\"destructive\"\n >\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleSubmit}\n >\n <AuthField\n error={form.formState.errors.currentPassword}\n errorTestId=\"remove-password-error\"\n id=\"current-password-remove\"\n label={t('profile.password.removeModal.currentPassword')}\n placeholder={t(\n 'profile.password.removeModal.currentPasswordPlaceholder',\n )}\n {...form.register('currentPassword')}\n type=\"password\"\n />\n {form.formState.errors.root && (\n <AlertBanner variant=\"error\">\n {form.formState.errors.root.message}\n </AlertBanner>\n )}\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"remove-password-cancel\"\n disabled={mutation.isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.removeModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"remove-password-submit\"\n disabled={mutation.isPending}\n intent=\"danger\"\n loading={mutation.isPending}\n loadingLabel={t('profile.password.removeModal.submitting')}\n type=\"submit\"\n uiSize=\"sm\"\n >\n {mutation.isPending\n ? undefined\n : t('profile.password.removeModal.submit')}\n </TRButton>\n </ModalActions>\n </form>\n </Modal>\n );\n}\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { KeyRoundIcon } from 'lucide-react';\nimport { 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { setPasswordMutationOptions } from '#frontend/queries/password.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface SetPasswordModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport function SetPasswordModal({ isOpen, onClose }: SetPasswordModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const passwordPolicy = configData.auth.password.policy;\n\n const schema = useMemo(\n () =>\n z\n .object({\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.string(),\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 form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { password: '', confirmPassword: '' },\n });\n\n const mutation = useMutation({\n ...setPasswordMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n },\n });\n\n const handleClose = () => {\n form.reset();\n onClose();\n };\n\n const handleSubmit = form.handleSubmit(async (data) => {\n try {\n await mutation.mutateAsync({ password: data.password });\n } catch (error) {\n void error;\n form.setError('root', {\n message: t('profile.password.setModal.error'),\n });\n }\n });\n\n return (\n <Modal\n description={t('profile.password.setModal.description')}\n icon={KeyRoundIcon}\n isOpen={isOpen}\n onClose={handleClose}\n title={t('profile.password.setModal.title')}\n >\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleSubmit}\n >\n <AuthField\n error={form.formState.errors.password}\n errorTestId=\"set-password-error-password\"\n id=\"new-password\"\n label={t('profile.password.setModal.newPassword')}\n placeholder={t('profile.password.setModal.newPasswordPlaceholder')}\n {...form.register('password')}\n type=\"password\"\n />\n <AuthField\n error={form.formState.errors.confirmPassword}\n errorTestId=\"set-password-error-confirmPassword\"\n id=\"confirm-password\"\n label={t('profile.password.setModal.confirmPassword')}\n placeholder={t(\n 'profile.password.setModal.confirmPasswordPlaceholder',\n )}\n {...form.register('confirmPassword')}\n type=\"password\"\n />\n {form.formState.errors.root && (\n <AlertBanner variant=\"error\">\n {form.formState.errors.root.message}\n </AlertBanner>\n )}\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"set-password-cancel\"\n disabled={mutation.isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.password.setModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"set-password-submit\"\n disabled={mutation.isPending}\n intent=\"primary\"\n loading={mutation.isPending}\n loadingLabel={t('profile.password.setModal.submitting')}\n type=\"submit\"\n uiSize=\"sm\"\n >\n {mutation.isPending\n ? undefined\n : t('profile.password.setModal.submit')}\n </TRButton>\n </ModalActions>\n </form>\n </Modal>\n );\n}\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { FingerprintIcon } from 'lucide-react';\nimport { useCallback, 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 { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { queryKeys } from '#frontend/queries/keys.ts';\nimport { registerPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ninterface SetupPasskeyModalProps {\n isOpen: boolean;\n onClose: () => void;\n isRequired?: boolean;\n canSwitchToTotp?: boolean;\n onSwitchToTotp?: () => void;\n}\n\ntype SetupStep = 'name' | 'register';\n\nexport function SetupPasskeyModal({\n isOpen,\n onClose,\n isRequired = false,\n canSwitchToTotp = false,\n onSwitchToTotp,\n}: SetupPasskeyModalProps) {\n const { t } = useTranslation();\n const queryClient = useQueryClient();\n const [step, setStep] = useState<SetupStep>('name');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const schema = useMemo(\n () =>\n z.object({\n name: z\n .string()\n .max(100, t('validation.passkey.name.max'))\n .optional()\n .transform((val) => val?.trim() || undefined),\n }),\n [t],\n );\n\n const form = useForm({\n resolver: standardSchemaResolver(schema),\n defaultValues: { name: '' },\n });\n\n const registerMutation = useMutation({\n ...registerPasskeyMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: queryKeys.passkeys() });\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n handleClose();\n },\n onError: (error) => {\n setStep('name');\n if (error.name === 'NotAllowedError') {\n setErrorMessage(t('profile.passkey.setupModal.cancelled'));\n } else {\n setErrorMessage(t('profile.passkey.setupModal.error'));\n }\n },\n });\n\n const handleClose = useCallback(() => {\n form.reset();\n setStep('name');\n setErrorMessage(null);\n onClose();\n }, [form, onClose]);\n\n const handleRegister = form.handleSubmit(async (data) => {\n setStep('register');\n setErrorMessage(null);\n try {\n await registerMutation.mutateAsync({ name: data.name });\n } catch {\n // Error handling is done via mutation callbacks\n }\n });\n\n const isPending = registerMutation.isPending;\n\n return (\n <Modal\n description={\n isRequired\n ? t('profile.passkey.setupModal.requiredDescription')\n : undefined\n }\n icon={FingerprintIcon}\n isOpen={isOpen}\n onClose={handleClose}\n preventClose={isRequired}\n size=\"sm\"\n title={t('profile.passkey.setupModal.title')}\n >\n {step === 'name' && (\n <form\n className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\"\n onSubmit={handleRegister}\n >\n <p className=\"text-tinyrack-text-muted text-tinyrack-xs\">\n {t('profile.passkey.setupModal.description')}\n </p>\n\n {errorMessage && (\n <AlertBanner variant=\"error\">{errorMessage}</AlertBanner>\n )}\n\n <AuthField\n error={form.formState.errors.name}\n errorTestId=\"setup-passkey-error\"\n hint={t('profile.passkey.setupModal.nameHint')}\n id=\"passkey-name\"\n label={t('profile.passkey.setupModal.nameLabel')}\n placeholder={t('profile.passkey.setupModal.namePlaceholder')}\n {...form.register('name')}\n type=\"text\"\n />\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"setup-passkey-cancel\"\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.setupModal.cancel')}\n </TRButton>\n <TRButton\n data-testid=\"setup-passkey-continue\"\n intent=\"primary\"\n type=\"submit\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.setupModal.continue')}\n </TRButton>\n </ModalActions>\n {canSwitchToTotp && onSwitchToTotp && (\n <p className=\"mt-tinyrack-sm text-center text-tinyrack-xs\">\n <TRButton\n appearance=\"ghost\"\n intent=\"primary\"\n onClick={onSwitchToTotp}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.setupModal.switchToTotp')}\n </TRButton>\n </p>\n )}\n </form>\n )}\n\n {step === 'register' && (\n <div className=\"mt-tinyrack-lg\">\n <div\n className=\"flex flex-col items-center gap-tinyrack-md py-tinyrack-xl\"\n data-testid=\"setup-passkey-loading\"\n >\n <TRSpinner uiSize=\"md\" />\n <p className=\"text-center text-tinyrack-text-muted text-tinyrack-xs\">\n {t('profile.passkey.setupModal.waitingForDevice')}\n </p>\n </div>\n\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n data-testid=\"setup-passkey-cancel\"\n disabled={isPending}\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.passkey.setupModal.cancel')}\n </TRButton>\n </ModalActions>\n </div>\n )}\n </Modal>\n );\n}\n","import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { ShieldCheckIcon } from 'lucide-react';\nimport { useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { QrStep } from '#frontend/components/totp/qr-step.tsx';\nimport { RecoveryCodesStep } from '#frontend/components/totp/recovery-codes-step.tsx';\nimport { VerifyStep } from '#frontend/components/totp/verify-step.tsx';\nimport { AlertBanner } from '#frontend/components/ui/alert-banner.tsx';\nimport { Modal, ModalActions } from '#frontend/components/ui/modal.tsx';\nimport { useTotpSetup } from '#frontend/features/totp/use-totp-setup.ts';\n\ninterface SetupTotpModalProps {\n isOpen: boolean;\n onClose: () => void;\n isRequired?: boolean;\n canSwitchToPasskey?: boolean;\n onSwitchToPasskey?: () => void;\n}\n\nexport function SetupTotpModal({\n isOpen,\n onClose,\n isRequired = false,\n canSwitchToPasskey = false,\n onSwitchToPasskey,\n}: SetupTotpModalProps) {\n const { t } = useTranslation();\n\n const {\n step,\n setupData,\n recoveryCodes,\n isSetupPending,\n isVerifyPending,\n isConfirmPending,\n startSetup,\n verify,\n goToVerify,\n goToQr,\n confirmRecoveryCodes,\n reset,\n } = useTotpSetup({\n autoStart: false,\n onConfirmSuccess: () => {\n reset();\n onClose();\n },\n });\n\n const handleClose = useCallback(() => {\n reset();\n onClose();\n }, [onClose, reset]);\n\n const handleModalOpen = useCallback(() => {\n if (!setupData && !isSetupPending) {\n startSetup();\n }\n }, [setupData, isSetupPending, startSetup]);\n\n if (isOpen && !setupData && !isSetupPending && step !== 'error') {\n handleModalOpen();\n }\n\n const handleVerify = useCallback(\n async (code: string) => {\n await verify(code);\n },\n [verify],\n );\n\n const modalTitle =\n step === 'recovery'\n ? t('setupTotp.recoveryCodes.title')\n : t('profile.totp.setupModal.title');\n\n return (\n <Modal\n description={\n isRequired\n ? t('profile.totp.setupModal.requiredDescription')\n : undefined\n }\n icon={ShieldCheckIcon}\n isOpen={isOpen}\n onClose={handleClose}\n preventClose={isRequired || step === 'recovery'}\n size=\"sm\"\n title={modalTitle}\n >\n {isSetupPending && (\n <div\n className=\"flex justify-center py-tinyrack-xl\"\n data-testid=\"setup-totp-loading\"\n >\n <TRSpinner uiSize=\"md\" />\n </div>\n )}\n\n {step === 'error' && (\n <div className=\"mt-tinyrack-lg flex flex-col gap-tinyrack-md\">\n <AlertBanner variant=\"error\">\n {t('profile.totp.setupModal.setupError')}\n </AlertBanner>\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.setupModal.cancel')}\n </TRButton>\n <TRButton\n intent=\"primary\"\n onClick={startSetup}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.setupModal.retry')}\n </TRButton>\n </ModalActions>\n </div>\n )}\n\n {setupData && step === 'qr' && (\n <div className=\"mt-tinyrack-lg\">\n <QrStep\n additionalActions={\n <>\n <ModalActions>\n <TRButton\n appearance=\"outline\"\n intent=\"neutral\"\n onClick={handleClose}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.setupModal.cancel')}\n </TRButton>\n </ModalActions>\n {canSwitchToPasskey && onSwitchToPasskey && (\n <p className=\"mt-tinyrack-sm text-center text-tinyrack-xs\">\n <TRButton\n appearance=\"ghost\"\n intent=\"primary\"\n onClick={onSwitchToPasskey}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('profile.totp.setupModal.switchToPasskey')}\n </TRButton>\n </p>\n )}\n </>\n }\n onNext={goToVerify}\n setupData={setupData}\n />\n </div>\n )}\n\n {setupData && step === 'verify' && (\n <div className=\"mt-tinyrack-lg\">\n <VerifyStep\n isPending={isVerifyPending}\n onBack={goToQr}\n onSubmit={handleVerify}\n />\n </div>\n )}\n\n {step === 'recovery' && recoveryCodes.length > 0 && (\n <div className=\"mt-tinyrack-lg\">\n <RecoveryCodesStep\n isLoading={isConfirmPending}\n onConfirm={confirmRecoveryCodes}\n recoveryCodes={recoveryCodes}\n />\n </div>\n )}\n </Modal>\n );\n}\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport {\n createFileRoute,\n type ErrorComponentProps,\n redirect,\n useRouter,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { CircleAlertIcon, LogOutIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { DangerZoneSection } from '#frontend/components/profile/danger-zone-section.tsx';\nimport { LinkedAccountsSection } from '#frontend/components/profile/linked-accounts-section.tsx';\nimport { PasskeySection } from '#frontend/components/profile/passkey-section.tsx';\nimport { PasswordSection } from '#frontend/components/profile/password-section.tsx';\nimport { TotpSection } from '#frontend/components/profile/totp-section.tsx';\nimport { UnlinkOAuthModal } from '#frontend/components/profile/unlink-oauth-modal.tsx';\nimport { UserInfoSection } from '#frontend/components/profile/user-info-section.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 { AppLayout } from '#frontend/features/layout/app-layout.tsx';\nimport { ChangePasswordModal } from '#frontend/features/profile/change-password-modal.tsx';\nimport { DeleteAccountModal } from '#frontend/features/profile/delete-account-modal.tsx';\nimport { DisableTotpModal } from '#frontend/features/profile/disable-totp-modal.tsx';\nimport { ManagePasskeysModal } from '#frontend/features/profile/manage-passkeys-modal.tsx';\nimport { RegenerateTotpRecoveryCodesModal } from '#frontend/features/profile/regenerate-totp-recovery-codes-modal.tsx';\nimport { RemovePasswordModal } from '#frontend/features/profile/remove-password-modal.tsx';\nimport { SetPasswordModal } from '#frontend/features/profile/set-password-modal.tsx';\nimport { SetupPasskeyModal } from '#frontend/features/profile/setup-passkey-modal.tsx';\nimport { SetupTotpModal } from '#frontend/features/profile/setup-totp-modal.tsx';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { logoutMutationOptions } from '#frontend/queries/logout.ts';\nimport {\n getOAuthAuthorizeUrl,\n oauthAccountsQueryOptions,\n unlinkOAuthMutationOptions,\n} from '#frontend/queries/oauth.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\ntype PasswordModalType = 'set' | 'change' | 'remove' | null;\ntype TotpModalType = 'setup' | 'disable' | 'regenerate' | null;\ntype PasskeyModalType = 'setup' | 'manage' | null;\n\nconst SearchSchema = z.object({\n oauth_error: z.string().optional(),\n oauth_error_description: z.string().optional(),\n});\n\nconst OAUTH_ERROR_I18N_MAP: Record<string, string> = {\n access_denied: 'oauth.error.accessDenied',\n temporarily_unavailable: 'oauth.error.temporarilyUnavailable',\n server_error: 'oauth.error.serverError',\n};\n\nfunction ProfileError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n window.location.href = '/login';\n }}\n />\n );\n}\n\nexport const Route = createFileRoute('/profile/')({\n component: Profile,\n errorComponent: ProfileError,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n if (!context.user) {\n throw redirect({\n to: '/login',\n });\n }\n },\n loader: async ({ context }) => {\n await Promise.all([\n context.queryClient.ensureQueryData(getSessionQueryOptions),\n context.queryClient.ensureQueryData(oauthAccountsQueryOptions),\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n ]);\n },\n});\n\nfunction Profile() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const oauthError = search.oauth_error;\n const oauthErrorMessage = oauthError\n ? t(OAUTH_ERROR_I18N_MAP[oauthError] ?? 'oauth.error.failed')\n : undefined;\n const [unlinkingProvider, setUnlinkingProvider] = useState<string | null>(\n null,\n );\n const [unlinkModal, setUnlinkModal] = useState<{\n id: string;\n name: string;\n } | null>(null);\n const [passwordModal, setPasswordModal] = useState<PasswordModalType>(null);\n const [totpModal, setTotpModal] = useState<TotpModalType>(null);\n const [passkeyModal, setPasskeyModal] = useState<PasskeyModalType>(null);\n const [showDeleteModal, setShowDeleteModal] = useState(false);\n\n const { data: session } = useSuspenseQuery(getSessionQueryOptions);\n const { data: oauthAccountsData } = useSuspenseQuery(\n oauthAccountsQueryOptions,\n );\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const logoutMutation = useMutation({\n ...logoutMutationOptions,\n onSuccess: async () => {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: null,\n });\n await tick();\n router.navigate({\n to: '/login',\n });\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const unlinkMutation = useMutation({\n ...unlinkOAuthMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: oauthAccountsQueryOptions.queryKey,\n });\n },\n onSettled: () => {\n setUnlinkingProvider(null);\n },\n });\n\n const handleUnlinkConfirm = async () => {\n if (!unlinkModal) return;\n setUnlinkingProvider(unlinkModal.id);\n await unlinkMutation.mutateAsync(unlinkModal.id);\n };\n\n if (!session.user) {\n return null;\n }\n\n const user = session.user;\n const availableProviders = oauthAccountsData.available_providers;\n const hasLinkedOAuth = availableProviders.some((p) => p.linked);\n const isConfigManaged = user.managed_by === 'config';\n\n const passwordAuthMethod = appConfig.auth.password;\n const passkeyAuthMethod = appConfig.auth.passkey;\n const totpEnabled = passwordAuthMethod.totp.enabled;\n const passkeyEnabled = passkeyAuthMethod.enabled;\n\n const accountDeletionEnabled = appConfig.account_deletion.enabled;\n const retentionPeriod = appConfig.account_deletion.retention;\n\n const retentionDays = (() => {\n const match = retentionPeriod.match(/^(\\d+)([dmy])$/);\n if (!match) return 30;\n const value = parseInt(match[1], 10);\n const unit = match[2];\n switch (unit) {\n case 'd':\n return value;\n case 'm':\n return value * 30;\n case 'y':\n return value * 365;\n default:\n return 30;\n }\n })();\n\n const showPasswordSection = true;\n const showTotpSection = !isConfigManaged && totpEnabled;\n const showPasskeySection = !isConfigManaged && passkeyEnabled;\n const showLinkedAccounts = availableProviders.length > 0;\n const hasSecurityOptions =\n showPasswordSection || showTotpSection || showPasskeySection;\n\n return (\n <AppLayout\n headerActions={\n <TRButton\n appearance=\"ghost\"\n /*\n The visible label collapses below `sm:`, which would leave the\n control unnamed on a phone. `profile.logout` is the same string\n the label renders, so this does not shadow it.\n */\n aria-label={t('profile.logout')}\n data-testid=\"profile-logout\"\n disabled={logoutMutation.isPending}\n loading={logoutMutation.isPending}\n onClick={() => logoutMutation.mutate()}\n type=\"button\"\n uiSize=\"sm\"\n >\n {logoutMutation.isPending ? undefined : (\n <>\n <LogOutIcon aria-hidden className=\"size-tinyrack-lg\" />\n <span className=\"hidden sm:inline\">{t('profile.logout')}</span>\n </>\n )}\n </TRButton>\n }\n >\n <div className=\"flex items-center gap-tinyrack-lg\">\n <InitialAvatar email={user.email} size=\"lg\" />\n <div className=\"flex min-w-0 flex-col gap-tinyrack-3xs\">\n <AuthPageHeader title={t('profile.title')} />\n {/*\n Rendered here rather than as the header's `subtitle`: this node\n carries a testid and needs to truncate, neither of which the\n shared header does, and growing its API for one non-auth caller\n would be the wrong trade.\n */}\n <TRText\n as=\"p\"\n color=\"muted\"\n data-testid=\"profile-user-email\"\n truncate\n variant=\"bodySm\"\n >\n {user.email}\n </TRText>\n </div>\n </div>\n\n {oauthErrorMessage && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {oauthErrorMessage}\n </Alert>\n )}\n\n {user && <UserInfoSection user={user} />}\n\n {hasSecurityOptions && (\n <TRCard.Root variant=\"outlined\">\n <TRCard.Header className=\"border-tinyrack-border border-b-tinyrack-default px-tinyrack-lg py-tinyrack-md\">\n <TRCard.Title>{t('profile.security.title')}</TRCard.Title>\n <TRCard.Description>\n {t('profile.security.description')}\n </TRCard.Description>\n </TRCard.Header>\n {showTotpSection && user.totp_recovery_codes_missing && (\n <div\n className=\"border-tinyrack-border border-b-tinyrack-default px-tinyrack-lg py-tinyrack-md\"\n data-testid=\"profile-totp-recovery-warning\"\n >\n <Alert icon={CircleAlertIcon} type=\"warning\">\n {t('profile.totp.recoveryCodesMissing')}\n </Alert>\n </div>\n )}\n <TRCard.Content className=\"divide-y divide-tinyrack-border p-0\">\n {showPasswordSection && (\n <PasswordSection\n hasLinkedOAuth={hasLinkedOAuth}\n hasPassword={user.has_password}\n hasSecondFactorOnly={\n user.has_password &&\n !hasLinkedOAuth &&\n (user.totp_registered || user.passkey_count > 0)\n }\n isConfigManaged={isConfigManaged}\n onOpenModal={setPasswordModal}\n />\n )}\n {showTotpSection && (\n <TotpSection\n onOpenModal={setTotpModal}\n recoveryCodesMissing={user.totp_recovery_codes_missing}\n totpEnabled={user.totp_registered}\n />\n )}\n {showPasskeySection && (\n <PasskeySection\n onOpenModal={setPasskeyModal}\n passkeyCount={user.passkey_count}\n />\n )}\n </TRCard.Content>\n </TRCard.Root>\n )}\n\n {showLinkedAccounts && (\n <LinkedAccountsSection\n getAuthorizeUrl={getOAuthAuthorizeUrl}\n onUnlinkRequest={(provider) =>\n setUnlinkModal({\n id: provider.id,\n name: provider.display_name,\n })\n }\n providers={availableProviders}\n unlinkingProvider={unlinkingProvider}\n />\n )}\n\n <DangerZoneSection\n isConfigManaged={isConfigManaged}\n isDeletionEnabled={accountDeletionEnabled}\n onDeleteClick={() => setShowDeleteModal(true)}\n />\n\n <SetPasswordModal\n isOpen={passwordModal === 'set'}\n onClose={() => setPasswordModal(null)}\n />\n <ChangePasswordModal\n isOpen={passwordModal === 'change'}\n onClose={() => setPasswordModal(null)}\n />\n <RemovePasswordModal\n isOpen={passwordModal === 'remove'}\n onClose={() => setPasswordModal(null)}\n />\n\n <SetupTotpModal\n isOpen={totpModal === 'setup'}\n onClose={() => setTotpModal(null)}\n />\n <DisableTotpModal\n isOpen={totpModal === 'disable'}\n onClose={() => setTotpModal(null)}\n />\n <RegenerateTotpRecoveryCodesModal\n isOpen={totpModal === 'regenerate'}\n onClose={() => setTotpModal(null)}\n />\n\n <SetupPasskeyModal\n isOpen={passkeyModal === 'setup'}\n onClose={() => setPasskeyModal(null)}\n />\n <ManagePasskeysModal\n isOpen={passkeyModal === 'manage'}\n onAddNew={() => setPasskeyModal('setup')}\n onClose={() => setPasskeyModal(null)}\n />\n\n <UnlinkOAuthModal\n isOpen={unlinkModal !== null}\n isPending={unlinkMutation.isPending}\n onClose={() => setUnlinkModal(null)}\n onConfirm={handleUnlinkConfirm}\n providerName={unlinkModal?.name ?? ''}\n />\n\n <DeleteAccountModal\n isOpen={showDeleteModal}\n onClose={() => setShowDeleteModal(false)}\n retentionDays={retentionDays}\n />\n </AppLayout>\n );\n}\n"],"x_google_ignoreList":[0,1,2,3,4],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider-logos-u1YURWQr.js","names":[],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/link.mjs","../../../frontend/src/components/auth/provider-logos.tsx"],"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: \"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\", key: \"1cjeqo\" }],\n [\"path\", { d: \"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\", key: \"19qd67\" }]\n];\nconst Link = createLucideIcon(\"link\", __iconNode);\n\nexport { __iconNode, Link as default };\n//# sourceMappingURL=link.mjs.map\n","import { LinkIcon } from 'lucide-react';\nimport type { SVGProps } from 'react';\n\n/**\n * Brand marks for the built-in identity providers.\n *\n * The design system mandates `lucide-react` as the only icon library, and\n * lucide ships no brand icons — its `Apple` is the fruit. These are the\n * official marks, inlined so no second icon package is needed. Google's is\n * multicolour by trademark requirement and so cannot be a stroke icon anyway.\n *\n * Each renders at the caller's `className` size and is decorative: the\n * accessible name comes from the surrounding button label.\n */\n\ntype ProviderLogoProps = Omit<SVGProps<SVGSVGElement>, 'children' | 'viewBox'>;\n\nconst LOGOS_BY_ID: Record<\n string,\n (props: ProviderLogoProps) => React.ReactElement\n> = {\n google: GoogleLogo,\n github: GithubLogo,\n apple: AppleLogo,\n};\n\ntype ProviderMarkProps = {\n /** Configured provider id. Built-in providers use their own name. */\n providerId: string;\n /** Deployment-supplied icon, used for providers with no built-in mark. */\n iconUrl?: string;\n className?: string;\n};\n\n/**\n * The mark for a configured provider, however it is identified.\n *\n * Profile's provider list carries an id and an optional icon URL but no\n * provider type, so this resolves by id first — which is what the built-in\n * providers are named — then the deployment's own icon, then a generic link\n * glyph for an unrecognised custom provider.\n */\nexport function ProviderMark({\n providerId,\n iconUrl,\n className = 'size-tinyrack-xl',\n}: ProviderMarkProps) {\n const Logo = LOGOS_BY_ID[providerId];\n if (Logo) {\n return <Logo className={className} />;\n }\n if (iconUrl) {\n return <img alt=\"\" className={className} src={iconUrl} />;\n }\n return <LinkIcon aria-hidden className={className} />;\n}\n\nexport function GoogleLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg aria-hidden focusable=\"false\" viewBox=\"0 0 24 24\" {...props}>\n <path\n d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1Z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23Z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M5.84 14.1a6.6 6.6 0 0 1 0-4.2V7.06H2.18a11 11 0 0 0 0 9.88l3.66-2.84Z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1a11 11 0 0 0-9.82 6.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52Z\"\n fill=\"#EA4335\"\n />\n </svg>\n );\n}\n\nexport function GithubLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg\n aria-hidden\n fill=\"currentColor\"\n focusable=\"false\"\n viewBox=\"0 0 24 24\"\n {...props}\n >\n <path d=\"M12 .5A11.5 11.5 0 0 0 .5 12a11.5 11.5 0 0 0 7.86 10.92c.58.1.79-.25.79-.56v-2.17c-3.2.7-3.88-1.36-3.88-1.36-.53-1.34-1.29-1.7-1.29-1.7-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.2 1.77 1.2 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.56-.29-5.25-1.28-5.25-5.7 0-1.26.45-2.29 1.19-3.1-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.5 3.17-1.18 3.17-1.18.63 1.59.24 2.76.12 3.05.74.81 1.19 1.84 1.19 3.1 0 4.43-2.7 5.4-5.27 5.69.42.36.79 1.07.79 2.15v3.19c0 .31.2.67.8.55A11.5 11.5 0 0 0 23.5 12 11.5 11.5 0 0 0 12 .5Z\" />\n </svg>\n );\n}\n\nexport function AppleLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg\n aria-hidden\n fill=\"currentColor\"\n focusable=\"false\"\n viewBox=\"0 0 24 24\"\n {...props}\n >\n <path d=\"M17.05 12.74c-.03-2.7 2.2-4 2.3-4.06-1.25-1.83-3.2-2.08-3.9-2.11-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.75.9 1.3 1.96 2.75 3.36 2.7 1.35-.06 1.86-.87 3.49-.87 1.63 0 2.09.87 3.51.84 1.45-.02 2.37-1.32 3.26-2.63 1.03-1.5 1.45-2.96 1.47-3.04-.03-.01-2.82-1.08-2.85-4.3ZM14.4 4.8c.74-.9 1.24-2.15 1.1-3.4-1.07.05-2.36.72-3.13 1.61-.68.79-1.28 2.06-1.12 3.28 1.19.09 2.41-.61 3.15-1.5Z\" />\n </svg>\n );\n}\n"],"x_google_ignoreList":[0],"mappings":"qDAaA,IAAM,EAAO,EAAiB,OAAQ,CAHpC,CAAC,OAAQ,CAAE,EAAG,8DAA+D,IAAK,QAAS,CAAC,EAC5F,CAAC,OAAQ,CAAE,EAAG,+DAAgE,IAAK,QAAS,CAAC,CAEzD,CAAU,QCI1C,EAGF,CACF,OAAQ,EACR,OAAQ,EACR,MAAO,CACT,EAkBA,SAAgB,EAAa,CAC3B,aACA,UACA,YAAY,oBACQ,CACpB,IAAM,EAAO,EAAY,GAOzB,OANI,GACK,EAAA,EAAA,IAAA,CAAC,EAAD,CAAiB,WAAY,CAAA,EAElC,GACK,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAI,GAAc,YAAW,IAAK,CAAU,CAAA,GAEnD,EAAA,EAAA,IAAA,CAAC,EAAD,CAAU,cAAA,GAAuB,WAAY,CAAA,CACtD,CAEA,SAAgB,EAAW,EAA0B,CACnD,OAEE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,cAAA,GAAY,UAAU,QAAQ,QAAQ,YAAY,GAAI,WAA3D,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,oHACF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,kIACF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,yEACF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,kIACF,KAAK,SACN,CAAA,CACE,GAET,CAEA,SAAgB,EAAW,EAA0B,CACnD,OAEE,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,cAAA,GACA,KAAK,eACL,UAAU,QACV,QAAQ,YACR,GAAI,YAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAE,8hBAAgiB,CAAA,CACriB,CAAA,CAET,CAEA,SAAgB,EAAU,EAA0B,CAClD,OAEE,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,cAAA,GACA,KAAK,eACL,UAAU,QACV,QAAQ,YACR,GAAI,YAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAE,+bAAic,CAAA,CACtc,CAAA,CAET"}
1
+ {"version":3,"file":"provider-logos-u1YURWQr.js","names":[],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/link.mjs","../../../frontend/src/components/auth/provider-logos.tsx"],"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: \"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\", key: \"1cjeqo\" }],\n [\"path\", { d: \"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\", key: \"19qd67\" }]\n];\nconst Link = createLucideIcon(\"link\", __iconNode);\n\nexport { __iconNode, Link as default };\n//# sourceMappingURL=link.mjs.map\n","import { LinkIcon } from 'lucide-react';\nimport type { SVGProps } from 'react';\n\n/**\n * Brand marks for the built-in identity providers.\n *\n * The design system mandates `lucide-react` as the only icon library, and\n * lucide ships no brand icons — its `Apple` is the fruit. These are the\n * official marks, inlined so no second icon package is needed. Google's is\n * multicolour by trademark requirement and so cannot be a stroke icon anyway.\n *\n * Each renders at the caller's `className` size and is decorative: the\n * accessible name comes from the surrounding button label.\n */\n\ntype ProviderLogoProps = Omit<SVGProps<SVGSVGElement>, 'children' | 'viewBox'>;\n\nconst LOGOS_BY_ID: Record<\n string,\n (props: ProviderLogoProps) => React.ReactElement\n> = {\n google: GoogleLogo,\n github: GithubLogo,\n apple: AppleLogo,\n};\n\ntype ProviderMarkProps = {\n /** Configured provider id. Built-in providers use their own name. */\n providerId: string;\n /** Deployment-supplied icon, used for providers with no built-in mark. */\n iconUrl?: string;\n className?: string;\n};\n\n/**\n * The mark for a configured provider, however it is identified.\n *\n * Profile's provider list carries an id and an optional icon URL but no\n * provider type, so this resolves by id first — which is what the built-in\n * providers are named — then the deployment's own icon, then a generic link\n * glyph for an unrecognised custom provider.\n */\nexport function ProviderMark({\n providerId,\n iconUrl,\n className = 'size-tinyrack-xl',\n}: ProviderMarkProps) {\n const Logo = LOGOS_BY_ID[providerId];\n if (Logo) {\n return <Logo className={className} />;\n }\n if (iconUrl) {\n return <img alt=\"\" className={className} src={iconUrl} />;\n }\n return <LinkIcon aria-hidden className={className} />;\n}\n\nexport function GoogleLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg aria-hidden focusable=\"false\" viewBox=\"0 0 24 24\" {...props}>\n <path\n d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1Z\"\n /* tinyrack-check-ignore-next-line tokens/no-literal -- Official Google brand color. */\n fill=\"#4285F4\"\n />\n <path\n d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23Z\"\n /* tinyrack-check-ignore-next-line tokens/no-literal -- Official Google brand color. */\n fill=\"#34A853\"\n />\n <path\n d=\"M5.84 14.1a6.6 6.6 0 0 1 0-4.2V7.06H2.18a11 11 0 0 0 0 9.88l3.66-2.84Z\"\n /* tinyrack-check-ignore-next-line tokens/no-literal -- Official Google brand color. */\n fill=\"#FBBC05\"\n />\n <path\n d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1a11 11 0 0 0-9.82 6.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52Z\"\n /* tinyrack-check-ignore-next-line tokens/no-literal -- Official Google brand color. */\n fill=\"#EA4335\"\n />\n </svg>\n );\n}\n\nexport function GithubLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg\n aria-hidden\n fill=\"currentColor\"\n focusable=\"false\"\n viewBox=\"0 0 24 24\"\n {...props}\n >\n <path d=\"M12 .5A11.5 11.5 0 0 0 .5 12a11.5 11.5 0 0 0 7.86 10.92c.58.1.79-.25.79-.56v-2.17c-3.2.7-3.88-1.36-3.88-1.36-.53-1.34-1.29-1.7-1.29-1.7-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.2 1.77 1.2 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.56-.29-5.25-1.28-5.25-5.7 0-1.26.45-2.29 1.19-3.1-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.5 3.17-1.18 3.17-1.18.63 1.59.24 2.76.12 3.05.74.81 1.19 1.84 1.19 3.1 0 4.43-2.7 5.4-5.27 5.69.42.36.79 1.07.79 2.15v3.19c0 .31.2.67.8.55A11.5 11.5 0 0 0 23.5 12 11.5 11.5 0 0 0 12 .5Z\" />\n </svg>\n );\n}\n\nexport function AppleLogo(props: ProviderLogoProps) {\n return (\n // biome-ignore lint/a11y/noSvgWithoutTitle: decorative, named by its button\n <svg\n aria-hidden\n fill=\"currentColor\"\n focusable=\"false\"\n viewBox=\"0 0 24 24\"\n {...props}\n >\n <path d=\"M17.05 12.74c-.03-2.7 2.2-4 2.3-4.06-1.25-1.83-3.2-2.08-3.9-2.11-1.66-.17-3.24.98-4.08.98-.84 0-2.14-.96-3.52-.93-1.81.03-3.48 1.05-4.41 2.67-1.88 3.27-.48 8.1 1.35 10.75.9 1.3 1.96 2.75 3.36 2.7 1.35-.06 1.86-.87 3.49-.87 1.63 0 2.09.87 3.51.84 1.45-.02 2.37-1.32 3.26-2.63 1.03-1.5 1.45-2.96 1.47-3.04-.03-.01-2.82-1.08-2.85-4.3ZM14.4 4.8c.74-.9 1.24-2.15 1.1-3.4-1.07.05-2.36.72-3.13 1.61-.68.79-1.28 2.06-1.12 3.28 1.19.09 2.41-.61 3.15-1.5Z\" />\n </svg>\n );\n}\n"],"x_google_ignoreList":[0],"mappings":"qDAaA,IAAM,EAAO,EAAiB,OAAQ,CAHpC,CAAC,OAAQ,CAAE,EAAG,8DAA+D,IAAK,QAAS,CAAC,EAC5F,CAAC,OAAQ,CAAE,EAAG,+DAAgE,IAAK,QAAS,CAAC,CAEzD,CAAU,QCI1C,EAGF,CACF,OAAQ,EACR,OAAQ,EACR,MAAO,CACT,EAkBA,SAAgB,EAAa,CAC3B,aACA,UACA,YAAY,oBACQ,CACpB,IAAM,EAAO,EAAY,GAOzB,OANI,GACK,EAAA,EAAA,IAAA,CAAC,EAAD,CAAiB,WAAY,CAAA,EAElC,GACK,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAI,GAAc,YAAW,IAAK,CAAU,CAAA,GAEnD,EAAA,EAAA,IAAA,CAAC,EAAD,CAAU,cAAA,GAAuB,WAAY,CAAA,CACtD,CAEA,SAAgB,EAAW,EAA0B,CACnD,OAEE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,cAAA,GAAY,UAAU,QAAQ,QAAQ,YAAY,GAAI,WAA3D,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,oHAEF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,kIAEF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,yEAEF,KAAK,SACN,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAE,kIAEF,KAAK,SACN,CAAA,CACE,GAET,CAEA,SAAgB,EAAW,EAA0B,CACnD,OAEE,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,cAAA,GACA,KAAK,eACL,UAAU,QACV,QAAQ,YACR,GAAI,YAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAE,8hBAAgiB,CAAA,CACriB,CAAA,CAET,CAEA,SAAgB,EAAU,EAA0B,CAClD,OAEE,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,cAAA,GACA,KAAK,eACL,UAAU,QACV,QAAQ,YACR,GAAI,YAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAE,+bAAic,CAAA,CACtc,CAAA,CAET"}
@@ -0,0 +1,2 @@
1
+ import{J as e,Z as t,i as n,q as r,r as i,s as a}from"./createLucideIcon-BPpAIXR_.js";import{On as o,Sn as s,Sr as c}from"./config-DTQCUlfZ.js";import{t as l}from"./useMutation-BWZ0G4Td.js";import{c as u,o as d}from"./zod-C87S6Tec.js";import{F as f,N as p,R as m,S as h,j as g,t as _}from"./index-CZ0rWM7l.js";import{t as v}from"./auth-page-header-COvqWINf.js";import{t as y}from"./alert-leqyvHOp.js";import{t as b}from"./auth-layout-DGTylGtX.js";import{t as x}from"./promise-OpBtq8tG.js";import{t as S}from"./field-89zKYDVL.js";import{t as C}from"./input-GPigtjgi.js";import{n as w,t as T}from"./auth-footer-Cz5ivsDc.js";import{r as E,t as D}from"./standard-schema-e8gtSzGE.js";import{a as O}from"./totp-BNZ904Zm.js";import{t as k}from"./labeled-separator-CV7nb0XW.js";var A=t(e()),j=r(),M={SECOND_FACTOR_SESSION_EXPIRED:`SECOND_FACTOR_SESSION_EXPIRED`,INVALID_RECOVERY_CODE:`INVALID_RECOVERY_CODE`,NO_RECOVERY_CODES_AVAILABLE:`NO_RECOVERY_CODES_AVAILABLE`,TOTP_NOT_ENABLED:`TOTP_NOT_ENABLED`},N=5;function P(){let{t:e}=n(),t=o(),r=c(),P=_.useSearch(),F=(0,A.useRef)(null),[I,L]=(0,A.useState)(!1),[R,z]=(0,A.useState)(N),B=(0,A.useMemo)(()=>d({code:u().transform(e=>e.toUpperCase()).pipe(u().regex(/^[A-HJ-NP-TV-Z2-9]{4}(?:-[A-HJ-NP-TV-Z2-9]{4}){3}$/,e(`verifyRecovery.error.invalid`)))}),[e]),V=l({...O,onSuccess:async e=>{r.setQueryData(h.queryKey,{user:e.user}),await x(),f(P)?window.location.href=g(P):t.navigate({to:`/profile`})},onSettled:()=>{r.invalidateQueries({queryKey:h.queryKey})}}),{register:H,setError:U,handleSubmit:W,setValue:G,formState:{errors:K}}=E({defaultValues:{code:``},resolver:D(B)}),q=(0,A.useCallback)(()=>{t.navigate({to:`/login`,search:p(P)})},[t,P]);(0,A.useEffect)(()=>{if(!I)return;let e=setInterval(()=>{z(t=>t<=1?(clearInterval(e),q(),0):t-1)},1e3);return()=>clearInterval(e)},[I,q]);let J=async t=>{try{await V.mutateAsync(t)}catch(t){if(t instanceof i)switch(t.code){case M.SECOND_FACTOR_SESSION_EXPIRED:L(!0),z(N);return;case M.TOTP_NOT_ENABLED:q();return;case M.NO_RECOVERY_CODES_AVAILABLE:U(`code`,{type:`manual`,message:e(`verifyRecovery.error.noCodesAvailable`)}),G(`code`,``),F.current?.focus();return;case M.INVALID_RECOVERY_CODE:U(`code`,{type:`manual`,message:e(`verifyRecovery.error.invalid`)}),G(`code`,``),F.current?.focus();return}U(`code`,{type:`manual`,message:e(`verifyRecovery.error.invalid`)}),G(`code`,``),F.current?.focus()}},{ref:Y,...X}=H(`code`);return(0,j.jsxs)(b,{children:[(0,j.jsx)(v,{subtitle:e(`verifyRecovery.subtitle`),title:e(`verifyRecovery.title`)}),I&&(0,j.jsx)(y,{"data-testid":`recovery-session-expired`,icon:m,type:`warning`,children:(0,j.jsxs)(`span`,{className:`flex flex-col items-start gap-tinyrack-3xs`,children:[(0,j.jsx)(`span`,{children:e(`verifyRecovery.error.expired`)}),(0,j.jsx)(`span`,{className:`text-tinyrack-sm opacity-tinyrack-hover`,children:e(`verifyRecovery.redirecting`,{seconds:R})}),(0,j.jsx)(a,{appearance:`ghost`,intent:`neutral`,onClick:q,type:`button`,uiSize:`sm`,children:e(`verifyRecovery.redirectNow`)})]})}),(0,j.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:W(J),children:[(0,j.jsxs)(S.Root,{children:[(0,j.jsx)(C,{...X,autoComplete:`off`,className:`w-full text-center font-tinyrack-mono`,"data-testid":`recovery-code-input`,disabled:I,placeholder:e(`verifyRecovery.placeholder`),ref:e=>{Y(e),F.current=e},type:`text`}),K.code&&(0,j.jsx)(`div`,{className:`tr-field-error`,"data-testid":`recovery-error`,children:K.code.message})]}),I?(0,j.jsx)(a,{className:`w-full`,disabled:!0,intent:`primary`,type:`button`,uiSize:`lg`,children:e(`verifyRecovery.submit`)}):(0,j.jsx)(a,{className:`w-full`,intent:`primary`,loading:V.isPending,loadingLabel:e(`verifyRecovery.submitting`),type:`submit`,uiSize:`lg`,children:e(`verifyRecovery.submit`)})]}),(0,j.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-md`,children:[(0,j.jsx)(k,{label:e(`common.or`)}),(0,j.jsx)(a,{appearance:`ghost`,className:`w-full`,"data-testid":`recovery-back-to-totp`,intent:`neutral`,onClick:()=>t.navigate({to:`/verify/totp`,search:p(P)}),type:`button`,children:e(`verifyRecovery.backToTotp`)})]}),(0,j.jsx)(T,{children:(0,j.jsx)(w,{link:(0,j.jsx)(s,{search:p(P),to:`/login`,children:e(`verifyRecovery.backToLogin`)})})})]})}export{P as component};
2
+ //# sourceMappingURL=recovery-DbgpqG6f.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recovery-CLExnfYX.js","names":["standardSchemaResolver","useMutation","useQueryClient","Link","useRouter","TRButton","TRField","TRInput","CircleAlertIcon","useCallback","useEffect","useMemo","useRef","useState","useForm","useTranslation","z","AuthFooter","AuthFooterLink","AuthPageHeader","Alert","LabeledSeparator","AuthLayout","IssuaryError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","getSessionQueryOptions","verifyRecoveryCodeMutationOptions","ERROR_CODES","SECOND_FACTOR_SESSION_EXPIRED","INVALID_RECOVERY_CODE","NO_RECOVERY_CODES_AVAILABLE","TOTP_NOT_ENABLED","const","Route","RecoveryFormValues","code","REDIRECT_COUNTDOWN_SECONDS","VerifyRecovery","t","router","queryClient","search","useSearch","inputRef","HTMLInputElement","sessionExpired","setSessionExpired","redirectCountdown","setRedirectCountdown","recoverySchema","object","string","transform","value","toUpperCase","pipe","regex","verifyMutation","onSuccess","data","setQueryData","queryKey","user","window","location","href","navigate","to","onSettled","invalidateQueries","register","setError","handleSubmit","setValue","formState","errors","defaultValues","resolver","redirectToLogin","timer","setInterval","prev","clearInterval","onSubmit","values","mutateAsync","error","type","message","current","focus","ref","formRef","registerRest","seconds","el","isPending","component"],"sources":["../../../frontend/src/routes/verify/totp/recovery/index.tsx?tsr-split=component"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRField } from '@tinyrack/ui/components/field';\nimport { TRInput } from '@tinyrack/ui/components/input';\nimport { CircleAlertIcon } from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { verifyRecoveryCodeMutationOptions } from '#frontend/queries/totp.ts';\n\n/** Error codes from backend */\nconst ERROR_CODES = {\n SECOND_FACTOR_SESSION_EXPIRED: 'SECOND_FACTOR_SESSION_EXPIRED',\n INVALID_RECOVERY_CODE: 'INVALID_RECOVERY_CODE',\n NO_RECOVERY_CODES_AVAILABLE: 'NO_RECOVERY_CODES_AVAILABLE',\n TOTP_NOT_ENABLED: 'TOTP_NOT_ENABLED',\n} as const;\n\nconst SearchSchema = OAuthSearchSchema;\n\nexport const Route = createFileRoute('/verify/totp/recovery/')({\n component: VerifyRecovery,\n validateSearch: SearchSchema,\n});\n\ntype RecoveryFormValues = {\n code: string;\n};\n\n/** Auto redirect countdown seconds */\nconst REDIRECT_COUNTDOWN_SECONDS = 5;\n\nfunction VerifyRecovery() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const [sessionExpired, setSessionExpired] = useState(false);\n const [redirectCountdown, setRedirectCountdown] = useState(\n REDIRECT_COUNTDOWN_SECONDS,\n );\n\n const recoverySchema = useMemo(\n () =>\n z.object({\n code: z\n .string()\n .transform((value) => value.toUpperCase())\n .pipe(\n z\n .string()\n .regex(\n /^[A-HJ-NP-TV-Z2-9]{4}(?:-[A-HJ-NP-TV-Z2-9]{4}){3}$/,\n t('verifyRecovery.error.invalid'),\n ),\n ),\n }),\n [t],\n );\n\n const verifyMutation = useMutation({\n ...verifyRecoveryCodeMutationOptions,\n onSuccess: async (data) => {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: data.user,\n });\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n setValue,\n formState: { errors },\n } = useForm<RecoveryFormValues>({\n defaultValues: {\n code: '',\n },\n resolver: standardSchemaResolver(recoverySchema),\n });\n\n // Auto redirect when session expires\n const redirectToLogin = useCallback(() => {\n router.navigate({\n to: '/login',\n search: extractOAuthParams(search),\n });\n }, [router, search]);\n\n useEffect(() => {\n if (!sessionExpired) return;\n\n const timer = setInterval(() => {\n setRedirectCountdown((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n redirectToLogin();\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [sessionExpired, redirectToLogin]);\n\n const onSubmit = async (values: RecoveryFormValues) => {\n try {\n await verifyMutation.mutateAsync(values);\n } catch (error) {\n if (error instanceof IssuaryError) {\n switch (error.code) {\n case ERROR_CODES.SECOND_FACTOR_SESSION_EXPIRED:\n setSessionExpired(true);\n setRedirectCountdown(REDIRECT_COUNTDOWN_SECONDS);\n return;\n\n case ERROR_CODES.TOTP_NOT_ENABLED:\n redirectToLogin();\n return;\n\n case ERROR_CODES.NO_RECOVERY_CODES_AVAILABLE:\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.noCodesAvailable'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n return;\n\n case ERROR_CODES.INVALID_RECOVERY_CODE:\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.invalid'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n return;\n }\n }\n\n // Generic error fallback\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.invalid'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n }\n };\n\n const { ref: formRef, ...registerRest } = register('code');\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyRecovery.subtitle')}\n title={t('verifyRecovery.title')}\n />\n\n {sessionExpired && (\n <Alert\n data-testid=\"recovery-session-expired\"\n icon={CircleAlertIcon}\n type=\"warning\"\n >\n <span className=\"flex flex-col items-start gap-tinyrack-3xs\">\n <span>{t('verifyRecovery.error.expired')}</span>\n <span className=\"text-tinyrack-sm opacity-tinyrack-hover\">\n {t('verifyRecovery.redirecting', {\n seconds: redirectCountdown,\n })}\n </span>\n <TRButton\n appearance=\"ghost\"\n intent=\"neutral\"\n onClick={redirectToLogin}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('verifyRecovery.redirectNow')}\n </TRButton>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <TRField.Root>\n <TRInput\n {...registerRest}\n autoComplete=\"off\"\n className=\"w-full text-center font-tinyrack-mono\"\n data-testid=\"recovery-code-input\"\n disabled={sessionExpired}\n placeholder={t('verifyRecovery.placeholder')}\n ref={(el) => {\n formRef(el);\n inputRef.current = el as HTMLInputElement | null;\n }}\n type=\"text\"\n />\n {errors.code && (\n <div className=\"tr-field-error\" data-testid=\"recovery-error\">\n {errors.code.message}\n </div>\n )}\n </TRField.Root>\n\n {sessionExpired ? (\n <TRButton\n className=\"w-full\"\n disabled\n intent=\"primary\"\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyRecovery.submit')}\n </TRButton>\n ) : (\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyMutation.isPending}\n loadingLabel={t('verifyRecovery.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyRecovery.submit')}\n </TRButton>\n )}\n </form>\n\n {/* Mirrors /verify/totp: the other second factor sits below the rule. */}\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator label={t('common.or')} />\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"recovery-back-to-totp\"\n intent=\"neutral\"\n onClick={() =>\n router.navigate({\n to: '/verify/totp',\n search: extractOAuthParams(search),\n })\n }\n type=\"button\"\n >\n {t('verifyRecovery.backToTotp')}\n </TRButton>\n </div>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('verifyRecovery.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recovery-DbgpqG6f.js","names":["standardSchemaResolver","useMutation","useQueryClient","Link","useRouter","TRButton","TRField","TRInput","CircleAlertIcon","useCallback","useEffect","useMemo","useRef","useState","useForm","useTranslation","z","AuthFooter","AuthFooterLink","AuthPageHeader","Alert","LabeledSeparator","AuthLayout","IssuaryError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","getSessionQueryOptions","verifyRecoveryCodeMutationOptions","ERROR_CODES","SECOND_FACTOR_SESSION_EXPIRED","INVALID_RECOVERY_CODE","NO_RECOVERY_CODES_AVAILABLE","TOTP_NOT_ENABLED","const","Route","RecoveryFormValues","code","REDIRECT_COUNTDOWN_SECONDS","VerifyRecovery","t","router","queryClient","search","useSearch","inputRef","HTMLInputElement","sessionExpired","setSessionExpired","redirectCountdown","setRedirectCountdown","recoverySchema","object","string","transform","value","toUpperCase","pipe","regex","verifyMutation","onSuccess","data","setQueryData","queryKey","user","window","location","href","navigate","to","onSettled","invalidateQueries","register","setError","handleSubmit","setValue","formState","errors","defaultValues","resolver","redirectToLogin","timer","setInterval","prev","clearInterval","onSubmit","values","mutateAsync","error","type","message","current","focus","ref","formRef","registerRest","seconds","el","isPending","component"],"sources":["../../../frontend/src/routes/verify/totp/recovery/index.tsx?tsr-split=component"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRField } from '@tinyrack/ui/components/field';\nimport { TRInput } from '@tinyrack/ui/components/input';\nimport { CircleAlertIcon } from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport { IssuaryError } from '#frontend/libs/error.ts';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport { verifyRecoveryCodeMutationOptions } from '#frontend/queries/totp.ts';\n\n/** Error codes from backend */\nconst ERROR_CODES = {\n SECOND_FACTOR_SESSION_EXPIRED: 'SECOND_FACTOR_SESSION_EXPIRED',\n INVALID_RECOVERY_CODE: 'INVALID_RECOVERY_CODE',\n NO_RECOVERY_CODES_AVAILABLE: 'NO_RECOVERY_CODES_AVAILABLE',\n TOTP_NOT_ENABLED: 'TOTP_NOT_ENABLED',\n} as const;\n\nconst SearchSchema = OAuthSearchSchema;\n\nexport const Route = createFileRoute('/verify/totp/recovery/')({\n component: VerifyRecovery,\n validateSearch: SearchSchema,\n});\n\ntype RecoveryFormValues = {\n code: string;\n};\n\n/** Auto redirect countdown seconds */\nconst REDIRECT_COUNTDOWN_SECONDS = 5;\n\nfunction VerifyRecovery() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const [sessionExpired, setSessionExpired] = useState(false);\n const [redirectCountdown, setRedirectCountdown] = useState(\n REDIRECT_COUNTDOWN_SECONDS,\n );\n\n const recoverySchema = useMemo(\n () =>\n z.object({\n code: z\n .string()\n .transform((value) => value.toUpperCase())\n .pipe(\n z\n .string()\n .regex(\n /^[A-HJ-NP-TV-Z2-9]{4}(?:-[A-HJ-NP-TV-Z2-9]{4}){3}$/,\n t('verifyRecovery.error.invalid'),\n ),\n ),\n }),\n [t],\n );\n\n const verifyMutation = useMutation({\n ...verifyRecoveryCodeMutationOptions,\n onSuccess: async (data) => {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: data.user,\n });\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n setValue,\n formState: { errors },\n } = useForm<RecoveryFormValues>({\n defaultValues: {\n code: '',\n },\n resolver: standardSchemaResolver(recoverySchema),\n });\n\n // Auto redirect when session expires\n const redirectToLogin = useCallback(() => {\n router.navigate({\n to: '/login',\n search: extractOAuthParams(search),\n });\n }, [router, search]);\n\n useEffect(() => {\n if (!sessionExpired) return;\n\n const timer = setInterval(() => {\n setRedirectCountdown((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n redirectToLogin();\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [sessionExpired, redirectToLogin]);\n\n const onSubmit = async (values: RecoveryFormValues) => {\n try {\n await verifyMutation.mutateAsync(values);\n } catch (error) {\n if (error instanceof IssuaryError) {\n switch (error.code) {\n case ERROR_CODES.SECOND_FACTOR_SESSION_EXPIRED:\n setSessionExpired(true);\n setRedirectCountdown(REDIRECT_COUNTDOWN_SECONDS);\n return;\n\n case ERROR_CODES.TOTP_NOT_ENABLED:\n redirectToLogin();\n return;\n\n case ERROR_CODES.NO_RECOVERY_CODES_AVAILABLE:\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.noCodesAvailable'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n return;\n\n case ERROR_CODES.INVALID_RECOVERY_CODE:\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.invalid'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n return;\n }\n }\n\n // Generic error fallback\n setError('code', {\n type: 'manual',\n message: t('verifyRecovery.error.invalid'),\n });\n setValue('code', '');\n inputRef.current?.focus();\n }\n };\n\n const { ref: formRef, ...registerRest } = register('code');\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyRecovery.subtitle')}\n title={t('verifyRecovery.title')}\n />\n\n {sessionExpired && (\n <Alert\n data-testid=\"recovery-session-expired\"\n icon={CircleAlertIcon}\n type=\"warning\"\n >\n <span className=\"flex flex-col items-start gap-tinyrack-3xs\">\n <span>{t('verifyRecovery.error.expired')}</span>\n <span className=\"text-tinyrack-sm opacity-tinyrack-hover\">\n {t('verifyRecovery.redirecting', {\n seconds: redirectCountdown,\n })}\n </span>\n <TRButton\n appearance=\"ghost\"\n intent=\"neutral\"\n onClick={redirectToLogin}\n type=\"button\"\n uiSize=\"sm\"\n >\n {t('verifyRecovery.redirectNow')}\n </TRButton>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <TRField.Root>\n <TRInput\n {...registerRest}\n autoComplete=\"off\"\n className=\"w-full text-center font-tinyrack-mono\"\n data-testid=\"recovery-code-input\"\n disabled={sessionExpired}\n placeholder={t('verifyRecovery.placeholder')}\n ref={(el) => {\n formRef(el);\n inputRef.current = el as HTMLInputElement | null;\n }}\n type=\"text\"\n />\n {errors.code && (\n <div className=\"tr-field-error\" data-testid=\"recovery-error\">\n {errors.code.message}\n </div>\n )}\n </TRField.Root>\n\n {sessionExpired ? (\n <TRButton\n className=\"w-full\"\n disabled\n intent=\"primary\"\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyRecovery.submit')}\n </TRButton>\n ) : (\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyMutation.isPending}\n loadingLabel={t('verifyRecovery.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyRecovery.submit')}\n </TRButton>\n )}\n </form>\n\n {/* Mirrors /verify/totp: the other second factor sits below the rule. */}\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator label={t('common.or')} />\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"recovery-back-to-totp\"\n intent=\"neutral\"\n onClick={() =>\n router.navigate({\n to: '/verify/totp',\n search: extractOAuthParams(search),\n })\n }\n type=\"button\"\n >\n {t('verifyRecovery.backToTotp')}\n </TRButton>\n </div>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('verifyRecovery.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{J as e,Z as t,i as n,q as r,r as ee,s as i}from"./createLucideIcon-BPpAIXR_.js";import{Sn as a,Sr as o,i as s,r as c,t as l}from"./config-DTQCUlfZ.js";import{t as u}from"./mutationOptions-DyYFUbyn.js";import{t as d}from"./useMutation-BWZ0G4Td.js";import{tt as f}from"./shell-header-bar-CgyNXbPS.js";import{t as p}from"./auth-field-DsLGh1U-.js";import{t as m}from"./authorization-context-banner-DVx1TsPY.js";import{t as te}from"./lock-7NRyvXBS.js";import{t as h}from"./mail-D0B_1Dzd.js";import{a as g,c as _,i as v,o as y,r as b}from"./zod-C87S6Tec.js";import{E as x,F as S,N as C,S as w,V as ne,j as T,k as E}from"./index-CZ0rWM7l.js";import{t as D}from"./auth-page-header-COvqWINf.js";import{t as O}from"./auth-layout-DGTylGtX.js";import{t as k}from"./promise-OpBtq8tG.js";import{n as A,t as j}from"./auth-footer-Cz5ivsDc.js";import{r as M,t as N}from"./standard-schema-e8gtSzGE.js";import{t as P}from"./password-strength-DTWF07Px.js";import{t as F}from"./terms-checkbox-list-DqbXU4qw.js";import{t as I}from"./labeled-separator-CV7nb0XW.js";var L=t(e()),R=u({mutationFn:async e=>s(await c.api.auth.register.$post({json:e,header:{}}))}),z=r();function B(){let{t:e,i18n:t}=n(),r=ne(),s=o(),c=x.useSearch(),u=c.lang??t.language,B=(0,L.useDeferredValue)(u),{data:V}=f(l),{data:H}=f(E(B)),U=V.auth.password.enabled,W=V.auth.password.policy,G=H.terms.length>0,K=(0,L.useMemo)(()=>H.terms.filter(e=>e.consentMode===`explicit`),[H.terms]),q=K.length>0,J=V.registration.signup_notice?.[u]??V.registration.signup_notice?.[V.i18n.fallback_language],Y=(0,L.useMemo)(()=>y({email:_().min(1,e(`validation.email.required`)).pipe(v(e(`validation.email.invalid`))),password:_().min(W.min_length,e(`validation.password.min`,{count:W.min_length})).max(W.max_length,e(`validation.password.max`,{count:W.max_length})),termsConsents:y(Object.fromEntries(K.map(t=>[t.id,t.required?g(!0,{message:e(`validation.terms.required`)}):b()])))}),[K,W.max_length,W.min_length,e]),X=d({...R,onSuccess:async(e,t)=>{let n=e.user;if(n.email_verification_required&&!n.email_verified)return r({to:`/verify/email`,search:{email:t.email,...C(c)}});if(s.setQueryData(w.queryKey,{user:n}),await k(),n.second_factor_required){let e=[];return V.auth.password.totp.enabled&&e.push(`totp`),V.auth.passkey.enabled&&e.push(`passkey`),e.length===1?e[0]===`totp`?r({to:`/setup/totp`,search:C(c)}):r({to:`/setup/passkey`,search:{...C(c),passkey_name:`default`}}):r({to:`/setup/2fa`,search:C(c)})}S(c)?window.location.href=T(c):r({to:`/profile`})},onError:t=>{t instanceof ee?t.code===`REGISTRATION_EMAIL_NOT_ALLOWED`?Q(`email`,{type:`manual`,message:e(`register.error.emailNotAllowed`)}):t.code===`REGISTRATION_DISABLED`?Q(`email`,{type:`manual`,message:e(`register.error.registrationDisabled`)}):Q(`email`,{type:`manual`,message:e(`register.error.emailExists`)}):Q(`email`,{type:`manual`,message:e(`register.error.emailExists`)})},onSettled:async()=>{await s.invalidateQueries({queryKey:w.queryKey})}}),{register:Z,setError:Q,handleSubmit:re,control:ie,setValue:ae,watch:oe,formState:{errors:$}}=M({defaultValues:{email:``,password:``,termsConsents:Object.fromEntries(K.map(e=>[e.id,!1]))},resolver:N(Y),mode:`onChange`}),se=oe(`password`);return(0,z.jsxs)(O,{children:[(0,z.jsx)(D,{subtitle:e(`register.subtitle`),title:e(`register.title`)}),(0,z.jsx)(m,{search:c}),U&&(0,z.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:re(e=>{let t=G?K.map(t=>({termsId:t.id,agreed:e.termsConsents[t.id]??!1})):void 0;X.mutate({email:e.email,password:e.password,consents:t})}),children:[(0,z.jsx)(p,{autoComplete:`email`,error:$.email,icon:h,label:e(`register.email.label`),placeholder:e(`register.email.placeholder`),...Z(`email`),type:`email`}),(0,z.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[(0,z.jsx)(p,{autoComplete:`new-password`,error:$.password,hint:e(`register.password.hint`,{count:W.min_length}),icon:te,label:e(`register.password.label`),placeholder:e(`register.password.placeholder`),...Z(`password`),type:`password`}),(0,z.jsx)(P,{password:se,policy:W})]}),(0,z.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-md`,children:[J&&(0,z.jsx)(`div`,{className:`text-center text-tinyrack-text-muted text-tinyrack-xs`,children:(0,z.jsx)(`div`,{className:`prose prose-sm text-tinyrack-xs! **:text-tinyrack-xs!`,dangerouslySetInnerHTML:{__html:J},"data-testid":`terms-implicit-notice`})}),J&&q&&(0,z.jsx)(I,{label:e(`terms.additionalOptionalConsent`)}),G&&q&&(0,z.jsx)(F,{control:ie,disabled:X.isPending,errors:$,setValue:ae,terms:K})]}),(0,z.jsx)(i,{className:`w-full`,intent:`primary`,loading:X.isPending,loadingLabel:e(`register.submitting`),type:`submit`,uiSize:`lg`,children:e(`register.submit`)})]}),(0,z.jsx)(j,{children:(0,z.jsx)(A,{link:(0,z.jsx)(a,{search:C(c),to:`/login`,children:e(`register.link.login`)}),text:e(`register.footer.haveAccount`)})})]})}export{B as component};
2
+ //# sourceMappingURL=register-B2Lixapy.js.map