@tinyrack/tinyauth-server 0.7.0 → 0.8.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 (104) hide show
  1. package/dist/services/account-selection.service.d.ts.map +1 -1
  2. package/dist/services/account-selection.service.js +5 -2
  3. package/dist/services/account-selection.service.js.map +1 -1
  4. package/dist/services/oauth-authorize.service.d.ts.map +1 -1
  5. package/dist/services/oauth-authorize.service.js +3 -1
  6. package/dist/services/oauth-authorize.service.js.map +1 -1
  7. package/package.json +1 -1
  8. package/public/assets/2fa-DZSOQLyd.js +2 -0
  9. package/public/assets/{2fa-Cteolf9l.js.map → 2fa-DZSOQLyd.js.map} +1 -1
  10. package/public/assets/2fa-DvD9N_wj.js +2 -0
  11. package/public/assets/{2fa-BMEIiDXv.js.map → 2fa-DvD9N_wj.js.map} +1 -1
  12. package/public/assets/{2fa-BhCgCfEc.js → 2fa-P8YqnDXC.js} +2 -2
  13. package/public/assets/{2fa-BhCgCfEc.js.map → 2fa-P8YqnDXC.js.map} +1 -1
  14. package/public/assets/{2fa-E0x3Samn.js → 2fa-fLX-LED8.js} +2 -2
  15. package/public/assets/{2fa-E0x3Samn.js.map → 2fa-fLX-LED8.js.map} +1 -1
  16. package/public/assets/{admin-CXJ0z2QN.js → admin-BLPp8bT8.js} +2 -2
  17. package/public/assets/{admin-CXJ0z2QN.js.map → admin-BLPp8bT8.js.map} +1 -1
  18. package/public/assets/{consent-C-h_pWHQ.js → consent-CjXe4bU5.js} +2 -2
  19. package/public/assets/{consent-C-h_pWHQ.js.map → consent-CjXe4bU5.js.map} +1 -1
  20. package/public/assets/consent-Cq6tCfwA.js +2 -0
  21. package/public/assets/{consent-B17hGrta.js.map → consent-Cq6tCfwA.js.map} +1 -1
  22. package/public/assets/{email-DjE7mqnX.js → email-CpOX6Ig_.js} +2 -2
  23. package/public/assets/email-CpOX6Ig_.js.map +1 -0
  24. package/public/assets/email-DvD9N_wj.js +2 -0
  25. package/public/assets/email-DvD9N_wj.js.map +1 -0
  26. package/public/assets/{error-BKXsXhdR.js → error-DMzYyopT.js} +2 -2
  27. package/public/assets/{error-BKXsXhdR.js.map → error-DMzYyopT.js.map} +1 -1
  28. package/public/assets/forgot-DvD9N_wj.js +2 -0
  29. package/public/assets/{forgot-BMEIiDXv.js.map → forgot-DvD9N_wj.js.map} +1 -1
  30. package/public/assets/{forgot-C9zSfaX9.js → forgot-RbSzzjWF.js} +2 -2
  31. package/public/assets/{forgot-C9zSfaX9.js.map → forgot-RbSzzjWF.js.map} +1 -1
  32. package/public/assets/{index-BmfaaNx6.js → index-CgkA6nnx.js} +3 -3
  33. package/public/assets/index-CgkA6nnx.js.map +1 -0
  34. package/public/assets/{login-DG1Yfmb8.js → login-BQI7QY9W.js} +2 -2
  35. package/public/assets/login-BQI7QY9W.js.map +1 -0
  36. package/public/assets/login-DvD9N_wj.js +2 -0
  37. package/public/assets/login-DvD9N_wj.js.map +1 -0
  38. package/public/assets/{passkey-BDrX3jVg.js → passkey-C0BKdldQ.js} +2 -2
  39. package/public/assets/passkey-C0BKdldQ.js.map +1 -0
  40. package/public/assets/{passkey-C-h8Fvqn.js → passkey-D9qQ8xoR.js} +2 -2
  41. package/public/assets/passkey-D9qQ8xoR.js.map +1 -0
  42. package/public/assets/password-CqjbZggE.js +2 -0
  43. package/public/assets/password-CqjbZggE.js.map +1 -0
  44. package/public/assets/password-DvD9N_wj.js +2 -0
  45. package/public/assets/password-DvD9N_wj.js.map +1 -0
  46. package/public/assets/{profile-8buOY6GT.js → profile-B5qQlPwW.js} +2 -2
  47. package/public/assets/{profile-8buOY6GT.js.map → profile-B5qQlPwW.js.map} +1 -1
  48. package/public/assets/profile-fwH7_EJ0.js +2 -0
  49. package/public/assets/{profile-DcpL_XUt.js.map → profile-fwH7_EJ0.js.map} +1 -1
  50. package/public/assets/{recovery-Csf6dtvj.js → recovery-DqEhUHzv.js} +2 -2
  51. package/public/assets/{recovery-Csf6dtvj.js.map → recovery-DqEhUHzv.js.map} +1 -1
  52. package/public/assets/{register-X6dycne4.js → register-CntgI-rd.js} +2 -2
  53. package/public/assets/register-CntgI-rd.js.map +1 -0
  54. package/public/assets/register-DvD9N_wj.js +2 -0
  55. package/public/assets/register-DvD9N_wj.js.map +1 -0
  56. package/public/assets/{reset-1nsqPlxL.js → reset-BK1XYJiv.js} +2 -2
  57. package/public/assets/{reset-1nsqPlxL.js.map → reset-BK1XYJiv.js.map} +1 -1
  58. package/public/assets/reset-DvD9N_wj.js +2 -0
  59. package/public/assets/{reset-BMEIiDXv.js.map → reset-DvD9N_wj.js.map} +1 -1
  60. package/public/assets/{select-CDdb3jyc.js → select-DeUoTgDw.js} +2 -2
  61. package/public/assets/{select-CDdb3jyc.js.map → select-DeUoTgDw.js.map} +1 -1
  62. package/public/assets/select-DvD9N_wj.js +2 -0
  63. package/public/assets/{select-BMEIiDXv.js.map → select-DvD9N_wj.js.map} +1 -1
  64. package/public/assets/{terms-CUhvMN9k.js → terms-CPEuXMaO.js} +2 -2
  65. package/public/assets/{terms-CUhvMN9k.js.map → terms-CPEuXMaO.js.map} +1 -1
  66. package/public/assets/terms-fwH7_EJ0.js +2 -0
  67. package/public/assets/{terms-DcpL_XUt.js.map → terms-fwH7_EJ0.js.map} +1 -1
  68. package/public/assets/{totp-DB9WqW4V.js → totp-B1ebEiOW.js} +2 -2
  69. package/public/assets/totp-B1ebEiOW.js.map +1 -0
  70. package/public/assets/{totp-UDT2P91H.js → totp-DWWZe-tJ.js} +2 -2
  71. package/public/assets/totp-DWWZe-tJ.js.map +1 -0
  72. package/public/assets/{use-totp-setup-9fktn_he.js → use-totp-setup-Z96pOMXt.js} +2 -2
  73. package/public/assets/{use-totp-setup-9fktn_he.js.map → use-totp-setup-Z96pOMXt.js.map} +1 -1
  74. package/public/assets/{useMutation-Iu4AJCB4.js → useMutation-Biy2-pH_.js} +2 -2
  75. package/public/assets/{useMutation-Iu4AJCB4.js.map → useMutation-Biy2-pH_.js.map} +1 -1
  76. package/public/assets/{users-Bi7gjreq.js → users-YtDGJlDl.js} +2 -2
  77. package/public/assets/{users-Bi7gjreq.js.map → users-YtDGJlDl.js.map} +1 -1
  78. package/public/index.html +1 -1
  79. package/public/assets/2fa-BMEIiDXv.js +0 -2
  80. package/public/assets/2fa-Cteolf9l.js +0 -2
  81. package/public/assets/consent-B17hGrta.js +0 -2
  82. package/public/assets/email-BMEIiDXv.js +0 -2
  83. package/public/assets/email-BMEIiDXv.js.map +0 -1
  84. package/public/assets/email-DjE7mqnX.js.map +0 -1
  85. package/public/assets/forgot-BMEIiDXv.js +0 -2
  86. package/public/assets/index-BmfaaNx6.js.map +0 -1
  87. package/public/assets/login-BMEIiDXv.js +0 -2
  88. package/public/assets/login-BMEIiDXv.js.map +0 -1
  89. package/public/assets/login-DG1Yfmb8.js.map +0 -1
  90. package/public/assets/passkey-BDrX3jVg.js.map +0 -1
  91. package/public/assets/passkey-C-h8Fvqn.js.map +0 -1
  92. package/public/assets/password-86C-hCze.js +0 -2
  93. package/public/assets/password-86C-hCze.js.map +0 -1
  94. package/public/assets/password-BMEIiDXv.js +0 -2
  95. package/public/assets/password-BMEIiDXv.js.map +0 -1
  96. package/public/assets/profile-DcpL_XUt.js +0 -2
  97. package/public/assets/register-BMEIiDXv.js +0 -2
  98. package/public/assets/register-BMEIiDXv.js.map +0 -1
  99. package/public/assets/register-X6dycne4.js.map +0 -1
  100. package/public/assets/reset-BMEIiDXv.js +0 -2
  101. package/public/assets/select-BMEIiDXv.js +0 -2
  102. package/public/assets/terms-DcpL_XUt.js +0 -2
  103. package/public/assets/totp-DB9WqW4V.js.map +0 -1
  104. package/public/assets/totp-UDT2P91H.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"select-CDdb3jyc.js","names":["a","a","r","a","i","t","a","s","t","ArrowRightIcon","PlusIcon","TrashIcon","UserCircleIcon","useMutation","useQueryClient","useSuspenseQuery","PageHeader","Alert","PageLayout","buildAuthorizeUrl","extractOAuthParams","accountsQueryOptions","removeAccountMutationOptions","selectAccountMutationOptions","queryKeys","Route","appendLoginPrompt","prompt","values","split","filter","Boolean","includes","push","join","buildLoginHref","search","ReturnType","useSearch","oauthParams","params","URLSearchParams","key","value","Object","entries","account_selected","undefined","set","String","toString","AccountSelect","queryClient","data","client_id","continueWithSelectedAccount","window","location","href","selectMutation","onSuccess","removeMutation","invalidateQueries","queryKey","accounts","length","map","account","sub","isPending","mutate","email","current","allow_remove_account","allow_add_account","component"],"sources":["../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/ArrowRight.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/Plus.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/UserCircle.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/ArrowRight.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/Plus.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/UserCircle.es.js","../../../frontend/src/routes/account/select/index.tsx?tsr-split=component"],"sourcesContent":["import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224.49,136.49l-72,72a12,12,0,0,1-17-17L187,140H40a12,12,0,0,1,0-24H187L135.51,64.48a12,12,0,0,1,17-17l72,72A12,12,0,0,1,224.49,136.49Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216,128l-72,72V56Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72A8,8,0,0,1,136,200V136H40a8,8,0,0,1,0-16h96V56a8,8,0,0,1,13.66-5.66l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220.24,132.24l-72,72a6,6,0,0,1-8.48-8.48L201.51,134H40a6,6,0,0,1,0-12H201.51L139.76,60.24a6,6,0,0,1,8.48-8.48l72,72A6,6,0,0,1,220.24,132.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M218.83,130.83l-72,72a4,4,0,0,1-5.66-5.66L206.34,132H40a4,4,0,0,1,0-8H206.34L141.17,58.83a4,4,0,0,1,5.66-5.66l72,72A4,4,0,0,1,218.83,130.83Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20ZM79.57,196.57a60,60,0,0,1,96.86,0,83.72,83.72,0,0,1-96.86,0ZM100,120a28,28,0,1,1,28,28A28,28,0,0,1,100,120ZM194,179.94a83.48,83.48,0,0,0-29-23.42,52,52,0,1,0-74,0,83.48,83.48,0,0,0-29,23.42,84,84,0,1,1,131.9,0Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M224,128a95.76,95.76,0,0,1-31.8,71.37A72,72,0,0,0,128,160a40,40,0,1,0-40-40,40,40,0,0,0,40,40,72,72,0,0,0-64.2,39.37h0A96,96,0,1,1,224,128Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M172,120a44,44,0,1,1-44-44A44.05,44.05,0,0,1,172,120Zm60,8A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88.09,88.09,0,0,0-91.47-87.93C77.43,41.89,39.87,81.12,40,128.25a87.65,87.65,0,0,0,22.24,58.16A79.71,79.71,0,0,1,84,165.1a4,4,0,0,1,4.83.32,59.83,59.83,0,0,0,78.28,0,4,4,0,0,1,4.83-.32,79.71,79.71,0,0,1,21.79,21.31A87.62,87.62,0,0,0,216,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26ZM71.44,198a66,66,0,0,1,113.12,0,89.8,89.8,0,0,1-113.12,0ZM94,120a34,34,0,1,1,34,34A34,34,0,0,1,94,120Zm99.51,69.64a77.53,77.53,0,0,0-40-31.38,46,46,0,1,0-51,0,77.53,77.53,0,0,0-40,31.38,90,90,0,1,1,131,0Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28ZM68.87,198.42a68,68,0,0,1,118.26,0,91.8,91.8,0,0,1-118.26,0Zm124.3-5.55a75.61,75.61,0,0,0-44.51-34,44,44,0,1,0-41.32,0,75.61,75.61,0,0,0-44.51,34,92,92,0,1,1,130.34,0ZM128,156a36,36,0,1,1,36-36A36,36,0,0,1,128,156Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/IconBase.es.js\";\nimport i from \"../defs/ArrowRight.es.js\";\nconst r = o.forwardRef((t, e) => /* @__PURE__ */ o.createElement(a, { ref: e, ...t, weights: i }));\nr.displayName = \"ArrowRightIcon\";\nconst s = r;\nexport {\n s as ArrowRight,\n r as ArrowRightIcon\n};\n","import * as o from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport a from \"../defs/Plus.es.js\";\nconst e = o.forwardRef((r, s) => /* @__PURE__ */ o.createElement(t, { ref: s, ...r, weights: a }));\ne.displayName = \"PlusIcon\";\nconst n = e;\nexport {\n n as Plus,\n e as PlusIcon\n};\n","import * as e from \"react\";\nimport s from \"../lib/IconBase.es.js\";\nimport t from \"../defs/UserCircle.es.js\";\nconst r = e.forwardRef((o, c) => /* @__PURE__ */ e.createElement(s, { ref: c, ...o, weights: t }));\nr.displayName = \"UserCircleIcon\";\nconst m = r;\nexport {\n m as UserCircle,\n r as UserCircleIcon\n};\n","import {\n ArrowRightIcon,\n PlusIcon,\n TrashIcon,\n UserCircleIcon,\n} from '@phosphor-icons/react';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: '1',\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: '1',\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle=\"Choose an account to continue to this application\"\n title=\"Choose an account\"\n />\n\n {data.accounts.length === 0 ? (\n <Alert className=\"mb-4\" icon={UserCircleIcon} type=\"info\">\n No remembered accounts are available in this browser session.\n </Alert>\n ) : (\n <div className=\"flex flex-col gap-2\" data-testid=\"account-list\">\n {data.accounts.map((account) => (\n <div\n className=\"flex items-center gap-3 rounded-box border border-base-300 bg-base-100 p-3 shadow-sm\"\n data-testid=\"remembered-account\"\n key={account.sub}\n >\n <div className=\"avatar placeholder\">\n <div className=\"w-10 rounded-full bg-primary/10 text-primary\">\n <UserCircleIcon className=\"size-7\" weight=\"duotone\" />\n </div>\n </div>\n <button\n className=\"min-w-0 flex-1 text-left\"\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <div className=\"truncate font-medium text-sm\">\n {account.email}\n </div>\n <div className=\"text-base-content/60 text-xs\">\n {account.current ? 'Current account' : 'Remembered account'}\n </div>\n </button>\n {account.current ? (\n <span className=\"badge badge-primary badge-sm\">Current</span>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <button\n aria-label={`Remove ${account.email}`}\n className=\"btn btn-ghost btn-square btn-sm text-base-content/60\"\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n onClick={() => removeMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <TrashIcon className=\"size-4\" />\n </button>\n ) : null}\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <a\n className=\"btn btn-outline mt-4 w-full justify-between\"\n href={buildLoginHref(search)}\n >\n <span className=\"inline-flex items-center gap-2\">\n <PlusIcon className=\"size-4\" />\n Use another account\n </span>\n <ArrowRightIcon className=\"size-4\" />\n </a>\n ) : null}\n </PageLayout>\n );\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5],"mappings":"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"}
1
+ {"version":3,"file":"select-DeUoTgDw.js","names":["a","a","r","a","i","t","a","s","t","ArrowRightIcon","PlusIcon","TrashIcon","UserCircleIcon","useMutation","useQueryClient","useSuspenseQuery","PageHeader","Alert","PageLayout","buildAuthorizeUrl","extractOAuthParams","accountsQueryOptions","removeAccountMutationOptions","selectAccountMutationOptions","queryKeys","Route","appendLoginPrompt","prompt","values","split","filter","Boolean","includes","push","join","buildLoginHref","search","ReturnType","useSearch","oauthParams","params","URLSearchParams","key","value","Object","entries","account_selected","undefined","set","String","toString","AccountSelect","queryClient","data","client_id","continueWithSelectedAccount","window","location","href","selectMutation","onSuccess","removeMutation","invalidateQueries","queryKey","accounts","length","map","account","sub","isPending","mutate","email","current","allow_remove_account","allow_add_account","component"],"sources":["../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/ArrowRight.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/Plus.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/UserCircle.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/ArrowRight.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/Plus.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/UserCircle.es.js","../../../frontend/src/routes/account/select/index.tsx?tsr-split=component"],"sourcesContent":["import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224.49,136.49l-72,72a12,12,0,0,1-17-17L187,140H40a12,12,0,0,1,0-24H187L135.51,64.48a12,12,0,0,1,17-17l72,72A12,12,0,0,1,224.49,136.49Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216,128l-72,72V56Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72A8,8,0,0,1,136,200V136H40a8,8,0,0,1,0-16h96V56a8,8,0,0,1,13.66-5.66l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220.24,132.24l-72,72a6,6,0,0,1-8.48-8.48L201.51,134H40a6,6,0,0,1,0-12H201.51L139.76,60.24a6,6,0,0,1,8.48-8.48l72,72A6,6,0,0,1,220.24,132.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M218.83,130.83l-72,72a4,4,0,0,1-5.66-5.66L206.34,132H40a4,4,0,0,1,0-8H206.34L141.17,58.83a4,4,0,0,1,5.66-5.66l72,72A4,4,0,0,1,218.83,130.83Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20ZM79.57,196.57a60,60,0,0,1,96.86,0,83.72,83.72,0,0,1-96.86,0ZM100,120a28,28,0,1,1,28,28A28,28,0,0,1,100,120ZM194,179.94a83.48,83.48,0,0,0-29-23.42,52,52,0,1,0-74,0,83.48,83.48,0,0,0-29,23.42,84,84,0,1,1,131.9,0Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M224,128a95.76,95.76,0,0,1-31.8,71.37A72,72,0,0,0,128,160a40,40,0,1,0-40-40,40,40,0,0,0,40,40,72,72,0,0,0-64.2,39.37h0A96,96,0,1,1,224,128Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M172,120a44,44,0,1,1-44-44A44.05,44.05,0,0,1,172,120Zm60,8A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88.09,88.09,0,0,0-91.47-87.93C77.43,41.89,39.87,81.12,40,128.25a87.65,87.65,0,0,0,22.24,58.16A79.71,79.71,0,0,1,84,165.1a4,4,0,0,1,4.83.32,59.83,59.83,0,0,0,78.28,0,4,4,0,0,1,4.83-.32,79.71,79.71,0,0,1,21.79,21.31A87.62,87.62,0,0,0,216,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26ZM71.44,198a66,66,0,0,1,113.12,0,89.8,89.8,0,0,1-113.12,0ZM94,120a34,34,0,1,1,34,34A34,34,0,0,1,94,120Zm99.51,69.64a77.53,77.53,0,0,0-40-31.38,46,46,0,1,0-51,0,77.53,77.53,0,0,0-40,31.38,90,90,0,1,1,131,0Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28ZM68.87,198.42a68,68,0,0,1,118.26,0,91.8,91.8,0,0,1-118.26,0Zm124.3-5.55a75.61,75.61,0,0,0-44.51-34,44,44,0,1,0-41.32,0,75.61,75.61,0,0,0-44.51,34,92,92,0,1,1,130.34,0ZM128,156a36,36,0,1,1,36-36A36,36,0,0,1,128,156Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/IconBase.es.js\";\nimport i from \"../defs/ArrowRight.es.js\";\nconst r = o.forwardRef((t, e) => /* @__PURE__ */ o.createElement(a, { ref: e, ...t, weights: i }));\nr.displayName = \"ArrowRightIcon\";\nconst s = r;\nexport {\n s as ArrowRight,\n r as ArrowRightIcon\n};\n","import * as o from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport a from \"../defs/Plus.es.js\";\nconst e = o.forwardRef((r, s) => /* @__PURE__ */ o.createElement(t, { ref: s, ...r, weights: a }));\ne.displayName = \"PlusIcon\";\nconst n = e;\nexport {\n n as Plus,\n e as PlusIcon\n};\n","import * as e from \"react\";\nimport s from \"../lib/IconBase.es.js\";\nimport t from \"../defs/UserCircle.es.js\";\nconst r = e.forwardRef((o, c) => /* @__PURE__ */ e.createElement(s, { ref: c, ...o, weights: t }));\nr.displayName = \"UserCircleIcon\";\nconst m = r;\nexport {\n m as UserCircle,\n r as UserCircleIcon\n};\n","import {\n ArrowRightIcon,\n PlusIcon,\n TrashIcon,\n UserCircleIcon,\n} from '@phosphor-icons/react';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: '1',\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: '1',\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle=\"Choose an account to continue to this application\"\n title=\"Choose an account\"\n />\n\n {data.accounts.length === 0 ? (\n <Alert className=\"mb-4\" icon={UserCircleIcon} type=\"info\">\n No remembered accounts are available in this browser session.\n </Alert>\n ) : (\n <div className=\"flex flex-col gap-2\" data-testid=\"account-list\">\n {data.accounts.map((account) => (\n <div\n className=\"flex items-center gap-3 rounded-box border border-base-300 bg-base-100 p-3 shadow-sm\"\n data-testid=\"remembered-account\"\n key={account.sub}\n >\n <div className=\"avatar placeholder\">\n <div className=\"w-10 rounded-full bg-primary/10 text-primary\">\n <UserCircleIcon className=\"size-7\" weight=\"duotone\" />\n </div>\n </div>\n <button\n className=\"min-w-0 flex-1 text-left\"\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <div className=\"truncate font-medium text-sm\">\n {account.email}\n </div>\n <div className=\"text-base-content/60 text-xs\">\n {account.current ? 'Current account' : 'Remembered account'}\n </div>\n </button>\n {account.current ? (\n <span className=\"badge badge-primary badge-sm\">Current</span>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <button\n aria-label={`Remove ${account.email}`}\n className=\"btn btn-ghost btn-square btn-sm text-base-content/60\"\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n onClick={() => removeMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <TrashIcon className=\"size-4\" />\n </button>\n ) : null}\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <a\n className=\"btn btn-outline mt-4 w-full justify-between\"\n href={buildLoginHref(search)}\n >\n <span className=\"inline-flex items-center gap-2\">\n <PlusIcon className=\"size-4\" />\n Use another account\n </span>\n <ArrowRightIcon className=\"size-4\" />\n </a>\n ) : null}\n </PageLayout>\n );\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{P as e}from"./index-CgkA6nnx.js";var t=e;export{t as errorComponent};
2
+ //# sourceMappingURL=select-DvD9N_wj.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select-BMEIiDXv.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/account/select/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import {\n ArrowRightIcon,\n PlusIcon,\n TrashIcon,\n UserCircleIcon,\n} from '@phosphor-icons/react';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: '1',\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: '1',\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle=\"Choose an account to continue to this application\"\n title=\"Choose an account\"\n />\n\n {data.accounts.length === 0 ? (\n <Alert className=\"mb-4\" icon={UserCircleIcon} type=\"info\">\n No remembered accounts are available in this browser session.\n </Alert>\n ) : (\n <div className=\"flex flex-col gap-2\" data-testid=\"account-list\">\n {data.accounts.map((account) => (\n <div\n className=\"flex items-center gap-3 rounded-box border border-base-300 bg-base-100 p-3 shadow-sm\"\n data-testid=\"remembered-account\"\n key={account.sub}\n >\n <div className=\"avatar placeholder\">\n <div className=\"w-10 rounded-full bg-primary/10 text-primary\">\n <UserCircleIcon className=\"size-7\" weight=\"duotone\" />\n </div>\n </div>\n <button\n className=\"min-w-0 flex-1 text-left\"\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <div className=\"truncate font-medium text-sm\">\n {account.email}\n </div>\n <div className=\"text-base-content/60 text-xs\">\n {account.current ? 'Current account' : 'Remembered account'}\n </div>\n </button>\n {account.current ? (\n <span className=\"badge badge-primary badge-sm\">Current</span>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <button\n aria-label={`Remove ${account.email}`}\n className=\"btn btn-ghost btn-square btn-sm text-base-content/60\"\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n onClick={() => removeMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <TrashIcon className=\"size-4\" />\n </button>\n ) : null}\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <a\n className=\"btn btn-outline mt-4 w-full justify-between\"\n href={buildLoginHref(search)}\n >\n <span className=\"inline-flex items-center gap-2\">\n <PlusIcon className=\"size-4\" />\n Use another account\n </span>\n <ArrowRightIcon className=\"size-4\" />\n </a>\n ) : null}\n </PageLayout>\n );\n}\n"],"mappings":"wCAcsF,IAAAC,EAA7ED"}
1
+ {"version":3,"file":"select-DvD9N_wj.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/account/select/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import {\n ArrowRightIcon,\n PlusIcon,\n TrashIcon,\n UserCircleIcon,\n} from '@phosphor-icons/react';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute } from '@tanstack/react-router';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport {\n buildAuthorizeUrl,\n extractOAuthParams,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n accountsQueryOptions,\n removeAccountMutationOptions,\n selectAccountMutationOptions,\n} from '#frontend/queries/accounts.ts';\nimport { queryKeys } from '#frontend/queries/keys.ts';\n\nexport const Route = createFileRoute('/account/select/')({\n component: AccountSelect,\n errorComponent: RouteErrorFallback,\n validateSearch: OAuthSearchSchema,\n loader: async ({ context, location }) => {\n const search = location.search as { client_id?: string };\n await context.queryClient.ensureQueryData(\n accountsQueryOptions(search.client_id),\n );\n },\n});\n\nfunction appendLoginPrompt(prompt: string | undefined): string {\n const values = prompt?.split(' ').filter(Boolean) ?? [];\n if (!values.includes('login')) {\n values.push('login');\n }\n return values.join(' ');\n}\n\nfunction buildLoginHref(search: ReturnType<typeof Route.useSearch>) {\n const oauthParams = extractOAuthParams(search);\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries({\n ...oauthParams,\n prompt: appendLoginPrompt(oauthParams.prompt),\n account_selected: '1',\n })) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n return `/login?${params.toString()}`;\n}\n\nfunction AccountSelect() {\n const search = Route.useSearch();\n const queryClient = useQueryClient();\n const { data } = useSuspenseQuery(accountsQueryOptions(search.client_id));\n\n const continueWithSelectedAccount = () => {\n window.location.href = buildAuthorizeUrl({\n ...search,\n account_selected: '1',\n });\n };\n\n const selectMutation = useMutation({\n ...selectAccountMutationOptions,\n onSuccess: continueWithSelectedAccount,\n });\n const removeMutation = useMutation({\n ...removeAccountMutationOptions,\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.accounts(search.client_id),\n });\n },\n });\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle=\"Choose an account to continue to this application\"\n title=\"Choose an account\"\n />\n\n {data.accounts.length === 0 ? (\n <Alert className=\"mb-4\" icon={UserCircleIcon} type=\"info\">\n No remembered accounts are available in this browser session.\n </Alert>\n ) : (\n <div className=\"flex flex-col gap-2\" data-testid=\"account-list\">\n {data.accounts.map((account) => (\n <div\n className=\"flex items-center gap-3 rounded-box border border-base-300 bg-base-100 p-3 shadow-sm\"\n data-testid=\"remembered-account\"\n key={account.sub}\n >\n <div className=\"avatar placeholder\">\n <div className=\"w-10 rounded-full bg-primary/10 text-primary\">\n <UserCircleIcon className=\"size-7\" weight=\"duotone\" />\n </div>\n </div>\n <button\n className=\"min-w-0 flex-1 text-left\"\n data-testid={`select-account-${account.sub}`}\n disabled={selectMutation.isPending}\n onClick={() => selectMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <div className=\"truncate font-medium text-sm\">\n {account.email}\n </div>\n <div className=\"text-base-content/60 text-xs\">\n {account.current ? 'Current account' : 'Remembered account'}\n </div>\n </button>\n {account.current ? (\n <span className=\"badge badge-primary badge-sm\">Current</span>\n ) : null}\n {data.allow_remove_account && !account.current ? (\n <button\n aria-label={`Remove ${account.email}`}\n className=\"btn btn-ghost btn-square btn-sm text-base-content/60\"\n data-testid={`remove-account-${account.sub}`}\n disabled={removeMutation.isPending}\n onClick={() => removeMutation.mutate({ sub: account.sub })}\n type=\"button\"\n >\n <TrashIcon className=\"size-4\" />\n </button>\n ) : null}\n </div>\n ))}\n </div>\n )}\n\n {data.allow_add_account ? (\n <a\n className=\"btn btn-outline mt-4 w-full justify-between\"\n href={buildLoginHref(search)}\n >\n <span className=\"inline-flex items-center gap-2\">\n <PlusIcon className=\"size-4\" />\n Use another account\n </span>\n <ArrowRightIcon className=\"size-4\" />\n </a>\n ) : null}\n </PageLayout>\n );\n}\n"],"mappings":"wCAcsF,IAAAC,EAA7ED"}
@@ -1,2 +1,2 @@
1
- import{o as e,r as t,s as n,u as r}from"./IconBase.es-d5KP98Ac.js";import{Q as i,h as a,n as o,ut as s}from"./use-theme-cVUDAjtt.js";import{t as c}from"./useMutation-Iu4AJCB4.js";import{n as l,t as u}from"./Warning.es-BPpZIJYZ.js";import{t as d}from"./page-layout-C475gs09.js";import{a as f,o as p,r as m}from"./zod-BItJDQBQ.js";import{D as h,O as g,S as _,k as v}from"./index-BmfaaNx6.js";import{t as y}from"./page-header-BYMFSGfT.js";import{t as b}from"./alert-CSXqgDVi.js";import{t as x}from"./promise-OpBtq8tG.js";import{r as S,t as C}from"./standard-schema-o4V-s4uY.js";import{t as w}from"./terms-checkbox-list-CdrbHxiF.js";var T=r(n()),E=e();function D(){let{t:e,i18n:n}=t(),r=a(),D=s(),O=h.useSearch(),{data:k}=i(o),A=O.lang??n.language,j=i(g(A)),M=k.registration.signup_notice?.[A]??k.registration.signup_notice?.[k.i18n.fallback_language],N=(0,T.useMemo)(()=>j.data.terms.filter(e=>e.consentMode===`explicit`),[j.data.terms]),P=(0,T.useMemo)(()=>j.data.terms.filter(e=>e.consentMode===`implicit`),[j.data.terms]),F=N.length>0,I=(0,T.useMemo)(()=>p({termsConsents:p(Object.fromEntries(N.map(t=>[t.id,t.required?f(!0,{message:e(`validation.terms.required`)}):m()])))}),[e,N]),{handleSubmit:L,control:R,setValue:z,formState:{errors:B}}=S({defaultValues:{termsConsents:Object.fromEntries(N.map(e=>[e.id,!!(e.userConsent?.agreed&&!e.userConsent.requiresUpdate)]))},resolver:C(I),mode:`onChange`}),V=c({...v,onSuccess:async()=>{await D.invalidateQueries({queryKey:_.queryKey}),await D.fetchQuery(_),await x(),O.redirect?window.location.href=O.redirect:r.navigate({to:`/profile`})}});return(0,E.jsxs)(d,{cardPadding:!0,maxWidth:`100`,children:[(0,E.jsx)(y,{title:e(`terms.title`)}),M&&(0,E.jsx)(`div`,{className:`text-center text-base-content/60 text-xs`,children:(0,E.jsx)(`div`,{className:`prose prose-sm text-xs! **:text-xs!`,dangerouslySetInnerHTML:{__html:M}})}),M&&F&&(0,E.jsx)(`div`,{className:`divider text-xs`,children:`AND`}),(0,E.jsxs)(`form`,{onSubmit:L(e=>{let t=Object.entries(e.termsConsents).map(([e,t])=>({termsId:e,agreed:t,consentType:`explicit`})),n=P.map(e=>({termsId:e.id,agreed:!0,consentType:`implicit`}));V.mutate({consents:[...t,...n],...O.registration_token&&{registrationToken:O.registration_token}})}),children:[F&&(0,E.jsx)(`div`,{className:`mb-6`,children:(0,E.jsx)(w,{control:R,disabled:V.isPending,errors:B,setValue:z,terms:N})}),V.isError&&(0,E.jsx)(b,{className:`mb-4`,icon:u,type:`error`,children:e(`terms.error.submitFailed`)}),(0,E.jsx)(`button`,{className:`btn btn-primary btn-block h-10 font-semibold text-[14px]`,disabled:V.isPending,type:`submit`,children:V.isPending?(0,E.jsx)(`span`,{className:`loading loading-spinner loading-sm`}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(l,{className:`size-4`,weight:`bold`}),e(`terms.submit`)]})})]})]})}export{D as component};
2
- //# sourceMappingURL=terms-CUhvMN9k.js.map
1
+ import{o as e,r as t,s as n,u as r}from"./IconBase.es-d5KP98Ac.js";import{Q as i,h as a,n as o,ut as s}from"./use-theme-cVUDAjtt.js";import{t as c}from"./useMutation-Biy2-pH_.js";import{n as l,t as u}from"./Warning.es-BPpZIJYZ.js";import{t as d}from"./page-layout-C475gs09.js";import{a as f,o as p,r as m}from"./zod-BItJDQBQ.js";import{D as h,O as g,S as _,k as v}from"./index-CgkA6nnx.js";import{t as y}from"./page-header-BYMFSGfT.js";import{t as b}from"./alert-CSXqgDVi.js";import{t as x}from"./promise-OpBtq8tG.js";import{r as S,t as C}from"./standard-schema-o4V-s4uY.js";import{t as w}from"./terms-checkbox-list-CdrbHxiF.js";var T=r(n()),E=e();function D(){let{t:e,i18n:n}=t(),r=a(),D=s(),O=h.useSearch(),{data:k}=i(o),A=O.lang??n.language,j=i(g(A)),M=k.registration.signup_notice?.[A]??k.registration.signup_notice?.[k.i18n.fallback_language],N=(0,T.useMemo)(()=>j.data.terms.filter(e=>e.consentMode===`explicit`),[j.data.terms]),P=(0,T.useMemo)(()=>j.data.terms.filter(e=>e.consentMode===`implicit`),[j.data.terms]),F=N.length>0,I=(0,T.useMemo)(()=>p({termsConsents:p(Object.fromEntries(N.map(t=>[t.id,t.required?f(!0,{message:e(`validation.terms.required`)}):m()])))}),[e,N]),{handleSubmit:L,control:R,setValue:z,formState:{errors:B}}=S({defaultValues:{termsConsents:Object.fromEntries(N.map(e=>[e.id,!!(e.userConsent?.agreed&&!e.userConsent.requiresUpdate)]))},resolver:C(I),mode:`onChange`}),V=c({...v,onSuccess:async()=>{await D.invalidateQueries({queryKey:_.queryKey}),await D.fetchQuery(_),await x(),O.redirect?window.location.href=O.redirect:r.navigate({to:`/profile`})}});return(0,E.jsxs)(d,{cardPadding:!0,maxWidth:`100`,children:[(0,E.jsx)(y,{title:e(`terms.title`)}),M&&(0,E.jsx)(`div`,{className:`text-center text-base-content/60 text-xs`,children:(0,E.jsx)(`div`,{className:`prose prose-sm text-xs! **:text-xs!`,dangerouslySetInnerHTML:{__html:M}})}),M&&F&&(0,E.jsx)(`div`,{className:`divider text-xs`,children:`AND`}),(0,E.jsxs)(`form`,{onSubmit:L(e=>{let t=Object.entries(e.termsConsents).map(([e,t])=>({termsId:e,agreed:t,consentType:`explicit`})),n=P.map(e=>({termsId:e.id,agreed:!0,consentType:`implicit`}));V.mutate({consents:[...t,...n],...O.registration_token&&{registrationToken:O.registration_token}})}),children:[F&&(0,E.jsx)(`div`,{className:`mb-6`,children:(0,E.jsx)(w,{control:R,disabled:V.isPending,errors:B,setValue:z,terms:N})}),V.isError&&(0,E.jsx)(b,{className:`mb-4`,icon:u,type:`error`,children:e(`terms.error.submitFailed`)}),(0,E.jsx)(`button`,{className:`btn btn-primary btn-block h-10 font-semibold text-[14px]`,disabled:V.isPending,type:`submit`,children:V.isPending?(0,E.jsx)(`span`,{className:`loading loading-spinner loading-sm`}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(l,{className:`size-4`,weight:`bold`}),e(`terms.submit`)]})})]})]})}export{D as component};
2
+ //# sourceMappingURL=terms-CPEuXMaO.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"terms-CUhvMN9k.js","names":["standardSchemaResolver","CheckIcon","WarningIcon","useMutation","useQueryClient","useSuspenseQuery","useRouter","useMemo","useForm","useTranslation","z","PageHeader","TermsCheckboxList","Alert","PageLayout","tick","appConfigQueryOptions","getSessionQueryOptions","getTermsQueryOptions","termsConsentMutationOptions","Route","Terms","t","i18n","router","queryClient","search","useSearch","data","configData","lang","language","termsQuery","implicitNotice","registration","signup_notice","fallback_language","explicitTerms","terms","filter","term","consentMode","implicitTerms","hasExplicitTerms","length","termsSchema","object","termsConsents","Object","fromEntries","map","id","required","literal","message","boolean","TermsFormValues","TermsConsentsField","handleSubmit","control","setValue","formState","errors","defaultValues","userConsent","agreed","requiresUpdate","resolver","mode","consentMutation","onSuccess","invalidateQueries","queryKey","fetchQuery","redirect","window","location","href","navigate","to","onSubmit","values","explicitConsents","entries","TermsConsentItem","termsId","consentType","implicitConsents","mutate","consents","registration_token","registrationToken","__html","isPending","isError","component"],"sources":["../../../frontend/src/routes/terms/index.tsx?tsr-split=component"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { CheckIcon, WarningIcon } from '@phosphor-icons/react';\nimport {\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 { useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport {\n TermsCheckboxList,\n type TermsConsentsField,\n} from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { OAuthSearchSchema } from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n getTermsQueryOptions,\n type TermsConsentItem,\n termsConsentMutationOptions,\n} from '#frontend/queries/terms.ts';\n\nconst TermsSearchSchema = OAuthSearchSchema.extend({\n redirect: z.string().optional(),\n lang: z.string().optional(),\n mode: z.enum(['normal', 'complete_registration']).optional(),\n registration_token: z.string().optional(),\n});\n\nfunction TermsError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n window.location.href = '/login';\n }}\n />\n );\n}\n\nexport const Route = createFileRoute('/terms/')({\n component: Terms,\n errorComponent: TermsError,\n validateSearch: TermsSearchSchema,\n beforeLoad: async ({ context, search }) => {\n // When mode=complete_registration, user is completing OAuth signup.\n // The registration_token in the URL references a DB record with OAuth data.\n // Skip auth check — backend will validate the token on consent submission.\n if (search.mode === 'complete_registration') {\n return;\n }\n\n // Standard mode: requires authentication\n // Users are redirected here after OAuth login when they need to agree to terms\n if (!context.user) {\n throw redirect({\n to: '/login',\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n await Promise.all([\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n ]);\n },\n});\n\nfunction Terms() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const lang = search.lang ?? i18n.language;\n const termsQuery = useSuspenseQuery(getTermsQueryOptions(lang));\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n // Separate explicit and implicit terms\n const explicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'explicit'),\n [termsQuery.data.terms],\n );\n\n const implicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'implicit'),\n [termsQuery.data.terms],\n );\n\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Schema only validates explicit terms (implicit are auto-agreed)\n const termsSchema = useMemo(\n () =>\n z.object({\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [t, explicitTerms],\n );\n\n type TermsFormValues = TermsConsentsField;\n\n const {\n handleSubmit,\n control,\n setValue,\n formState: { errors },\n } = useForm<TermsFormValues>({\n defaultValues: {\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n !!(term.userConsent?.agreed && !term.userConsent.requiresUpdate),\n ]),\n ),\n },\n resolver: standardSchemaResolver(termsSchema),\n mode: 'onChange',\n });\n\n const consentMutation = useMutation({\n ...termsConsentMutationOptions,\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n await queryClient.fetchQuery(getSessionQueryOptions);\n await tick();\n\n // Redirect after successful consent\n if (search.redirect) {\n window.location.href = search.redirect;\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n });\n\n const onSubmit = (values: TermsFormValues) => {\n const explicitConsents = Object.entries(\n values.termsConsents,\n ).map<TermsConsentItem>(([termsId, agreed]) => ({\n termsId,\n agreed,\n consentType: 'explicit',\n }));\n\n const implicitConsents = implicitTerms.map<TermsConsentItem>((term) => ({\n termsId: term.id,\n agreed: true,\n consentType: 'implicit',\n }));\n\n consentMutation.mutate({\n consents: [...explicitConsents, ...implicitConsents],\n ...(search.registration_token && {\n registrationToken: search.registration_token,\n }),\n });\n };\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader title={t('terms.title')} />\n\n {implicitNotice && (\n <div className=\"text-center text-base-content/60 text-xs\">\n <div\n className=\"prose prose-sm text-xs! **:text-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <div className=\"divider text-xs\">AND</div>\n )}\n\n <form onSubmit={handleSubmit(onSubmit)}>\n {/* Explicit terms with checkboxes */}\n {hasExplicitTerms && (\n <div className=\"mb-6\">\n <TermsCheckboxList\n control={control}\n disabled={consentMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n </div>\n )}\n\n {/* Error message */}\n {consentMutation.isError && (\n <Alert className=\"mb-4\" icon={WarningIcon} type=\"error\">\n {t('terms.error.submitFailed')}\n </Alert>\n )}\n\n {/* Submit button */}\n <button\n className=\"btn btn-primary btn-block h-10 font-semibold text-[14px]\"\n disabled={consentMutation.isPending}\n type=\"submit\"\n >\n {consentMutation.isPending ? (\n <span className=\"loading loading-spinner loading-sm\" />\n ) : (\n <>\n <CheckIcon className=\"size-4\" weight=\"bold\" />\n {t('terms.submit')}\n </>\n )}\n </button>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"terms-CPEuXMaO.js","names":["standardSchemaResolver","CheckIcon","WarningIcon","useMutation","useQueryClient","useSuspenseQuery","useRouter","useMemo","useForm","useTranslation","z","PageHeader","TermsCheckboxList","Alert","PageLayout","tick","appConfigQueryOptions","getSessionQueryOptions","getTermsQueryOptions","termsConsentMutationOptions","Route","Terms","t","i18n","router","queryClient","search","useSearch","data","configData","lang","language","termsQuery","implicitNotice","registration","signup_notice","fallback_language","explicitTerms","terms","filter","term","consentMode","implicitTerms","hasExplicitTerms","length","termsSchema","object","termsConsents","Object","fromEntries","map","id","required","literal","message","boolean","TermsFormValues","TermsConsentsField","handleSubmit","control","setValue","formState","errors","defaultValues","userConsent","agreed","requiresUpdate","resolver","mode","consentMutation","onSuccess","invalidateQueries","queryKey","fetchQuery","redirect","window","location","href","navigate","to","onSubmit","values","explicitConsents","entries","TermsConsentItem","termsId","consentType","implicitConsents","mutate","consents","registration_token","registrationToken","__html","isPending","isError","component"],"sources":["../../../frontend/src/routes/terms/index.tsx?tsr-split=component"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { CheckIcon, WarningIcon } from '@phosphor-icons/react';\nimport {\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 { useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport {\n TermsCheckboxList,\n type TermsConsentsField,\n} from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { OAuthSearchSchema } from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n getTermsQueryOptions,\n type TermsConsentItem,\n termsConsentMutationOptions,\n} from '#frontend/queries/terms.ts';\n\nconst TermsSearchSchema = OAuthSearchSchema.extend({\n redirect: z.string().optional(),\n lang: z.string().optional(),\n mode: z.enum(['normal', 'complete_registration']).optional(),\n registration_token: z.string().optional(),\n});\n\nfunction TermsError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n window.location.href = '/login';\n }}\n />\n );\n}\n\nexport const Route = createFileRoute('/terms/')({\n component: Terms,\n errorComponent: TermsError,\n validateSearch: TermsSearchSchema,\n beforeLoad: async ({ context, search }) => {\n // When mode=complete_registration, user is completing OAuth signup.\n // The registration_token in the URL references a DB record with OAuth data.\n // Skip auth check — backend will validate the token on consent submission.\n if (search.mode === 'complete_registration') {\n return;\n }\n\n // Standard mode: requires authentication\n // Users are redirected here after OAuth login when they need to agree to terms\n if (!context.user) {\n throw redirect({\n to: '/login',\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n await Promise.all([\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n ]);\n },\n});\n\nfunction Terms() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const lang = search.lang ?? i18n.language;\n const termsQuery = useSuspenseQuery(getTermsQueryOptions(lang));\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n // Separate explicit and implicit terms\n const explicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'explicit'),\n [termsQuery.data.terms],\n );\n\n const implicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'implicit'),\n [termsQuery.data.terms],\n );\n\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Schema only validates explicit terms (implicit are auto-agreed)\n const termsSchema = useMemo(\n () =>\n z.object({\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [t, explicitTerms],\n );\n\n type TermsFormValues = TermsConsentsField;\n\n const {\n handleSubmit,\n control,\n setValue,\n formState: { errors },\n } = useForm<TermsFormValues>({\n defaultValues: {\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n !!(term.userConsent?.agreed && !term.userConsent.requiresUpdate),\n ]),\n ),\n },\n resolver: standardSchemaResolver(termsSchema),\n mode: 'onChange',\n });\n\n const consentMutation = useMutation({\n ...termsConsentMutationOptions,\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n await queryClient.fetchQuery(getSessionQueryOptions);\n await tick();\n\n // Redirect after successful consent\n if (search.redirect) {\n window.location.href = search.redirect;\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n });\n\n const onSubmit = (values: TermsFormValues) => {\n const explicitConsents = Object.entries(\n values.termsConsents,\n ).map<TermsConsentItem>(([termsId, agreed]) => ({\n termsId,\n agreed,\n consentType: 'explicit',\n }));\n\n const implicitConsents = implicitTerms.map<TermsConsentItem>((term) => ({\n termsId: term.id,\n agreed: true,\n consentType: 'implicit',\n }));\n\n consentMutation.mutate({\n consents: [...explicitConsents, ...implicitConsents],\n ...(search.registration_token && {\n registrationToken: search.registration_token,\n }),\n });\n };\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader title={t('terms.title')} />\n\n {implicitNotice && (\n <div className=\"text-center text-base-content/60 text-xs\">\n <div\n className=\"prose prose-sm text-xs! **:text-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <div className=\"divider text-xs\">AND</div>\n )}\n\n <form onSubmit={handleSubmit(onSubmit)}>\n {/* Explicit terms with checkboxes */}\n {hasExplicitTerms && (\n <div className=\"mb-6\">\n <TermsCheckboxList\n control={control}\n disabled={consentMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n </div>\n )}\n\n {/* Error message */}\n {consentMutation.isError && (\n <Alert className=\"mb-4\" icon={WarningIcon} type=\"error\">\n {t('terms.error.submitFailed')}\n </Alert>\n )}\n\n {/* Submit button */}\n <button\n className=\"btn btn-primary btn-block h-10 font-semibold text-[14px]\"\n disabled={consentMutation.isPending}\n type=\"submit\"\n >\n {consentMutation.isPending ? (\n <span className=\"loading loading-spinner loading-sm\" />\n ) : (\n <>\n <CheckIcon className=\"size-4\" weight=\"bold\" />\n {t('terms.submit')}\n </>\n )}\n </button>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{o as e}from"./IconBase.es-d5KP98Ac.js";import{P as t}from"./index-CgkA6nnx.js";var n=e();function r(e){return(0,n.jsx)(t,{...e,onUnauthorized:()=>{window.location.href=`/login`}})}export{r as errorComponent};
2
+ //# sourceMappingURL=terms-fwH7_EJ0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"terms-DcpL_XUt.js","names":["RouteErrorFallback","TermsError","props","ErrorComponentProps","window","location","href","errorComponent"],"sources":["../../../frontend/src/routes/terms/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { CheckIcon, WarningIcon } from '@phosphor-icons/react';\nimport {\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 { useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport {\n TermsCheckboxList,\n type TermsConsentsField,\n} from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { OAuthSearchSchema } from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n getTermsQueryOptions,\n type TermsConsentItem,\n termsConsentMutationOptions,\n} from '#frontend/queries/terms.ts';\n\nconst TermsSearchSchema = OAuthSearchSchema.extend({\n redirect: z.string().optional(),\n lang: z.string().optional(),\n mode: z.enum(['normal', 'complete_registration']).optional(),\n registration_token: z.string().optional(),\n});\n\nfunction TermsError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n window.location.href = '/login';\n }}\n />\n );\n}\n\nexport const Route = createFileRoute('/terms/')({\n component: Terms,\n errorComponent: TermsError,\n validateSearch: TermsSearchSchema,\n beforeLoad: async ({ context, search }) => {\n // When mode=complete_registration, user is completing OAuth signup.\n // The registration_token in the URL references a DB record with OAuth data.\n // Skip auth check — backend will validate the token on consent submission.\n if (search.mode === 'complete_registration') {\n return;\n }\n\n // Standard mode: requires authentication\n // Users are redirected here after OAuth login when they need to agree to terms\n if (!context.user) {\n throw redirect({\n to: '/login',\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n await Promise.all([\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n ]);\n },\n});\n\nfunction Terms() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const lang = search.lang ?? i18n.language;\n const termsQuery = useSuspenseQuery(getTermsQueryOptions(lang));\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n // Separate explicit and implicit terms\n const explicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'explicit'),\n [termsQuery.data.terms],\n );\n\n const implicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'implicit'),\n [termsQuery.data.terms],\n );\n\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Schema only validates explicit terms (implicit are auto-agreed)\n const termsSchema = useMemo(\n () =>\n z.object({\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [t, explicitTerms],\n );\n\n type TermsFormValues = TermsConsentsField;\n\n const {\n handleSubmit,\n control,\n setValue,\n formState: { errors },\n } = useForm<TermsFormValues>({\n defaultValues: {\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n !!(term.userConsent?.agreed && !term.userConsent.requiresUpdate),\n ]),\n ),\n },\n resolver: standardSchemaResolver(termsSchema),\n mode: 'onChange',\n });\n\n const consentMutation = useMutation({\n ...termsConsentMutationOptions,\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n await queryClient.fetchQuery(getSessionQueryOptions);\n await tick();\n\n // Redirect after successful consent\n if (search.redirect) {\n window.location.href = search.redirect;\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n });\n\n const onSubmit = (values: TermsFormValues) => {\n const explicitConsents = Object.entries(\n values.termsConsents,\n ).map<TermsConsentItem>(([termsId, agreed]) => ({\n termsId,\n agreed,\n consentType: 'explicit',\n }));\n\n const implicitConsents = implicitTerms.map<TermsConsentItem>((term) => ({\n termsId: term.id,\n agreed: true,\n consentType: 'implicit',\n }));\n\n consentMutation.mutate({\n consents: [...explicitConsents, ...implicitConsents],\n ...(search.registration_token && {\n registrationToken: search.registration_token,\n }),\n });\n };\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader title={t('terms.title')} />\n\n {implicitNotice && (\n <div className=\"text-center text-base-content/60 text-xs\">\n <div\n className=\"prose prose-sm text-xs! **:text-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <div className=\"divider text-xs\">AND</div>\n )}\n\n <form onSubmit={handleSubmit(onSubmit)}>\n {/* Explicit terms with checkboxes */}\n {hasExplicitTerms && (\n <div className=\"mb-6\">\n <TermsCheckboxList\n control={control}\n disabled={consentMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n </div>\n )}\n\n {/* Error message */}\n {consentMutation.isError && (\n <Alert className=\"mb-4\" icon={WarningIcon} type=\"error\">\n {t('terms.error.submitFailed')}\n </Alert>\n )}\n\n {/* Submit button */}\n <button\n className=\"btn btn-primary btn-block h-10 font-semibold text-[14px]\"\n disabled={consentMutation.isPending}\n type=\"submit\"\n >\n {consentMutation.isPending ? (\n <span className=\"loading loading-spinner loading-sm\" />\n ) : (\n <>\n <CheckIcon className=\"size-4\" weight=\"bold\" />\n {t('terms.submit')}\n </>\n )}\n </button>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"gGA0CA,SAASC,EAAWC,EAA4B,CAC9C,OACE,EAAA,EAAA,KAAC,EAAD,CACE,GAAIA,EACJ,mBAAsB,CACpBE,OAAOC,SAASC,KAAO,QACzB,CAAE,CAAA,CAGR"}
1
+ {"version":3,"file":"terms-fwH7_EJ0.js","names":["RouteErrorFallback","TermsError","props","ErrorComponentProps","window","location","href","errorComponent"],"sources":["../../../frontend/src/routes/terms/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { CheckIcon, WarningIcon } from '@phosphor-icons/react';\nimport {\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 { useMemo } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport {\n TermsCheckboxList,\n type TermsConsentsField,\n} from '#frontend/components/terms/terms-checkbox-list.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { OAuthSearchSchema } from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n getTermsQueryOptions,\n type TermsConsentItem,\n termsConsentMutationOptions,\n} from '#frontend/queries/terms.ts';\n\nconst TermsSearchSchema = OAuthSearchSchema.extend({\n redirect: z.string().optional(),\n lang: z.string().optional(),\n mode: z.enum(['normal', 'complete_registration']).optional(),\n registration_token: z.string().optional(),\n});\n\nfunction TermsError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n window.location.href = '/login';\n }}\n />\n );\n}\n\nexport const Route = createFileRoute('/terms/')({\n component: Terms,\n errorComponent: TermsError,\n validateSearch: TermsSearchSchema,\n beforeLoad: async ({ context, search }) => {\n // When mode=complete_registration, user is completing OAuth signup.\n // The registration_token in the URL references a DB record with OAuth data.\n // Skip auth check — backend will validate the token on consent submission.\n if (search.mode === 'complete_registration') {\n return;\n }\n\n // Standard mode: requires authentication\n // Users are redirected here after OAuth login when they need to agree to terms\n if (!context.user) {\n throw redirect({\n to: '/login',\n });\n }\n },\n loaderDeps: ({ search }) => ({\n lang: search.lang,\n }),\n loader: async ({ context, deps }) => {\n const lang = deps.lang ?? context.i18n.language;\n await Promise.all([\n context.queryClient.ensureQueryData(getTermsQueryOptions(lang)),\n context.queryClient.ensureQueryData(appConfigQueryOptions),\n ]);\n },\n});\n\nfunction Terms() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n const lang = search.lang ?? i18n.language;\n const termsQuery = useSuspenseQuery(getTermsQueryOptions(lang));\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n\n // Separate explicit and implicit terms\n const explicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'explicit'),\n [termsQuery.data.terms],\n );\n\n const implicitTerms = useMemo(\n () =>\n termsQuery.data.terms.filter((term) => term.consentMode === 'implicit'),\n [termsQuery.data.terms],\n );\n\n const hasExplicitTerms = explicitTerms.length > 0;\n\n // Schema only validates explicit terms (implicit are auto-agreed)\n const termsSchema = useMemo(\n () =>\n z.object({\n termsConsents: z.object(\n Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n term.required\n ? z.literal(true, {\n message: t('validation.terms.required'),\n })\n : z.boolean(),\n ]),\n ),\n ),\n }),\n [t, explicitTerms],\n );\n\n type TermsFormValues = TermsConsentsField;\n\n const {\n handleSubmit,\n control,\n setValue,\n formState: { errors },\n } = useForm<TermsFormValues>({\n defaultValues: {\n termsConsents: Object.fromEntries(\n explicitTerms.map((term) => [\n term.id,\n !!(term.userConsent?.agreed && !term.userConsent.requiresUpdate),\n ]),\n ),\n },\n resolver: standardSchemaResolver(termsSchema),\n mode: 'onChange',\n });\n\n const consentMutation = useMutation({\n ...termsConsentMutationOptions,\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n await queryClient.fetchQuery(getSessionQueryOptions);\n await tick();\n\n // Redirect after successful consent\n if (search.redirect) {\n window.location.href = search.redirect;\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n });\n\n const onSubmit = (values: TermsFormValues) => {\n const explicitConsents = Object.entries(\n values.termsConsents,\n ).map<TermsConsentItem>(([termsId, agreed]) => ({\n termsId,\n agreed,\n consentType: 'explicit',\n }));\n\n const implicitConsents = implicitTerms.map<TermsConsentItem>((term) => ({\n termsId: term.id,\n agreed: true,\n consentType: 'implicit',\n }));\n\n consentMutation.mutate({\n consents: [...explicitConsents, ...implicitConsents],\n ...(search.registration_token && {\n registrationToken: search.registration_token,\n }),\n });\n };\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader title={t('terms.title')} />\n\n {implicitNotice && (\n <div className=\"text-center text-base-content/60 text-xs\">\n <div\n className=\"prose prose-sm text-xs! **:text-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n </div>\n )}\n\n {implicitNotice && hasExplicitTerms && (\n <div className=\"divider text-xs\">AND</div>\n )}\n\n <form onSubmit={handleSubmit(onSubmit)}>\n {/* Explicit terms with checkboxes */}\n {hasExplicitTerms && (\n <div className=\"mb-6\">\n <TermsCheckboxList\n control={control}\n disabled={consentMutation.isPending}\n errors={errors}\n setValue={setValue}\n terms={explicitTerms}\n />\n </div>\n )}\n\n {/* Error message */}\n {consentMutation.isError && (\n <Alert className=\"mb-4\" icon={WarningIcon} type=\"error\">\n {t('terms.error.submitFailed')}\n </Alert>\n )}\n\n {/* Submit button */}\n <button\n className=\"btn btn-primary btn-block h-10 font-semibold text-[14px]\"\n disabled={consentMutation.isPending}\n type=\"submit\"\n >\n {consentMutation.isPending ? (\n <span className=\"loading loading-spinner loading-sm\" />\n ) : (\n <>\n <CheckIcon className=\"size-4\" weight=\"bold\" />\n {t('terms.submit')}\n </>\n )}\n </button>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"gGA0CA,SAASC,EAAWC,EAA4B,CAC9C,OACE,EAAA,EAAA,KAAC,EAAD,CACE,GAAIA,EACJ,mBAAsB,CACpBE,OAAOC,SAASC,KAAO,QACzB,CAAE,CAAA,CAGR"}
@@ -1,2 +1,2 @@
1
- import{n as e,o as t,r as n,s as r,t as i,u as a}from"./IconBase.es-d5KP98Ac.js";import{f as o,h as s,ut as c}from"./use-theme-cVUDAjtt.js";import{t as l}from"./page-layout-C475gs09.js";import{a as u,i as d,n as f,r as p,t as m}from"./use-totp-setup-9fktn_he.js";import{t as h}from"./ShieldCheck.es-CscPsYbC.js";import{A as g,M as _,P as v,S as y,d as b,j as x}from"./index-BmfaaNx6.js";import{t as S}from"./page-header-BYMFSGfT.js";import{t as C}from"./alert-CSXqgDVi.js";import{t as w}from"./promise-OpBtq8tG.js";import{t as T}from"./footer-link-Ib1Hd-fr.js";var E=a(r(),1),D=new Map([[`bold`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M168.49,104.49,145,128l23.52,23.51a12,12,0,0,1-17,17L128,145l-23.51,23.52a12,12,0,0,1-17-17L111,128,87.51,104.49a12,12,0,0,1,17-17L128,111l23.51-23.52a12,12,0,0,1,17,17ZM236,128A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Z`}))],[`duotone`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z`,opacity:`0.2`}),E.createElement(`path`,{d:`M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z`}))],[`fill`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm37.66,130.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32L139.31,128Z`}))],[`light`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M164.24,100.24,136.48,128l27.76,27.76a6,6,0,1,1-8.48,8.48L128,136.48l-27.76,27.76a6,6,0,0,1-8.48-8.48L119.52,128,91.76,100.24a6,6,0,0,1,8.48-8.48L128,119.52l27.76-27.76a6,6,0,0,1,8.48,8.48ZM230,128A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z`}))],[`regular`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z`}))],[`thin`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M162.83,98.83,133.66,128l29.17,29.17a4,4,0,0,1-5.66,5.66L128,133.66,98.83,162.83a4,4,0,0,1-5.66-5.66L122.34,128,93.17,98.83a4,4,0,0,1,5.66-5.66L128,122.34l29.17-29.17a4,4,0,1,1,5.66,5.66ZM228,128A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z`}))]]),O=E.forwardRef((e,t)=>E.createElement(i,{ref:t,...e,weights:D}));O.displayName=`XCircleIcon`;var k=t(),A={TOTP_ALREADY_ENABLED:`TOTP_ALREADY_ENABLED`,TOTP_NOT_SETUP:`TOTP_NOT_SETUP`,INVALID_TOTP_CODE:`INVALID_TOTP_CODE`,UNAUTHORIZED:`UNAUTHORIZED`,SECOND_FACTOR_SESSION_EXPIRED:`SECOND_FACTOR_SESSION_EXPIRED`},j=5;function M(){let{t}=n(),r=s(),i=c(),a=b.useSearch(),[D,M]=(0,E.useState)(`generic`),[N,P]=(0,E.useState)(j),F=(0,E.useCallback)(()=>{r.navigate({to:`/login`,search:x(a)})},[r,a]),I=(0,E.useCallback)(()=>{r.navigate({to:`/profile`})},[r]),{step:L,setupData:R,recoveryCodes:z,isSetupPending:B,isVerifyPending:V,isConfirmPending:H,startSetup:U,verify:W,goToQr:G,goToVerify:K,confirmRecoveryCodes:q}=m({autoStart:!0,onSetupError:(0,E.useCallback)(t=>{if(t instanceof e)switch(t.code){case A.TOTP_ALREADY_ENABLED:M(`already_enabled`);break;case A.UNAUTHORIZED:case A.SECOND_FACTOR_SESSION_EXPIRED:M(`session_expired`),P(j);break;default:M(`generic`)}else M(`generic`)},[]),onVerifySuccess:(0,E.useCallback)(async e=>{await w()},[]),onConfirmSuccess:(0,E.useCallback)(async e=>{i.setQueryData(y.queryKey,{user:e.user}),await w(),_(a)?window.location.href=g(a):r.navigate({to:`/profile`})},[i,r,a]),onVerifyError:t=>{if(t instanceof e)switch(t.code){case A.TOTP_ALREADY_ENABLED:I();break;case A.UNAUTHORIZED:case A.SECOND_FACTOR_SESSION_EXPIRED:M(`session_expired`),P(j);break;case A.TOTP_NOT_SETUP:U();break}}});(0,E.useEffect)(()=>{if(D!==`session_expired`||L!==`error`)return;let e=setInterval(()=>{P(t=>t<=1?(clearInterval(e),F(),0):t-1)},1e3);return()=>clearInterval(e)},[D,L,F]);let J=(0,E.useCallback)(async e=>{await W(e)},[W]);return L===`loading`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(`div`,{className:`flex justify-center py-8`,"data-testid":`totp-setup-loading`,children:(0,k.jsx)(`span`,{className:`loading loading-spinner loading-lg`})})]}):L===`error`?D===`session_expired`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsxs)(`div`,{className:`alert alert-warning mb-4`,"data-testid":`totp-setup-session-expired`,children:[(0,k.jsx)(v,{className:`size-5`,weight:`fill`}),(0,k.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,k.jsx)(`span`,{children:t(`setupTotp.error.expired`)}),(0,k.jsx)(`span`,{className:`text-sm opacity-80`,children:t(`setupTotp.redirecting`,{seconds:N})}),(0,k.jsx)(`button`,{className:`btn btn-sm btn-ghost mt-2 w-fit`,onClick:F,type:`button`,children:t(`setupTotp.redirectNow`)})]})]}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:x(a),text:``,to:`/login`})]}):D===`already_enabled`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(C,{className:`mb-4`,icon:u,type:`info`,children:t(`setupTotp.error.alreadyEnabled`)}),(0,k.jsx)(`button`,{className:`btn btn-primary btn-block`,onClick:I,type:`button`,children:t(`setupTotp.goToProfile`)}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:x(a),text:``,to:`/login`})]}):(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(C,{className:`mb-4`,icon:O,type:`error`,children:t(`setupTotp.error.setupFailed`)}),(0,k.jsx)(`button`,{className:`btn btn-primary btn-block`,disabled:B,onClick:U,type:`button`,children:t(`setupTotp.retry`)}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:x(a),text:``,to:`/login`})]}):L===`recovery`&&z.length>0?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.recoveryCodes.title`)}),(0,k.jsx)(d,{isLoading:H,onConfirm:q,recoveryCodes:z})]}):L===`qr`&&R?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsxs)(`div`,{className:`alert alert-info mb-4`,children:[(0,k.jsx)(h,{className:`size-5`,weight:`fill`}),(0,k.jsx)(`span`,{children:t(`setupTotp.required`)})]}),(0,k.jsx)(f,{onNext:K,setupData:R}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:x(a),text:``,to:`/login`})]}):(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.verifySubtitle`),title:t(`setupTotp.verifyTitle`)}),(0,k.jsx)(p,{isPending:V,onBack:G,onSubmit:J})]})}export{M as component};
2
- //# sourceMappingURL=totp-DB9WqW4V.js.map
1
+ import{n as e,o as t,r as n,s as r,t as i,u as a}from"./IconBase.es-d5KP98Ac.js";import{f as o,h as s,ut as c}from"./use-theme-cVUDAjtt.js";import{t as l}from"./page-layout-C475gs09.js";import{a as u,i as d,n as f,r as p,t as m}from"./use-totp-setup-Z96pOMXt.js";import{t as h}from"./ShieldCheck.es-CscPsYbC.js";import{A as g,F as _,M as v,N as y,S as b,d as x}from"./index-CgkA6nnx.js";import{t as S}from"./page-header-BYMFSGfT.js";import{t as C}from"./alert-CSXqgDVi.js";import{t as w}from"./promise-OpBtq8tG.js";import{t as T}from"./footer-link-Ib1Hd-fr.js";var E=a(r(),1),D=new Map([[`bold`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M168.49,104.49,145,128l23.52,23.51a12,12,0,0,1-17,17L128,145l-23.51,23.52a12,12,0,0,1-17-17L111,128,87.51,104.49a12,12,0,0,1,17-17L128,111l23.51-23.52a12,12,0,0,1,17,17ZM236,128A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Z`}))],[`duotone`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z`,opacity:`0.2`}),E.createElement(`path`,{d:`M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z`}))],[`fill`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm37.66,130.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32L139.31,128Z`}))],[`light`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M164.24,100.24,136.48,128l27.76,27.76a6,6,0,1,1-8.48,8.48L128,136.48l-27.76,27.76a6,6,0,0,1-8.48-8.48L119.52,128,91.76,100.24a6,6,0,0,1,8.48-8.48L128,119.52l27.76-27.76a6,6,0,0,1,8.48,8.48ZM230,128A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z`}))],[`regular`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z`}))],[`thin`,E.createElement(E.Fragment,null,E.createElement(`path`,{d:`M162.83,98.83,133.66,128l29.17,29.17a4,4,0,0,1-5.66,5.66L128,133.66,98.83,162.83a4,4,0,0,1-5.66-5.66L122.34,128,93.17,98.83a4,4,0,0,1,5.66-5.66L128,122.34l29.17-29.17a4,4,0,1,1,5.66,5.66ZM228,128A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z`}))]]),O=E.forwardRef((e,t)=>E.createElement(i,{ref:t,...e,weights:D}));O.displayName=`XCircleIcon`;var k=t(),A={TOTP_ALREADY_ENABLED:`TOTP_ALREADY_ENABLED`,TOTP_NOT_SETUP:`TOTP_NOT_SETUP`,INVALID_TOTP_CODE:`INVALID_TOTP_CODE`,UNAUTHORIZED:`UNAUTHORIZED`,SECOND_FACTOR_SESSION_EXPIRED:`SECOND_FACTOR_SESSION_EXPIRED`},j=5;function M(){let{t}=n(),r=s(),i=c(),a=x.useSearch(),[D,M]=(0,E.useState)(`generic`),[N,P]=(0,E.useState)(j),F=(0,E.useCallback)(()=>{r.navigate({to:`/login`,search:v(a)})},[r,a]),I=(0,E.useCallback)(()=>{r.navigate({to:`/profile`})},[r]),{step:L,setupData:R,recoveryCodes:z,isSetupPending:B,isVerifyPending:V,isConfirmPending:H,startSetup:U,verify:W,goToQr:G,goToVerify:K,confirmRecoveryCodes:q}=m({autoStart:!0,onSetupError:(0,E.useCallback)(t=>{if(t instanceof e)switch(t.code){case A.TOTP_ALREADY_ENABLED:M(`already_enabled`);break;case A.UNAUTHORIZED:case A.SECOND_FACTOR_SESSION_EXPIRED:M(`session_expired`),P(j);break;default:M(`generic`)}else M(`generic`)},[]),onVerifySuccess:(0,E.useCallback)(async e=>{await w()},[]),onConfirmSuccess:(0,E.useCallback)(async e=>{i.setQueryData(b.queryKey,{user:e.user}),await w(),y(a)?window.location.href=g(a):r.navigate({to:`/profile`})},[i,r,a]),onVerifyError:t=>{if(t instanceof e)switch(t.code){case A.TOTP_ALREADY_ENABLED:I();break;case A.UNAUTHORIZED:case A.SECOND_FACTOR_SESSION_EXPIRED:M(`session_expired`),P(j);break;case A.TOTP_NOT_SETUP:U();break}}});(0,E.useEffect)(()=>{if(D!==`session_expired`||L!==`error`)return;let e=setInterval(()=>{P(t=>t<=1?(clearInterval(e),F(),0):t-1)},1e3);return()=>clearInterval(e)},[D,L,F]);let J=(0,E.useCallback)(async e=>{await W(e)},[W]);return L===`loading`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(`div`,{className:`flex justify-center py-8`,"data-testid":`totp-setup-loading`,children:(0,k.jsx)(`span`,{className:`loading loading-spinner loading-lg`})})]}):L===`error`?D===`session_expired`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsxs)(`div`,{className:`alert alert-warning mb-4`,"data-testid":`totp-setup-session-expired`,children:[(0,k.jsx)(_,{className:`size-5`,weight:`fill`}),(0,k.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,k.jsx)(`span`,{children:t(`setupTotp.error.expired`)}),(0,k.jsx)(`span`,{className:`text-sm opacity-80`,children:t(`setupTotp.redirecting`,{seconds:N})}),(0,k.jsx)(`button`,{className:`btn btn-sm btn-ghost mt-2 w-fit`,onClick:F,type:`button`,children:t(`setupTotp.redirectNow`)})]})]}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:v(a),text:``,to:`/login`})]}):D===`already_enabled`?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(C,{className:`mb-4`,icon:u,type:`info`,children:t(`setupTotp.error.alreadyEnabled`)}),(0,k.jsx)(`button`,{className:`btn btn-primary btn-block`,onClick:I,type:`button`,children:t(`setupTotp.goToProfile`)}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:v(a),text:``,to:`/login`})]}):(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsx)(C,{className:`mb-4`,icon:O,type:`error`,children:t(`setupTotp.error.setupFailed`)}),(0,k.jsx)(`button`,{className:`btn btn-primary btn-block`,disabled:B,onClick:U,type:`button`,children:t(`setupTotp.retry`)}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:v(a),text:``,to:`/login`})]}):L===`recovery`&&z.length>0?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.recoveryCodes.title`)}),(0,k.jsx)(d,{isLoading:H,onConfirm:q,recoveryCodes:z})]}):L===`qr`&&R?(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.subtitle`),title:t(`setupTotp.title`)}),(0,k.jsxs)(`div`,{className:`alert alert-info mb-4`,children:[(0,k.jsx)(h,{className:`size-5`,weight:`fill`}),(0,k.jsx)(`span`,{children:t(`setupTotp.required`)})]}),(0,k.jsx)(f,{onNext:K,setupData:R}),(0,k.jsx)(T,{as:o,linkText:t(`setupTotp.backToLogin`),search:v(a),text:``,to:`/login`})]}):(0,k.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,k.jsx)(S,{subtitle:t(`setupTotp.verifySubtitle`),title:t(`setupTotp.verifyTitle`)}),(0,k.jsx)(p,{isPending:V,onBack:G,onSubmit:J})]})}export{M as component};
2
+ //# sourceMappingURL=totp-B1ebEiOW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totp-B1ebEiOW.js","names":["t","InfoIcon","ShieldCheckIcon","WarningCircleIcon","XCircleIcon","useQueryClient","Link","useRouter","useCallback","useEffect","useState","useTranslation","FooterLink","PageHeader","QrStep","RecoveryCodesStep","VerifyStep","Alert","PageLayout","useTotpSetup","TinyAuthError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","getSessionQueryOptions","ERROR_CODES","TOTP_ALREADY_ENABLED","TOTP_NOT_SETUP","INVALID_TOTP_CODE","UNAUTHORIZED","SECOND_FACTOR_SESSION_EXPIRED","const","Route","ErrorType","REDIRECT_COUNTDOWN_SECONDS","SetupTotp","t","router","queryClient","search","useSearch","errorType","setErrorType","redirectCountdown","setRedirectCountdown","redirectToLogin","navigate","to","redirectToProfile","handleSetupError","error","Error","code","handleVerifySuccess","_data","TotpSetupVerifyResponse","handleConfirmSuccess","data","TotpConfirmResponse","setQueryData","queryKey","user","window","location","href","step","setupData","recoveryCodes","isSetupPending","isVerifyPending","isConfirmPending","startSetup","verify","goToQr","goToVerify","confirmRecoveryCodes","autoStart","onSetupError","onVerifySuccess","onConfirmSuccess","onVerifyError","timer","setInterval","prev","clearInterval","handleVerify","seconds","length","component"],"sources":["../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/defs/XCircle.es.js","../../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@phosphor-icons/react/dist/csr/XCircle.es.js","../../../frontend/src/routes/setup/totp/index.tsx?tsr-split=component"],"sourcesContent":["import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M168.49,104.49,145,128l23.52,23.51a12,12,0,0,1-17,17L128,145l-23.51,23.52a12,12,0,0,1-17-17L111,128,87.51,104.49a12,12,0,0,1,17-17L128,111l23.51-23.52a12,12,0,0,1,17,17ZM236,128A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm37.66,130.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M164.24,100.24,136.48,128l27.76,27.76a6,6,0,1,1-8.48,8.48L128,136.48l-27.76,27.76a6,6,0,0,1-8.48-8.48L119.52,128,91.76,100.24a6,6,0,0,1,8.48-8.48L128,119.52l27.76-27.76a6,6,0,0,1,8.48,8.48ZM230,128A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M162.83,98.83,133.66,128l29.17,29.17a4,4,0,0,1-5.66,5.66L128,133.66,98.83,162.83a4,4,0,0,1-5.66-5.66L122.34,128,93.17,98.83a4,4,0,0,1,5.66-5.66L128,122.34l29.17-29.17a4,4,0,1,1,5.66,5.66ZM228,128A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport a from \"../defs/XCircle.es.js\";\nconst o = e.forwardRef((r, c) => /* @__PURE__ */ e.createElement(t, { ref: c, ...r, weights: a }));\no.displayName = \"XCircleIcon\";\nconst s = o;\nexport {\n s as XCircle,\n o as XCircleIcon\n};\n","import {\n InfoIcon,\n ShieldCheckIcon,\n WarningCircleIcon,\n XCircleIcon,\n} from '@phosphor-icons/react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { FooterLink } from '#frontend/components/auth/footer-link.tsx';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\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 { Alert } from '#frontend/components/ui/alert.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { useTotpSetup } from '#frontend/features/totp/use-totp-setup.ts';\nimport { TinyAuthError } 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 type {\n TotpConfirmResponse,\n TotpSetupVerifyResponse,\n} from '#frontend/queries/totp.ts';\n\n/** Error codes from backend */\nconst ERROR_CODES = {\n TOTP_ALREADY_ENABLED: 'TOTP_ALREADY_ENABLED',\n TOTP_NOT_SETUP: 'TOTP_NOT_SETUP',\n INVALID_TOTP_CODE: 'INVALID_TOTP_CODE',\n UNAUTHORIZED: 'UNAUTHORIZED',\n SECOND_FACTOR_SESSION_EXPIRED: 'SECOND_FACTOR_SESSION_EXPIRED',\n} as const;\n\nconst SearchSchema = OAuthSearchSchema;\n\nexport const Route = createFileRoute('/setup/totp/')({\n component: SetupTotp,\n validateSearch: SearchSchema,\n});\n\ntype ErrorType = 'generic' | 'already_enabled' | 'session_expired';\n\n/** Auto redirect countdown seconds */\nconst REDIRECT_COUNTDOWN_SECONDS = 5;\n\nfunction SetupTotp() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const [errorType, setErrorType] = useState<ErrorType>('generic');\n const [redirectCountdown, setRedirectCountdown] = useState(\n REDIRECT_COUNTDOWN_SECONDS,\n );\n\n const redirectToLogin = useCallback(() => {\n router.navigate({\n to: '/login',\n search: extractOAuthParams(search),\n });\n }, [router, search]);\n\n const redirectToProfile = useCallback(() => {\n router.navigate({ to: '/profile' });\n }, [router]);\n\n const handleSetupError = useCallback((error: Error) => {\n if (error instanceof TinyAuthError) {\n switch (error.code) {\n case ERROR_CODES.TOTP_ALREADY_ENABLED:\n setErrorType('already_enabled');\n break;\n case ERROR_CODES.UNAUTHORIZED:\n case ERROR_CODES.SECOND_FACTOR_SESSION_EXPIRED:\n setErrorType('session_expired');\n setRedirectCountdown(REDIRECT_COUNTDOWN_SECONDS);\n break;\n default:\n setErrorType('generic');\n }\n } else {\n setErrorType('generic');\n }\n }, []);\n\n const handleVerifySuccess = useCallback(\n async (_data: TotpSetupVerifyResponse) => {\n // Recovery codes step is handled by useTotpSetup hook\n // (step transitions to 'recovery' automatically)\n // Session update happens after confirm, not here\n await tick();\n },\n [],\n );\n\n const handleConfirmSuccess = useCallback(\n async (data: TotpConfirmResponse) => {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: data.user,\n });\n await tick();\n // Navigate after successful confirm\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n },\n [queryClient, router, search],\n );\n\n const {\n step,\n setupData,\n recoveryCodes,\n isSetupPending,\n isVerifyPending,\n isConfirmPending,\n startSetup,\n verify,\n goToQr,\n goToVerify,\n confirmRecoveryCodes,\n } = useTotpSetup({\n autoStart: true,\n onSetupError: handleSetupError,\n onVerifySuccess: handleVerifySuccess,\n onConfirmSuccess: handleConfirmSuccess,\n onVerifyError: (error) => {\n if (error instanceof TinyAuthError) {\n switch (error.code) {\n case ERROR_CODES.TOTP_ALREADY_ENABLED:\n redirectToProfile();\n break;\n case ERROR_CODES.UNAUTHORIZED:\n case ERROR_CODES.SECOND_FACTOR_SESSION_EXPIRED:\n setErrorType('session_expired');\n setRedirectCountdown(REDIRECT_COUNTDOWN_SECONDS);\n break;\n case ERROR_CODES.TOTP_NOT_SETUP:\n startSetup();\n break;\n }\n }\n },\n });\n\n // Auto redirect when session expires\n useEffect(() => {\n if (errorType !== 'session_expired' || step !== 'error') 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 }, [errorType, step, redirectToLogin]);\n\n const handleVerify = useCallback(\n async (code: string) => {\n await verify(code);\n },\n [verify],\n );\n\n // Loading state\n if (step === 'loading') {\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.title')}\n />\n <div\n className=\"flex justify-center py-8\"\n data-testid=\"totp-setup-loading\"\n >\n <span className=\"loading loading-spinner loading-lg\" />\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (step === 'error') {\n // Session expired error - show countdown and redirect\n if (errorType === 'session_expired') {\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.title')}\n />\n <div\n className=\"alert alert-warning mb-4\"\n data-testid=\"totp-setup-session-expired\"\n >\n <WarningCircleIcon className=\"size-5\" weight=\"fill\" />\n <div className=\"flex flex-col gap-1\">\n <span>{t('setupTotp.error.expired')}</span>\n <span className=\"text-sm opacity-80\">\n {t('setupTotp.redirecting', {\n seconds: redirectCountdown,\n })}\n </span>\n <button\n className=\"btn btn-sm btn-ghost mt-2 w-fit\"\n onClick={redirectToLogin}\n type=\"button\"\n >\n {t('setupTotp.redirectNow')}\n </button>\n </div>\n </div>\n <FooterLink\n as={Link}\n linkText={t('setupTotp.backToLogin')}\n search={extractOAuthParams(search)}\n text=\"\"\n to=\"/login\"\n />\n </PageLayout>\n );\n }\n\n // TOTP already enabled error - redirect to profile\n if (errorType === 'already_enabled') {\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.title')}\n />\n <Alert className=\"mb-4\" icon={InfoIcon} type=\"info\">\n {t('setupTotp.error.alreadyEnabled')}\n </Alert>\n <button\n className=\"btn btn-primary btn-block\"\n onClick={redirectToProfile}\n type=\"button\"\n >\n {t('setupTotp.goToProfile')}\n </button>\n <FooterLink\n as={Link}\n linkText={t('setupTotp.backToLogin')}\n search={extractOAuthParams(search)}\n text=\"\"\n to=\"/login\"\n />\n </PageLayout>\n );\n }\n\n // Generic error - show retry button\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.title')}\n />\n <Alert className=\"mb-4\" icon={XCircleIcon} type=\"error\">\n {t('setupTotp.error.setupFailed')}\n </Alert>\n <button\n className=\"btn btn-primary btn-block\"\n disabled={isSetupPending}\n onClick={startSetup}\n type=\"button\"\n >\n {t('setupTotp.retry')}\n </button>\n <FooterLink\n as={Link}\n linkText={t('setupTotp.backToLogin')}\n search={extractOAuthParams(search)}\n text=\"\"\n to=\"/login\"\n />\n </PageLayout>\n );\n }\n\n // Recovery codes step\n if (step === 'recovery' && recoveryCodes.length > 0) {\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.recoveryCodes.title')}\n />\n <RecoveryCodesStep\n isLoading={isConfirmPending}\n onConfirm={confirmRecoveryCodes}\n recoveryCodes={recoveryCodes}\n />\n </PageLayout>\n );\n }\n\n // QR code step\n if (step === 'qr' && setupData) {\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.subtitle')}\n title={t('setupTotp.title')}\n />\n\n <div className=\"alert alert-info mb-4\">\n <ShieldCheckIcon className=\"size-5\" weight=\"fill\" />\n <span>{t('setupTotp.required')}</span>\n </div>\n\n <QrStep onNext={goToVerify} setupData={setupData} />\n\n <FooterLink\n as={Link}\n linkText={t('setupTotp.backToLogin')}\n search={extractOAuthParams(search)}\n text=\"\"\n to=\"/login\"\n />\n </PageLayout>\n );\n }\n\n // Verify step\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('setupTotp.verifySubtitle')}\n title={t('setupTotp.verifyTitle')}\n />\n\n <VerifyStep\n isPending={isVerifyPending}\n onBack={goToQr}\n onSubmit={handleVerify}\n />\n </PageLayout>\n );\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{n as e,o as t,r as n,s as r,u as i}from"./IconBase.es-d5KP98Ac.js";import{f as a,h as o,ut as s}from"./use-theme-cVUDAjtt.js";import{t as c}from"./useMutation-Iu4AJCB4.js";import{t as l}from"./page-layout-C475gs09.js";import{c as u,o as d}from"./zod-BItJDQBQ.js";import{A as f,M as p,P as m,S as h,h as g,j as _}from"./index-BmfaaNx6.js";import{t as v}from"./page-header-BYMFSGfT.js";import{t as y}from"./promise-OpBtq8tG.js";import{r as b,t as x}from"./standard-schema-o4V-s4uY.js";import{t as S}from"./footer-link-Ib1Hd-fr.js";import{t as C}from"./submit-button-Xx6DwLyh.js";import{o as w}from"./totp-NlqqRp4a.js";import{t as T}from"./pin-input-BM1UizHr.js";var E=i(r()),D=t(),O={SECOND_FACTOR_SESSION_EXPIRED:`SECOND_FACTOR_SESSION_EXPIRED`,INVALID_TOTP_CODE:`INVALID_TOTP_CODE`,TOTP_NOT_ENABLED:`TOTP_NOT_ENABLED`},k=5;function A(){let{t}=n(),r=o(),i=s(),A=g.useSearch(),j=(0,E.useRef)(null),[M,N]=(0,E.useState)(!1),[P,F]=(0,E.useState)(k),I=(0,E.useMemo)(()=>d({code:u().length(6,t(`validation.totp.length`)).regex(/^\d{6}$/,t(`validation.totp.digits`))}),[t]),L=c({...w,onSuccess:async e=>{i.setQueryData(h.queryKey,{user:e.user}),await y(),p(A)?window.location.href=f(A):r.navigate({to:`/profile`})},onSettled:()=>{i.invalidateQueries({queryKey:h.queryKey})}}),{setValue:R,setError:z,handleSubmit:B,watch:V,formState:{errors:H}}=b({defaultValues:{code:``},resolver:x(I)}),U=V(`code`),W=(0,E.useCallback)(()=>{r.navigate({to:`/login`,search:_(A)})},[r,A]);(0,E.useEffect)(()=>{if(!M)return;let e=setInterval(()=>{F(t=>t<=1?(clearInterval(e),W(),0):t-1)},1e3);return()=>clearInterval(e)},[M,W]);let G=async n=>{try{await L.mutateAsync(n)}catch(n){if(n instanceof e)switch(n.code){case O.SECOND_FACTOR_SESSION_EXPIRED:N(!0),F(k);return;case O.TOTP_NOT_ENABLED:W();return;case O.INVALID_TOTP_CODE:z(`code`,{type:`manual`,message:t(`verifyTotp.error.invalid`)}),R(`code`,``),j.current?.focus();return}z(`code`,{type:`manual`,message:t(`verifyTotp.error.invalid`)}),R(`code`,``),j.current?.focus()}};return(0,D.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,D.jsx)(v,{subtitle:t(`verifyTotp.subtitle`),title:t(`verifyTotp.title`)}),M&&(0,D.jsxs)(`div`,{className:`alert alert-warning mb-4`,"data-testid":`totp-verify-session-expired`,children:[(0,D.jsx)(m,{className:`size-5`,weight:`fill`}),(0,D.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,D.jsx)(`span`,{children:t(`verifyTotp.error.expired`)}),(0,D.jsx)(`span`,{className:`text-sm opacity-80`,children:t(`verifyTotp.redirecting`,{seconds:P})}),(0,D.jsx)(`button`,{className:`btn btn-sm btn-ghost mt-2 w-fit`,onClick:W,type:`button`,children:t(`verifyTotp.redirectNow`)})]})]}),(0,D.jsxs)(`form`,{className:`flex flex-col gap-4`,onSubmit:B(G),children:[(0,D.jsx)(T,{autoFocus:!0,disabled:M,error:H.code,length:6,onChange:e=>R(`code`,e),onComplete:()=>B(G)(),ref:j,value:U}),M?(0,D.jsx)(`button`,{className:`btn btn-block btn-disabled mt-2`,disabled:!0,type:`button`,children:t(`verifyTotp.submit`)}):(0,D.jsx)(C,{className:`mt-2`,isPending:L.isPending,pendingText:t(`verifyTotp.submitting`),children:t(`verifyTotp.submit`)})]}),(0,D.jsx)(`div`,{className:`mt-4 text-center`,children:(0,D.jsx)(`button`,{className:`link link-info font-medium text-xs`,"data-testid":`totp-verify-recovery-link`,onClick:()=>r.navigate({to:`/verify/totp/recovery`,search:_(A)}),type:`button`,children:t(`verifyTotp.useRecoveryCode`)})}),(0,D.jsx)(S,{as:a,linkText:t(`verifyTotp.backToLogin`),search:_(A),text:``,to:`/login`})]})}export{A as component};
2
- //# sourceMappingURL=totp-UDT2P91H.js.map
1
+ import{n as e,o as t,r as n,s as r,u as i}from"./IconBase.es-d5KP98Ac.js";import{f as a,h as o,ut as s}from"./use-theme-cVUDAjtt.js";import{t as c}from"./useMutation-Biy2-pH_.js";import{t as l}from"./page-layout-C475gs09.js";import{c as u,o as d}from"./zod-BItJDQBQ.js";import{A as f,F as p,M as m,N as h,S as g,h as _}from"./index-CgkA6nnx.js";import{t as v}from"./page-header-BYMFSGfT.js";import{t as y}from"./promise-OpBtq8tG.js";import{r as b,t as x}from"./standard-schema-o4V-s4uY.js";import{t as S}from"./footer-link-Ib1Hd-fr.js";import{t as C}from"./submit-button-Xx6DwLyh.js";import{o as w}from"./totp-NlqqRp4a.js";import{t as T}from"./pin-input-BM1UizHr.js";var E=i(r()),D=t(),O={SECOND_FACTOR_SESSION_EXPIRED:`SECOND_FACTOR_SESSION_EXPIRED`,INVALID_TOTP_CODE:`INVALID_TOTP_CODE`,TOTP_NOT_ENABLED:`TOTP_NOT_ENABLED`},k=5;function A(){let{t}=n(),r=o(),i=s(),A=_.useSearch(),j=(0,E.useRef)(null),[M,N]=(0,E.useState)(!1),[P,F]=(0,E.useState)(k),I=(0,E.useMemo)(()=>d({code:u().length(6,t(`validation.totp.length`)).regex(/^\d{6}$/,t(`validation.totp.digits`))}),[t]),L=c({...w,onSuccess:async e=>{i.setQueryData(g.queryKey,{user:e.user}),await y(),h(A)?window.location.href=f(A):r.navigate({to:`/profile`})},onSettled:()=>{i.invalidateQueries({queryKey:g.queryKey})}}),{setValue:R,setError:z,handleSubmit:B,watch:V,formState:{errors:H}}=b({defaultValues:{code:``},resolver:x(I)}),U=V(`code`),W=(0,E.useCallback)(()=>{r.navigate({to:`/login`,search:m(A)})},[r,A]);(0,E.useEffect)(()=>{if(!M)return;let e=setInterval(()=>{F(t=>t<=1?(clearInterval(e),W(),0):t-1)},1e3);return()=>clearInterval(e)},[M,W]);let G=async n=>{try{await L.mutateAsync(n)}catch(n){if(n instanceof e)switch(n.code){case O.SECOND_FACTOR_SESSION_EXPIRED:N(!0),F(k);return;case O.TOTP_NOT_ENABLED:W();return;case O.INVALID_TOTP_CODE:z(`code`,{type:`manual`,message:t(`verifyTotp.error.invalid`)}),R(`code`,``),j.current?.focus();return}z(`code`,{type:`manual`,message:t(`verifyTotp.error.invalid`)}),R(`code`,``),j.current?.focus()}};return(0,D.jsxs)(l,{cardPadding:!0,maxWidth:`100`,children:[(0,D.jsx)(v,{subtitle:t(`verifyTotp.subtitle`),title:t(`verifyTotp.title`)}),M&&(0,D.jsxs)(`div`,{className:`alert alert-warning mb-4`,"data-testid":`totp-verify-session-expired`,children:[(0,D.jsx)(p,{className:`size-5`,weight:`fill`}),(0,D.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,D.jsx)(`span`,{children:t(`verifyTotp.error.expired`)}),(0,D.jsx)(`span`,{className:`text-sm opacity-80`,children:t(`verifyTotp.redirecting`,{seconds:P})}),(0,D.jsx)(`button`,{className:`btn btn-sm btn-ghost mt-2 w-fit`,onClick:W,type:`button`,children:t(`verifyTotp.redirectNow`)})]})]}),(0,D.jsxs)(`form`,{className:`flex flex-col gap-4`,onSubmit:B(G),children:[(0,D.jsx)(T,{autoFocus:!0,disabled:M,error:H.code,length:6,onChange:e=>R(`code`,e),onComplete:()=>B(G)(),ref:j,value:U}),M?(0,D.jsx)(`button`,{className:`btn btn-block btn-disabled mt-2`,disabled:!0,type:`button`,children:t(`verifyTotp.submit`)}):(0,D.jsx)(C,{className:`mt-2`,isPending:L.isPending,pendingText:t(`verifyTotp.submitting`),children:t(`verifyTotp.submit`)})]}),(0,D.jsx)(`div`,{className:`mt-4 text-center`,children:(0,D.jsx)(`button`,{className:`link link-info font-medium text-xs`,"data-testid":`totp-verify-recovery-link`,onClick:()=>r.navigate({to:`/verify/totp/recovery`,search:m(A)}),type:`button`,children:t(`verifyTotp.useRecoveryCode`)})}),(0,D.jsx)(S,{as:a,linkText:t(`verifyTotp.backToLogin`),search:m(A),text:``,to:`/login`})]})}export{A as component};
2
+ //# sourceMappingURL=totp-DWWZe-tJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totp-DWWZe-tJ.js","names":["standardSchemaResolver","WarningCircleIcon","useMutation","useQueryClient","Link","useRouter","useCallback","useEffect","useMemo","useRef","useState","useForm","useTranslation","z","FooterLink","PageHeader","SubmitButton","PinInput","PageLayout","TinyAuthError","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","getSessionQueryOptions","verifyTotpLoginMutationOptions","ERROR_CODES","SECOND_FACTOR_SESSION_EXPIRED","INVALID_TOTP_CODE","TOTP_NOT_ENABLED","const","Route","VerifyTotpFormValues","code","REDIRECT_COUNTDOWN_SECONDS","VerifyTotp","t","router","queryClient","search","useSearch","pinInputRef","PinInputRef","sessionExpired","setSessionExpired","redirectCountdown","setRedirectCountdown","verifySchema","object","string","length","regex","verifyMutation","onSuccess","data","setQueryData","queryKey","user","window","location","href","navigate","to","onSettled","invalidateQueries","setValue","setError","handleSubmit","watch","formState","errors","defaultValues","resolver","codeValue","redirectToLogin","timer","setInterval","prev","clearInterval","onSubmit","values","mutateAsync","error","type","message","current","focus","seconds","value","isPending","component"],"sources":["../../../frontend/src/routes/verify/totp/index.tsx?tsr-split=component"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport { WarningCircleIcon } from '@phosphor-icons/react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { FooterLink } from '#frontend/components/auth/footer-link.tsx';\nimport { PageHeader } from '#frontend/components/auth/page-header.tsx';\nimport { SubmitButton } from '#frontend/components/auth/submit-button.tsx';\nimport {\n PinInput,\n type PinInputRef,\n} from '#frontend/components/ui/pin-input.tsx';\nimport { PageLayout } from '#frontend/features/layout/page-layout.tsx';\nimport { TinyAuthError } 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 { verifyTotpLoginMutationOptions } 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_TOTP_CODE: 'INVALID_TOTP_CODE',\n TOTP_NOT_ENABLED: 'TOTP_NOT_ENABLED',\n} as const;\n\nconst SearchSchema = OAuthSearchSchema;\n\nexport const Route = createFileRoute('/verify/totp/')({\n component: VerifyTotp,\n validateSearch: SearchSchema,\n});\n\ntype VerifyTotpFormValues = {\n code: string;\n};\n\n/** Auto redirect countdown seconds */\nconst REDIRECT_COUNTDOWN_SECONDS = 5;\n\nfunction VerifyTotp() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const pinInputRef = useRef<PinInputRef>(null);\n\n const [sessionExpired, setSessionExpired] = useState(false);\n const [redirectCountdown, setRedirectCountdown] = useState(\n REDIRECT_COUNTDOWN_SECONDS,\n );\n\n const verifySchema = 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 verifyMutation = useMutation({\n ...verifyTotpLoginMutationOptions,\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 setValue,\n setError,\n handleSubmit,\n watch,\n formState: { errors },\n } = useForm<VerifyTotpFormValues>({\n defaultValues: {\n code: '',\n },\n resolver: standardSchemaResolver(verifySchema),\n });\n\n const codeValue = watch('code');\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: VerifyTotpFormValues) => {\n try {\n await verifyMutation.mutateAsync(values);\n } catch (error) {\n if (error instanceof TinyAuthError) {\n switch (error.code) {\n case ERROR_CODES.SECOND_FACTOR_SESSION_EXPIRED:\n // Session expired - show alert and start auto redirect\n setSessionExpired(true);\n setRedirectCountdown(REDIRECT_COUNTDOWN_SECONDS);\n return;\n\n case ERROR_CODES.TOTP_NOT_ENABLED:\n // TOTP not enabled - redirect to login\n redirectToLogin();\n return;\n\n case ERROR_CODES.INVALID_TOTP_CODE:\n // Invalid code - show specific error and clear input\n setError('code', {\n type: 'manual',\n message: t('verifyTotp.error.invalid'),\n });\n setValue('code', '');\n pinInputRef.current?.focus();\n return;\n }\n }\n\n // Generic error fallback\n setError('code', {\n type: 'manual',\n message: t('verifyTotp.error.invalid'),\n });\n setValue('code', '');\n pinInputRef.current?.focus();\n }\n };\n\n return (\n <PageLayout cardPadding maxWidth=\"100\">\n <PageHeader\n subtitle={t('verifyTotp.subtitle')}\n title={t('verifyTotp.title')}\n />\n\n {sessionExpired && (\n <div\n className=\"alert alert-warning mb-4\"\n data-testid=\"totp-verify-session-expired\"\n >\n <WarningCircleIcon className=\"size-5\" weight=\"fill\" />\n <div className=\"flex flex-col gap-1\">\n <span>{t('verifyTotp.error.expired')}</span>\n <span className=\"text-sm opacity-80\">\n {t('verifyTotp.redirecting', { seconds: redirectCountdown })}\n </span>\n <button\n className=\"btn btn-sm btn-ghost mt-2 w-fit\"\n onClick={redirectToLogin}\n type=\"button\"\n >\n {t('verifyTotp.redirectNow')}\n </button>\n </div>\n </div>\n )}\n\n <form className=\"flex flex-col gap-4\" onSubmit={handleSubmit(onSubmit)}>\n <PinInput\n autoFocus\n disabled={sessionExpired}\n error={errors.code}\n length={6}\n onChange={(value) => setValue('code', value)}\n onComplete={() => handleSubmit(onSubmit)()}\n ref={pinInputRef}\n value={codeValue}\n />\n\n {sessionExpired ? (\n <button\n className=\"btn btn-block btn-disabled mt-2\"\n disabled\n type=\"button\"\n >\n {t('verifyTotp.submit')}\n </button>\n ) : (\n <SubmitButton\n className=\"mt-2\"\n isPending={verifyMutation.isPending}\n pendingText={t('verifyTotp.submitting')}\n >\n {t('verifyTotp.submit')}\n </SubmitButton>\n )}\n </form>\n\n <div className=\"mt-4 text-center\">\n <button\n className=\"link link-info font-medium text-xs\"\n data-testid=\"totp-verify-recovery-link\"\n onClick={() =>\n router.navigate({\n to: '/verify/totp/recovery',\n search: extractOAuthParams(search),\n })\n }\n type=\"button\"\n >\n {t('verifyTotp.useRecoveryCode')}\n </button>\n </div>\n\n <FooterLink\n as={Link}\n linkText={t('verifyTotp.backToLogin')}\n search={extractOAuthParams(search)}\n text=\"\"\n to=\"/login\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import{o as e,r as t,s as n,t as r,u as i}from"./IconBase.es-d5KP98Ac.js";import{ut as a}from"./use-theme-cVUDAjtt.js";import{t as o}from"./useMutation-Iu4AJCB4.js";import{n as s,t as c}from"./Warning.es-BPpZIJYZ.js";import{c as l,o as u}from"./zod-BItJDQBQ.js";import{S as d}from"./index-BmfaaNx6.js";import{r as f,t as p}from"./standard-schema-o4V-s4uY.js";import{t as m}from"./submit-button-Xx6DwLyh.js";import{i as h,s as g,t as _}from"./totp-NlqqRp4a.js";import{t as v}from"./pin-input-BM1UizHr.js";var y=i(n(),1),b=new Map([[`bold`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z`}))],[`duotone`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,40V168H168V88H88V40Z`,opacity:`0.2`}),y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`fill`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-8,128H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`light`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,34H88a6,6,0,0,0-6,6V82H40a6,6,0,0,0-6,6V216a6,6,0,0,0,6,6H168a6,6,0,0,0,6-6V174h42a6,6,0,0,0,6-6V40A6,6,0,0,0,216,34ZM162,210H46V94H162Zm48-48H174V88a6,6,0,0,0-6-6H94V46H210Z`}))],[`regular`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`thin`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,36H88a4,4,0,0,0-4,4V84H40a4,4,0,0,0-4,4V216a4,4,0,0,0,4,4H168a4,4,0,0,0,4-4V172h44a4,4,0,0,0,4-4V40A4,4,0,0,0,216,36ZM164,212H44V92H164Zm48-48H172V88a4,4,0,0,0-4-4H92V44H212Z`}))]]),x=new Map([[`bold`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z`}))],[`duotone`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z`,opacity:`0.2`}),y.createElement(`path`,{d:`M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z`}))],[`fill`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z`}))],[`light`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M142,176a6,6,0,0,1-6,6,14,14,0,0,1-14-14V128a2,2,0,0,0-2-2,6,6,0,0,1,0-12,14,14,0,0,1,14,14v40a2,2,0,0,0,2,2A6,6,0,0,1,142,176ZM124,94a10,10,0,1,0-10-10A10,10,0,0,0,124,94Zm106,34A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z`}))],[`regular`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z`}))],[`thin`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M140,176a4,4,0,0,1-4,4,12,12,0,0,1-12-12V128a4,4,0,0,0-4-4,4,4,0,0,1,0-8,12,12,0,0,1,12,12v40a4,4,0,0,0,4,4A4,4,0,0,1,140,176ZM124,92a8,8,0,1,0-8-8A8,8,0,0,0,124,92Zm104,36A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z`}))]]),S=y.forwardRef((e,t)=>y.createElement(r,{ref:t,...e,weights:b}));S.displayName=`CopyIcon`;var C=y.forwardRef((e,t)=>y.createElement(r,{ref:t,...e,weights:x}));C.displayName=`InfoIcon`;var w=e();function T({recoveryCodes:e,onConfirm:n,isLoading:r=!1,className:i=``}){let{t:a}=t(),[o,l]=(0,y.useState)(!1),[u,d]=(0,y.useState)(!1),f=(0,y.useCallback)(async()=>{let t=e.join(`
1
+ import{o as e,r as t,s as n,t as r,u as i}from"./IconBase.es-d5KP98Ac.js";import{ut as a}from"./use-theme-cVUDAjtt.js";import{t as o}from"./useMutation-Biy2-pH_.js";import{n as s,t as c}from"./Warning.es-BPpZIJYZ.js";import{c as l,o as u}from"./zod-BItJDQBQ.js";import{S as d}from"./index-CgkA6nnx.js";import{r as f,t as p}from"./standard-schema-o4V-s4uY.js";import{t as m}from"./submit-button-Xx6DwLyh.js";import{i as h,s as g,t as _}from"./totp-NlqqRp4a.js";import{t as v}from"./pin-input-BM1UizHr.js";var y=i(n(),1),b=new Map([[`bold`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z`}))],[`duotone`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,40V168H168V88H88V40Z`,opacity:`0.2`}),y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`fill`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-8,128H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`light`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,34H88a6,6,0,0,0-6,6V82H40a6,6,0,0,0-6,6V216a6,6,0,0,0,6,6H168a6,6,0,0,0,6-6V174h42a6,6,0,0,0,6-6V40A6,6,0,0,0,216,34ZM162,210H46V94H162Zm48-48H174V88a6,6,0,0,0-6-6H94V46H210Z`}))],[`regular`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z`}))],[`thin`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M216,36H88a4,4,0,0,0-4,4V84H40a4,4,0,0,0-4,4V216a4,4,0,0,0,4,4H168a4,4,0,0,0,4-4V172h44a4,4,0,0,0,4-4V40A4,4,0,0,0,216,36ZM164,212H44V92H164Zm48-48H172V88a4,4,0,0,0-4-4H92V44H212Z`}))]]),x=new Map([[`bold`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z`}))],[`duotone`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z`,opacity:`0.2`}),y.createElement(`path`,{d:`M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z`}))],[`fill`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z`}))],[`light`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M142,176a6,6,0,0,1-6,6,14,14,0,0,1-14-14V128a2,2,0,0,0-2-2,6,6,0,0,1,0-12,14,14,0,0,1,14,14v40a2,2,0,0,0,2,2A6,6,0,0,1,142,176ZM124,94a10,10,0,1,0-10-10A10,10,0,0,0,124,94Zm106,34A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z`}))],[`regular`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z`}))],[`thin`,y.createElement(y.Fragment,null,y.createElement(`path`,{d:`M140,176a4,4,0,0,1-4,4,12,12,0,0,1-12-12V128a4,4,0,0,0-4-4,4,4,0,0,1,0-8,12,12,0,0,1,12,12v40a4,4,0,0,0,4,4A4,4,0,0,1,140,176ZM124,92a8,8,0,1,0-8-8A8,8,0,0,0,124,92Zm104,36A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z`}))]]),S=y.forwardRef((e,t)=>y.createElement(r,{ref:t,...e,weights:b}));S.displayName=`CopyIcon`;var C=y.forwardRef((e,t)=>y.createElement(r,{ref:t,...e,weights:x}));C.displayName=`InfoIcon`;var w=e();function T({recoveryCodes:e,onConfirm:n,isLoading:r=!1,className:i=``}){let{t:a}=t(),[o,l]=(0,y.useState)(!1),[u,d]=(0,y.useState)(!1),f=(0,y.useCallback)(async()=>{let t=e.join(`
2
2
  `);await navigator.clipboard.writeText(t),l(!0),setTimeout(()=>l(!1),2e3)},[e]);return(0,w.jsxs)(`div`,{className:`space-y-3 ${i}`,children:[(0,w.jsx)(`p`,{className:`text-center text-base-content/60 text-xs`,children:a(`setupTotp.recoveryCodes.description`)}),(0,w.jsx)(`div`,{className:`grid grid-cols-2 gap-2 rounded-lg bg-base-200 p-3`,"data-testid":`recovery-codes-grid`,children:e.map(e=>(0,w.jsx)(`code`,{className:`rounded bg-base-300 px-2 py-1 text-center font-mono text-sm`,children:e},e))}),(0,w.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg bg-warning/10 p-2.5 text-warning`,children:[(0,w.jsx)(c,{className:`mt-0.5 size-4 shrink-0`,weight:`fill`}),(0,w.jsx)(`p`,{className:`text-xs`,children:a(`setupTotp.recoveryCodes.warning`)})]}),(0,w.jsx)(`button`,{className:`btn btn-sm btn-outline btn-block gap-2`,onClick:f,type:`button`,children:o?(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(s,{className:`size-4`}),a(`setupTotp.recoveryCodes.copied`)]}):(0,w.jsxs)(w.Fragment,{children:[(0,w.jsx)(S,{className:`size-4`}),a(`setupTotp.recoveryCodes.copy`)]})}),(0,w.jsxs)(`label`,{className:`flex cursor-pointer items-center gap-2`,children:[(0,w.jsx)(`input`,{checked:u,className:`checkbox checkbox-sm`,"data-testid":`recovery-codes-confirm`,disabled:r,onChange:e=>d(e.target.checked),type:`checkbox`}),(0,w.jsx)(`span`,{className:`text-sm`,children:a(`setupTotp.recoveryCodes.confirmCheckbox`)})]}),(0,w.jsx)(`button`,{className:`btn btn-sm btn-primary btn-block`,"data-testid":`recovery-codes-submit`,disabled:!u||r,onClick:n,type:`button`,children:r?(0,w.jsx)(`span`,{className:`loading loading-spinner loading-sm`}):a(`setupTotp.recoveryCodes.confirm`)})]})}function E({onSubmit:e,onBack:n,isPending:r,invalidMessage:i,submitLabel:a,pendingText:o,backLabel:s,className:c=``}){let{t:d}=t(),h=(0,y.useRef)(null),{setValue:g,setError:_,handleSubmit:b,watch:x,formState:{errors:S}}=f({defaultValues:{code:``},resolver:p((0,y.useMemo)(()=>u({code:l().length(6,d(`validation.totp.length`)).regex(/^\d{6}$/,d(`validation.totp.digits`))}),[d]))}),C=x(`code`),T=(0,y.useCallback)(async t=>{try{await e(t.code)}catch{_(`code`,{type:`manual`,message:i??d(`setupTotp.error.invalid`)}),g(`code`,``),h.current?.focus()}},[i,e,_,g,d]);return(0,w.jsxs)(`div`,{className:c,children:[(0,w.jsxs)(`form`,{className:`flex flex-col gap-3`,onSubmit:b(T),children:[(0,w.jsx)(v,{autoFocus:!0,error:S.code,length:6,onChange:e=>g(`code`,e),onComplete:()=>b(T)(),ref:h,value:C}),(0,w.jsx)(m,{className:`btn-sm mt-1`,isPending:r,pendingText:o??d(`setupTotp.verifying`),children:a??d(`setupTotp.verify`)})]}),n&&(0,w.jsx)(`div`,{className:`mt-3 text-center`,children:(0,w.jsx)(`button`,{className:`btn btn-ghost btn-xs`,"data-testid":`totp-verify-back`,disabled:r,onClick:n,type:`button`,children:s??d(`setupTotp.back`)})})]})}function D({setupData:e,onNext:n,additionalActions:r,className:i=``}){let{t:a}=t();return(0,w.jsxs)(`div`,{className:`space-y-3 ${i}`,children:[(0,w.jsx)(`p`,{className:`text-center text-base-content/60 text-xs`,children:a(`setupTotp.qrDescription`)}),(0,w.jsx)(`div`,{className:`flex justify-center`,children:(0,w.jsx)(`img`,{alt:`TOTP QR Code`,className:`h-40 w-40 rounded-lg border`,src:e.qr_code})}),(0,w.jsxs)(`div`,{className:`collapse-arrow collapse bg-base-200`,children:[(0,w.jsx)(`input`,{type:`checkbox`}),(0,w.jsx)(`div`,{className:`collapse-title font-medium text-xs`,children:a(`setupTotp.manualEntry`)}),(0,w.jsx)(`div`,{className:`collapse-content`,children:(0,w.jsx)(`code`,{className:`block break-all rounded bg-base-300 p-1.5 text-xs`,children:e.secret})})]}),(0,w.jsx)(`button`,{className:`btn btn-sm btn-primary btn-block`,"data-testid":`totp-qr-next`,onClick:n,type:`button`,children:a(`setupTotp.next`)}),r]})}function O(e={}){let{onSetupSuccess:t,onSetupError:n,onVerifySuccess:r,onVerifyError:i,onConfirmSuccess:s,onConfirmError:c,autoStart:l=!1}=e,u=a(),[f,p]=(0,y.useState)(l?`loading`:`qr`),[m,v]=(0,y.useState)(null),[b,x]=(0,y.useState)([]),S=(0,y.useRef)(!1),C=o({...h,onSuccess:e=>{v(e),p(`qr`),t?.(e)},onError:e=>{p(`error`),n?.(e)}}),w=o({...g,onSuccess:e=>{x(e.recovery_codes),p(`recovery`),r?.(e)},onError:e=>{i?.(e)}}),T=o({..._,onSuccess:e=>{u.invalidateQueries({queryKey:d.queryKey}),s?.(e)},onError:e=>{c?.(e)}}),E=(0,y.useCallback)(()=>{S.current=!0,p(`loading`),C.mutate()},[C]),D=(0,y.useCallback)(async e=>w.mutateAsync({code:e}),[w]),O=(0,y.useCallback)(()=>{p(`qr`)},[]),k=(0,y.useCallback)(()=>{p(`verify`)},[]),A=(0,y.useCallback)(async()=>T.mutateAsync(),[T]),j=(0,y.useCallback)(()=>{p(l?`loading`:`qr`),v(null),x([]),S.current=!1,C.reset(),w.reset(),T.reset()},[l,C,w,T]);return(0,y.useEffect)(()=>{l&&!S.current&&f===`loading`&&(S.current=!0,C.mutate())},[l,f,C]),{step:f,setupData:m,recoveryCodes:b,isSetupPending:C.isPending,isVerifyPending:w.isPending,isConfirmPending:T.isPending,isPending:C.isPending||w.isPending||T.isPending,setupError:C.error,verifyError:w.error,confirmError:T.error,startSetup:E,verify:D,goToQr:O,goToVerify:k,confirmRecoveryCodes:A,reset:j}}export{C as a,T as i,D as n,E as r,O as t};
3
- //# sourceMappingURL=use-totp-setup-9fktn_he.js.map
3
+ //# sourceMappingURL=use-totp-setup-Z96pOMXt.js.map