@cedros/login-react 0.0.29 → 0.0.30

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 (175) hide show
  1. package/README.md +2 -0
  2. package/dist/{AdminDepositList-CyT4VBH8.js → AdminDepositList-BUm_ZcAW.js} +1 -1
  3. package/dist/{AdminDepositList-CyT4VBH8.js.map → AdminDepositList-BUm_ZcAW.js.map} +1 -1
  4. package/dist/{AdminDepositList-b2AXtLg0.cjs → AdminDepositList-B_z6x3j5.cjs} +1 -1
  5. package/dist/{AdminDepositList-b2AXtLg0.cjs.map → AdminDepositList-B_z6x3j5.cjs.map} +1 -1
  6. package/dist/{AdminWithdrawalHistory-DL9zbu2b.cjs → AdminWithdrawalHistory-B2EY2ZmH.cjs} +1 -1
  7. package/dist/{AdminWithdrawalHistory-DL9zbu2b.cjs.map → AdminWithdrawalHistory-B2EY2ZmH.cjs.map} +1 -1
  8. package/dist/{AdminWithdrawalHistory-Cud-yuWy.js → AdminWithdrawalHistory-C76bkbjX.js} +1 -1
  9. package/dist/{AdminWithdrawalHistory-Cud-yuWy.js.map → AdminWithdrawalHistory-C76bkbjX.js.map} +1 -1
  10. package/dist/{AuthenticationSettings-D6GvSw3g.cjs → AuthenticationSettings-C-aYDXNH.cjs} +1 -1
  11. package/dist/{AuthenticationSettings-D6GvSw3g.cjs.map → AuthenticationSettings-C-aYDXNH.cjs.map} +1 -1
  12. package/dist/AuthenticationSettings-CoTic-d_.cjs +1 -0
  13. package/dist/AuthenticationSettings-CoTic-d_.cjs.map +1 -0
  14. package/dist/{AuthenticationSettings-rb4Fksw5.js → AuthenticationSettings-CsPbxwP7.js} +1 -1
  15. package/dist/{AuthenticationSettings-rb4Fksw5.js.map → AuthenticationSettings-CsPbxwP7.js.map} +1 -1
  16. package/dist/AuthenticationSettings-DIVk0OP8.js +712 -0
  17. package/dist/AuthenticationSettings-DIVk0OP8.js.map +1 -0
  18. package/dist/AutosaveStatus-DGNI4lXn.cjs +1 -0
  19. package/dist/AutosaveStatus-DGNI4lXn.cjs.map +1 -0
  20. package/dist/{AutosaveStatus-vkJxtuEw.js → AutosaveStatus-f-jw25Ay.js} +141 -93
  21. package/dist/AutosaveStatus-f-jw25Ay.js.map +1 -0
  22. package/dist/{CreditSystemSettings-ChA_lbef.cjs → CreditSystemSettings-BNkvsgsk.cjs} +1 -1
  23. package/dist/{CreditSystemSettings-ChA_lbef.cjs.map → CreditSystemSettings-BNkvsgsk.cjs.map} +1 -1
  24. package/dist/{CreditSystemSettings-DsRipb2R.js → CreditSystemSettings-C6ed3yp7.js} +1 -1
  25. package/dist/{CreditSystemSettings-DsRipb2R.js.map → CreditSystemSettings-C6ed3yp7.js.map} +1 -1
  26. package/dist/{CreditSystemSettings-r3gnGjiU.cjs → CreditSystemSettings-DM9ep1TF.cjs} +1 -1
  27. package/dist/{CreditSystemSettings-r3gnGjiU.cjs.map → CreditSystemSettings-DM9ep1TF.cjs.map} +1 -1
  28. package/dist/{CreditSystemSettings-a31pqSYS.js → CreditSystemSettings-uinhzoha.js} +1 -1
  29. package/dist/{CreditSystemSettings-a31pqSYS.js.map → CreditSystemSettings-uinhzoha.js.map} +1 -1
  30. package/dist/{DepositsSection-DD9MKUFt.js → DepositsSection-Bb4ISzvE.js} +1 -1
  31. package/dist/{DepositsSection-DD9MKUFt.js.map → DepositsSection-Bb4ISzvE.js.map} +1 -1
  32. package/dist/{DepositsSection-BkKUS4vk.cjs → DepositsSection-uQUdGeVb.cjs} +1 -1
  33. package/dist/{DepositsSection-BkKUS4vk.cjs.map → DepositsSection-uQUdGeVb.cjs.map} +1 -1
  34. package/dist/EmailRegisterForm-B_TiJkD6.cjs +1 -0
  35. package/dist/EmailRegisterForm-B_TiJkD6.cjs.map +1 -0
  36. package/dist/EmailRegisterForm-CCEuQA-w.js +773 -0
  37. package/dist/EmailRegisterForm-CCEuQA-w.js.map +1 -0
  38. package/dist/{EmailSettings-BVJ4vz0Y.js → EmailSettings-BAuQtEfM.js} +1 -1
  39. package/dist/{EmailSettings-BVJ4vz0Y.js.map → EmailSettings-BAuQtEfM.js.map} +1 -1
  40. package/dist/{EmailSettings-Dg2SAiHj.cjs → EmailSettings-BC0f1PCI.cjs} +1 -1
  41. package/dist/{EmailSettings-Dg2SAiHj.cjs.map → EmailSettings-BC0f1PCI.cjs.map} +1 -1
  42. package/dist/{EmailSettings-CM5l8qqK.cjs → EmailSettings-BF5EiPl9.cjs} +1 -1
  43. package/dist/{EmailSettings-CM5l8qqK.cjs.map → EmailSettings-BF5EiPl9.cjs.map} +1 -1
  44. package/dist/{EmailSettings-xtVl4kXD.js → EmailSettings-BKuXy8sc.js} +1 -1
  45. package/dist/{EmailSettings-xtVl4kXD.js.map → EmailSettings-BKuXy8sc.js.map} +1 -1
  46. package/dist/{EmbeddedWalletSettings-Bmx8x21f.cjs → EmbeddedWalletSettings-BRjt2PAj.cjs} +1 -1
  47. package/dist/{EmbeddedWalletSettings-Bmx8x21f.cjs.map → EmbeddedWalletSettings-BRjt2PAj.cjs.map} +1 -1
  48. package/dist/{EmbeddedWalletSettings-BuDgqv-K.js → EmbeddedWalletSettings-C27X9He2.js} +1 -1
  49. package/dist/{EmbeddedWalletSettings-BuDgqv-K.js.map → EmbeddedWalletSettings-C27X9He2.js.map} +1 -1
  50. package/dist/{EmbeddedWalletSettings-BXN9VbNJ.cjs → EmbeddedWalletSettings-CJY39UZN.cjs} +1 -1
  51. package/dist/{EmbeddedWalletSettings-BXN9VbNJ.cjs.map → EmbeddedWalletSettings-CJY39UZN.cjs.map} +1 -1
  52. package/dist/{EmbeddedWalletSettings-DY5iJhS0.js → EmbeddedWalletSettings-Dmi-EQ7W.js} +1 -1
  53. package/dist/{EmbeddedWalletSettings-DY5iJhS0.js.map → EmbeddedWalletSettings-Dmi-EQ7W.js.map} +1 -1
  54. package/dist/GoogleLoginButton-CjBO3Rf1.cjs +1 -0
  55. package/dist/GoogleLoginButton-CjBO3Rf1.cjs.map +1 -0
  56. package/dist/{GoogleLoginButton-B6qnNMZp.js → GoogleLoginButton-DEbiQngr.js} +51 -51
  57. package/dist/GoogleLoginButton-DEbiQngr.js.map +1 -0
  58. package/dist/LoadingSpinner-6vml-zwr.js.map +1 -1
  59. package/dist/LoadingSpinner-d6sSxgQN.cjs.map +1 -1
  60. package/dist/{PermissionsSection-BPbE-hNx.cjs → PermissionsSection-DEMVp7X3.cjs} +1 -1
  61. package/dist/PermissionsSection-DEMVp7X3.cjs.map +1 -0
  62. package/dist/{PermissionsSection-CighC1p6.js → PermissionsSection-DNzOL1xW.js} +27 -25
  63. package/dist/PermissionsSection-DNzOL1xW.js.map +1 -0
  64. package/dist/{ServerSettings-BAstMKHS.js → ServerSettings-BT9weFPz.js} +1 -1
  65. package/dist/{ServerSettings-BAstMKHS.js.map → ServerSettings-BT9weFPz.js.map} +1 -1
  66. package/dist/{ServerSettings-LIIP5TPz.cjs → ServerSettings-CKfiLfXi.cjs} +1 -1
  67. package/dist/{ServerSettings-LIIP5TPz.cjs.map → ServerSettings-CKfiLfXi.cjs.map} +1 -1
  68. package/dist/{ServerSettings-9Q091f3o.js → ServerSettings-CZfBdMxG.js} +1 -1
  69. package/dist/{ServerSettings-9Q091f3o.js.map → ServerSettings-CZfBdMxG.js.map} +1 -1
  70. package/dist/{ServerSettings-PH7T8JKI.cjs → ServerSettings-rHrVN8O8.cjs} +1 -1
  71. package/dist/{ServerSettings-PH7T8JKI.cjs.map → ServerSettings-rHrVN8O8.cjs.map} +1 -1
  72. package/dist/SolanaLoginButton-DAV3r4oB.cjs +1 -0
  73. package/dist/SolanaLoginButton-DAV3r4oB.cjs.map +1 -0
  74. package/dist/{mobileWalletAdapter-Cm_AUXhg.js → SolanaLoginButton-DFOoLqoj.js} +75 -74
  75. package/dist/SolanaLoginButton-DFOoLqoj.js.map +1 -0
  76. package/dist/{TeamSection-BIECkp7g.js → TeamSection-CoMXyFtz.js} +2 -2
  77. package/dist/{TeamSection-BIECkp7g.js.map → TeamSection-CoMXyFtz.js.map} +1 -1
  78. package/dist/{TeamSection-BOH9pv_E.cjs → TeamSection-DopbZClq.cjs} +1 -1
  79. package/dist/{TeamSection-BOH9pv_E.cjs.map → TeamSection-DopbZClq.cjs.map} +1 -1
  80. package/dist/{UsersSection-t-zm0jZW.js → UsersSection--PAE1XRh.js} +1 -1
  81. package/dist/{UsersSection-t-zm0jZW.js.map → UsersSection--PAE1XRh.js.map} +1 -1
  82. package/dist/{UsersSection-e6q7FHzx.cjs → UsersSection-C7aRNkK2.cjs} +1 -1
  83. package/dist/{UsersSection-e6q7FHzx.cjs.map → UsersSection-C7aRNkK2.cjs.map} +1 -1
  84. package/dist/{WebhookSettings-D0F8ESlB.js → WebhookSettings-Bgld6D_T.js} +1 -1
  85. package/dist/{WebhookSettings-D0F8ESlB.js.map → WebhookSettings-Bgld6D_T.js.map} +1 -1
  86. package/dist/{WebhookSettings-2p9abGm5.cjs → WebhookSettings-DXjnq-c7.cjs} +1 -1
  87. package/dist/{WebhookSettings-2p9abGm5.cjs.map → WebhookSettings-DXjnq-c7.cjs.map} +1 -1
  88. package/dist/{WebhookSettings-CdFM7_V-.cjs → WebhookSettings-DnLk97Mr.cjs} +1 -1
  89. package/dist/{WebhookSettings-CdFM7_V-.cjs.map → WebhookSettings-DnLk97Mr.cjs.map} +1 -1
  90. package/dist/{WebhookSettings-CXMBju7N.js → WebhookSettings-ufiGTmbG.js} +1 -1
  91. package/dist/{WebhookSettings-CXMBju7N.js.map → WebhookSettings-ufiGTmbG.js.map} +1 -1
  92. package/dist/{WithdrawalsSection-yRDTVFsb.js → WithdrawalsSection-BN-FjTEV.js} +1 -1
  93. package/dist/{WithdrawalsSection-yRDTVFsb.js.map → WithdrawalsSection-BN-FjTEV.js.map} +1 -1
  94. package/dist/{WithdrawalsSection-sljIyeaz.cjs → WithdrawalsSection-BhuCwFat.cjs} +1 -1
  95. package/dist/{WithdrawalsSection-sljIyeaz.cjs.map → WithdrawalsSection-BhuCwFat.cjs.map} +1 -1
  96. package/dist/admin-only.cjs +1 -1
  97. package/dist/admin-only.js +1 -1
  98. package/dist/email-only.cjs +1 -1
  99. package/dist/email-only.d.ts +17 -1
  100. package/dist/email-only.js +3 -3
  101. package/dist/google-only.cjs +1 -1
  102. package/dist/google-only.d.ts +16 -0
  103. package/dist/google-only.js +3 -3
  104. package/dist/index.cjs +12 -12
  105. package/dist/index.cjs.map +1 -1
  106. package/dist/index.d.ts +246 -10
  107. package/dist/index.js +3384 -2508
  108. package/dist/index.js.map +1 -1
  109. package/dist/login-react.css +1 -1
  110. package/dist/{plugin-DbkijwEV.js → plugin-BbExid4E.js} +1 -1
  111. package/dist/{plugin-DbkijwEV.js.map → plugin-BbExid4E.js.map} +1 -1
  112. package/dist/{plugin-PU2vAozn.cjs → plugin-Xca67fp7.cjs} +1 -1
  113. package/dist/{plugin-PU2vAozn.cjs.map → plugin-Xca67fp7.cjs.map} +1 -1
  114. package/dist/{shamir-CiBczzDN.cjs → shamir-DBpHm7WN.cjs} +1 -1
  115. package/dist/{shamir-CiBczzDN.cjs.map → shamir-DBpHm7WN.cjs.map} +1 -1
  116. package/dist/{shamir-OAB2zD9Y.js → shamir-R8ddesFt.js} +1 -1
  117. package/dist/{shamir-OAB2zD9Y.js.map → shamir-R8ddesFt.js.map} +1 -1
  118. package/dist/{silentWalletEnroll-FqXS7Rvh.js → silentWalletEnroll-Dp1GTeNr.js} +3 -3
  119. package/dist/{silentWalletEnroll-FqXS7Rvh.js.map → silentWalletEnroll-Dp1GTeNr.js.map} +1 -1
  120. package/dist/{silentWalletEnroll-wnkcB9HP.cjs → silentWalletEnroll-HPvsbd2J.cjs} +1 -1
  121. package/dist/{silentWalletEnroll-wnkcB9HP.cjs.map → silentWalletEnroll-HPvsbd2J.cjs.map} +1 -1
  122. package/dist/solana-only.cjs +1 -1
  123. package/dist/solana-only.d.ts +16 -0
  124. package/dist/solana-only.js +3 -3
  125. package/dist/{useAdminDeposits-BTSyeAfg.js → useAdminDeposits-C76B2Q_8.js} +1 -1
  126. package/dist/{useAdminDeposits-BTSyeAfg.js.map → useAdminDeposits-C76B2Q_8.js.map} +1 -1
  127. package/dist/{useAdminDeposits-BkkCwHWp.cjs → useAdminDeposits-CpLd68oP.cjs} +1 -1
  128. package/dist/{useAdminDeposits-BkkCwHWp.cjs.map → useAdminDeposits-CpLd68oP.cjs.map} +1 -1
  129. package/dist/{useAuth-m5Hf89v8.js → useAuth-CVLv2oKA.js} +547 -545
  130. package/dist/useAuth-CVLv2oKA.js.map +1 -0
  131. package/dist/useAuth-XZaciuLg.cjs +1 -0
  132. package/dist/useAuth-XZaciuLg.cjs.map +1 -0
  133. package/dist/useCedrosLogin-CFfID-0i.js +228 -0
  134. package/dist/useCedrosLogin-CFfID-0i.js.map +1 -0
  135. package/dist/useCedrosLogin-DtJorrE7.cjs +1 -0
  136. package/dist/useCedrosLogin-DtJorrE7.cjs.map +1 -0
  137. package/dist/{useOrgs-C3pzMA9h.js → useOrgs-C90KT9KP.js} +1 -1
  138. package/dist/{useOrgs-C3pzMA9h.js.map → useOrgs-C90KT9KP.js.map} +1 -1
  139. package/dist/{useOrgs-DDVRCaVi.cjs → useOrgs-CNqfn-fk.cjs} +1 -1
  140. package/dist/{useOrgs-DDVRCaVi.cjs.map → useOrgs-CNqfn-fk.cjs.map} +1 -1
  141. package/dist/{useSystemSettings-DRrreszl.cjs → useSystemSettings-B2jY51ob.cjs} +1 -1
  142. package/dist/{useSystemSettings-DRrreszl.cjs.map → useSystemSettings-B2jY51ob.cjs.map} +1 -1
  143. package/dist/{useSystemSettings-DBlAMjFi.js → useSystemSettings-rgskaDqP.js} +1 -1
  144. package/dist/{useSystemSettings-DBlAMjFi.js.map → useSystemSettings-rgskaDqP.js.map} +1 -1
  145. package/dist/{useUsersStatsSummary-NjEFvWuz.js → useUsersStatsSummary-5DJwzntC.js} +2 -2
  146. package/dist/{useUsersStatsSummary-NjEFvWuz.js.map → useUsersStatsSummary-5DJwzntC.js.map} +1 -1
  147. package/dist/{useUsersStatsSummary-8qY7iP4G.cjs → useUsersStatsSummary-DgKaUIfs.cjs} +1 -1
  148. package/dist/{useUsersStatsSummary-8qY7iP4G.cjs.map → useUsersStatsSummary-DgKaUIfs.cjs.map} +1 -1
  149. package/package.json +1 -1
  150. package/dist/AuthenticationSettings-C9f5MKgj.cjs +0 -1
  151. package/dist/AuthenticationSettings-C9f5MKgj.cjs.map +0 -1
  152. package/dist/AuthenticationSettings-DC64o_J6.js +0 -525
  153. package/dist/AuthenticationSettings-DC64o_J6.js.map +0 -1
  154. package/dist/AutosaveStatus-BFj5GIab.cjs +0 -1
  155. package/dist/AutosaveStatus-BFj5GIab.cjs.map +0 -1
  156. package/dist/AutosaveStatus-vkJxtuEw.js.map +0 -1
  157. package/dist/EmailRegisterForm-B1DB-bqe.cjs +0 -1
  158. package/dist/EmailRegisterForm-B1DB-bqe.cjs.map +0 -1
  159. package/dist/EmailRegisterForm-BAX_uBIt.js +0 -927
  160. package/dist/EmailRegisterForm-BAX_uBIt.js.map +0 -1
  161. package/dist/GoogleLoginButton-B6qnNMZp.js.map +0 -1
  162. package/dist/GoogleLoginButton-D7CoMXLq.cjs +0 -1
  163. package/dist/GoogleLoginButton-D7CoMXLq.cjs.map +0 -1
  164. package/dist/PermissionsSection-BPbE-hNx.cjs.map +0 -1
  165. package/dist/PermissionsSection-CighC1p6.js.map +0 -1
  166. package/dist/mobileWalletAdapter-B6ELaZp1.cjs +0 -1
  167. package/dist/mobileWalletAdapter-B6ELaZp1.cjs.map +0 -1
  168. package/dist/mobileWalletAdapter-Cm_AUXhg.js.map +0 -1
  169. package/dist/useAuth-X6Ds6WW4.cjs +0 -1
  170. package/dist/useAuth-X6Ds6WW4.cjs.map +0 -1
  171. package/dist/useAuth-m5Hf89v8.js.map +0 -1
  172. package/dist/useCedrosLogin-C9MrcZvh.cjs +0 -1
  173. package/dist/useCedrosLogin-C9MrcZvh.cjs.map +0 -1
  174. package/dist/useCedrosLogin-_94MmGGq.js +0 -216
  175. package/dist/useCedrosLogin-_94MmGGq.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionsSection-DNzOL1xW.js","sources":["../src/components/members/MemberList.tsx","../src/components/invites/InviteForm.tsx","../src/components/invites/InviteList.tsx","../src/utils/memberApi.ts","../src/hooks/useMembers.ts","../src/utils/inviteApi.ts","../src/hooks/useInvites.ts","../src/hooks/useServerFeatures.ts","../src/types/org.ts","../src/hooks/useDashboardPermissions.ts","../src/components/admin/PermissionsSection.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport type { Member, OrgRole, DisplayError } from '../../types';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { sanitizeImageUrl } from '../../utils/sanitization';\n\ntype SortField = 'name' | 'role' | 'joinedAt';\ntype SortOrder = 'asc' | 'desc';\n\nexport interface MemberListProps {\n /** List of members to display */\n members: Member[];\n /** Current user's ID (to prevent self-actions) */\n currentUserId?: string;\n /** Loading state */\n isLoading?: boolean;\n /** Error message */\n error?: DisplayError;\n /** Whether the current user can manage members */\n canManage?: boolean;\n /** Whether the current user can change roles */\n canChangeRoles?: boolean;\n /** Callback when role is updated */\n onUpdateRole?: (userId: string, role: OrgRole) => Promise<void>;\n /** Callback when member is removed */\n onRemove?: (userId: string) => Promise<void>;\n /** Additional CSS class */\n className?: string;\n}\n\nconst ROLE_OPTIONS: OrgRole[] = ['owner', 'admin', 'member'];\n\n/**\n * Display and manage organization members.\n *\n * @example\n * ```tsx\n * function TeamMembers() {\n * const { activeOrg, hasPermission } = useOrgs();\n * const { members, isLoading, error, updateMemberRole, removeMember } = useMembers(activeOrg?.id);\n * const { user } = useAuth();\n *\n * return (\n * <MemberList\n * members={members}\n * currentUserId={user?.id}\n * isLoading={isLoading}\n * error={error?.message}\n * canManage={hasPermission('member:remove')}\n * canChangeRoles={hasPermission('member:role_change')}\n * onUpdateRole={updateMemberRole}\n * onRemove={removeMember}\n * />\n * );\n * }\n * ```\n */\nexport function MemberList({\n members,\n currentUserId,\n isLoading = false,\n error,\n canManage = false,\n canChangeRoles = false,\n onUpdateRole,\n onRemove,\n className = '',\n}: MemberListProps) {\n const [sortField, setSortField] = useState<SortField>('name');\n const [sortOrder, setSortOrder] = useState<SortOrder>('asc');\n\n const toggleSort = (field: SortField) => {\n if (sortField === field) {\n setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');\n } else {\n setSortField(field);\n setSortOrder('asc');\n }\n };\n\n const sortedMembers = useMemo(() => {\n const roleOrder: Record<OrgRole, number> = { owner: 0, admin: 1, member: 2 };\n return [...members].sort((a, b) => {\n let aVal: string | number;\n let bVal: string | number;\n\n switch (sortField) {\n case 'name':\n aVal = (a.user.name || a.user.email || '').toLowerCase();\n bVal = (b.user.name || b.user.email || '').toLowerCase();\n break;\n case 'role':\n aVal = roleOrder[a.role] ?? 99;\n bVal = roleOrder[b.role] ?? 99;\n break;\n case 'joinedAt':\n aVal = new Date(a.joinedAt).getTime();\n bVal = new Date(b.joinedAt).getTime();\n break;\n default:\n return 0;\n }\n\n if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;\n if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;\n return 0;\n });\n }, [members, sortField, sortOrder]);\n\n if (isLoading && members.length === 0) {\n return (\n <div className={`cedros-member-list cedros-member-list-loading ${className}`}>\n <LoadingSpinner />\n <span>Loading members...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-member-list ${className}`}>\n <ErrorMessage error={error} />\n </div>\n );\n }\n\n if (members.length === 0) {\n return (\n <div className={`cedros-member-list cedros-member-list-empty ${className}`}>\n <p>No members found.</p>\n </div>\n );\n }\n\n return (\n <div className={`cedros-member-list ${className}`}>\n <table className=\"cedros-member-table\">\n <thead>\n <tr>\n <th>\n <button\n type=\"button\"\n className={`cedros-admin-sort-button ${sortField === 'name' ? 'cedros-admin-sort-active' : ''}`}\n onClick={() => toggleSort('name')}\n >\n Member{' '}\n <span className=\"cedros-admin-sort-icon\">\n {sortField === 'name' ? (sortOrder === 'asc' ? '↑' : '↓') : '↕'}\n </span>\n </button>\n </th>\n <th>\n <button\n type=\"button\"\n className={`cedros-admin-sort-button ${sortField === 'role' ? 'cedros-admin-sort-active' : ''}`}\n onClick={() => toggleSort('role')}\n >\n Role{' '}\n <span className=\"cedros-admin-sort-icon\">\n {sortField === 'role' ? (sortOrder === 'asc' ? '↑' : '↓') : '↕'}\n </span>\n </button>\n </th>\n <th>\n <button\n type=\"button\"\n className={`cedros-admin-sort-button ${sortField === 'joinedAt' ? 'cedros-admin-sort-active' : ''}`}\n onClick={() => toggleSort('joinedAt')}\n >\n Joined{' '}\n <span className=\"cedros-admin-sort-icon\">\n {sortField === 'joinedAt' ? (sortOrder === 'asc' ? '↑' : '↓') : '↕'}\n </span>\n </button>\n </th>\n {(canManage || canChangeRoles) && <th>Actions</th>}\n </tr>\n </thead>\n <tbody>\n {sortedMembers.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n isCurrentUser={member.userId === currentUserId}\n canManage={canManage}\n canChangeRoles={canChangeRoles}\n onUpdateRole={onUpdateRole}\n onRemove={onRemove}\n />\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n// Member row component\ninterface MemberRowProps {\n member: Member;\n isCurrentUser: boolean;\n canManage: boolean;\n canChangeRoles: boolean;\n onUpdateRole?: (userId: string, role: OrgRole) => Promise<void>;\n onRemove?: (userId: string) => Promise<void>;\n}\n\nfunction MemberRow({\n member,\n isCurrentUser,\n canManage,\n canChangeRoles,\n onUpdateRole,\n onRemove,\n}: MemberRowProps) {\n const [isUpdating, setIsUpdating] = useState(false);\n const [selectedRole, setSelectedRole] = useState<OrgRole>(member.role);\n\n const handleRoleChange = useCallback(\n async (newRole: OrgRole) => {\n if (!onUpdateRole || newRole === member.role) return;\n\n setIsUpdating(true);\n try {\n await onUpdateRole(member.userId, newRole);\n setSelectedRole(newRole);\n } catch {\n // Revert on error\n setSelectedRole(member.role);\n } finally {\n setIsUpdating(false);\n }\n },\n [member.userId, member.role, onUpdateRole]\n );\n\n const handleRemove = useCallback(async () => {\n if (!onRemove) return;\n\n const confirmed = window.confirm(\n `Are you sure you want to remove ${member.user.name || member.user.email} from this organization?`\n );\n if (!confirmed) return;\n\n setIsUpdating(true);\n try {\n await onRemove(member.userId);\n } finally {\n setIsUpdating(false);\n }\n }, [member.userId, member.user.name, member.user.email, onRemove]);\n\n const isOwner = member.role === 'owner';\n const canModify = !isCurrentUser && !isOwner;\n\n return (\n <tr className={`cedros-member-row ${isCurrentUser ? 'cedros-member-row-current' : ''}`}>\n <td className=\"cedros-member-info\">\n <MemberAvatar user={member.user} />\n <div className=\"cedros-member-details\">\n <span className=\"cedros-member-name\">\n {member.user.name || 'Unknown'}\n {isCurrentUser && <span className=\"cedros-member-you\">(you)</span>}\n </span>\n <span className=\"cedros-member-email\">{member.user.email}</span>\n </div>\n </td>\n <td className=\"cedros-member-role\">\n {canChangeRoles && canModify && onUpdateRole ? (\n <select\n value={selectedRole}\n onChange={(e) => handleRoleChange(e.target.value as OrgRole)}\n disabled={isUpdating}\n className=\"cedros-role-select\"\n >\n {ROLE_OPTIONS.map((role) => (\n <option key={role} value={role}>\n {role.charAt(0).toUpperCase() + role.slice(1)}\n </option>\n ))}\n </select>\n ) : (\n <span className={`cedros-role-badge cedros-role-badge-${member.role}`}>\n {member.role.charAt(0).toUpperCase() + member.role.slice(1)}\n </span>\n )}\n </td>\n <td className=\"cedros-member-joined\">{formatDate(member.joinedAt)}</td>\n {(canManage || canChangeRoles) && (\n <td className=\"cedros-member-actions\">\n {canManage && canModify && onRemove && (\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-danger cedros-button-sm\"\n onClick={handleRemove}\n disabled={isUpdating}\n aria-label={`Remove ${member.user.name || member.user.email}`}\n >\n {isUpdating ? <LoadingSpinner size=\"sm\" /> : 'Remove'}\n </button>\n )}\n </td>\n )}\n </tr>\n );\n}\n\n// Helper components\nfunction MemberAvatar({ user }: { user: Member['user'] }) {\n // COMP-06: Cache sanitized URL and verify before use (blocks dangerous protocols)\n const sanitizedPicture = sanitizeImageUrl(user.picture);\n if (sanitizedPicture) {\n return (\n <img\n src={sanitizedPicture}\n alt={user.name || user.email || 'Member'}\n className=\"cedros-member-avatar\"\n referrerPolicy=\"no-referrer\"\n />\n );\n }\n\n const initial = (user.name?.[0] || user.email?.[0] || '?').toUpperCase();\n return <div className=\"cedros-member-avatar-placeholder\">{initial}</div>;\n}\n\nfunction formatDate(dateString: string): string {\n const date = new Date(dateString);\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { OrgRole, DisplayError } from '../../types';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { validateEmail } from '../../utils/validation';\n\ntype InviteRole = Exclude<OrgRole, 'owner'>;\n\nexport interface InviteFormProps {\n /** Callback when invite is submitted */\n onSubmit: (email: string, role: InviteRole) => Promise<void>;\n /** Loading state */\n isLoading?: boolean;\n /** Error message */\n error?: DisplayError;\n /** Available roles for invite (default: admin, member) */\n availableRoles?: InviteRole[];\n /** Default role for new invites */\n defaultRole?: InviteRole;\n /** Additional CSS class */\n className?: string;\n}\n\nconst DEFAULT_ROLES: InviteRole[] = ['admin', 'member'];\n\n/**\n * Form for inviting new members to an organization.\n *\n * @example\n * ```tsx\n * function InviteManager() {\n * const { activeOrg } = useOrgs();\n * const { createInvite, isLoading, error } = useInvites(activeOrg?.id);\n *\n * return (\n * <InviteForm\n * onSubmit={createInvite}\n * isLoading={isLoading}\n * error={error?.message}\n * defaultRole=\"member\"\n * />\n * );\n * }\n * ```\n */\nexport function InviteForm({\n onSubmit,\n isLoading = false,\n error,\n availableRoles = DEFAULT_ROLES,\n defaultRole = 'member',\n className = '',\n}: InviteFormProps) {\n const [email, setEmail] = useState('');\n const [role, setRole] = useState<InviteRole>(defaultRole);\n const [formError, setFormError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n const successTimerRef = useRef<number | null>(null);\n // UI-03: Track mounted state to prevent state updates after unmount\n const isMountedRef = useRef(true);\n\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n if (successTimerRef.current !== null) {\n window.clearTimeout(successTimerRef.current);\n successTimerRef.current = null;\n }\n };\n }, []);\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n setSuccess(false);\n\n const trimmedEmail = email.trim();\n\n if (!trimmedEmail) {\n setFormError('Email is required');\n return;\n }\n\n if (!validateEmail(trimmedEmail)) {\n setFormError('Please enter a valid email address');\n return;\n }\n\n try {\n await onSubmit(trimmedEmail, role);\n setEmail('');\n setRole(defaultRole);\n setSuccess(true);\n // Clear success message after 3 seconds\n if (successTimerRef.current !== null) {\n window.clearTimeout(successTimerRef.current);\n }\n successTimerRef.current = window.setTimeout(() => {\n // UI-03: Check mounted before state update to prevent memory leak warnings\n if (isMountedRef.current) {\n setSuccess(false);\n }\n successTimerRef.current = null;\n }, 3000);\n } catch {\n // H-05: Error handling is delegated to parent component.\n // Parent's onSubmit throws -> parent catches -> sets error prop -> ErrorMessage displays it.\n // This pattern allows the parent to control error display and retry logic.\n }\n },\n [email, role, defaultRole, onSubmit]\n );\n\n return (\n <form className={`cedros-invite-form ${className}`} onSubmit={handleSubmit}>\n {(error || formError) && <ErrorMessage error={formError ?? error ?? null} />}\n\n {success && (\n <div className=\"cedros-invite-success\" role=\"status\">\n <CheckIcon />\n <span>Invitation sent successfully!</span>\n </div>\n )}\n\n <div className=\"cedros-invite-form-row\">\n <div className=\"cedros-form-group cedros-invite-email-group\">\n <label htmlFor=\"invite-email\" className=\"cedros-form-label\">\n Email Address\n </label>\n <input\n id=\"invite-email\"\n type=\"email\"\n className=\"cedros-form-input\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"colleague@example.com\"\n disabled={isLoading}\n autoComplete=\"email\"\n />\n </div>\n\n <div className=\"cedros-form-group cedros-invite-role-group\">\n <label htmlFor=\"invite-role\" className=\"cedros-form-label\">\n Role\n </label>\n <select\n id=\"invite-role\"\n className=\"cedros-form-select\"\n value={role}\n onChange={(e) => setRole(e.target.value as InviteRole)}\n disabled={isLoading}\n >\n {availableRoles.map((r) => (\n <option key={r} value={r}>\n {r.charAt(0).toUpperCase() + r.slice(1)}\n </option>\n ))}\n </select>\n </div>\n\n <button\n type=\"submit\"\n className=\"cedros-button cedros-button-primary cedros-invite-submit\"\n disabled={isLoading || !email.trim()}\n >\n {isLoading ? <LoadingSpinner size=\"sm\" /> : 'Send Invite'}\n </button>\n </div>\n\n <p className=\"cedros-form-hint\">\n The invited user will receive an email with a link to join your organization.\n </p>\n </form>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg\n className=\"cedros-invite-check\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 8L6 11L13 5\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Invite, DisplayError } from '../../types';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\n\nexport interface InviteListProps {\n /** List of pending invites */\n invites: Invite[];\n /** Loading state */\n isLoading?: boolean;\n /** Error message */\n error?: DisplayError;\n /** Whether the current user can manage invites */\n canManage?: boolean;\n /** Callback when invite is cancelled */\n onCancel?: (inviteId: string) => Promise<void>;\n /** Callback when invite is resent */\n onResend?: (inviteId: string) => Promise<void>;\n /** Additional CSS class */\n className?: string;\n}\n\n/**\n * Display and manage pending organization invites.\n *\n * @example\n * ```tsx\n * function PendingInvites() {\n * const { activeOrg, hasPermission } = useOrgs();\n * const { invites, isLoading, error, cancelInvite, resendInvite } = useInvites(activeOrg?.id);\n *\n * return (\n * <InviteList\n * invites={invites}\n * isLoading={isLoading}\n * error={error?.message}\n * canManage={hasPermission('invite:cancel')}\n * onCancel={cancelInvite}\n * onResend={resendInvite}\n * />\n * );\n * }\n * ```\n */\nexport function InviteList({\n invites,\n isLoading = false,\n error,\n canManage = false,\n onCancel,\n onResend,\n className = '',\n}: InviteListProps) {\n if (isLoading && invites.length === 0) {\n return (\n <div className={`cedros-invite-list cedros-invite-list-loading ${className}`}>\n <LoadingSpinner />\n <span>Loading invites...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-invite-list ${className}`}>\n <ErrorMessage error={error} />\n </div>\n );\n }\n\n if (invites.length === 0) {\n return (\n <div className={`cedros-invite-list cedros-invite-list-empty ${className}`}>\n <p>No pending invites.</p>\n </div>\n );\n }\n\n return (\n <div className={`cedros-invite-list ${className}`}>\n <ul className=\"cedros-invite-items\">\n {invites.map((invite) => (\n <InviteItem\n key={invite.id}\n invite={invite}\n canManage={canManage}\n onCancel={onCancel}\n onResend={onResend}\n />\n ))}\n </ul>\n </div>\n );\n}\n\n// Invite item component\ninterface InviteItemProps {\n invite: Invite;\n canManage: boolean;\n onCancel?: (inviteId: string) => Promise<void>;\n onResend?: (inviteId: string) => Promise<void>;\n}\n\nfunction InviteItem({ invite, canManage, onCancel, onResend }: InviteItemProps) {\n const [isUpdating, setIsUpdating] = useState(false);\n const [resendSuccess, setResendSuccess] = useState(false);\n const resendTimerRef = useRef<number | null>(null);\n\n const isExpired = new Date(invite.expiresAt) < new Date();\n\n const handleCancel = useCallback(async () => {\n if (!onCancel) return;\n\n const confirmed = window.confirm(\n `Are you sure you want to cancel the invite for ${invite.email}?`\n );\n if (!confirmed) return;\n\n setIsUpdating(true);\n try {\n await onCancel(invite.id);\n } finally {\n setIsUpdating(false);\n }\n }, [invite.id, invite.email, onCancel]);\n\n const handleResend = useCallback(async () => {\n if (!onResend) return;\n\n setIsUpdating(true);\n setResendSuccess(false);\n try {\n await onResend(invite.id);\n setResendSuccess(true);\n if (resendTimerRef.current !== null) {\n window.clearTimeout(resendTimerRef.current);\n }\n resendTimerRef.current = window.setTimeout(() => {\n setResendSuccess(false);\n resendTimerRef.current = null;\n }, 3000);\n } finally {\n setIsUpdating(false);\n }\n }, [invite.id, onResend]);\n\n useEffect(() => {\n return () => {\n if (resendTimerRef.current !== null) {\n window.clearTimeout(resendTimerRef.current);\n resendTimerRef.current = null;\n }\n };\n }, []);\n\n return (\n <li className={`cedros-invite-item ${isExpired ? 'cedros-invite-item-expired' : ''}`}>\n <div className=\"cedros-invite-item-info\">\n <div className=\"cedros-invite-item-main\">\n <span className=\"cedros-invite-item-email\">{invite.email}</span>\n <span className={`cedros-role-badge cedros-role-badge-${invite.role}`}>\n {invite.role.charAt(0).toUpperCase() + invite.role.slice(1)}\n </span>\n {isExpired && <span className=\"cedros-invite-expired-badge\">Expired</span>}\n </div>\n <div className=\"cedros-invite-item-meta\">\n <span className=\"cedros-invite-item-date\">\n {/* TYPE-05: invitedByName removed - backend doesn't populate it */}\n Invited {formatDate(invite.createdAt)}\n </span>\n {!isExpired && (\n <span className=\"cedros-invite-item-expires\">\n Expires {formatRelativeTime(invite.expiresAt)}\n </span>\n )}\n </div>\n </div>\n\n {canManage && (\n <div className=\"cedros-invite-item-actions\">\n {resendSuccess && <span className=\"cedros-invite-resend-success\">Sent!</span>}\n {onResend && !isExpired && (\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-outline cedros-button-sm\"\n onClick={handleResend}\n disabled={isUpdating}\n aria-label={`Resend invite to ${invite.email}`}\n >\n {isUpdating ? <LoadingSpinner size=\"sm\" /> : 'Resend'}\n </button>\n )}\n {onCancel && (\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-danger cedros-button-sm\"\n onClick={handleCancel}\n disabled={isUpdating}\n aria-label={`Cancel invite for ${invite.email}`}\n >\n Cancel\n </button>\n )}\n </div>\n )}\n </li>\n );\n}\n\n// Helper functions\nfunction formatDate(dateString: string): string {\n const date = new Date(dateString);\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n}\n\nfunction formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = date.getTime() - now.getTime();\n const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays < 0) {\n return 'expired';\n } else if (diffDays === 0) {\n return 'today';\n } else if (diffDays === 1) {\n return 'tomorrow';\n } else if (diffDays < 7) {\n return `in ${diffDays} days`;\n } else {\n return formatDate(dateString);\n }\n}\n","import type {\n Member,\n MemberApiResponse,\n UpdateMemberRoleRequest,\n ListMembersResponse,\n} from '../types';\nimport { ApiClient, handleApiError } from './apiClient';\n\n/**\n * API client for member operations within an organization\n */\nexport class MemberApiClient {\n private client: ApiClient;\n\n constructor(\n baseUrl: string,\n timeoutMs?: number,\n retryAttempts?: number,\n getAccessToken?: () => string | null\n ) {\n this.client = new ApiClient({ baseUrl, timeoutMs, retryAttempts, getAccessToken });\n }\n\n /**\n * List all members of an organization\n */\n async listMembers(\n orgId: string,\n limit: number = 50,\n offset: number = 0\n ): Promise<{ members: Member[]; total: number }> {\n try {\n const response = await this.client.get<ListMembersResponse>(\n `/orgs/${orgId}/members?limit=${limit}&offset=${offset}`\n );\n return {\n members: response.members.map((member: MemberApiResponse) => ({\n id: member.id,\n userId: member.userId,\n orgId,\n role: member.role,\n joinedAt: member.joinedAt,\n user: {\n id: member.userId,\n email: member.email,\n name: member.name,\n },\n })),\n total: response.total,\n };\n } catch (error) {\n throw handleApiError(error, 'Failed to list members');\n }\n }\n\n /**\n * Update a member's role\n */\n async updateMemberRole(\n orgId: string,\n userId: string,\n data: UpdateMemberRoleRequest\n ): Promise<Member> {\n try {\n return await this.client.patch<Member>(`/orgs/${orgId}/members/${userId}`, data);\n } catch (error) {\n throw handleApiError(error, 'Failed to update member role');\n }\n }\n\n /**\n * Remove a member from the organization\n */\n async removeMember(orgId: string, userId: string): Promise<void> {\n try {\n await this.client.delete<void>(`/orgs/${orgId}/members/${userId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to remove member');\n }\n }\n}\n","import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport type { Member, OrgRole, AuthError } from '../types';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { MemberApiClient } from '../utils/memberApi';\n\nexport interface UseMembersReturn {\n /** List of members */\n members: Member[];\n /** Total members available on the server */\n total: number;\n /** Loading state */\n isLoading: boolean;\n /** Error state */\n error: AuthError | null;\n /** Fetch/refresh members list */\n fetchMembers: (options?: { limit?: number; offset?: number }) => Promise<void>;\n /** Update a member's role */\n updateMemberRole: (userId: string, role: OrgRole) => Promise<void>;\n /** Remove a member */\n removeMember: (userId: string) => Promise<void>;\n}\n\n/**\n * Hook for managing organization members.\n *\n * @param orgId - The organization ID to manage members for\n *\n * @example\n * ```tsx\n * function MembersList() {\n * const { activeOrg } = useOrgs();\n * const { members, isLoading, updateMemberRole, removeMember } = useMembers(activeOrg?.id);\n *\n * if (!activeOrg) return null;\n *\n * return (\n * <ul>\n * {members.map(member => (\n * <li key={member.id}>\n * {member.user.name} - {member.role}\n * <button onClick={() => updateMemberRole(member.userId, 'admin')}>\n * Make Admin\n * </button>\n * <button onClick={() => removeMember(member.userId)}>\n * Remove\n * </button>\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMembers(orgId: string | undefined): UseMembersReturn {\n const { config, authState, _internal } = useCedrosLogin();\n\n const [members, setMembers] = useState<Member[]>([]);\n const [total, setTotal] = useState(0);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n // P-12: Track fetched orgId to prevent duplicate fetches on remount\n const fetchedOrgIdRef = useRef<string | undefined>(undefined);\n const requestIdRef = useRef(0);\n\n const apiClient = useMemo(\n () =>\n new MemberApiClient(\n config.serverUrl,\n config.requestTimeout,\n config.retryAttempts,\n _internal?.getAccessToken\n ),\n [config.serverUrl, config.requestTimeout, config.retryAttempts, _internal]\n );\n\n // C-02: Use ref to avoid apiClient in callback dependencies\n // Prevents infinite loop when apiClient identity changes\n const apiClientRef = useRef(apiClient);\n apiClientRef.current = apiClient;\n\n const fetchMembers = useCallback(\n async (options?: { limit?: number; offset?: number }) => {\n if (!orgId || authState !== 'authenticated') {\n setMembers([]);\n setTotal(0);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n const requestId = ++requestIdRef.current;\n\n try {\n const { limit = 50, offset = 0 } = options ?? {};\n const response = await apiClientRef.current.listMembers(orgId, limit, offset);\n if (requestId !== requestIdRef.current) return;\n setMembers(response.members);\n setTotal(response.total);\n } catch (err) {\n if (requestId !== requestIdRef.current) return;\n setError(err as AuthError);\n } finally {\n if (requestId === requestIdRef.current) {\n setIsLoading(false);\n }\n }\n },\n [orgId, authState]\n );\n\n // P-12: Auto-fetch when orgId changes, but not on every remount\n useEffect(() => {\n if (authState !== 'authenticated') {\n fetchedOrgIdRef.current = undefined;\n return;\n }\n\n if (orgId !== fetchedOrgIdRef.current) {\n fetchedOrgIdRef.current = orgId;\n fetchMembers();\n }\n }, [orgId, authState, fetchMembers]);\n\n const updateMemberRole = useCallback(\n async (userId: string, role: OrgRole): Promise<void> => {\n if (!orgId) {\n throw new Error('No organization selected');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.updateMemberRole(orgId, userId, { role });\n // Refresh members list\n await fetchMembers();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [orgId, fetchMembers]\n );\n\n const removeMember = useCallback(\n async (userId: string): Promise<void> => {\n if (!orgId) {\n throw new Error('No organization selected');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.removeMember(orgId, userId);\n // Refresh members list\n await fetchMembers();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [orgId, fetchMembers]\n );\n\n return {\n members,\n total,\n isLoading,\n error,\n fetchMembers,\n updateMemberRole,\n removeMember,\n };\n}\n","import type {\n Invite,\n InviteApiResponse,\n CreateInviteRequest,\n AcceptInviteRequest,\n ListInvitesResponse,\n CreateInviteResponse,\n AcceptInviteResponse,\n} from '../types';\nimport { ApiClient, handleApiError } from './apiClient';\n\n/**\n * API client for invite operations\n */\nexport class InviteApiClient {\n private client: ApiClient;\n\n constructor(\n baseUrl: string,\n timeoutMs?: number,\n retryAttempts?: number,\n getAccessToken?: () => string | null\n ) {\n this.client = new ApiClient({ baseUrl, timeoutMs, retryAttempts, getAccessToken });\n }\n\n /**\n * List all pending invites for an organization\n */\n async listInvites(\n orgId: string,\n limit: number = 50,\n offset: number = 0\n ): Promise<{ invites: Invite[]; total: number }> {\n try {\n const response = await this.client.get<ListInvitesResponse>(\n `/orgs/${orgId}/invites?limit=${limit}&offset=${offset}`\n );\n return {\n invites: response.invites.map((invite: InviteApiResponse) => ({\n id: invite.id,\n orgId: invite.orgId,\n email: invite.email,\n role: invite.role,\n invitedBy: invite.invitedBy,\n createdAt: invite.createdAt,\n expiresAt: invite.expiresAt,\n })),\n total: response.total,\n };\n } catch (error) {\n throw handleApiError(error, 'Failed to list invites');\n }\n }\n\n /**\n * Create a new invite\n */\n async createInvite(orgId: string, data: CreateInviteRequest): Promise<CreateInviteResponse> {\n try {\n return await this.client.post<CreateInviteResponse>(`/orgs/${orgId}/invites`, data);\n } catch (error) {\n throw handleApiError(error, 'Failed to create invite');\n }\n }\n\n /**\n * Cancel a pending invite\n */\n async cancelInvite(orgId: string, inviteId: string): Promise<void> {\n try {\n await this.client.delete<void>(`/orgs/${orgId}/invites/${inviteId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to cancel invite');\n }\n }\n\n /**\n * Resend an invite email\n */\n async resendInvite(orgId: string, inviteId: string): Promise<void> {\n try {\n await this.client.post<void>(`/orgs/${orgId}/invites/${inviteId}/resend`, {});\n } catch (error) {\n throw handleApiError(error, 'Failed to resend invite');\n }\n }\n\n /**\n * Accept an invite (public endpoint)\n */\n async acceptInvite(data: AcceptInviteRequest): Promise<AcceptInviteResponse> {\n try {\n return await this.client.post<AcceptInviteResponse>('/invites/accept', data);\n } catch (error) {\n throw handleApiError(error, 'Failed to accept invite');\n }\n }\n}\n","import { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport type { Invite, OrgRole, AuthError, AcceptInviteResponse } from '../types';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { InviteApiClient } from '../utils/inviteApi';\n\nexport interface UseInvitesReturn {\n /** List of pending invites */\n invites: Invite[];\n /** Total pending invites available on the server */\n total: number;\n /** Loading state */\n isLoading: boolean;\n /** Error state */\n error: AuthError | null;\n /** Fetch/refresh invites list */\n fetchInvites: (options?: { limit?: number; offset?: number }) => Promise<void>;\n /** Create a new invite */\n createInvite: (email: string, role?: Exclude<OrgRole, 'owner'>) => Promise<void>;\n /** Cancel a pending invite */\n cancelInvite: (inviteId: string) => Promise<void>;\n /** Resend an invite email */\n resendInvite: (inviteId: string) => Promise<void>;\n /** Accept an invite (public) */\n acceptInvite: (token: string) => Promise<AcceptInviteResponse>;\n}\n\n/**\n * Hook for managing organization invites.\n *\n * @param orgId - The organization ID to manage invites for\n *\n * @example\n * ```tsx\n * function InviteManager() {\n * const { activeOrg } = useOrgs();\n * const { invites, createInvite, cancelInvite, resendInvite } = useInvites(activeOrg?.id);\n *\n * const handleInvite = async (email: string) => {\n * await createInvite(email, 'member');\n * };\n *\n * return (\n * <div>\n * <InviteForm onSubmit={handleInvite} />\n * <ul>\n * {invites.map(invite => (\n * <li key={invite.id}>\n * {invite.email} ({invite.role})\n * <button onClick={() => resendInvite(invite.id)}>Resend</button>\n * <button onClick={() => cancelInvite(invite.id)}>Cancel</button>\n * </li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function useInvites(orgId: string | undefined): UseInvitesReturn {\n const { config, authState, _internal } = useCedrosLogin();\n\n const [invites, setInvites] = useState<Invite[]>([]);\n const [total, setTotal] = useState(0);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n // P-13: Track fetched orgId to prevent duplicate fetches on remount\n const fetchedOrgIdRef = useRef<string | undefined>(undefined);\n const requestIdRef = useRef(0);\n\n const apiClient = useMemo(\n () =>\n new InviteApiClient(\n config.serverUrl,\n config.requestTimeout,\n config.retryAttempts,\n _internal?.getAccessToken\n ),\n [config.serverUrl, config.requestTimeout, config.retryAttempts, _internal]\n );\n\n // H-02: Use ref to avoid apiClient in callback dependencies\n // Prevents infinite loop when apiClient identity changes\n const apiClientRef = useRef(apiClient);\n apiClientRef.current = apiClient;\n\n const fetchInvites = useCallback(\n async (options?: { limit?: number; offset?: number }) => {\n if (!orgId || authState !== 'authenticated') {\n setInvites([]);\n setTotal(0);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n const requestId = ++requestIdRef.current;\n\n try {\n const { limit = 50, offset = 0 } = options ?? {};\n const response = await apiClientRef.current.listInvites(orgId, limit, offset);\n if (requestId !== requestIdRef.current) return;\n setInvites(response.invites);\n setTotal(response.total);\n } catch (err) {\n if (requestId !== requestIdRef.current) return;\n setError(err as AuthError);\n } finally {\n if (requestId === requestIdRef.current) {\n setIsLoading(false);\n }\n }\n },\n [orgId, authState]\n );\n\n // P-13: Auto-fetch when orgId changes, but not on every remount\n useEffect(() => {\n if (authState !== 'authenticated') {\n fetchedOrgIdRef.current = undefined;\n return;\n }\n\n if (orgId !== fetchedOrgIdRef.current) {\n fetchedOrgIdRef.current = orgId;\n fetchInvites();\n }\n }, [orgId, authState, fetchInvites]);\n\n const createInvite = useCallback(\n async (email: string, role: Exclude<OrgRole, 'owner'> = 'member'): Promise<void> => {\n if (!orgId) {\n throw new Error('No organization selected');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.createInvite(orgId, { email, role });\n // Refresh invites list\n await fetchInvites();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [orgId, fetchInvites]\n );\n\n const cancelInvite = useCallback(\n async (inviteId: string): Promise<void> => {\n if (!orgId) {\n throw new Error('No organization selected');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.cancelInvite(orgId, inviteId);\n // Refresh invites list\n await fetchInvites();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [orgId, fetchInvites]\n );\n\n const resendInvite = useCallback(\n async (inviteId: string): Promise<void> => {\n if (!orgId) {\n throw new Error('No organization selected');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.resendInvite(orgId, inviteId);\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [orgId]\n );\n\n const acceptInvite = useCallback(async (token: string): Promise<AcceptInviteResponse> => {\n setIsLoading(true);\n setError(null);\n\n try {\n return await apiClientRef.current.acceptInvite({ token });\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n return {\n invites,\n total,\n isLoading,\n error,\n fetchInvites,\n createInvite,\n cancelInvite,\n resendInvite,\n acceptInvite,\n };\n}\n","import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useSystemSettings } from './useSystemSettings';\n\n/**\n * Server-side feature flags stored in system settings.\n *\n * These control which features are available in the application.\n * Unlike client-side FeatureFlags (passed to CedrosLoginProvider),\n * these can be toggled at runtime via the admin dashboard.\n *\n * **Cosmetic vs enforced:**\n * Some flags only affect UI visibility (cosmetic) while others are\n * enforced server-side. See per-field docs below.\n *\n * Settings that are enforced per-request by the server (hard 403 if disabled):\n * `auth_email_enabled`, `auth_google_enabled`, `auth_apple_enabled`,\n * `auth_instantlink_enabled`, `feature_user_withdrawals`, `privacy_period_secs`,\n * withdrawal worker settings, and deposit fee settings.\n *\n * Settings applied at server startup (take effect after restart):\n * `security_cors_origins`, `rate_limit_*`, `auth_webauthn_*`, `webhook_*`.\n */\nexport interface ServerFeatures {\n /**\n * Enable multi-tenant organizations. Controls: Team, Invites sections.\n *\n * **Cosmetic** — org endpoints are always reachable server-side.\n * This flag only controls admin dashboard section visibility.\n */\n organizations: boolean;\n /** Enable Enterprise SSO for organizations. Startup-config enforced. */\n sso: boolean;\n /**\n * Enable two-factor authentication (TOTP).\n *\n * **Cosmetic** — MFA is actually gated by whether the user has TOTP\n * enrolled (`has_mfa_enabled()`), not by this flag. This controls\n * admin dashboard visibility only.\n */\n mfa: boolean;\n /**\n * Require email/password users to set up TOTP two-factor authentication.\n *\n * **Enforced** — the server returns a `setup_mfa` post-login action for\n * password users who haven't enrolled TOTP. Works independently of `mfa`\n * (which only controls UI visibility).\n */\n mfaRequired: boolean;\n /** Enable embedded wallet for transaction signing. Startup-config enforced. */\n walletSigning: boolean;\n /**\n * Enable deposits and credits system.\n * Controls: Deposits, Withdrawals, Credit System admin sections.\n * Deposit/withdrawal endpoints are enforced server-side.\n */\n credits: boolean;\n /**\n * Enable user withdrawals from embedded wallet to external addresses.\n * **Enforced** — server returns 403 on withdrawal endpoints when disabled.\n */\n userWithdrawals: boolean;\n /**\n * Enable Cedros Pay integration.\n * Controls: Products, Transactions, Refunds admin sections.\n *\n * **Cosmetic** — only controls admin dashboard tab visibility.\n */\n cedrosPay: boolean;\n}\n\n/**\n * Default feature values when settings haven't loaded yet.\n * All features disabled by default for safety.\n */\nconst DEFAULT_FEATURES: ServerFeatures = {\n organizations: false,\n sso: false,\n mfa: false,\n mfaRequired: false,\n walletSigning: false,\n credits: false,\n userWithdrawals: false,\n cedrosPay: false,\n};\n\nexport interface UseServerFeaturesReturn {\n /** Current feature flag states */\n features: ServerFeatures;\n /** Whether settings are still loading */\n isLoading: boolean;\n /** Error if settings failed to load */\n error: Error | null;\n /** Refresh feature flags from server */\n refetch: () => Promise<void>;\n /** Check if a specific feature is enabled */\n isEnabled: (feature: keyof ServerFeatures) => boolean;\n}\n\n/**\n * Hook for reading server-side feature flags from system settings.\n *\n * Use this to conditionally show/hide features based on admin settings.\n *\n * @example\n * ```tsx\n * function AdminDashboard() {\n * const { features, isLoading, isEnabled } = useServerFeatures();\n *\n * if (isLoading) return <LoadingSpinner />;\n *\n * return (\n * <nav>\n * {isEnabled('organizations') && <NavItem>Team</NavItem>}\n * {isEnabled('credits') && <NavItem>Deposits</NavItem>}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useServerFeatures(): UseServerFeaturesReturn {\n const { settings, isLoading, error, fetchSettings, getValue } = useSystemSettings();\n const [hasFetched, setHasFetched] = useState(false);\n\n // Fetch settings on mount\n useEffect(() => {\n if (!hasFetched) {\n fetchSettings();\n setHasFetched(true);\n }\n }, [fetchSettings, hasFetched]);\n\n // Parse boolean value from string setting\n const parseBoolean = useCallback((value: string | undefined): boolean => {\n if (value === undefined) return false;\n return value === 'true' || value === '1';\n }, []);\n\n // Derive features from settings\n const features = useMemo<ServerFeatures>(() => {\n // If no settings loaded yet, return defaults\n if (Object.keys(settings).length === 0) {\n return DEFAULT_FEATURES;\n }\n\n return {\n organizations: parseBoolean(getValue('feature_organizations')),\n sso: parseBoolean(getValue('feature_sso')),\n mfa: parseBoolean(getValue('feature_mfa')),\n mfaRequired: parseBoolean(getValue('security_require_mfa')),\n walletSigning: parseBoolean(getValue('feature_wallet_signing')),\n credits: parseBoolean(getValue('feature_credits')),\n userWithdrawals: parseBoolean(getValue('feature_user_withdrawals')),\n cedrosPay: parseBoolean(getValue('feature_cedros_pay')),\n };\n }, [settings, getValue, parseBoolean]);\n\n const refetch = useCallback(async () => {\n await fetchSettings();\n }, [fetchSettings]);\n\n const isEnabled = useCallback(\n (feature: keyof ServerFeatures): boolean => {\n return features[feature];\n },\n [features]\n );\n\n return {\n features,\n isLoading,\n error,\n refetch,\n isEnabled,\n };\n}\n","/**\n * Organization role in RBAC hierarchy\n * owner > admin > member\n */\nexport type OrgRole = 'owner' | 'admin' | 'member';\n\n/**\n * Organization entity\n */\nexport interface Organization {\n id: string;\n name: string;\n slug: string;\n logoUrl?: string;\n isPersonal: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Membership - user's relationship to an organization\n */\nexport interface Membership {\n id?: string;\n userId?: string;\n orgId?: string;\n role: OrgRole;\n joinedAt?: string;\n}\n\n/**\n * Organization with membership details for the current user\n */\nexport interface OrgWithMembership extends Organization {\n membership: Membership;\n}\n\n/**\n * Permission types for RBAC\n */\nexport type Permission =\n | 'org:delete'\n | 'org:update'\n | 'org:read'\n | 'member:invite'\n | 'member:remove'\n | 'member:role_change'\n | 'member:read'\n | 'invite:create'\n | 'invite:cancel'\n | 'invite:read'\n | 'audit:read';\n\n/**\n * Create organization request\n */\nexport interface CreateOrgRequest {\n name: string;\n slug?: string;\n}\n\n/**\n * Update organization request\n */\nexport interface UpdateOrgRequest {\n name?: string;\n slug?: string;\n logoUrl?: string;\n}\n\n/**\n * List organizations response\n */\nexport interface ListOrgsResponse {\n orgs: Array<Organization & { role: OrgRole }>;\n total?: number;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Authorization check request\n */\nexport interface AuthorizeRequest {\n orgId: string;\n action: string;\n resource?: string;\n resourceId?: string;\n}\n\n/**\n * Authorization check response\n */\nexport interface AuthorizeResponse {\n allowed: boolean;\n reason?: string;\n}\n\n/**\n * Permissions response\n */\nexport interface PermissionsResponse {\n permissions: Permission[];\n role: OrgRole;\n}\n\n/**\n * Organization state for context\n */\nexport interface OrgState {\n /** Currently active organization */\n activeOrg: OrgWithMembership | null;\n /** All organizations the user belongs to */\n orgs: OrgWithMembership[];\n /** User's permissions in the active org */\n permissions: Permission[];\n /** User's role in the active org */\n role: OrgRole | null;\n /** Loading state for org operations */\n isLoading: boolean;\n}\n\n// =============================================================================\n// DASHBOARD PERMISSIONS\n// =============================================================================\n\n/**\n * Admin dashboard sections that can be permission-controlled\n */\nexport type DashboardSection =\n // Cedros Login sections\n | 'users'\n | 'team'\n | 'deposits'\n | 'withdrawals'\n | 'settings-wallet'\n | 'settings-auth'\n | 'settings-messaging'\n | 'settings-credits'\n | 'settings-server'\n // Cedros Pay sections\n | 'pay-products'\n | 'pay-subscriptions'\n | 'pay-transactions'\n | 'pay-coupons'\n | 'pay-refunds'\n | 'pay-storefront'\n | 'pay-ai'\n | 'pay-payment'\n | 'pay-messaging'\n | 'pay-settings';\n\n/**\n * Cedros Login dashboard sections\n */\nexport const LOGIN_DASHBOARD_SECTIONS: DashboardSection[] = [\n 'users',\n 'team',\n 'deposits',\n 'withdrawals',\n 'settings-wallet',\n 'settings-auth',\n 'settings-messaging',\n 'settings-credits',\n 'settings-server',\n];\n\n/**\n * Cedros Pay dashboard sections\n */\nexport const PAY_DASHBOARD_SECTIONS: DashboardSection[] = [\n 'pay-products',\n 'pay-subscriptions',\n 'pay-transactions',\n 'pay-coupons',\n 'pay-refunds',\n 'pay-storefront',\n 'pay-ai',\n 'pay-payment',\n 'pay-messaging',\n 'pay-settings',\n];\n\n/**\n * All available dashboard sections\n */\nexport const ALL_DASHBOARD_SECTIONS: DashboardSection[] = [\n ...LOGIN_DASHBOARD_SECTIONS,\n ...PAY_DASHBOARD_SECTIONS,\n];\n\n/**\n * Human-readable labels for dashboard sections\n */\nexport const DASHBOARD_SECTION_LABELS: Record<DashboardSection, string> = {\n // Cedros Login\n users: 'Users',\n team: 'Team',\n deposits: 'Deposits',\n withdrawals: 'Withdrawals',\n 'settings-wallet': 'Wallet Settings',\n 'settings-auth': 'Auth Settings',\n 'settings-messaging': 'Messages Settings',\n 'settings-credits': 'Credits Settings',\n 'settings-server': 'Server Settings',\n // Cedros Pay\n 'pay-products': 'Products',\n 'pay-subscriptions': 'Subscriptions',\n 'pay-transactions': 'Transactions',\n 'pay-coupons': 'Coupons',\n 'pay-refunds': 'Refunds',\n 'pay-storefront': 'Storefront',\n 'pay-ai': 'Store AI',\n 'pay-payment': 'Payment Options',\n 'pay-messaging': 'Store Messages',\n 'pay-settings': 'Store Server',\n};\n\n/**\n * Dashboard permissions per role\n * Only admin and member are configurable - owner always has full access\n */\nexport interface DashboardPermissions {\n admin: Record<DashboardSection, boolean>;\n member: Record<DashboardSection, boolean>;\n}\n\n/**\n * Default dashboard permissions for new orgs\n */\nexport const DEFAULT_DASHBOARD_PERMISSIONS: DashboardPermissions = {\n admin: {\n // Cedros Login\n users: true,\n team: true,\n deposits: true,\n withdrawals: true,\n 'settings-wallet': true,\n 'settings-auth': true,\n 'settings-messaging': true,\n 'settings-credits': true,\n 'settings-server': true,\n // Cedros Pay\n 'pay-products': true,\n 'pay-subscriptions': true,\n 'pay-transactions': true,\n 'pay-coupons': true,\n 'pay-refunds': true,\n 'pay-storefront': true,\n 'pay-ai': true,\n 'pay-payment': true,\n 'pay-messaging': true,\n 'pay-settings': true,\n },\n member: {\n // Cedros Login\n users: false,\n team: true,\n deposits: false,\n withdrawals: false,\n 'settings-wallet': false,\n 'settings-auth': false,\n 'settings-messaging': false,\n 'settings-credits': false,\n 'settings-server': false,\n // Cedros Pay\n 'pay-products': false,\n 'pay-subscriptions': false,\n 'pay-transactions': false,\n 'pay-coupons': false,\n 'pay-refunds': false,\n 'pay-storefront': false,\n 'pay-ai': false,\n 'pay-payment': false,\n 'pay-messaging': false,\n 'pay-settings': false,\n },\n};\n","import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport type { DashboardSection, DashboardPermissions, AuthError } from '../types';\nimport { DEFAULT_DASHBOARD_PERMISSIONS } from '../types/org';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { useOrgs } from './useOrgs';\nimport { ApiClient } from '../utils/apiClient';\n\nexport interface UseDashboardPermissionsReturn {\n /** Current dashboard permissions config */\n permissions: DashboardPermissions;\n /** Whether the current user can access a specific section */\n canAccess: (section: DashboardSection) => boolean;\n /** Update permissions (owner only) */\n updatePermissions: (permissions: DashboardPermissions) => Promise<void>;\n /** Loading state */\n isLoading: boolean;\n /** Updating state */\n isUpdating: boolean;\n /** Error state */\n error: AuthError | null;\n /** Refresh permissions from server */\n fetchPermissions: () => Promise<void>;\n}\n\n/**\n * Hook for managing dashboard permissions per role.\n *\n * Allows org owners to configure which dashboard sections each role can access.\n * - Owner always has full access (not configurable)\n * - Admin and Member roles are configurable\n *\n * @example\n * ```tsx\n * function AdminDashboard() {\n * const { canAccess, permissions, updatePermissions } = useDashboardPermissions();\n *\n * // Check if current user can access a section\n * if (!canAccess('deposits')) {\n * return <div>You don't have access to deposits</div>;\n * }\n *\n * // Update permissions (owner only)\n * const toggleMemberDeposits = () => {\n * updatePermissions({\n * ...permissions,\n * member: { ...permissions.member, deposits: !permissions.member.deposits },\n * });\n * };\n * }\n * ```\n */\nexport function useDashboardPermissions(): UseDashboardPermissionsReturn {\n const { config, authState, _internal } = useCedrosLogin();\n const { activeOrg, role } = useOrgs();\n\n const [permissions, setPermissions] = useState<DashboardPermissions>(\n DEFAULT_DASHBOARD_PERMISSIONS\n );\n const [isLoading, setIsLoading] = useState(false);\n const [isUpdating, setIsUpdating] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n const requestIdRef = useRef(0);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n getAccessToken: _internal?.getAccessToken,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts, _internal]\n );\n\n // Use ref to avoid apiClient in callback dependencies\n const apiClientRef = useRef(apiClient);\n apiClientRef.current = apiClient;\n\n const fetchPermissions = useCallback(async () => {\n if (authState !== 'authenticated' || !activeOrg) {\n setPermissions(DEFAULT_DASHBOARD_PERMISSIONS);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n const requestId = ++requestIdRef.current;\n\n try {\n const response = await apiClientRef.current.get<{ permissions: DashboardPermissions }>(\n '/admin/dashboard-permissions'\n );\n if (requestId !== requestIdRef.current) return;\n setPermissions(response.permissions);\n } catch (err) {\n if (requestId !== requestIdRef.current) return;\n // If 404, use defaults (permissions not configured yet)\n if (err instanceof Error && err.message.includes('404')) {\n setPermissions(DEFAULT_DASHBOARD_PERMISSIONS);\n } else {\n const message = err instanceof Error ? err.message : 'Failed to fetch permissions';\n setError({ code: 'NETWORK_ERROR', message } as AuthError);\n // Fall back to defaults on error\n setPermissions(DEFAULT_DASHBOARD_PERMISSIONS);\n }\n } finally {\n if (requestId === requestIdRef.current) {\n setIsLoading(false);\n }\n }\n }, [authState, activeOrg]);\n\n const updatePermissions = useCallback(\n async (newPermissions: DashboardPermissions): Promise<void> => {\n if (authState !== 'authenticated' || !activeOrg) {\n throw new Error('Not authenticated');\n }\n\n if (role !== 'owner') {\n throw new Error('Only owners can modify dashboard permissions');\n }\n\n setIsUpdating(true);\n setError(null);\n\n try {\n await apiClientRef.current.request<{ permissions: DashboardPermissions }>({\n method: 'PUT',\n path: '/admin/dashboard-permissions',\n body: newPermissions,\n });\n setPermissions(newPermissions);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to update permissions';\n const authError = { code: 'NETWORK_ERROR', message } as AuthError;\n setError(authError);\n throw new Error(message);\n } finally {\n setIsUpdating(false);\n }\n },\n [authState, activeOrg, role]\n );\n\n /**\n * Check if the current user can access a dashboard section\n * - No org context (system admin): full access (permissions are org-scoped)\n * - Owner: always has full access\n * - Admin: check permissions.admin[section]\n * - Member: check permissions.member[section]\n */\n const canAccess = useCallback(\n (section: DashboardSection): boolean => {\n // No org context = system admin level, allow all\n // (dashboard permissions are org-scoped, not system-scoped)\n if (!activeOrg || !role) {\n return true;\n }\n\n // Owner always has full access\n if (role === 'owner') {\n return true;\n }\n\n // Admin and member: check configured permissions\n const rolePermissions = permissions[role as 'admin' | 'member'];\n return rolePermissions?.[section] ?? false;\n },\n [activeOrg, role, permissions]\n );\n\n // Fetch permissions when org changes\n useEffect(() => {\n if (activeOrg?.id) {\n fetchPermissions();\n }\n }, [activeOrg?.id, fetchPermissions]);\n\n return {\n permissions,\n canAccess,\n updatePermissions,\n isLoading,\n isUpdating,\n error,\n fetchPermissions,\n };\n}\n","/**\n * PermissionsSection - Dashboard permissions matrix for role configuration\n *\n * Allows org owners to configure which dashboard sections each role can access.\n */\n\nimport React, { useCallback, useRef, useEffect } from 'react';\nimport { useDashboardPermissions } from '../../hooks/useDashboardPermissions';\nimport { useServerFeatures } from '../../hooks/useServerFeatures';\nimport {\n LOGIN_DASHBOARD_SECTIONS,\n PAY_DASHBOARD_SECTIONS,\n DASHBOARD_SECTION_LABELS,\n type DashboardSection,\n type DashboardPermissions,\n type OrgRole,\n} from '../../types/org';\n\ninterface PermissionToggleProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n label: string;\n}\n\nfunction PermissionToggle({ checked, onChange, disabled, label }: PermissionToggleProps) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={label}\n disabled={disabled}\n className={`cedros-toggle cedros-toggle-sm ${checked ? 'cedros-toggle-on' : 'cedros-toggle-off'} ${disabled ? 'cedros-toggle-disabled' : ''}`}\n onClick={() => !disabled && onChange(!checked)}\n >\n <span className=\"cedros-toggle-track\">\n <span className=\"cedros-toggle-thumb\" />\n </span>\n </button>\n );\n}\n\nexport interface PermissionsSectionProps {\n /** Current user's role in the org */\n userRole?: OrgRole | null;\n}\n\nexport function PermissionsSection({ userRole }: PermissionsSectionProps): React.JSX.Element {\n const { permissions, updatePermissions, isLoading, isUpdating, error } =\n useDashboardPermissions();\n const { features, isLoading: featuresLoading } = useServerFeatures();\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingPermissions = useRef<DashboardPermissions | null>(null);\n\n const isOwner = userRole === 'owner';\n const cedrosPayEnabled = features.cedrosPay;\n\n // Debounced save - batch rapid changes\n const debouncedSave = useCallback(\n (newPermissions: DashboardPermissions) => {\n pendingPermissions.current = newPermissions;\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n debounceRef.current = setTimeout(() => {\n if (pendingPermissions.current) {\n updatePermissions(pendingPermissions.current).catch(() => {\n // Error is already set in state by the hook\n });\n pendingPermissions.current = null;\n }\n }, 500);\n },\n [updatePermissions]\n );\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n };\n }, []);\n\n const handleToggle = useCallback(\n (role: 'admin' | 'member', section: DashboardSection, enabled: boolean) => {\n const newPermissions: DashboardPermissions = {\n ...permissions,\n [role]: {\n ...permissions[role],\n [section]: enabled,\n },\n };\n debouncedSave(newPermissions);\n },\n [permissions, debouncedSave]\n );\n\n if (isLoading || featuresLoading) {\n return (\n <div className=\"cedros-dashboard__section\">\n <div className=\"cedros-dashboard__loading\">Loading permissions...</div>\n </div>\n );\n }\n\n if (!isOwner) {\n return (\n <div className=\"cedros-dashboard__section\">\n <div className=\"cedros-dashboard__empty\">\n Only organization owners can configure dashboard permissions.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"cedros-dashboard__section cedros-permissions-section\">\n <div className=\"cedros-permissions-header\">\n <p className=\"cedros-permissions-description\">\n Configure which dashboard sections each role can access. Owners always have full access.\n </p>\n {error && <div className=\"cedros-permissions-error\">{error.message}</div>}\n {isUpdating && <span className=\"cedros-permissions-saving\">Saving...</span>}\n </div>\n\n <div className=\"cedros-permissions-matrix\">\n <table className=\"cedros-permissions-table\">\n <tbody>\n {/* Cedros Login Sections */}\n <tr className=\"cedros-permissions-group-header\">\n <th className=\"cedros-permissions-section-header\">Cedros Login</th>\n <th className=\"cedros-permissions-role-header\">Admin</th>\n <th className=\"cedros-permissions-role-header\">Member</th>\n </tr>\n {LOGIN_DASHBOARD_SECTIONS.map((section) => (\n <tr key={section} className=\"cedros-permissions-row\">\n <td className=\"cedros-permissions-section-label\">\n {DASHBOARD_SECTION_LABELS[section]}\n </td>\n <td className=\"cedros-permissions-toggle-cell\">\n <PermissionToggle\n checked={permissions.admin[section] ?? false}\n onChange={(enabled) => handleToggle('admin', section, enabled)}\n disabled={isUpdating}\n label={`Admin access to ${DASHBOARD_SECTION_LABELS[section]}`}\n />\n </td>\n <td className=\"cedros-permissions-toggle-cell\">\n <PermissionToggle\n checked={permissions.member[section] ?? false}\n onChange={(enabled) => handleToggle('member', section, enabled)}\n disabled={isUpdating}\n label={`Member access to ${DASHBOARD_SECTION_LABELS[section]}`}\n />\n </td>\n </tr>\n ))}\n\n {/* Cedros Pay Sections (only when cedrosPay feature is enabled) */}\n {cedrosPayEnabled && (\n <>\n <tr className=\"cedros-permissions-group-header\">\n <th className=\"cedros-permissions-section-header\">Cedros Pay</th>\n <th className=\"cedros-permissions-role-header\">Admin</th>\n <th className=\"cedros-permissions-role-header\">Member</th>\n </tr>\n {PAY_DASHBOARD_SECTIONS.map((section) => (\n <tr key={section} className=\"cedros-permissions-row\">\n <td className=\"cedros-permissions-section-label\">\n {DASHBOARD_SECTION_LABELS[section]}\n </td>\n <td className=\"cedros-permissions-toggle-cell\">\n <PermissionToggle\n checked={permissions.admin[section] ?? false}\n onChange={(enabled) => handleToggle('admin', section, enabled)}\n disabled={isUpdating}\n label={`Admin access to ${DASHBOARD_SECTION_LABELS[section]}`}\n />\n </td>\n <td className=\"cedros-permissions-toggle-cell\">\n <PermissionToggle\n checked={permissions.member[section] ?? false}\n onChange={(enabled) => handleToggle('member', section, enabled)}\n disabled={isUpdating}\n label={`Member access to ${DASHBOARD_SECTION_LABELS[section]}`}\n />\n </td>\n </tr>\n ))}\n </>\n )}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"names":["ROLE_OPTIONS","MemberList","members","currentUserId","isLoading","error","canManage","canChangeRoles","onUpdateRole","onRemove","className","sortField","setSortField","useState","sortOrder","setSortOrder","toggleSort","field","sortedMembers","useMemo","roleOrder","a","b","aVal","bVal","jsxs","jsx","LoadingSpinner","ErrorMessage","member","MemberRow","isCurrentUser","isUpdating","setIsUpdating","selectedRole","setSelectedRole","handleRoleChange","useCallback","newRole","handleRemove","isOwner","canModify","MemberAvatar","e","role","formatDate","user","sanitizedPicture","sanitizeImageUrl","initial","dateString","DEFAULT_ROLES","InviteForm","onSubmit","availableRoles","defaultRole","email","setEmail","setRole","formError","setFormError","success","setSuccess","successTimerRef","useRef","isMountedRef","useEffect","handleSubmit","trimmedEmail","validateEmail","CheckIcon","r","InviteList","invites","onCancel","onResend","invite","InviteItem","resendSuccess","setResendSuccess","resendTimerRef","isExpired","handleCancel","handleResend","formatRelativeTime","date","now","diffMs","diffDays","MemberApiClient","baseUrl","timeoutMs","retryAttempts","getAccessToken","ApiClient","orgId","limit","offset","response","handleApiError","userId","data","useMembers","config","authState","_internal","useCedrosLogin","setMembers","total","setTotal","setIsLoading","setError","fetchedOrgIdRef","requestIdRef","apiClient","apiClientRef","fetchMembers","options","requestId","err","updateMemberRole","removeMember","InviteApiClient","inviteId","useInvites","setInvites","fetchInvites","createInvite","cancelInvite","resendInvite","acceptInvite","token","DEFAULT_FEATURES","useServerFeatures","settings","fetchSettings","getValue","useSystemSettings","hasFetched","setHasFetched","parseBoolean","value","features","refetch","isEnabled","feature","LOGIN_DASHBOARD_SECTIONS","PAY_DASHBOARD_SECTIONS","DASHBOARD_SECTION_LABELS","DEFAULT_DASHBOARD_PERMISSIONS","useDashboardPermissions","activeOrg","useOrgs","permissions","setPermissions","fetchPermissions","message","updatePermissions","newPermissions","canAccess","section","PermissionToggle","checked","onChange","disabled","label","PermissionsSection","userRole","featuresLoading","debounceRef","pendingPermissions","cedrosPayEnabled","debouncedSave","handleToggle","enabled","Fragment"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsxs as a, jsx as s } from "react/jsx-runtime";
2
2
  import { useMemo as v, useState as N, useEffect as h } from "react";
3
3
  import { L as A } from "./LoadingSpinner-6vml-zwr.js";
4
4
  import { E as x } from "./ErrorMessage-CcEK0pYO.js";
5
- import { u as E, A as L, S as T } from "./AutosaveStatus-vkJxtuEw.js";
5
+ import { u as E, A as L, S as T } from "./AutosaveStatus-f-jw25Ay.js";
6
6
  const $ = [
7
7
  {
8
8
  id: "integrations",
@@ -1 +1 @@
1
- {"version":3,"file":"ServerSettings-BAstMKHS.js","sources":["../src/components/admin/settings/ServerSettings.tsx"],"sourcesContent":["/**\n * Server settings page - infrastructure configuration with tabs\n *\n * Tab visibility:\n * - Integrations: only shown when `feature_cedros_pay` is enabled.\n * This tab contains the Cedros Pay API Key, which is only needed when\n * cedros-pay runs as a separate microservice. In co-located deployments\n * (e.g., cedros-login and cedros-pay in the same Rust binary), inter-service\n * auth uses JWT/JWKS and no API key is needed.\n * - Logging, Metrics, Security: always visible.\n */\nimport { useState, useEffect, useMemo } from 'react';\nimport { LoadingSpinner } from '../../shared/LoadingSpinner';\nimport { ErrorMessage } from '../../shared/ErrorMessage';\nimport { useSettingsAutosave } from '../../../hooks/useSettingsAutosave';\nimport { SettingsSection } from './settingsInputs';\nimport { AutosaveStatus } from './AutosaveStatus';\n\ntype ServerTab = 'integrations' | 'logging' | 'metrics' | 'security';\n\ninterface TabConfig {\n id: ServerTab;\n label: string;\n /** Categories to pull settings from (defaults to ['server']) */\n categories?: string[];\n keys: string[];\n /** When set, tab is only visible if this setting is 'true' */\n requiredSetting?: string;\n}\n\nconst TABS: TabConfig[] = [\n {\n id: 'integrations',\n label: 'Integrations',\n keys: ['server_cedros_pay_api_key'],\n requiredSetting: 'feature_cedros_pay',\n },\n {\n id: 'logging',\n label: 'Logging',\n keys: ['server_log_level'],\n },\n {\n id: 'metrics',\n label: 'Metrics',\n keys: ['server_metrics_api_key'],\n },\n {\n id: 'security',\n label: 'Security',\n categories: ['security', 'features'],\n keys: ['feature_mfa', 'security_cors_origins', 'security_session_timeout'],\n },\n];\n\nexport interface ServerSettingsProps {\n className?: string;\n}\n\nexport function ServerSettings({ className }: ServerSettingsProps) {\n const {\n settings,\n edits,\n isLoading,\n autosaveStatus,\n autosaveError,\n error,\n fetchSettings,\n handleChange,\n getEffectiveValue,\n } = useSettingsAutosave();\n\n // Filter tabs based on feature flags\n const visibleTabs = useMemo(\n () =>\n TABS.filter((tab) => {\n if (!tab.requiredSetting) return true;\n return getEffectiveValue(tab.requiredSetting) === 'true';\n }),\n [getEffectiveValue]\n );\n\n // Default to first visible tab\n const [activeTab, setActiveTab] = useState<ServerTab | null>(null);\n\n // Set initial tab when tabs become available\n useEffect(() => {\n if (activeTab === null && visibleTabs.length > 0) {\n setActiveTab(visibleTabs[0].id);\n } else if (activeTab && !visibleTabs.some((t) => t.id === activeTab)) {\n // Active tab was hidden (feature toggled off) — fall back\n setActiveTab(visibleTabs[0]?.id ?? null);\n }\n }, [visibleTabs, activeTab]);\n\n useEffect(() => {\n fetchSettings();\n }, [fetchSettings]);\n\n // Get current tab config\n const currentTab = visibleTabs.find((t) => t.id === activeTab);\n\n // Collect settings from all categories for the current tab\n const currentSettings = useMemo(() => {\n if (!currentTab) return [];\n const categories = currentTab.categories ?? ['server'];\n const allSettings: (typeof settings)[string] = [];\n for (const category of categories) {\n const categorySettings = settings[category] ?? [];\n allSettings.push(...categorySettings);\n }\n // Filter by keys and sort by keys order\n return allSettings\n .filter((s) => currentTab.keys.includes(s.key))\n .sort((a, b) => currentTab.keys.indexOf(a.key) - currentTab.keys.indexOf(b.key));\n }, [settings, currentTab]);\n\n if (isLoading && Object.keys(settings).length === 0) {\n return (\n <div className={`cedros-system-settings cedros-system-settings-loading ${className ?? ''}`}>\n <LoadingSpinner />\n <span>Loading settings...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <ErrorMessage error={error.message} />\n </div>\n );\n }\n\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <div className=\"cedros-settings-page-header\">\n <div className=\"cedros-settings-page-header-content\">\n <h2 className=\"cedros-settings-page-title\">Auth Server</h2>\n <p className=\"cedros-settings-page-description\">\n Server infrastructure settings. Some may be overridden by environment variables.\n </p>\n </div>\n <AutosaveStatus status={autosaveStatus} error={autosaveError} />\n </div>\n\n {/* Tabs */}\n <div className=\"cedros-admin-tabs cedros-admin-tabs--line\">\n {visibleTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n className={`cedros-admin-tab ${activeTab === tab.id ? 'cedros-admin-tab-active' : ''}`}\n onClick={() => setActiveTab(tab.id)}\n aria-selected={activeTab === tab.id}\n role=\"tab\"\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"cedros-admin-tab-content\" role=\"tabpanel\">\n {currentSettings.length === 0 ? (\n <div className=\"cedros-system-settings-empty\">\n <p>No settings found for {currentTab?.label ?? 'this section'}.</p>\n </div>\n ) : (\n <SettingsSection settings={currentSettings} edits={edits} onChange={handleChange} />\n )}\n </div>\n </div>\n );\n}\n"],"names":["TABS","ServerSettings","className","settings","edits","isLoading","autosaveStatus","autosaveError","error","fetchSettings","handleChange","getEffectiveValue","useSettingsAutosave","visibleTabs","useMemo","tab","activeTab","setActiveTab","useState","useEffect","t","currentTab","currentSettings","categories","allSettings","category","categorySettings","s","a","b","jsx","LoadingSpinner","ErrorMessage","jsxs","AutosaveStatus","SettingsSection"],"mappings":"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"}
1
+ {"version":3,"file":"ServerSettings-BT9weFPz.js","sources":["../src/components/admin/settings/ServerSettings.tsx"],"sourcesContent":["/**\n * Server settings page - infrastructure configuration with tabs\n *\n * Tab visibility:\n * - Integrations: only shown when `feature_cedros_pay` is enabled.\n * This tab contains the Cedros Pay API Key, which is only needed when\n * cedros-pay runs as a separate microservice. In co-located deployments\n * (e.g., cedros-login and cedros-pay in the same Rust binary), inter-service\n * auth uses JWT/JWKS and no API key is needed.\n * - Logging, Metrics, Security: always visible.\n */\nimport { useState, useEffect, useMemo } from 'react';\nimport { LoadingSpinner } from '../../shared/LoadingSpinner';\nimport { ErrorMessage } from '../../shared/ErrorMessage';\nimport { useSettingsAutosave } from '../../../hooks/useSettingsAutosave';\nimport { SettingsSection } from './settingsInputs';\nimport { AutosaveStatus } from './AutosaveStatus';\n\ntype ServerTab = 'integrations' | 'logging' | 'metrics' | 'security';\n\ninterface TabConfig {\n id: ServerTab;\n label: string;\n /** Categories to pull settings from (defaults to ['server']) */\n categories?: string[];\n keys: string[];\n /** When set, tab is only visible if this setting is 'true' */\n requiredSetting?: string;\n}\n\nconst TABS: TabConfig[] = [\n {\n id: 'integrations',\n label: 'Integrations',\n keys: ['server_cedros_pay_api_key'],\n requiredSetting: 'feature_cedros_pay',\n },\n {\n id: 'logging',\n label: 'Logging',\n keys: ['server_log_level'],\n },\n {\n id: 'metrics',\n label: 'Metrics',\n keys: ['server_metrics_api_key'],\n },\n {\n id: 'security',\n label: 'Security',\n categories: ['security', 'features'],\n keys: ['feature_mfa', 'security_cors_origins', 'security_session_timeout'],\n },\n];\n\nexport interface ServerSettingsProps {\n className?: string;\n}\n\nexport function ServerSettings({ className }: ServerSettingsProps) {\n const {\n settings,\n edits,\n isLoading,\n autosaveStatus,\n autosaveError,\n error,\n fetchSettings,\n handleChange,\n getEffectiveValue,\n } = useSettingsAutosave();\n\n // Filter tabs based on feature flags\n const visibleTabs = useMemo(\n () =>\n TABS.filter((tab) => {\n if (!tab.requiredSetting) return true;\n return getEffectiveValue(tab.requiredSetting) === 'true';\n }),\n [getEffectiveValue]\n );\n\n // Default to first visible tab\n const [activeTab, setActiveTab] = useState<ServerTab | null>(null);\n\n // Set initial tab when tabs become available\n useEffect(() => {\n if (activeTab === null && visibleTabs.length > 0) {\n setActiveTab(visibleTabs[0].id);\n } else if (activeTab && !visibleTabs.some((t) => t.id === activeTab)) {\n // Active tab was hidden (feature toggled off) — fall back\n setActiveTab(visibleTabs[0]?.id ?? null);\n }\n }, [visibleTabs, activeTab]);\n\n useEffect(() => {\n fetchSettings();\n }, [fetchSettings]);\n\n // Get current tab config\n const currentTab = visibleTabs.find((t) => t.id === activeTab);\n\n // Collect settings from all categories for the current tab\n const currentSettings = useMemo(() => {\n if (!currentTab) return [];\n const categories = currentTab.categories ?? ['server'];\n const allSettings: (typeof settings)[string] = [];\n for (const category of categories) {\n const categorySettings = settings[category] ?? [];\n allSettings.push(...categorySettings);\n }\n // Filter by keys and sort by keys order\n return allSettings\n .filter((s) => currentTab.keys.includes(s.key))\n .sort((a, b) => currentTab.keys.indexOf(a.key) - currentTab.keys.indexOf(b.key));\n }, [settings, currentTab]);\n\n if (isLoading && Object.keys(settings).length === 0) {\n return (\n <div className={`cedros-system-settings cedros-system-settings-loading ${className ?? ''}`}>\n <LoadingSpinner />\n <span>Loading settings...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <ErrorMessage error={error.message} />\n </div>\n );\n }\n\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <div className=\"cedros-settings-page-header\">\n <div className=\"cedros-settings-page-header-content\">\n <h2 className=\"cedros-settings-page-title\">Auth Server</h2>\n <p className=\"cedros-settings-page-description\">\n Server infrastructure settings. Some may be overridden by environment variables.\n </p>\n </div>\n <AutosaveStatus status={autosaveStatus} error={autosaveError} />\n </div>\n\n {/* Tabs */}\n <div className=\"cedros-admin-tabs cedros-admin-tabs--line\">\n {visibleTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n className={`cedros-admin-tab ${activeTab === tab.id ? 'cedros-admin-tab-active' : ''}`}\n onClick={() => setActiveTab(tab.id)}\n aria-selected={activeTab === tab.id}\n role=\"tab\"\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"cedros-admin-tab-content\" role=\"tabpanel\">\n {currentSettings.length === 0 ? (\n <div className=\"cedros-system-settings-empty\">\n <p>No settings found for {currentTab?.label ?? 'this section'}.</p>\n </div>\n ) : (\n <SettingsSection settings={currentSettings} edits={edits} onChange={handleChange} />\n )}\n </div>\n </div>\n );\n}\n"],"names":["TABS","ServerSettings","className","settings","edits","isLoading","autosaveStatus","autosaveError","error","fetchSettings","handleChange","getEffectiveValue","useSettingsAutosave","visibleTabs","useMemo","tab","activeTab","setActiveTab","useState","useEffect","t","currentTab","currentSettings","categories","allSettings","category","categorySettings","s","a","b","jsx","LoadingSpinner","ErrorMessage","jsxs","AutosaveStatus","SettingsSection"],"mappings":"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"}
@@ -1 +1 @@
1
- "use strict";const e=require("react/jsx-runtime"),a=require("react"),p=require("./LoadingSpinner-d6sSxgQN.cjs"),_=require("./ErrorMessage-CHbYbVi2.cjs"),g=require("./AutosaveStatus-BFj5GIab.cjs"),k=[{id:"integrations",label:"Integrations",keys:["server_cedros_pay_api_key"],requiredSetting:"feature_cedros_pay"},{id:"logging",label:"Logging",keys:["server_log_level"]},{id:"metrics",label:"Metrics",keys:["server_metrics_api_key"]},{id:"security",label:"Security",categories:["security","features"],keys:["feature_mfa","security_cors_origins","security_session_timeout"]}];function N({className:c}){const{settings:o,edits:h,isLoading:S,autosaveStatus:x,autosaveError:b,error:u,fetchSettings:m,handleChange:j,getEffectiveValue:y}=g.useSettingsAutosave(),t=a.useMemo(()=>k.filter(s=>s.requiredSetting?y(s.requiredSetting)==="true":!0),[y]),[r,d]=a.useState(null);a.useEffect(()=>{r===null&&t.length>0?d(t[0].id):r&&!t.some(s=>s.id===r)&&d(t[0]?.id??null)},[t,r]),a.useEffect(()=>{m()},[m]);const i=t.find(s=>s.id===r),v=a.useMemo(()=>{if(!i)return[];const s=i.categories??["server"],f=[];for(const n of s){const l=o[n]??[];f.push(...l)}return f.filter(n=>i.keys.includes(n.key)).sort((n,l)=>i.keys.indexOf(n.key)-i.keys.indexOf(l.key))},[o,i]);return S&&Object.keys(o).length===0?e.jsxs("div",{className:`cedros-system-settings cedros-system-settings-loading ${c??""}`,children:[e.jsx(p.LoadingSpinner,{}),e.jsx("span",{children:"Loading settings..."})]}):u?e.jsx("div",{className:`cedros-system-settings ${c??""}`,children:e.jsx(_.ErrorMessage,{error:u.message})}):e.jsxs("div",{className:`cedros-system-settings ${c??""}`,children:[e.jsxs("div",{className:"cedros-settings-page-header",children:[e.jsxs("div",{className:"cedros-settings-page-header-content",children:[e.jsx("h2",{className:"cedros-settings-page-title",children:"Auth Server"}),e.jsx("p",{className:"cedros-settings-page-description",children:"Server infrastructure settings. Some may be overridden by environment variables."})]}),e.jsx(g.AutosaveStatus,{status:x,error:b})]}),e.jsx("div",{className:"cedros-admin-tabs cedros-admin-tabs--line",children:t.map(s=>e.jsx("button",{type:"button",className:`cedros-admin-tab ${r===s.id?"cedros-admin-tab-active":""}`,onClick:()=>d(s.id),"aria-selected":r===s.id,role:"tab",children:s.label},s.id))}),e.jsx("div",{className:"cedros-admin-tab-content",role:"tabpanel",children:v.length===0?e.jsx("div",{className:"cedros-system-settings-empty",children:e.jsxs("p",{children:["No settings found for ",i?.label??"this section","."]})}):e.jsx(g.SettingsSection,{settings:v,edits:h,onChange:j})})]})}exports.ServerSettings=N;
1
+ "use strict";const e=require("react/jsx-runtime"),a=require("react"),p=require("./LoadingSpinner-d6sSxgQN.cjs"),_=require("./ErrorMessage-CHbYbVi2.cjs"),g=require("./AutosaveStatus-DGNI4lXn.cjs"),k=[{id:"integrations",label:"Integrations",keys:["server_cedros_pay_api_key"],requiredSetting:"feature_cedros_pay"},{id:"logging",label:"Logging",keys:["server_log_level"]},{id:"metrics",label:"Metrics",keys:["server_metrics_api_key"]},{id:"security",label:"Security",categories:["security","features"],keys:["feature_mfa","security_cors_origins","security_session_timeout"]}];function N({className:c}){const{settings:o,edits:h,isLoading:S,autosaveStatus:x,autosaveError:b,error:u,fetchSettings:m,handleChange:j,getEffectiveValue:y}=g.useSettingsAutosave(),t=a.useMemo(()=>k.filter(s=>s.requiredSetting?y(s.requiredSetting)==="true":!0),[y]),[r,d]=a.useState(null);a.useEffect(()=>{r===null&&t.length>0?d(t[0].id):r&&!t.some(s=>s.id===r)&&d(t[0]?.id??null)},[t,r]),a.useEffect(()=>{m()},[m]);const i=t.find(s=>s.id===r),v=a.useMemo(()=>{if(!i)return[];const s=i.categories??["server"],f=[];for(const n of s){const l=o[n]??[];f.push(...l)}return f.filter(n=>i.keys.includes(n.key)).sort((n,l)=>i.keys.indexOf(n.key)-i.keys.indexOf(l.key))},[o,i]);return S&&Object.keys(o).length===0?e.jsxs("div",{className:`cedros-system-settings cedros-system-settings-loading ${c??""}`,children:[e.jsx(p.LoadingSpinner,{}),e.jsx("span",{children:"Loading settings..."})]}):u?e.jsx("div",{className:`cedros-system-settings ${c??""}`,children:e.jsx(_.ErrorMessage,{error:u.message})}):e.jsxs("div",{className:`cedros-system-settings ${c??""}`,children:[e.jsxs("div",{className:"cedros-settings-page-header",children:[e.jsxs("div",{className:"cedros-settings-page-header-content",children:[e.jsx("h2",{className:"cedros-settings-page-title",children:"Auth Server"}),e.jsx("p",{className:"cedros-settings-page-description",children:"Server infrastructure settings. Some may be overridden by environment variables."})]}),e.jsx(g.AutosaveStatus,{status:x,error:b})]}),e.jsx("div",{className:"cedros-admin-tabs cedros-admin-tabs--line",children:t.map(s=>e.jsx("button",{type:"button",className:`cedros-admin-tab ${r===s.id?"cedros-admin-tab-active":""}`,onClick:()=>d(s.id),"aria-selected":r===s.id,role:"tab",children:s.label},s.id))}),e.jsx("div",{className:"cedros-admin-tab-content",role:"tabpanel",children:v.length===0?e.jsx("div",{className:"cedros-system-settings-empty",children:e.jsxs("p",{children:["No settings found for ",i?.label??"this section","."]})}):e.jsx(g.SettingsSection,{settings:v,edits:h,onChange:j})})]})}exports.ServerSettings=N;
@@ -1 +1 @@
1
- {"version":3,"file":"ServerSettings-LIIP5TPz.cjs","sources":["../src/components/admin/settings/ServerSettings.tsx"],"sourcesContent":["/**\n * Server settings page - infrastructure configuration with tabs\n *\n * Tab visibility:\n * - Integrations: only shown when `feature_cedros_pay` is enabled.\n * This tab contains the Cedros Pay API Key, which is only needed when\n * cedros-pay runs as a separate microservice. In co-located deployments\n * (e.g., cedros-login and cedros-pay in the same Rust binary), inter-service\n * auth uses JWT/JWKS and no API key is needed.\n * - Logging, Metrics, Security: always visible.\n */\nimport { useState, useEffect, useMemo } from 'react';\nimport { LoadingSpinner } from '../../shared/LoadingSpinner';\nimport { ErrorMessage } from '../../shared/ErrorMessage';\nimport { useSettingsAutosave } from '../../../hooks/useSettingsAutosave';\nimport { SettingsSection } from './settingsInputs';\nimport { AutosaveStatus } from './AutosaveStatus';\n\ntype ServerTab = 'integrations' | 'logging' | 'metrics' | 'security';\n\ninterface TabConfig {\n id: ServerTab;\n label: string;\n /** Categories to pull settings from (defaults to ['server']) */\n categories?: string[];\n keys: string[];\n /** When set, tab is only visible if this setting is 'true' */\n requiredSetting?: string;\n}\n\nconst TABS: TabConfig[] = [\n {\n id: 'integrations',\n label: 'Integrations',\n keys: ['server_cedros_pay_api_key'],\n requiredSetting: 'feature_cedros_pay',\n },\n {\n id: 'logging',\n label: 'Logging',\n keys: ['server_log_level'],\n },\n {\n id: 'metrics',\n label: 'Metrics',\n keys: ['server_metrics_api_key'],\n },\n {\n id: 'security',\n label: 'Security',\n categories: ['security', 'features'],\n keys: ['feature_mfa', 'security_cors_origins', 'security_session_timeout'],\n },\n];\n\nexport interface ServerSettingsProps {\n className?: string;\n}\n\nexport function ServerSettings({ className }: ServerSettingsProps) {\n const {\n settings,\n edits,\n isLoading,\n autosaveStatus,\n autosaveError,\n error,\n fetchSettings,\n handleChange,\n getEffectiveValue,\n } = useSettingsAutosave();\n\n // Filter tabs based on feature flags\n const visibleTabs = useMemo(\n () =>\n TABS.filter((tab) => {\n if (!tab.requiredSetting) return true;\n return getEffectiveValue(tab.requiredSetting) === 'true';\n }),\n [getEffectiveValue]\n );\n\n // Default to first visible tab\n const [activeTab, setActiveTab] = useState<ServerTab | null>(null);\n\n // Set initial tab when tabs become available\n useEffect(() => {\n if (activeTab === null && visibleTabs.length > 0) {\n setActiveTab(visibleTabs[0].id);\n } else if (activeTab && !visibleTabs.some((t) => t.id === activeTab)) {\n // Active tab was hidden (feature toggled off) — fall back\n setActiveTab(visibleTabs[0]?.id ?? null);\n }\n }, [visibleTabs, activeTab]);\n\n useEffect(() => {\n fetchSettings();\n }, [fetchSettings]);\n\n // Get current tab config\n const currentTab = visibleTabs.find((t) => t.id === activeTab);\n\n // Collect settings from all categories for the current tab\n const currentSettings = useMemo(() => {\n if (!currentTab) return [];\n const categories = currentTab.categories ?? ['server'];\n const allSettings: (typeof settings)[string] = [];\n for (const category of categories) {\n const categorySettings = settings[category] ?? [];\n allSettings.push(...categorySettings);\n }\n // Filter by keys and sort by keys order\n return allSettings\n .filter((s) => currentTab.keys.includes(s.key))\n .sort((a, b) => currentTab.keys.indexOf(a.key) - currentTab.keys.indexOf(b.key));\n }, [settings, currentTab]);\n\n if (isLoading && Object.keys(settings).length === 0) {\n return (\n <div className={`cedros-system-settings cedros-system-settings-loading ${className ?? ''}`}>\n <LoadingSpinner />\n <span>Loading settings...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <ErrorMessage error={error.message} />\n </div>\n );\n }\n\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <div className=\"cedros-settings-page-header\">\n <div className=\"cedros-settings-page-header-content\">\n <h2 className=\"cedros-settings-page-title\">Auth Server</h2>\n <p className=\"cedros-settings-page-description\">\n Server infrastructure settings. Some may be overridden by environment variables.\n </p>\n </div>\n <AutosaveStatus status={autosaveStatus} error={autosaveError} />\n </div>\n\n {/* Tabs */}\n <div className=\"cedros-admin-tabs cedros-admin-tabs--line\">\n {visibleTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n className={`cedros-admin-tab ${activeTab === tab.id ? 'cedros-admin-tab-active' : ''}`}\n onClick={() => setActiveTab(tab.id)}\n aria-selected={activeTab === tab.id}\n role=\"tab\"\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"cedros-admin-tab-content\" role=\"tabpanel\">\n {currentSettings.length === 0 ? (\n <div className=\"cedros-system-settings-empty\">\n <p>No settings found for {currentTab?.label ?? 'this section'}.</p>\n </div>\n ) : (\n <SettingsSection settings={currentSettings} edits={edits} onChange={handleChange} />\n )}\n </div>\n </div>\n );\n}\n"],"names":["TABS","ServerSettings","className","settings","edits","isLoading","autosaveStatus","autosaveError","error","fetchSettings","handleChange","getEffectiveValue","useSettingsAutosave","visibleTabs","useMemo","tab","activeTab","setActiveTab","useState","useEffect","t","currentTab","currentSettings","categories","allSettings","category","categorySettings","s","a","b","jsx","LoadingSpinner","ErrorMessage","jsxs","AutosaveStatus","SettingsSection"],"mappings":"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"}
1
+ {"version":3,"file":"ServerSettings-CKfiLfXi.cjs","sources":["../src/components/admin/settings/ServerSettings.tsx"],"sourcesContent":["/**\n * Server settings page - infrastructure configuration with tabs\n *\n * Tab visibility:\n * - Integrations: only shown when `feature_cedros_pay` is enabled.\n * This tab contains the Cedros Pay API Key, which is only needed when\n * cedros-pay runs as a separate microservice. In co-located deployments\n * (e.g., cedros-login and cedros-pay in the same Rust binary), inter-service\n * auth uses JWT/JWKS and no API key is needed.\n * - Logging, Metrics, Security: always visible.\n */\nimport { useState, useEffect, useMemo } from 'react';\nimport { LoadingSpinner } from '../../shared/LoadingSpinner';\nimport { ErrorMessage } from '../../shared/ErrorMessage';\nimport { useSettingsAutosave } from '../../../hooks/useSettingsAutosave';\nimport { SettingsSection } from './settingsInputs';\nimport { AutosaveStatus } from './AutosaveStatus';\n\ntype ServerTab = 'integrations' | 'logging' | 'metrics' | 'security';\n\ninterface TabConfig {\n id: ServerTab;\n label: string;\n /** Categories to pull settings from (defaults to ['server']) */\n categories?: string[];\n keys: string[];\n /** When set, tab is only visible if this setting is 'true' */\n requiredSetting?: string;\n}\n\nconst TABS: TabConfig[] = [\n {\n id: 'integrations',\n label: 'Integrations',\n keys: ['server_cedros_pay_api_key'],\n requiredSetting: 'feature_cedros_pay',\n },\n {\n id: 'logging',\n label: 'Logging',\n keys: ['server_log_level'],\n },\n {\n id: 'metrics',\n label: 'Metrics',\n keys: ['server_metrics_api_key'],\n },\n {\n id: 'security',\n label: 'Security',\n categories: ['security', 'features'],\n keys: ['feature_mfa', 'security_cors_origins', 'security_session_timeout'],\n },\n];\n\nexport interface ServerSettingsProps {\n className?: string;\n}\n\nexport function ServerSettings({ className }: ServerSettingsProps) {\n const {\n settings,\n edits,\n isLoading,\n autosaveStatus,\n autosaveError,\n error,\n fetchSettings,\n handleChange,\n getEffectiveValue,\n } = useSettingsAutosave();\n\n // Filter tabs based on feature flags\n const visibleTabs = useMemo(\n () =>\n TABS.filter((tab) => {\n if (!tab.requiredSetting) return true;\n return getEffectiveValue(tab.requiredSetting) === 'true';\n }),\n [getEffectiveValue]\n );\n\n // Default to first visible tab\n const [activeTab, setActiveTab] = useState<ServerTab | null>(null);\n\n // Set initial tab when tabs become available\n useEffect(() => {\n if (activeTab === null && visibleTabs.length > 0) {\n setActiveTab(visibleTabs[0].id);\n } else if (activeTab && !visibleTabs.some((t) => t.id === activeTab)) {\n // Active tab was hidden (feature toggled off) — fall back\n setActiveTab(visibleTabs[0]?.id ?? null);\n }\n }, [visibleTabs, activeTab]);\n\n useEffect(() => {\n fetchSettings();\n }, [fetchSettings]);\n\n // Get current tab config\n const currentTab = visibleTabs.find((t) => t.id === activeTab);\n\n // Collect settings from all categories for the current tab\n const currentSettings = useMemo(() => {\n if (!currentTab) return [];\n const categories = currentTab.categories ?? ['server'];\n const allSettings: (typeof settings)[string] = [];\n for (const category of categories) {\n const categorySettings = settings[category] ?? [];\n allSettings.push(...categorySettings);\n }\n // Filter by keys and sort by keys order\n return allSettings\n .filter((s) => currentTab.keys.includes(s.key))\n .sort((a, b) => currentTab.keys.indexOf(a.key) - currentTab.keys.indexOf(b.key));\n }, [settings, currentTab]);\n\n if (isLoading && Object.keys(settings).length === 0) {\n return (\n <div className={`cedros-system-settings cedros-system-settings-loading ${className ?? ''}`}>\n <LoadingSpinner />\n <span>Loading settings...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <ErrorMessage error={error.message} />\n </div>\n );\n }\n\n return (\n <div className={`cedros-system-settings ${className ?? ''}`}>\n <div className=\"cedros-settings-page-header\">\n <div className=\"cedros-settings-page-header-content\">\n <h2 className=\"cedros-settings-page-title\">Auth Server</h2>\n <p className=\"cedros-settings-page-description\">\n Server infrastructure settings. Some may be overridden by environment variables.\n </p>\n </div>\n <AutosaveStatus status={autosaveStatus} error={autosaveError} />\n </div>\n\n {/* Tabs */}\n <div className=\"cedros-admin-tabs cedros-admin-tabs--line\">\n {visibleTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n className={`cedros-admin-tab ${activeTab === tab.id ? 'cedros-admin-tab-active' : ''}`}\n onClick={() => setActiveTab(tab.id)}\n aria-selected={activeTab === tab.id}\n role=\"tab\"\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"cedros-admin-tab-content\" role=\"tabpanel\">\n {currentSettings.length === 0 ? (\n <div className=\"cedros-system-settings-empty\">\n <p>No settings found for {currentTab?.label ?? 'this section'}.</p>\n </div>\n ) : (\n <SettingsSection settings={currentSettings} edits={edits} onChange={handleChange} />\n )}\n </div>\n </div>\n );\n}\n"],"names":["TABS","ServerSettings","className","settings","edits","isLoading","autosaveStatus","autosaveError","error","fetchSettings","handleChange","getEffectiveValue","useSettingsAutosave","visibleTabs","useMemo","tab","activeTab","setActiveTab","useState","useEffect","t","currentTab","currentSettings","categories","allSettings","category","categorySettings","s","a","b","jsx","LoadingSpinner","ErrorMessage","jsxs","AutosaveStatus","SettingsSection"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as r } from "react/jsx-runtime";
2
2
  import "react";
3
- import { S as e } from "./ServerSettings-BAstMKHS.js";
3
+ import { S as e } from "./ServerSettings-BT9weFPz.js";
4
4
  function i() {
5
5
  return /* @__PURE__ */ r("div", { className: "cedros-dashboard__section", children: /* @__PURE__ */ r(e, {}) });
6
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ServerSettings-9Q091f3o.js","sources":["../src/admin/sections/ServerSettings.tsx"],"sourcesContent":["/**\n * Server Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport { ServerSettings as Settings } from '../../components/admin/settings';\n\nexport default function ServerSettings(): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["ServerSettings","jsx","Settings"],"mappings":";;;AAOA,SAAwBA,IAAoC;AAC1D,2BACG,OAAA,EAAI,WAAU,6BACb,UAAA,gBAAAC,EAACC,KAAS,GACZ;AAEJ;"}
1
+ {"version":3,"file":"ServerSettings-CZfBdMxG.js","sources":["../src/admin/sections/ServerSettings.tsx"],"sourcesContent":["/**\n * Server Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport { ServerSettings as Settings } from '../../components/admin/settings';\n\nexport default function ServerSettings(): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["ServerSettings","jsx","Settings"],"mappings":";;;AAOA,SAAwBA,IAAoC;AAC1D,2BACG,OAAA,EAAI,WAAU,6BACb,UAAA,gBAAAC,EAACC,KAAS,GACZ;AAEJ;"}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime");require("react");const r=require("./ServerSettings-LIIP5TPz.cjs");function t(){return e.jsx("div",{className:"cedros-dashboard__section",children:e.jsx(r.ServerSettings,{})})}exports.default=t;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime");require("react");const r=require("./ServerSettings-CKfiLfXi.cjs");function t(){return e.jsx("div",{className:"cedros-dashboard__section",children:e.jsx(r.ServerSettings,{})})}exports.default=t;
@@ -1 +1 @@
1
- {"version":3,"file":"ServerSettings-PH7T8JKI.cjs","sources":["../src/admin/sections/ServerSettings.tsx"],"sourcesContent":["/**\n * Server Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport { ServerSettings as Settings } from '../../components/admin/settings';\n\nexport default function ServerSettings(): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["ServerSettings","jsx","Settings"],"mappings":"uLAOA,SAAwBA,GAAoC,CAC1D,aACG,MAAA,CAAI,UAAU,4BACb,SAAAC,MAACC,EAAAA,iBAAS,EACZ,CAEJ"}
1
+ {"version":3,"file":"ServerSettings-rHrVN8O8.cjs","sources":["../src/admin/sections/ServerSettings.tsx"],"sourcesContent":["/**\n * Server Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport { ServerSettings as Settings } from '../../components/admin/settings';\n\nexport default function ServerSettings(): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["ServerSettings","jsx","Settings"],"mappings":"uLAOA,SAAwBA,GAAoC,CAC1D,aACG,MAAA,CAAI,UAAU,4BACb,SAAAC,MAACC,EAAAA,iBAAS,EACZ,CAEJ"}
@@ -0,0 +1 @@
1
+ "use strict";const l=require("react/jsx-runtime"),n=require("react"),T=require("@solana/wallet-adapter-react"),D=require("@solana/wallet-adapter-react-ui"),W=require("./useCedrosLogin-DtJorrE7.cjs"),N=require("./validation-BuGQrA-K.cjs"),J=require("./LoadingSpinner-d6sSxgQN.cjs");function _(){const{config:e,_internal:t}=W.useCedrosLogin(),[S,o]=n.useState(!1),[j,c]=n.useState(null),g=n.useMemo(()=>new W.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]),L=n.useCallback(async f=>{if(!N.validateSolanaPublicKey(f)){const u={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw c(u),u}o(!0),c(null);try{return await g.post("/solana/challenge",{publicKey:f},{credentials:"omit"})}catch(u){const r=W.handleApiError(u,"Unable to start wallet verification. Please try again.");throw c(r),r}finally{o(!1)}},[g]),i=n.useCallback(async(f,u,r)=>{if(!N.validateSolanaPublicKey(f)){const s={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw c(s),s}o(!0),c(null);try{const s=await g.post("/solana",{publicKey:f,signature:u,message:r});return e.callbacks?.onLoginSuccess?.(s.user,"solana"),t?.handleLoginSuccess(s.user,s.tokens),s}catch(s){const A=W.handleApiError(s,"Unable to sign in with your wallet. Please try again.");throw c(A),A}finally{o(!1)}},[g,e.callbacks,t]),C=n.useCallback(()=>c(null),[]);return{requestChallenge:L,signIn:i,isLoading:S,error:j,clearError:C}}function H(e){if(typeof window>"u")return!1;try{const t=require("@solana-mobile/wallet-standard-mobile"),S=e?.chains??["solana:mainnet"],o={appIdentity:{name:e?.name,uri:e?.uri,icon:e?.icon},chains:S};return typeof t.createDefaultAuthorizationCache=="function"&&(o.authorizationCache=t.createDefaultAuthorizationCache()),typeof t.createDefaultChainSelector=="function"&&(o.chainSelector=t.createDefaultChainSelector()),typeof t.createDefaultWalletNotFoundHandler=="function"&&(o.onWalletNotFound=t.createDefaultWalletNotFoundHandler()),t.registerMwa(o),!0}catch{return!1}}typeof navigator<"u"&&/Android/i.test(navigator.userAgent)&&H();const Q=[];function X(e){return e.walletContext?l.jsx(D.WalletModalProvider,{children:l.jsx(K,{...e})}):l.jsx(T.WalletProvider,{wallets:Q,localStorageKey:"cedros-walletName",children:l.jsx(D.WalletModalProvider,{children:l.jsx(K,{...e})})})}function K({onSuccess:e,onError:t,className:S="",variant:o="default",size:j="md",disabled:c=!1,hideIfNoWallet:g=!0,onLoadingChange:L,walletContext:i}){const{requestChallenge:C,signIn:f,isLoading:u}=_(),r=T.useWallet(),{visible:s,setVisible:A}=D.useWalletModal(),[h,y]=n.useState(!1),[U,P]=n.useState(!1),E=n.useRef(!1),q=n.useRef(!1),v=n.useRef(!1),d=i?.connected??r.connected,b=i?.connecting??r.connecting,m=i?.publicKey??r.publicKey,w=i?.signMessage??r.signMessage,p=i?.wallet??r.wallet,V=i?.wallets??r.wallets,F=i?i.select:a=>r.select(a),z=i?.connect??r.connect,k=V.filter(a=>a.adapter.readyState==="Installed"||a.adapter.readyState==="Loadable"),R=n.useCallback(async()=>{if(!E.current){if(!m||!w){t?.(new Error("Wallet not ready"));return}E.current=!0;try{const a=m.toBase58(),I=await C(a),G=new TextEncoder().encode(I.message),x=await w(G);if(!(x instanceof Uint8Array)||x.length===0)throw new Error("Wallet returned invalid signature");let B;try{B=btoa(String.fromCharCode(...x))}catch{throw new Error("Failed to encode signature")}await f(a,B,I.message),v.current=!1,e?.()}catch(a){const I=a instanceof Error?a:new Error(String(a));v.current=!0,t?.(I)}finally{E.current=!1,y(!1)}}},[m,w,C,f,e,t]);if(n.useEffect(()=>{U&&p&&!d&&!b&&(P(!1),z().catch(a=>{t?.(a instanceof Error?a:new Error(String(a))),y(!1)}))},[U,p,d,b,z,t]),n.useEffect(()=>{h&&d&&m&&w&&!E.current&&R().catch(()=>{})},[h,d,m,w,R]),n.useEffect(()=>{s?q.current=!0:q.current&&(q.current=!1,h&&!d&&p&&!b?P(!0):h&&!d&&y(!1))},[s,h,d,p,b]),g&&k.length===0)return null;const Y=async()=>{c||u||b||(d&&m&&w&&!v.current?(y(!0),await R()):k.length===1&&!p?(F(k[0].adapter.name),y(!0),P(!0)):(v.current=!1,p&&r.select(null),A(!0),y(!0)))},$={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},O={default:"cedros-button-social",outline:"cedros-button-social-outline"},M=u||b||h&&!d;return n.useEffect(()=>{L?.(M)},[M,L]),l.jsxs("button",{type:"button",className:`cedros-button ${O[o]} ${$[j]} ${S}`,onClick:Y,disabled:c||M,"aria-label":"Continue with Solana",children:[M?l.jsx(J.LoadingSpinner,{size:"sm"}):l.jsxs("svg",{className:"cedros-button-icon",width:"18",height:"18",viewBox:"0 0 128 128",fill:"currentColor","aria-hidden":"true",children:[l.jsx("path",{d:"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z"}),l.jsx("path",{d:"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z"}),l.jsx("path",{d:"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z"})]}),l.jsx("span",{children:"Continue with Solana"})]})}exports.SolanaLoginButton=X;exports.registerMobileWallet=H;exports.useSolanaAuth=_;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolanaLoginButton-DAV3r4oB.cjs","sources":["../src/hooks/useSolanaAuth.ts","../src/utils/mobileWalletAdapter.ts","../src/components/solana/SolanaLoginButton.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Unable to start wallet verification. Please try again.');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in with your wallet. Please try again.');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { WalletProvider, useWallet } from '@solana/wallet-adapter-react';\nimport { WalletModalProvider, useWalletModal } from '@solana/wallet-adapter-react-ui';\nimport type { WalletName } from '@solana/wallet-adapter-base';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { registerMobileWallet } from '../../utils/mobileWalletAdapter';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\n// Auto-register Mobile Wallet Adapter on Android for Solana phone (Seeker/Seed Vault) support.\n// Runs at import time so detectSolanaWallets() in LoginForm picks up MWA before first render.\nif (typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent)) {\n registerMobileWallet();\n}\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /** Called when the button's loading state changes (connecting, signing, etc.). */\n onLoadingChange?: (loading: boolean) => void;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * When provided, the component assumes a WalletProvider exists in the React tree and\n * uses the consumer's wallet context for wallet discovery and connection.\n * When omitted, the component provides its own WalletProvider with wallet-standard discovery.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/** Stable empty array to avoid re-renders in self-contained WalletProvider. */\nconst EMPTY_ADAPTERS: [] = [];\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Uses the standard wallet adapter modal for wallet selection, which provides\n * real brand icons and discovers all wallet-standard-compliant wallets.\n *\n * When `walletContext` is provided, assumes a WalletProvider exists in the tree.\n * Otherwise, wraps itself with WalletProvider for self-contained operation.\n */\nexport function SolanaLoginButton(props: SolanaLoginButtonProps) {\n if (props.walletContext) {\n // Consumer has their own WalletProvider; just add modal capability\n return (\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n );\n }\n\n // Self-contained: provide wallet-standard discovery + modal\n return (\n <WalletProvider wallets={EMPTY_ADAPTERS} localStorageKey=\"cedros-walletName\">\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n </WalletProvider>\n );\n}\n\nfunction SolanaLoginInner({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n onLoadingChange,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const adapterWallet = useWallet();\n const { visible: modalVisible, setVisible: setModalVisible } = useWalletModal();\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n const modalWasOpen = useRef(false);\n const signRejectedRef = useRef(false);\n\n // Use walletContext if provided, otherwise use adapter's useWallet()\n const connected = walletContext?.connected ?? adapterWallet.connected;\n const connecting = walletContext?.connecting ?? adapterWallet.connecting;\n const publicKey = walletContext?.publicKey ?? adapterWallet.publicKey;\n const signMessage = walletContext?.signMessage ?? adapterWallet.signMessage;\n const wallet = walletContext?.wallet ?? adapterWallet.wallet;\n const wallets = walletContext?.wallets ?? adapterWallet.wallets;\n const select = walletContext\n ? walletContext.select\n : (name: string) => adapterWallet.select(name as WalletName);\n const connect = walletContext?.connect ?? adapterWallet.connect;\n\n // Get installed/ready wallets\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Execute the sign-in flow (challenge → sign → verify)\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n const challenge = await requestChallenge(pubKeyString);\n\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n await signIn(pubKeyString, signature, challenge.message);\n signRejectedRef.current = false;\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n signRejectedRef.current = true;\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting) {\n setTriggerConnect(false);\n connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, connect, onError]);\n\n // Auto-execute sign-in when connected with pending login\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // When modal closes: connect if a wallet was selected, else reset.\n // Wallet is always deselected before opening, so any non-null wallet = user chose one.\n useEffect(() => {\n if (modalVisible) {\n modalWasOpen.current = true;\n } else if (modalWasOpen.current) {\n modalWasOpen.current = false;\n if (pendingLogin && !connected && wallet && !connecting) {\n setTriggerConnect(true);\n } else if (pendingLogin && !connected) {\n setPendingLogin(false);\n }\n }\n }, [modalVisible, pendingLogin, connected, wallet, connecting]);\n\n // Hide button if no wallets detected\n if (hideIfNoWallet && installedWallets.length === 0) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage && !signRejectedRef.current) {\n // Already connected, previous attempt wasn't rejected — sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (installedWallets.length === 1 && !wallet) {\n // Single installed wallet, nothing remembered — auto-select + connect\n select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else {\n // Show wallet picker — deselect any remembered wallet first so that\n // every selection in the modal registers as \"new\" and dismissing\n // leaves wallet as null (no accidental auto-connect).\n signRejectedRef.current = false;\n if (wallet) {\n adapterWallet.select(null as unknown as WalletName);\n }\n setModalVisible(true);\n setPendingLogin(true);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n // Notify parent of loading state changes\n useEffect(() => {\n onLoadingChange?.(isLoading);\n }, [isLoading, onLoadingChange]);\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n );\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","registerMobileWallet","mwa","chains","registrationConfig","EMPTY_ADAPTERS","SolanaLoginButton","props","WalletModalProvider","jsx","SolanaLoginInner","WalletProvider","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","onLoadingChange","walletContext","isAuthLoading","adapterWallet","useWallet","modalVisible","setModalVisible","useWalletModal","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","modalWasOpen","signRejectedRef","connected","connecting","signMessage","wallet","wallets","select","name","connect","installedWallets","w","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","sizeClasses","variantClasses","jsxs","LoadingSpinner"],"mappings":"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"}