@stigmer/react 0.0.71 → 0.0.73

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 (440) hide show
  1. package/agent/AgentEnvForm.d.ts +2 -0
  2. package/agent/AgentEnvForm.d.ts.map +1 -1
  3. package/agent/AgentEnvForm.js.map +1 -1
  4. package/agent/AgentPicker.d.ts +2 -0
  5. package/agent/AgentPicker.d.ts.map +1 -1
  6. package/agent/AgentPicker.js.map +1 -1
  7. package/agent/agentSetupReducer.d.ts +69 -7
  8. package/agent/agentSetupReducer.d.ts.map +1 -1
  9. package/agent/agentSetupReducer.js.map +1 -1
  10. package/agent/useAgentCount.d.ts +2 -0
  11. package/agent/useAgentCount.d.ts.map +1 -1
  12. package/agent/useAgentCount.js.map +1 -1
  13. package/attachment/AttachmentChipList.d.ts +15 -0
  14. package/attachment/AttachmentChipList.d.ts.map +1 -1
  15. package/attachment/AttachmentChipList.js +15 -0
  16. package/attachment/AttachmentChipList.js.map +1 -1
  17. package/attachment/useAttachments.d.ts +14 -0
  18. package/attachment/useAttachments.d.ts.map +1 -1
  19. package/attachment/useAttachments.js.map +1 -1
  20. package/composer/useComposer.d.ts +5 -0
  21. package/composer/useComposer.d.ts.map +1 -1
  22. package/composer/useComposer.js.map +1 -1
  23. package/execution/ExecutionPhaseBadge.d.ts +1 -1
  24. package/execution/ExecutionPhaseBadge.js +1 -1
  25. package/execution/McpToolDetail.d.ts +16 -8
  26. package/execution/McpToolDetail.d.ts.map +1 -1
  27. package/execution/McpToolDetail.js +0 -6
  28. package/execution/McpToolDetail.js.map +1 -1
  29. package/execution/TodoList.d.ts +8 -0
  30. package/execution/TodoList.d.ts.map +1 -1
  31. package/execution/TodoList.js +8 -0
  32. package/execution/TodoList.js.map +1 -1
  33. package/execution/ToolArgsView.d.ts +1 -0
  34. package/execution/ToolArgsView.d.ts.map +1 -1
  35. package/execution/ToolArgsView.js.map +1 -1
  36. package/execution/UsageWidget.d.ts +1 -1
  37. package/execution/UsageWidget.js +1 -1
  38. package/execution/file-path-resolver.d.ts +10 -0
  39. package/execution/file-path-resolver.d.ts.map +1 -1
  40. package/execution/file-path-resolver.js.map +1 -1
  41. package/execution/index.d.ts +1 -1
  42. package/execution/index.d.ts.map +1 -1
  43. package/execution/index.js.map +1 -1
  44. package/execution/useArtifactContent.d.ts +33 -0
  45. package/execution/useArtifactContent.d.ts.map +1 -1
  46. package/execution/useArtifactContent.js +33 -0
  47. package/execution/useArtifactContent.js.map +1 -1
  48. package/execution/useSubmitApproval.d.ts +2 -0
  49. package/execution/useSubmitApproval.d.ts.map +1 -1
  50. package/execution/useSubmitApproval.js.map +1 -1
  51. package/github/GitHubRepoPicker.d.ts +13 -0
  52. package/github/GitHubRepoPicker.d.ts.map +1 -1
  53. package/github/GitHubRepoPicker.js +13 -0
  54. package/github/GitHubRepoPicker.js.map +1 -1
  55. package/github/useGitHubConnection.d.ts +29 -1
  56. package/github/useGitHubConnection.d.ts.map +1 -1
  57. package/github/useGitHubConnection.js +29 -1
  58. package/github/useGitHubConnection.js.map +1 -1
  59. package/github/useGitHubRepos.d.ts +31 -0
  60. package/github/useGitHubRepos.d.ts.map +1 -1
  61. package/github/useGitHubRepos.js +31 -0
  62. package/github/useGitHubRepos.js.map +1 -1
  63. package/github/useGitHubSearch.d.ts +25 -0
  64. package/github/useGitHubSearch.d.ts.map +1 -1
  65. package/github/useGitHubSearch.js +25 -0
  66. package/github/useGitHubSearch.js.map +1 -1
  67. package/iam-policy/GrantAccessForm.d.ts +39 -0
  68. package/iam-policy/GrantAccessForm.d.ts.map +1 -0
  69. package/iam-policy/GrantAccessForm.js +75 -0
  70. package/iam-policy/GrantAccessForm.js.map +1 -0
  71. package/iam-policy/OrgMembersPanel.d.ts +28 -0
  72. package/iam-policy/OrgMembersPanel.d.ts.map +1 -0
  73. package/iam-policy/OrgMembersPanel.js +192 -0
  74. package/iam-policy/OrgMembersPanel.js.map +1 -0
  75. package/iam-policy/RoleSelector.d.ts +37 -0
  76. package/iam-policy/RoleSelector.d.ts.map +1 -0
  77. package/iam-policy/RoleSelector.js +42 -0
  78. package/iam-policy/RoleSelector.js.map +1 -0
  79. package/iam-policy/index.d.ts +12 -0
  80. package/iam-policy/index.d.ts.map +1 -0
  81. package/iam-policy/index.js +12 -0
  82. package/iam-policy/index.js.map +1 -0
  83. package/iam-policy/useCreateIamPolicy.d.ts +35 -0
  84. package/iam-policy/useCreateIamPolicy.d.ts.map +1 -0
  85. package/iam-policy/useCreateIamPolicy.js +46 -0
  86. package/iam-policy/useCreateIamPolicy.js.map +1 -0
  87. package/iam-policy/useDeleteIamPolicy.d.ts +34 -0
  88. package/iam-policy/useDeleteIamPolicy.d.ts.map +1 -0
  89. package/iam-policy/useDeleteIamPolicy.js +45 -0
  90. package/iam-policy/useDeleteIamPolicy.js.map +1 -0
  91. package/iam-policy/useGrantableRoles.d.ts +28 -0
  92. package/iam-policy/useGrantableRoles.d.ts.map +1 -0
  93. package/iam-policy/useGrantableRoles.js +32 -0
  94. package/iam-policy/useGrantableRoles.js.map +1 -0
  95. package/iam-policy/usePrincipalsCount.d.ts +31 -0
  96. package/iam-policy/usePrincipalsCount.d.ts.map +1 -0
  97. package/iam-policy/usePrincipalsCount.js +72 -0
  98. package/iam-policy/usePrincipalsCount.js.map +1 -0
  99. package/iam-policy/useResourceAccess.d.ts +51 -0
  100. package/iam-policy/useResourceAccess.d.ts.map +1 -0
  101. package/iam-policy/useResourceAccess.js +85 -0
  102. package/iam-policy/useResourceAccess.js.map +1 -0
  103. package/iam-policy/useRevokeOrgAccess.d.ts +36 -0
  104. package/iam-policy/useRevokeOrgAccess.d.ts.map +1 -0
  105. package/iam-policy/useRevokeOrgAccess.js +48 -0
  106. package/iam-policy/useRevokeOrgAccess.js.map +1 -0
  107. package/iam-policy/useRoleSelector.d.ts +52 -0
  108. package/iam-policy/useRoleSelector.d.ts.map +1 -0
  109. package/iam-policy/useRoleSelector.js +50 -0
  110. package/iam-policy/useRoleSelector.js.map +1 -0
  111. package/iam-policy/useWhoAmI.d.ts +30 -0
  112. package/iam-policy/useWhoAmI.d.ts.map +1 -0
  113. package/iam-policy/useWhoAmI.js +56 -0
  114. package/iam-policy/useWhoAmI.js.map +1 -0
  115. package/identity-provider/CreateIdentityProviderForm.d.ts +39 -0
  116. package/identity-provider/CreateIdentityProviderForm.d.ts.map +1 -0
  117. package/identity-provider/CreateIdentityProviderForm.js +102 -0
  118. package/identity-provider/CreateIdentityProviderForm.js.map +1 -0
  119. package/identity-provider/IdentityProviderDetailPanel.d.ts +42 -0
  120. package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -0
  121. package/identity-provider/IdentityProviderDetailPanel.js +156 -0
  122. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -0
  123. package/identity-provider/IdentityProviderListPanel.d.ts +41 -0
  124. package/identity-provider/IdentityProviderListPanel.d.ts.map +1 -0
  125. package/identity-provider/IdentityProviderListPanel.js +110 -0
  126. package/identity-provider/IdentityProviderListPanel.js.map +1 -0
  127. package/identity-provider/IdentityProviderWizard.d.ts +40 -0
  128. package/identity-provider/IdentityProviderWizard.d.ts.map +1 -0
  129. package/identity-provider/IdentityProviderWizard.js +180 -0
  130. package/identity-provider/IdentityProviderWizard.js.map +1 -0
  131. package/identity-provider/ProviderPicker.d.ts +28 -0
  132. package/identity-provider/ProviderPicker.d.ts.map +1 -0
  133. package/identity-provider/ProviderPicker.js +64 -0
  134. package/identity-provider/ProviderPicker.js.map +1 -0
  135. package/identity-provider/SsoLoginPrompt.d.ts +51 -0
  136. package/identity-provider/SsoLoginPrompt.d.ts.map +1 -0
  137. package/identity-provider/SsoLoginPrompt.js +119 -0
  138. package/identity-provider/SsoLoginPrompt.js.map +1 -0
  139. package/identity-provider/index.d.ts +14 -0
  140. package/identity-provider/index.d.ts.map +1 -0
  141. package/identity-provider/index.js +14 -0
  142. package/identity-provider/index.js.map +1 -0
  143. package/identity-provider/presets.d.ts +65 -0
  144. package/identity-provider/presets.d.ts.map +1 -0
  145. package/identity-provider/presets.js +200 -0
  146. package/identity-provider/presets.js.map +1 -0
  147. package/identity-provider/useCreateIdentityProvider.d.ts +37 -0
  148. package/identity-provider/useCreateIdentityProvider.d.ts.map +1 -0
  149. package/identity-provider/useCreateIdentityProvider.js +48 -0
  150. package/identity-provider/useCreateIdentityProvider.js.map +1 -0
  151. package/identity-provider/useDeleteIdentityProvider.d.ts +34 -0
  152. package/identity-provider/useDeleteIdentityProvider.d.ts.map +1 -0
  153. package/identity-provider/useDeleteIdentityProvider.js +45 -0
  154. package/identity-provider/useDeleteIdentityProvider.js.map +1 -0
  155. package/identity-provider/useIdentityProvider.d.ts +37 -0
  156. package/identity-provider/useIdentityProvider.d.ts.map +1 -0
  157. package/identity-provider/useIdentityProvider.js +62 -0
  158. package/identity-provider/useIdentityProvider.js.map +1 -0
  159. package/identity-provider/useIdentityProviderList.d.ts +48 -0
  160. package/identity-provider/useIdentityProviderList.d.ts.map +1 -0
  161. package/identity-provider/useIdentityProviderList.js +75 -0
  162. package/identity-provider/useIdentityProviderList.js.map +1 -0
  163. package/identity-provider/useOidcDiscovery.d.ts +39 -0
  164. package/identity-provider/useOidcDiscovery.d.ts.map +1 -0
  165. package/identity-provider/useOidcDiscovery.js +76 -0
  166. package/identity-provider/useOidcDiscovery.js.map +1 -0
  167. package/identity-provider/useSsoProvider.d.ts +50 -0
  168. package/identity-provider/useSsoProvider.d.ts.map +1 -0
  169. package/identity-provider/useSsoProvider.js +83 -0
  170. package/identity-provider/useSsoProvider.js.map +1 -0
  171. package/identity-provider/useUpdateIdentityProvider.d.ts +38 -0
  172. package/identity-provider/useUpdateIdentityProvider.d.ts.map +1 -0
  173. package/identity-provider/useUpdateIdentityProvider.js +49 -0
  174. package/identity-provider/useUpdateIdentityProvider.js.map +1 -0
  175. package/index.d.ts +14 -5
  176. package/index.d.ts.map +1 -1
  177. package/index.js +10 -2
  178. package/index.js.map +1 -1
  179. package/internal/CloudFeatureNotice.d.ts +12 -0
  180. package/internal/CloudFeatureNotice.d.ts.map +1 -1
  181. package/internal/CloudFeatureNotice.js +12 -0
  182. package/internal/CloudFeatureNotice.js.map +1 -1
  183. package/invitation/InvitationCreatedAlert.d.ts +35 -0
  184. package/invitation/InvitationCreatedAlert.d.ts.map +1 -0
  185. package/invitation/InvitationCreatedAlert.js +60 -0
  186. package/invitation/InvitationCreatedAlert.js.map +1 -0
  187. package/invitation/InvitationManager.d.ts +44 -0
  188. package/invitation/InvitationManager.d.ts.map +1 -0
  189. package/invitation/InvitationManager.js +248 -0
  190. package/invitation/InvitationManager.js.map +1 -0
  191. package/invitation/InvitationRedemption.d.ts +69 -0
  192. package/invitation/InvitationRedemption.d.ts.map +1 -0
  193. package/invitation/InvitationRedemption.js +140 -0
  194. package/invitation/InvitationRedemption.js.map +1 -0
  195. package/invitation/index.d.ts +17 -0
  196. package/invitation/index.d.ts.map +1 -0
  197. package/invitation/index.js +9 -0
  198. package/invitation/index.js.map +1 -0
  199. package/invitation/useCreateInvitation.d.ts +52 -0
  200. package/invitation/useCreateInvitation.d.ts.map +1 -0
  201. package/invitation/useCreateInvitation.js +55 -0
  202. package/invitation/useCreateInvitation.js.map +1 -0
  203. package/invitation/useInvitationPreview.d.ts +51 -0
  204. package/invitation/useInvitationPreview.d.ts.map +1 -0
  205. package/invitation/useInvitationPreview.js +80 -0
  206. package/invitation/useInvitationPreview.js.map +1 -0
  207. package/invitation/useOrgInvitations.d.ts +36 -0
  208. package/invitation/useOrgInvitations.d.ts.map +1 -0
  209. package/invitation/useOrgInvitations.js +65 -0
  210. package/invitation/useOrgInvitations.js.map +1 -0
  211. package/invitation/useRedeemInvitation.d.ts +47 -0
  212. package/invitation/useRedeemInvitation.d.ts.map +1 -0
  213. package/invitation/useRedeemInvitation.js +53 -0
  214. package/invitation/useRedeemInvitation.js.map +1 -0
  215. package/invitation/useRevokeInvitation.d.ts +35 -0
  216. package/invitation/useRevokeInvitation.d.ts.map +1 -0
  217. package/invitation/useRevokeInvitation.js +47 -0
  218. package/invitation/useRevokeInvitation.js.map +1 -0
  219. package/library/detect-skill-package.d.ts +1 -0
  220. package/library/detect-skill-package.d.ts.map +1 -1
  221. package/library/detect-skill-package.js.map +1 -1
  222. package/library/detect-stigmer-resource.d.ts +1 -0
  223. package/library/detect-stigmer-resource.d.ts.map +1 -1
  224. package/library/detect-stigmer-resource.js.map +1 -1
  225. package/library/parse-resource-yaml.d.ts +4 -0
  226. package/library/parse-resource-yaml.d.ts.map +1 -1
  227. package/library/parse-resource-yaml.js.map +1 -1
  228. package/mcp-server/McpServerDetailView.d.ts +2 -2
  229. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  230. package/mcp-server/McpServerDetailView.js.map +1 -1
  231. package/mcp-server/index.d.ts +1 -1
  232. package/mcp-server/index.d.ts.map +1 -1
  233. package/mcp-server/index.js.map +1 -1
  234. package/mcp-server/mcpServerSetupReducer.d.ts +54 -0
  235. package/mcp-server/mcpServerSetupReducer.d.ts.map +1 -1
  236. package/mcp-server/mcpServerSetupReducer.js.map +1 -1
  237. package/models/ModelSelector.d.ts +9 -0
  238. package/models/ModelSelector.d.ts.map +1 -1
  239. package/models/ModelSelector.js +9 -0
  240. package/models/ModelSelector.js.map +1 -1
  241. package/models/registry.d.ts +11 -1
  242. package/models/registry.d.ts.map +1 -1
  243. package/models/registry.js.map +1 -1
  244. package/models/useModelRegistry.d.ts +20 -0
  245. package/models/useModelRegistry.d.ts.map +1 -1
  246. package/models/useModelRegistry.js +20 -0
  247. package/models/useModelRegistry.js.map +1 -1
  248. package/organization/OrgProfilePanel.d.ts +31 -0
  249. package/organization/OrgProfilePanel.d.ts.map +1 -0
  250. package/organization/OrgProfilePanel.js +147 -0
  251. package/organization/OrgProfilePanel.js.map +1 -0
  252. package/organization/index.d.ts +6 -0
  253. package/organization/index.d.ts.map +1 -1
  254. package/organization/index.js +3 -0
  255. package/organization/index.js.map +1 -1
  256. package/organization/useOrganization.d.ts +31 -0
  257. package/organization/useOrganization.d.ts.map +1 -0
  258. package/organization/useOrganization.js +56 -0
  259. package/organization/useOrganization.js.map +1 -0
  260. package/organization/useUpdateOrganization.d.ts +40 -0
  261. package/organization/useUpdateOrganization.d.ts.map +1 -0
  262. package/organization/useUpdateOrganization.js +51 -0
  263. package/organization/useUpdateOrganization.js.map +1 -0
  264. package/package.json +4 -4
  265. package/provider.d.ts +1 -0
  266. package/provider.d.ts.map +1 -1
  267. package/provider.js.map +1 -1
  268. package/search/useResourceList.d.ts +4 -2
  269. package/search/useResourceList.d.ts.map +1 -1
  270. package/search/useResourceList.js +2 -1
  271. package/search/useResourceList.js.map +1 -1
  272. package/search/useResourceSearch.d.ts +10 -2
  273. package/search/useResourceSearch.d.ts.map +1 -1
  274. package/search/useResourceSearch.js +2 -1
  275. package/search/useResourceSearch.js.map +1 -1
  276. package/session/group-sessions.d.ts +23 -0
  277. package/session/group-sessions.d.ts.map +1 -1
  278. package/session/group-sessions.js +23 -0
  279. package/session/group-sessions.js.map +1 -1
  280. package/session/index.d.ts +1 -1
  281. package/session/index.d.ts.map +1 -1
  282. package/session/index.js.map +1 -1
  283. package/session/useCreateSession.d.ts +11 -2
  284. package/session/useCreateSession.d.ts.map +1 -1
  285. package/session/useCreateSession.js.map +1 -1
  286. package/session/useSession.d.ts +24 -0
  287. package/session/useSession.d.ts.map +1 -1
  288. package/session/useSession.js +24 -0
  289. package/session/useSession.js.map +1 -1
  290. package/session/useSessionArtifacts.d.ts +1 -0
  291. package/session/useSessionArtifacts.d.ts.map +1 -1
  292. package/session/useSessionArtifacts.js.map +1 -1
  293. package/session/useSessionConversation.d.ts +7 -0
  294. package/session/useSessionConversation.d.ts.map +1 -1
  295. package/session/useSessionConversation.js.map +1 -1
  296. package/session/useSessionExecutions.d.ts +23 -0
  297. package/session/useSessionExecutions.d.ts.map +1 -1
  298. package/session/useSessionExecutions.js +23 -0
  299. package/session/useSessionExecutions.js.map +1 -1
  300. package/session/useSessionList.d.ts +19 -0
  301. package/session/useSessionList.d.ts.map +1 -1
  302. package/session/useSessionList.js +19 -0
  303. package/session/useSessionList.js.map +1 -1
  304. package/session/useSessionWriteBacks.d.ts +1 -0
  305. package/session/useSessionWriteBacks.d.ts.map +1 -1
  306. package/session/useSessionWriteBacks.js.map +1 -1
  307. package/session/useUpdateSession.d.ts +22 -0
  308. package/session/useUpdateSession.d.ts.map +1 -1
  309. package/session/useUpdateSession.js +22 -0
  310. package/session/useUpdateSession.js.map +1 -1
  311. package/skill/SkillPicker.d.ts +15 -0
  312. package/skill/SkillPicker.d.ts.map +1 -1
  313. package/skill/SkillPicker.js +15 -0
  314. package/skill/SkillPicker.js.map +1 -1
  315. package/src/agent/AgentEnvForm.tsx +2 -0
  316. package/src/agent/AgentPicker.tsx +2 -0
  317. package/src/agent/agentSetupReducer.ts +109 -20
  318. package/src/agent/useAgentCount.ts +2 -0
  319. package/src/attachment/AttachmentChipList.tsx +15 -0
  320. package/src/attachment/useAttachments.ts +14 -0
  321. package/src/composer/useComposer.ts +5 -0
  322. package/src/execution/ExecutionPhaseBadge.tsx +1 -1
  323. package/src/execution/McpToolDetail.tsx +18 -6
  324. package/src/execution/TodoList.tsx +8 -0
  325. package/src/execution/ToolArgsView.tsx +1 -0
  326. package/src/execution/UsageWidget.tsx +1 -1
  327. package/src/execution/file-path-resolver.ts +28 -4
  328. package/src/execution/index.ts +5 -1
  329. package/src/execution/useArtifactContent.ts +33 -0
  330. package/src/execution/useSubmitApproval.ts +2 -0
  331. package/src/github/GitHubRepoPicker.tsx +13 -0
  332. package/src/github/useGitHubConnection.ts +29 -1
  333. package/src/github/useGitHubRepos.ts +31 -0
  334. package/src/github/useGitHubSearch.ts +25 -0
  335. package/src/iam-policy/GrantAccessForm.tsx +198 -0
  336. package/src/iam-policy/OrgMembersPanel.tsx +572 -0
  337. package/src/iam-policy/RoleSelector.tsx +105 -0
  338. package/src/iam-policy/index.ts +51 -0
  339. package/src/iam-policy/useCreateIamPolicy.ts +66 -0
  340. package/src/iam-policy/useDeleteIamPolicy.ts +65 -0
  341. package/src/iam-policy/useGrantableRoles.ts +45 -0
  342. package/src/iam-policy/usePrincipalsCount.ts +96 -0
  343. package/src/iam-policy/useResourceAccess.ts +128 -0
  344. package/src/iam-policy/useRevokeOrgAccess.ts +74 -0
  345. package/src/iam-policy/useRoleSelector.ts +97 -0
  346. package/src/iam-policy/useWhoAmI.ts +78 -0
  347. package/src/identity-provider/CreateIdentityProviderForm.tsx +308 -0
  348. package/src/identity-provider/IdentityProviderDetailPanel.tsx +583 -0
  349. package/src/identity-provider/IdentityProviderListPanel.tsx +370 -0
  350. package/src/identity-provider/IdentityProviderWizard.tsx +684 -0
  351. package/src/identity-provider/ProviderPicker.tsx +152 -0
  352. package/src/identity-provider/SsoLoginPrompt.tsx +404 -0
  353. package/src/identity-provider/index.ts +67 -0
  354. package/src/identity-provider/presets.ts +262 -0
  355. package/src/identity-provider/useCreateIdentityProvider.ts +68 -0
  356. package/src/identity-provider/useDeleteIdentityProvider.ts +65 -0
  357. package/src/identity-provider/useIdentityProvider.ts +86 -0
  358. package/src/identity-provider/useIdentityProviderList.ts +100 -0
  359. package/src/identity-provider/useOidcDiscovery.ts +124 -0
  360. package/src/identity-provider/useSsoProvider.ts +99 -0
  361. package/src/identity-provider/useUpdateIdentityProvider.ts +69 -0
  362. package/src/index.ts +121 -1
  363. package/src/internal/CloudFeatureNotice.tsx +12 -0
  364. package/src/invitation/InvitationCreatedAlert.tsx +185 -0
  365. package/src/invitation/InvitationManager.tsx +842 -0
  366. package/src/invitation/InvitationRedemption.tsx +434 -0
  367. package/src/invitation/index.ts +16 -0
  368. package/src/invitation/useCreateInvitation.ts +83 -0
  369. package/src/invitation/useInvitationPreview.ts +103 -0
  370. package/src/invitation/useOrgInvitations.ts +88 -0
  371. package/src/invitation/useRedeemInvitation.ts +82 -0
  372. package/src/invitation/useRevokeInvitation.ts +66 -0
  373. package/src/library/detect-skill-package.ts +4 -1
  374. package/src/library/detect-stigmer-resource.ts +4 -1
  375. package/src/library/parse-resource-yaml.ts +12 -2
  376. package/src/mcp-server/McpServerDetailView.tsx +2 -1
  377. package/src/mcp-server/index.ts +4 -1
  378. package/src/mcp-server/mcpServerSetupReducer.ts +86 -8
  379. package/src/models/ModelSelector.tsx +9 -0
  380. package/src/models/registry.ts +11 -1
  381. package/src/models/useModelRegistry.ts +20 -0
  382. package/src/organization/OrgProfilePanel.tsx +451 -0
  383. package/src/organization/index.ts +6 -0
  384. package/src/organization/useOrganization.ts +79 -0
  385. package/src/organization/useUpdateOrganization.ts +71 -0
  386. package/src/provider.tsx +1 -0
  387. package/src/search/useResourceList.ts +6 -3
  388. package/src/search/useResourceSearch.ts +12 -3
  389. package/src/session/group-sessions.ts +23 -0
  390. package/src/session/index.ts +1 -0
  391. package/src/session/useCreateSession.ts +19 -3
  392. package/src/session/useSession.ts +24 -0
  393. package/src/session/useSessionArtifacts.ts +1 -0
  394. package/src/session/useSessionConversation.ts +7 -0
  395. package/src/session/useSessionExecutions.ts +23 -0
  396. package/src/session/useSessionList.ts +19 -0
  397. package/src/session/useSessionWriteBacks.ts +1 -0
  398. package/src/session/useUpdateSession.ts +22 -0
  399. package/src/skill/SkillPicker.tsx +15 -0
  400. package/src/usage/OrgUsagePanel.tsx +465 -0
  401. package/src/usage/date-range.ts +86 -0
  402. package/src/usage/index.ts +13 -0
  403. package/src/usage/useOrgUsageReport.ts +110 -0
  404. package/src/workspace/FolderBrowser.tsx +9 -0
  405. package/src/workspace/WorkspaceEditor.tsx +17 -0
  406. package/src/workspace/useFolderListing.ts +24 -0
  407. package/src/workspace/useWorkspaceEntries.ts +38 -0
  408. package/styles.css +1 -1
  409. package/usage/OrgUsagePanel.d.ts +24 -0
  410. package/usage/OrgUsagePanel.d.ts.map +1 -0
  411. package/usage/OrgUsagePanel.js +134 -0
  412. package/usage/OrgUsagePanel.js.map +1 -0
  413. package/usage/date-range.d.ts +36 -0
  414. package/usage/date-range.d.ts.map +1 -0
  415. package/usage/date-range.js +69 -0
  416. package/usage/date-range.js.map +1 -0
  417. package/usage/index.d.ts +7 -0
  418. package/usage/index.d.ts.map +1 -0
  419. package/usage/index.js +4 -0
  420. package/usage/index.js.map +1 -0
  421. package/usage/useOrgUsageReport.d.ts +48 -0
  422. package/usage/useOrgUsageReport.d.ts.map +1 -0
  423. package/usage/useOrgUsageReport.js +72 -0
  424. package/usage/useOrgUsageReport.js.map +1 -0
  425. package/workspace/FolderBrowser.d.ts +9 -0
  426. package/workspace/FolderBrowser.d.ts.map +1 -1
  427. package/workspace/FolderBrowser.js +9 -0
  428. package/workspace/FolderBrowser.js.map +1 -1
  429. package/workspace/WorkspaceEditor.d.ts +17 -0
  430. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  431. package/workspace/WorkspaceEditor.js +17 -0
  432. package/workspace/WorkspaceEditor.js.map +1 -1
  433. package/workspace/useFolderListing.d.ts +24 -0
  434. package/workspace/useFolderListing.d.ts.map +1 -1
  435. package/workspace/useFolderListing.js +24 -0
  436. package/workspace/useFolderListing.js.map +1 -1
  437. package/workspace/useWorkspaceEntries.d.ts +38 -0
  438. package/workspace/useWorkspaceEntries.d.ts.map +1 -1
  439. package/workspace/useWorkspaceEntries.js +25 -0
  440. package/workspace/useWorkspaceEntries.js.map +1 -1
@@ -0,0 +1,842 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState, type FormEvent } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+ import {
6
+ getUserMessage,
7
+ iamRoleDisplayName,
8
+ } from "@stigmer/sdk";
9
+ import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
10
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
11
+ import { InvitationState } from "@stigmer/protos/ai/stigmer/iam/invitation/v1/enum_pb";
12
+ import type { Invitation } from "@stigmer/protos/ai/stigmer/iam/invitation/v1/api_pb";
13
+ import { timestampDate } from "@bufbuild/protobuf/wkt";
14
+ import { useOrgInvitations } from "./useOrgInvitations";
15
+ import { useCreateInvitation } from "./useCreateInvitation";
16
+ import { useRevokeInvitation } from "./useRevokeInvitation";
17
+ import { InvitationCreatedAlert } from "./InvitationCreatedAlert";
18
+ import { RoleSelector } from "../iam-policy/RoleSelector";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Public API
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /** Props for {@link InvitationManager}. */
25
+ export interface InvitationManagerProps {
26
+ /** Organization slug whose invitations to manage. */
27
+ readonly org: string;
28
+ /**
29
+ * Build the full invite URL from a token.
30
+ *
31
+ * Platform builders override this when their invite route differs
32
+ * from the default `/invite/<token>` path or runs on a different
33
+ * domain.
34
+ *
35
+ * @default `${window.location.origin}/invite/${token}`
36
+ */
37
+ readonly buildInviteUrl?: (token: string) => string;
38
+ /** Additional CSS class names for the root container. */
39
+ readonly className?: string;
40
+ }
41
+
42
+ function defaultBuildInviteUrl(token: string): string {
43
+ if (typeof window !== "undefined") {
44
+ return `${window.location.origin}/invite/${token}`;
45
+ }
46
+ return `/invite/${token}`;
47
+ }
48
+
49
+ type FlowState =
50
+ | { phase: "idle" }
51
+ | { phase: "creating" }
52
+ | { phase: "created"; inviteUrl: string; label: string };
53
+
54
+ /**
55
+ * Self-contained panel for managing organization invitation links.
56
+ *
57
+ * Displays all invitations for the organization with inline actions
58
+ * (copy link, revoke) and a create form for generating new invite
59
+ * links. Follows the same pattern as {@link OrgMembersPanel}: a
60
+ * single embeddable component that handles the full management flow.
61
+ *
62
+ * The component composes three invitation hooks internally:
63
+ * - {@link useOrgInvitations} for listing
64
+ * - {@link useCreateInvitation} for creation
65
+ * - {@link useRevokeInvitation} for revocation
66
+ *
67
+ * All visual properties flow through `--stgm-*` design tokens.
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * <InvitationManager org="acme" />
72
+ *
73
+ * <InvitationManager
74
+ * org="acme"
75
+ * buildInviteUrl={(token) => `https://myapp.com/join/${token}`}
76
+ * />
77
+ * ```
78
+ */
79
+ export function InvitationManager({
80
+ org,
81
+ buildInviteUrl = defaultBuildInviteUrl,
82
+ className,
83
+ }: InvitationManagerProps) {
84
+ const { invitations, isLoading, error, refetch } = useOrgInvitations(org);
85
+ const [flow, setFlow] = useState<FlowState>({ phase: "idle" });
86
+ const [revokingId, setRevokingId] = useState<string | null>(null);
87
+
88
+ const activeCount = invitations.filter(
89
+ (inv) => inv.status?.state === InvitationState.active,
90
+ ).length;
91
+
92
+ const handleCreated = useCallback(
93
+ (invitation: Invitation) => {
94
+ const token = invitation.status?.token ?? "";
95
+ const label = invitation.spec?.label || invitation.metadata?.name || "Invite link";
96
+ setFlow({ phase: "created", inviteUrl: buildInviteUrl(token), label });
97
+ refetch();
98
+ },
99
+ [buildInviteUrl, refetch],
100
+ );
101
+
102
+ const handleRevoked = useCallback(() => {
103
+ setRevokingId(null);
104
+ refetch();
105
+ }, [refetch]);
106
+
107
+ if (isLoading) {
108
+ return (
109
+ <div
110
+ className={cn("space-y-2", className)}
111
+ aria-busy="true"
112
+ aria-label="Loading invitations"
113
+ >
114
+ {Array.from({ length: 2 }, (_, i) => (
115
+ <div
116
+ key={i}
117
+ className="bg-muted/40 h-14 animate-pulse rounded-lg"
118
+ />
119
+ ))}
120
+ </div>
121
+ );
122
+ }
123
+
124
+ if (error) {
125
+ return (
126
+ <p className={cn("text-destructive text-xs", className)} role="alert">
127
+ {getUserMessage(error)}
128
+ </p>
129
+ );
130
+ }
131
+
132
+ return (
133
+ <div className={cn("space-y-3", className)}>
134
+ {/* Header */}
135
+ <div className="flex items-center justify-between">
136
+ <div className="flex items-center gap-2">
137
+ <span className="text-sm font-semibold text-foreground">
138
+ Invite Links
139
+ </span>
140
+ {activeCount > 0 && (
141
+ <span className="inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[0.65rem] font-medium text-muted-foreground">
142
+ {activeCount} active
143
+ </span>
144
+ )}
145
+ </div>
146
+ {flow.phase === "idle" && (
147
+ <button
148
+ type="button"
149
+ onClick={() => setFlow({ phase: "creating" })}
150
+ className="text-primary hover:text-foreground text-xs font-medium transition-colors"
151
+ >
152
+ + Create invite link
153
+ </button>
154
+ )}
155
+ </div>
156
+
157
+ {/* Created alert */}
158
+ {flow.phase === "created" && (
159
+ <InvitationCreatedAlert
160
+ inviteUrl={flow.inviteUrl}
161
+ label={flow.label}
162
+ onDismiss={() => setFlow({ phase: "idle" })}
163
+ />
164
+ )}
165
+
166
+ {/* Create form */}
167
+ {flow.phase === "creating" && (
168
+ <div className="border-border bg-card rounded-lg border p-4">
169
+ <CreateInvitationForm
170
+ org={org}
171
+ onCreated={handleCreated}
172
+ onCancel={() => setFlow({ phase: "idle" })}
173
+ />
174
+ </div>
175
+ )}
176
+
177
+ {/* Invitation list */}
178
+ {invitations.length === 0 ? (
179
+ <p className="text-muted-foreground py-4 text-center text-xs">
180
+ No invite links yet. Create one to start inviting people.
181
+ </p>
182
+ ) : (
183
+ <div
184
+ role="list"
185
+ aria-label="Invitation links"
186
+ className="space-y-2"
187
+ >
188
+ {invitations.map((inv) => {
189
+ const id = inv.metadata?.id ?? "";
190
+ return (
191
+ <InvitationRow
192
+ key={id}
193
+ invitation={inv}
194
+ buildInviteUrl={buildInviteUrl}
195
+ isRevoking={revokingId === id}
196
+ onStartRevoke={() => setRevokingId(id)}
197
+ onCancelRevoke={() => setRevokingId(null)}
198
+ onRevoked={handleRevoked}
199
+ />
200
+ );
201
+ })}
202
+ </div>
203
+ )}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ // ---------------------------------------------------------------------------
209
+ // CreateInvitationForm (internal)
210
+ // ---------------------------------------------------------------------------
211
+
212
+ type ExpiryOption = "7" | "14" | "30";
213
+
214
+ const EXPIRY_OPTIONS: { value: ExpiryOption; label: string }[] = [
215
+ { value: "7", label: "7 days" },
216
+ { value: "14", label: "14 days" },
217
+ { value: "30", label: "30 days" },
218
+ ];
219
+
220
+ type RedemptionMode = "unlimited" | "single";
221
+
222
+ function CreateInvitationForm({
223
+ org,
224
+ onCreated,
225
+ onCancel,
226
+ }: {
227
+ org: string;
228
+ onCreated: (invitation: Invitation) => void;
229
+ onCancel: () => void;
230
+ }) {
231
+ const { create, isCreating, error, clearError } = useCreateInvitation();
232
+
233
+ const [label, setLabel] = useState("");
234
+ const [role, setRole] = useState<IamRole>(IamRole.viewer);
235
+ const [expiry, setExpiry] = useState<ExpiryOption>("30");
236
+ const [redemptionMode, setRedemptionMode] = useState<RedemptionMode>("unlimited");
237
+
238
+ const canSubmit = !isCreating;
239
+
240
+ const handleSubmit = useCallback(
241
+ async (e: FormEvent) => {
242
+ e.preventDefault();
243
+ if (!canSubmit) return;
244
+
245
+ clearError();
246
+ try {
247
+ const invitation = await create({
248
+ name: label.trim() || `invite-${Date.now()}`,
249
+ org,
250
+ role,
251
+ expiresAt: daysFromNow(Number(expiry)),
252
+ maxRedemptions: redemptionMode === "single" ? 1 : 0,
253
+ label: label.trim() || undefined,
254
+ });
255
+ onCreated(invitation);
256
+ } catch {
257
+ // error state is managed by useCreateInvitation
258
+ }
259
+ },
260
+ [canSubmit, label, org, role, expiry, redemptionMode, create, clearError, onCreated],
261
+ );
262
+
263
+ return (
264
+ <form onSubmit={handleSubmit} className="space-y-3">
265
+ {/* Label */}
266
+ <div className="space-y-1">
267
+ <label
268
+ htmlFor="stgm-new-invite-label"
269
+ className="text-xs font-medium text-foreground"
270
+ >
271
+ Label{" "}
272
+ <span className="font-normal text-muted-foreground">(optional)</span>
273
+ </label>
274
+ <input
275
+ id="stgm-new-invite-label"
276
+ type="text"
277
+ value={label}
278
+ onChange={(e) => setLabel(e.target.value)}
279
+ placeholder="e.g. Engineering team link"
280
+ disabled={isCreating}
281
+ autoFocus
282
+ maxLength={200}
283
+ className={cn(
284
+ "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground",
285
+ "placeholder:text-muted-foreground",
286
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
287
+ "disabled:pointer-events-none disabled:opacity-50",
288
+ )}
289
+ />
290
+ </div>
291
+
292
+ {/* Role */}
293
+ <RoleSelector
294
+ kind={ApiResourceKind.organization}
295
+ selected={role}
296
+ onSelect={(r) => setRole(r)}
297
+ disabled={isCreating}
298
+ />
299
+
300
+ {/* Expiry */}
301
+ <fieldset className="space-y-1.5">
302
+ <legend className="text-xs font-medium text-foreground">
303
+ Expires in
304
+ </legend>
305
+ <div className="flex flex-wrap gap-2">
306
+ {EXPIRY_OPTIONS.map(({ value, label: optLabel }) => (
307
+ <ExpiryRadio
308
+ key={value}
309
+ value={value}
310
+ label={optLabel}
311
+ checked={expiry === value}
312
+ disabled={isCreating}
313
+ onChange={setExpiry}
314
+ />
315
+ ))}
316
+ </div>
317
+ </fieldset>
318
+
319
+ {/* Redemption mode */}
320
+ <fieldset className="space-y-1.5">
321
+ <legend className="text-xs font-medium text-foreground">
322
+ Usage limit
323
+ </legend>
324
+ <div className="flex flex-wrap gap-2">
325
+ <RedemptionRadio
326
+ value="unlimited"
327
+ label="Unlimited"
328
+ description="Anyone with the link can join"
329
+ checked={redemptionMode === "unlimited"}
330
+ disabled={isCreating}
331
+ onChange={setRedemptionMode}
332
+ />
333
+ <RedemptionRadio
334
+ value="single"
335
+ label="Single use"
336
+ description="One person only"
337
+ checked={redemptionMode === "single"}
338
+ disabled={isCreating}
339
+ onChange={setRedemptionMode}
340
+ />
341
+ </div>
342
+ </fieldset>
343
+
344
+ {error && (
345
+ <p className="text-destructive text-[0.65rem]" role="alert">
346
+ {getUserMessage(error)}
347
+ </p>
348
+ )}
349
+
350
+ <div className="flex items-center gap-2">
351
+ <button
352
+ type="submit"
353
+ disabled={!canSubmit}
354
+ className={cn(
355
+ "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium",
356
+ "bg-primary text-primary-foreground hover:bg-primary/90",
357
+ "disabled:pointer-events-none disabled:opacity-40",
358
+ )}
359
+ >
360
+ {isCreating && <SpinnerIcon />}
361
+ Create invite link
362
+ </button>
363
+
364
+ <button
365
+ type="button"
366
+ onClick={onCancel}
367
+ disabled={isCreating}
368
+ className={cn(
369
+ "rounded-md px-3 py-1.5 text-xs",
370
+ "text-muted-foreground hover:text-foreground hover:bg-accent/50",
371
+ "disabled:pointer-events-none disabled:opacity-50",
372
+ )}
373
+ >
374
+ Cancel
375
+ </button>
376
+ </div>
377
+ </form>
378
+ );
379
+ }
380
+
381
+ // ---------------------------------------------------------------------------
382
+ // InvitationRow (internal)
383
+ // ---------------------------------------------------------------------------
384
+
385
+ function InvitationRow({
386
+ invitation,
387
+ buildInviteUrl,
388
+ isRevoking,
389
+ onStartRevoke,
390
+ onCancelRevoke,
391
+ onRevoked,
392
+ }: {
393
+ invitation: Invitation;
394
+ buildInviteUrl: (token: string) => string;
395
+ isRevoking: boolean;
396
+ onStartRevoke: () => void;
397
+ onCancelRevoke: () => void;
398
+ onRevoked: () => void;
399
+ }) {
400
+ const [copied, setCopied] = useState(false);
401
+
402
+ const id = invitation.metadata?.id ?? "";
403
+ const label = invitation.spec?.label || invitation.metadata?.name || "Unnamed invite";
404
+ const role = invitation.spec?.role ?? IamRole.iam_role_unspecified;
405
+ const state = invitation.status?.state ?? InvitationState.invitation_state_unspecified;
406
+ const token = invitation.status?.token ?? "";
407
+ const redemptionCount = invitation.status?.redemptionCount ?? 0;
408
+ const maxRedemptions = invitation.spec?.maxRedemptions ?? 0;
409
+ const expiresAt = invitation.spec?.expiresAt;
410
+ const isActive = state === InvitationState.active;
411
+
412
+ const handleCopy = useCallback(async () => {
413
+ try {
414
+ await navigator.clipboard.writeText(buildInviteUrl(token));
415
+ setCopied(true);
416
+ setTimeout(() => setCopied(false), 2000);
417
+ } catch {
418
+ // silently fail — copy is a convenience, not critical
419
+ }
420
+ }, [token, buildInviteUrl]);
421
+
422
+ if (isRevoking) {
423
+ return (
424
+ <RevokeConfirmation
425
+ invitationId={id}
426
+ label={label}
427
+ onRevoked={onRevoked}
428
+ onCancel={onCancelRevoke}
429
+ />
430
+ );
431
+ }
432
+
433
+ return (
434
+ <div
435
+ role="listitem"
436
+ className="flex items-center gap-3 rounded-lg border border-border/60 px-3 py-2.5 hover:border-border transition-colors"
437
+ >
438
+ {/* Icon */}
439
+ <LinkIcon active={isActive} />
440
+
441
+ {/* Label + redemption info */}
442
+ <div className="min-w-0 flex-1">
443
+ <span
444
+ className={cn(
445
+ "block truncate text-sm font-medium",
446
+ isActive ? "text-foreground" : "text-muted-foreground",
447
+ )}
448
+ >
449
+ {label}
450
+ </span>
451
+ <span className="block text-xs text-muted-foreground">
452
+ {formatRedemptions(redemptionCount, maxRedemptions)}
453
+ </span>
454
+ </div>
455
+
456
+ {/* Metadata columns */}
457
+ <div className="hidden sm:flex shrink-0 items-center gap-3">
458
+ {/* Role badge */}
459
+ <span className="inline-flex items-center rounded-md border border-border bg-muted px-2 py-0.5 text-[0.65rem] font-medium text-foreground">
460
+ {iamRoleDisplayName(role)}
461
+ </span>
462
+
463
+ {/* State badge */}
464
+ <StateBadge state={state} />
465
+
466
+ {/* Expiry */}
467
+ {expiresAt && (
468
+ <span className="text-xs text-muted-foreground" title={timestampDate(expiresAt).toISOString()}>
469
+ {isActive
470
+ ? formatRelativeExpiry(timestampDate(expiresAt))
471
+ : formatShortDate(timestampDate(expiresAt))}
472
+ </span>
473
+ )}
474
+ </div>
475
+
476
+ {/* Actions */}
477
+ <div className="flex shrink-0 items-center gap-1">
478
+ {isActive && (
479
+ <button
480
+ type="button"
481
+ onClick={handleCopy}
482
+ aria-label={copied ? "Link copied" : `Copy invite link for ${label}`}
483
+ className={cn(
484
+ "shrink-0 rounded p-1 transition-colors",
485
+ copied
486
+ ? "text-primary"
487
+ : "text-muted-foreground hover:text-foreground hover:bg-accent/50",
488
+ )}
489
+ >
490
+ {copied ? <CheckIcon /> : <CopyIcon />}
491
+ </button>
492
+ )}
493
+ {isActive && (
494
+ <button
495
+ type="button"
496
+ onClick={onStartRevoke}
497
+ aria-label={`Revoke ${label}`}
498
+ className="shrink-0 rounded p-1 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
499
+ >
500
+ <RevokeIcon />
501
+ </button>
502
+ )}
503
+ </div>
504
+ </div>
505
+ );
506
+ }
507
+
508
+ // ---------------------------------------------------------------------------
509
+ // RevokeConfirmation (internal)
510
+ // ---------------------------------------------------------------------------
511
+
512
+ function RevokeConfirmation({
513
+ invitationId,
514
+ label,
515
+ onRevoked,
516
+ onCancel,
517
+ }: {
518
+ invitationId: string;
519
+ label: string;
520
+ onRevoked: () => void;
521
+ onCancel: () => void;
522
+ }) {
523
+ const { revoke, isRevoking, error } = useRevokeInvitation();
524
+
525
+ const handleConfirm = useCallback(async () => {
526
+ try {
527
+ await revoke(invitationId);
528
+ onRevoked();
529
+ } catch {
530
+ // error state is surfaced via the hook
531
+ }
532
+ }, [invitationId, revoke, onRevoked]);
533
+
534
+ return (
535
+ <div
536
+ role="listitem"
537
+ className="flex items-center justify-between rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5"
538
+ >
539
+ <div className="min-w-0 flex-1">
540
+ <p className="text-xs text-foreground">
541
+ Revoke <span className="font-medium">{label}</span>? The link will
542
+ stop working immediately.
543
+ </p>
544
+ {error && (
545
+ <p className="mt-0.5 text-[0.65rem] text-destructive">
546
+ {getUserMessage(error)}
547
+ </p>
548
+ )}
549
+ </div>
550
+
551
+ <div className="flex shrink-0 items-center gap-1.5 ml-3">
552
+ <button
553
+ type="button"
554
+ onClick={handleConfirm}
555
+ disabled={isRevoking}
556
+ className={cn(
557
+ "inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium",
558
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
559
+ "disabled:pointer-events-none disabled:opacity-50",
560
+ )}
561
+ >
562
+ {isRevoking && <SpinnerIcon />}
563
+ Revoke
564
+ </button>
565
+ <button
566
+ type="button"
567
+ onClick={onCancel}
568
+ disabled={isRevoking}
569
+ className={cn(
570
+ "rounded-md px-2.5 py-1 text-xs",
571
+ "text-muted-foreground hover:text-foreground hover:bg-accent/50",
572
+ "disabled:pointer-events-none disabled:opacity-50",
573
+ )}
574
+ >
575
+ Cancel
576
+ </button>
577
+ </div>
578
+ </div>
579
+ );
580
+ }
581
+
582
+ // ---------------------------------------------------------------------------
583
+ // StateBadge (internal)
584
+ // ---------------------------------------------------------------------------
585
+
586
+ function StateBadge({ state }: { state: InvitationState }) {
587
+ const config = STATE_BADGE_CONFIG[state] ?? STATE_BADGE_CONFIG[InvitationState.invitation_state_unspecified];
588
+ return (
589
+ <span
590
+ className={cn(
591
+ "inline-flex items-center rounded-full px-2 py-0.5 text-[0.6rem] font-medium",
592
+ config.className,
593
+ )}
594
+ >
595
+ {config.label}
596
+ </span>
597
+ );
598
+ }
599
+
600
+ const STATE_BADGE_CONFIG: Record<InvitationState, { label: string; className: string }> = {
601
+ [InvitationState.invitation_state_unspecified]: {
602
+ label: "Unknown",
603
+ className: "bg-muted text-muted-foreground",
604
+ },
605
+ [InvitationState.active]: {
606
+ label: "Active",
607
+ className: "bg-primary-subtle text-primary",
608
+ },
609
+ [InvitationState.expired]: {
610
+ label: "Expired",
611
+ className: "bg-muted text-muted-foreground",
612
+ },
613
+ [InvitationState.revoked]: {
614
+ label: "Revoked",
615
+ className: "bg-muted text-muted-foreground",
616
+ },
617
+ [InvitationState.fully_redeemed]: {
618
+ label: "Fully redeemed",
619
+ className: "bg-muted text-muted-foreground",
620
+ },
621
+ };
622
+
623
+ // ---------------------------------------------------------------------------
624
+ // Radio helpers (internal)
625
+ // ---------------------------------------------------------------------------
626
+
627
+ function ExpiryRadio({
628
+ value,
629
+ label,
630
+ checked,
631
+ disabled,
632
+ onChange,
633
+ }: {
634
+ value: ExpiryOption;
635
+ label: string;
636
+ checked: boolean;
637
+ disabled: boolean;
638
+ onChange: (v: ExpiryOption) => void;
639
+ }) {
640
+ return (
641
+ <label
642
+ className={cn(
643
+ "inline-flex cursor-pointer items-center rounded-md border px-2.5 py-1 text-xs transition-colors",
644
+ checked
645
+ ? "border-primary bg-primary-subtle text-primary font-medium"
646
+ : "border-input bg-background text-muted-foreground hover:border-border hover:text-foreground",
647
+ disabled && "pointer-events-none opacity-50",
648
+ )}
649
+ >
650
+ <input
651
+ type="radio"
652
+ name="stgm-invite-expiry"
653
+ value={value}
654
+ checked={checked}
655
+ disabled={disabled}
656
+ onChange={() => onChange(value)}
657
+ className="sr-only"
658
+ />
659
+ {label}
660
+ </label>
661
+ );
662
+ }
663
+
664
+ function RedemptionRadio({
665
+ value,
666
+ label,
667
+ description,
668
+ checked,
669
+ disabled,
670
+ onChange,
671
+ }: {
672
+ value: RedemptionMode;
673
+ label: string;
674
+ description: string;
675
+ checked: boolean;
676
+ disabled: boolean;
677
+ onChange: (v: RedemptionMode) => void;
678
+ }) {
679
+ return (
680
+ <label
681
+ className={cn(
682
+ "inline-flex cursor-pointer flex-col rounded-md border px-3 py-1.5 text-xs transition-colors",
683
+ checked
684
+ ? "border-primary bg-primary-subtle text-primary font-medium"
685
+ : "border-input bg-background text-muted-foreground hover:border-border hover:text-foreground",
686
+ disabled && "pointer-events-none opacity-50",
687
+ )}
688
+ >
689
+ <input
690
+ type="radio"
691
+ name="stgm-invite-redemption"
692
+ value={value}
693
+ checked={checked}
694
+ disabled={disabled}
695
+ onChange={() => onChange(value)}
696
+ className="sr-only"
697
+ />
698
+ <span>{label}</span>
699
+ <span className="text-[0.625rem] text-muted-foreground font-normal mt-0.5">
700
+ {description}
701
+ </span>
702
+ </label>
703
+ );
704
+ }
705
+
706
+ // ---------------------------------------------------------------------------
707
+ // Formatting helpers
708
+ // ---------------------------------------------------------------------------
709
+
710
+ function daysFromNow(days: number): Date {
711
+ const date = new Date();
712
+ date.setDate(date.getDate() + days);
713
+ return date;
714
+ }
715
+
716
+ function formatRedemptions(count: number, max: number): string {
717
+ if (max === 0) {
718
+ return count === 0 ? "No redemptions yet" : `${count} redeemed`;
719
+ }
720
+ return `${count} / ${max} redeemed`;
721
+ }
722
+
723
+ function formatRelativeExpiry(date: Date): string {
724
+ const now = Date.now();
725
+ const diffMs = date.getTime() - now;
726
+
727
+ if (diffMs <= 0) return "Expired";
728
+
729
+ const days = Math.ceil(diffMs / 86_400_000);
730
+ if (days === 1) return "Expires tomorrow";
731
+ if (days <= 30) return `Expires in ${days} days`;
732
+ return `Expires ${formatShortDate(date)}`;
733
+ }
734
+
735
+ function formatShortDate(date: Date): string {
736
+ return date.toLocaleDateString(undefined, {
737
+ month: "short",
738
+ day: "numeric",
739
+ year: "numeric",
740
+ });
741
+ }
742
+
743
+ // ---------------------------------------------------------------------------
744
+ // Icons
745
+ // ---------------------------------------------------------------------------
746
+
747
+ function LinkIcon({ active }: { active: boolean }) {
748
+ return (
749
+ <svg
750
+ width="14"
751
+ height="14"
752
+ viewBox="0 0 16 16"
753
+ fill="none"
754
+ stroke="currentColor"
755
+ strokeWidth="1.5"
756
+ strokeLinecap="round"
757
+ strokeLinejoin="round"
758
+ aria-hidden="true"
759
+ className={cn(
760
+ "shrink-0",
761
+ active ? "text-primary" : "text-muted-foreground",
762
+ )}
763
+ >
764
+ <path d="M6.5 9.5a3.5 3.5 0 0 0 5 0l2-2a3.5 3.5 0 0 0-5-5l-1 1" />
765
+ <path d="M9.5 6.5a3.5 3.5 0 0 0-5 0l-2 2a3.5 3.5 0 0 0 5 5l1-1" />
766
+ </svg>
767
+ );
768
+ }
769
+
770
+ function CopyIcon() {
771
+ return (
772
+ <svg
773
+ width="14"
774
+ height="14"
775
+ viewBox="0 0 16 16"
776
+ fill="none"
777
+ stroke="currentColor"
778
+ strokeWidth="1.5"
779
+ strokeLinecap="round"
780
+ strokeLinejoin="round"
781
+ aria-hidden="true"
782
+ >
783
+ <rect x="5" y="5" width="9" height="9" rx="1.5" />
784
+ <path d="M5 11H3.5A1.5 1.5 0 0 1 2 9.5V3.5A1.5 1.5 0 0 1 3.5 2h6A1.5 1.5 0 0 1 11 3.5V5" />
785
+ </svg>
786
+ );
787
+ }
788
+
789
+ function CheckIcon() {
790
+ return (
791
+ <svg
792
+ width="14"
793
+ height="14"
794
+ viewBox="0 0 16 16"
795
+ fill="none"
796
+ stroke="currentColor"
797
+ strokeWidth="2"
798
+ strokeLinecap="round"
799
+ strokeLinejoin="round"
800
+ aria-hidden="true"
801
+ >
802
+ <path d="M3 8.5l3.5 3.5L13 5" />
803
+ </svg>
804
+ );
805
+ }
806
+
807
+ function RevokeIcon() {
808
+ return (
809
+ <svg
810
+ width="14"
811
+ height="14"
812
+ viewBox="0 0 16 16"
813
+ fill="none"
814
+ stroke="currentColor"
815
+ strokeWidth="1.5"
816
+ strokeLinecap="round"
817
+ strokeLinejoin="round"
818
+ aria-hidden="true"
819
+ >
820
+ <circle cx="8" cy="8" r="6" />
821
+ <path d="M4.5 11.5l7-7" />
822
+ </svg>
823
+ );
824
+ }
825
+
826
+ function SpinnerIcon() {
827
+ return (
828
+ <svg
829
+ width="12"
830
+ height="12"
831
+ viewBox="0 0 16 16"
832
+ fill="none"
833
+ stroke="currentColor"
834
+ strokeWidth="2"
835
+ strokeLinecap="round"
836
+ className="animate-spin"
837
+ aria-hidden="true"
838
+ >
839
+ <path d="M8 2a6 6 0 1 0 6 6" />
840
+ </svg>
841
+ );
842
+ }