@thunderid/react 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs +1 -1
  2. package/dist/cjs/components/admin/AgentDetails/AgentDetails.cjs +13 -2
  3. package/dist/cjs/components/admin/AgentDetails/AgentDetails.cjs.map +1 -1
  4. package/dist/cjs/components/admin/AgentDetails/BaseAgentDetails.cjs +25 -7
  5. package/dist/cjs/components/admin/AgentDetails/BaseAgentDetails.cjs.map +1 -1
  6. package/dist/cjs/components/admin/ApplicationDetails/ApplicationDetails.cjs +15 -2
  7. package/dist/cjs/components/admin/ApplicationDetails/ApplicationDetails.cjs.map +1 -1
  8. package/dist/cjs/components/admin/ApplicationDetails/BaseApplicationDetails.cjs +24 -7
  9. package/dist/cjs/components/admin/ApplicationDetails/BaseApplicationDetails.cjs.map +1 -1
  10. package/dist/cjs/components/admin/GroupMembers/GroupMembers.cjs +2 -1
  11. package/dist/cjs/components/admin/GroupMembers/GroupMembers.cjs.map +1 -1
  12. package/dist/cjs/components/admin/RoleAssignments/RoleAssignments.cjs +2 -1
  13. package/dist/cjs/components/admin/RoleAssignments/RoleAssignments.cjs.map +1 -1
  14. package/dist/cjs/components/admin/UserDetails/BaseUserDetails.cjs +32 -14
  15. package/dist/cjs/components/admin/UserDetails/BaseUserDetails.cjs.map +1 -1
  16. package/dist/cjs/components/admin/UserDetails/UserDetails.cjs +14 -2
  17. package/dist/cjs/components/admin/UserDetails/UserDetails.cjs.map +1 -1
  18. package/dist/cjs/components/admin/shared/AccountLifecycleParts.cjs +105 -0
  19. package/dist/cjs/components/admin/shared/AccountLifecycleParts.cjs.map +1 -0
  20. package/dist/cjs/components/admin/shared/ApplicationFormFields.cjs +1 -1
  21. package/dist/cjs/components/auth/AcceptInvite/BaseAcceptInvite.cjs +4 -5
  22. package/dist/cjs/components/auth/AcceptInvite/BaseAcceptInvite.cjs.map +1 -1
  23. package/dist/cjs/components/auth/SignIn/SignIn.cjs +5 -5
  24. package/dist/cjs/components/auth/SignIn/SignIn.cjs.map +1 -1
  25. package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs +4 -5
  26. package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs.map +1 -1
  27. package/dist/cjs/components/auth/flow/Consent.cjs +53 -28
  28. package/dist/cjs/components/auth/flow/Consent.cjs.map +1 -1
  29. package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.cjs +104 -0
  30. package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.cjs.map +1 -0
  31. package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.styles.cjs +59 -0
  32. package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.styles.cjs.map +1 -0
  33. package/dist/cjs/components/auth/flow/ConsentDetailField.cjs +171 -0
  34. package/dist/cjs/components/auth/flow/ConsentDetailField.cjs.map +1 -0
  35. package/dist/cjs/components/auth/flow/ConsentDetailField.styles.cjs +85 -0
  36. package/dist/cjs/components/auth/flow/ConsentDetailField.styles.cjs.map +1 -0
  37. package/dist/cjs/components/auth/flow/ConsentDetailText.cjs +63 -0
  38. package/dist/cjs/components/auth/flow/ConsentDetailText.cjs.map +1 -0
  39. package/dist/cjs/components/auth/flow/ConsentDetailText.styles.cjs +83 -0
  40. package/dist/cjs/components/auth/flow/ConsentDetailText.styles.cjs.map +1 -0
  41. package/dist/cjs/components/auth/flow/FieldFactory.cjs +1 -1
  42. package/dist/cjs/components/auth/flow/resolveConsentText.cjs +25 -0
  43. package/dist/cjs/components/auth/flow/resolveConsentText.cjs.map +1 -0
  44. package/dist/cjs/components/primitives/KeyValueInput/KeyValueInput.cjs +1 -1
  45. package/dist/cjs/components/primitives/MultiInput/MultiInput.cjs +1 -1
  46. package/dist/cjs/components/primitives/PasswordField/PasswordField.cjs +1 -1
  47. package/dist/cjs/hooks/auth/useConsentDetailField.cjs +357 -0
  48. package/dist/cjs/hooks/auth/useConsentDetailField.cjs.map +1 -0
  49. package/dist/cjs/hooks/management/useAccountLifecycle.cjs +59 -0
  50. package/dist/cjs/hooks/management/useAccountLifecycle.cjs.map +1 -0
  51. package/dist/cjs/index.cjs +66 -1
  52. package/dist/cjs/utils/formatConsentDetailValue.cjs +26 -0
  53. package/dist/cjs/utils/formatConsentDetailValue.cjs.map +1 -0
  54. package/dist/cjs/utils/getConsentDetailLayout.cjs +44 -0
  55. package/dist/cjs/utils/getConsentDetailLayout.cjs.map +1 -0
  56. package/dist/cjs/utils/getConsentDetailListDisplay.cjs +30 -0
  57. package/dist/cjs/utils/getConsentDetailListDisplay.cjs.map +1 -0
  58. package/dist/cjs/utils/isConsentApprovalBlocked.cjs +41 -0
  59. package/dist/cjs/utils/isConsentApprovalBlocked.cjs.map +1 -0
  60. package/dist/cjs/utils/isConsentDetailPlainList.cjs +24 -0
  61. package/dist/cjs/utils/isConsentDetailPlainList.cjs.map +1 -0
  62. package/dist/cjs/utils/isConsentDetailStructured.cjs +15 -0
  63. package/dist/cjs/utils/isConsentDetailStructured.cjs.map +1 -0
  64. package/dist/cjs/utils/isConsentDetailTextClamped.cjs +25 -0
  65. package/dist/cjs/utils/isConsentDetailTextClamped.cjs.map +1 -0
  66. package/dist/components/account/UserProfile/BaseUserProfile.js +1 -1
  67. package/dist/components/admin/AgentDetails/AgentDetails.d.ts +8 -1
  68. package/dist/components/admin/AgentDetails/AgentDetails.d.ts.map +1 -1
  69. package/dist/components/admin/AgentDetails/AgentDetails.js +13 -2
  70. package/dist/components/admin/AgentDetails/AgentDetails.js.map +1 -1
  71. package/dist/components/admin/AgentDetails/BaseAgentDetails.d.ts +21 -1
  72. package/dist/components/admin/AgentDetails/BaseAgentDetails.d.ts.map +1 -1
  73. package/dist/components/admin/AgentDetails/BaseAgentDetails.js +25 -7
  74. package/dist/components/admin/AgentDetails/BaseAgentDetails.js.map +1 -1
  75. package/dist/components/admin/ApplicationDetails/ApplicationDetails.d.ts +15 -1
  76. package/dist/components/admin/ApplicationDetails/ApplicationDetails.d.ts.map +1 -1
  77. package/dist/components/admin/ApplicationDetails/ApplicationDetails.js +15 -2
  78. package/dist/components/admin/ApplicationDetails/ApplicationDetails.js.map +1 -1
  79. package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.d.ts +20 -1
  80. package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.d.ts.map +1 -1
  81. package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.js +24 -7
  82. package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.js.map +1 -1
  83. package/dist/components/admin/GroupMembers/GroupMembers.d.ts +7 -0
  84. package/dist/components/admin/GroupMembers/GroupMembers.d.ts.map +1 -1
  85. package/dist/components/admin/GroupMembers/GroupMembers.js +2 -1
  86. package/dist/components/admin/GroupMembers/GroupMembers.js.map +1 -1
  87. package/dist/components/admin/RoleAssignments/RoleAssignments.d.ts +7 -0
  88. package/dist/components/admin/RoleAssignments/RoleAssignments.d.ts.map +1 -1
  89. package/dist/components/admin/RoleAssignments/RoleAssignments.js +2 -1
  90. package/dist/components/admin/RoleAssignments/RoleAssignments.js.map +1 -1
  91. package/dist/components/admin/UserDetails/BaseUserDetails.d.ts +22 -1
  92. package/dist/components/admin/UserDetails/BaseUserDetails.d.ts.map +1 -1
  93. package/dist/components/admin/UserDetails/BaseUserDetails.js +32 -14
  94. package/dist/components/admin/UserDetails/BaseUserDetails.js.map +1 -1
  95. package/dist/components/admin/UserDetails/UserDetails.d.ts +15 -1
  96. package/dist/components/admin/UserDetails/UserDetails.d.ts.map +1 -1
  97. package/dist/components/admin/UserDetails/UserDetails.js +14 -2
  98. package/dist/components/admin/UserDetails/UserDetails.js.map +1 -1
  99. package/dist/components/admin/shared/AccountLifecycleParts.d.ts +32 -0
  100. package/dist/components/admin/shared/AccountLifecycleParts.d.ts.map +1 -0
  101. package/dist/components/admin/shared/AccountLifecycleParts.js +101 -0
  102. package/dist/components/admin/shared/AccountLifecycleParts.js.map +1 -0
  103. package/dist/components/admin/shared/ApplicationFormFields.js +1 -1
  104. package/dist/components/auth/AcceptInvite/BaseAcceptInvite.d.ts.map +1 -1
  105. package/dist/components/auth/AcceptInvite/BaseAcceptInvite.js +5 -6
  106. package/dist/components/auth/AcceptInvite/BaseAcceptInvite.js.map +1 -1
  107. package/dist/components/auth/SignIn/SignIn.d.ts.map +1 -1
  108. package/dist/components/auth/SignIn/SignIn.js +6 -6
  109. package/dist/components/auth/SignIn/SignIn.js.map +1 -1
  110. package/dist/components/auth/flow/AuthOptionFactory.d.ts.map +1 -1
  111. package/dist/components/auth/flow/AuthOptionFactory.js +5 -6
  112. package/dist/components/auth/flow/AuthOptionFactory.js.map +1 -1
  113. package/dist/components/auth/flow/Consent.d.ts +6 -0
  114. package/dist/components/auth/flow/Consent.d.ts.map +1 -1
  115. package/dist/components/auth/flow/Consent.js +54 -29
  116. package/dist/components/auth/flow/Consent.js.map +1 -1
  117. package/dist/components/auth/flow/ConsentAuthorizationDetail.d.ts +68 -0
  118. package/dist/components/auth/flow/ConsentAuthorizationDetail.d.ts.map +1 -0
  119. package/dist/components/auth/flow/ConsentAuthorizationDetail.js +100 -0
  120. package/dist/components/auth/flow/ConsentAuthorizationDetail.js.map +1 -0
  121. package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.d.ts +10 -0
  122. package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.d.ts.map +1 -0
  123. package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.js +57 -0
  124. package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.js.map +1 -0
  125. package/dist/components/auth/flow/ConsentDetailField.d.ts +39 -0
  126. package/dist/components/auth/flow/ConsentDetailField.d.ts.map +1 -0
  127. package/dist/components/auth/flow/ConsentDetailField.js +168 -0
  128. package/dist/components/auth/flow/ConsentDetailField.js.map +1 -0
  129. package/dist/components/auth/flow/ConsentDetailField.styles.d.ts +11 -0
  130. package/dist/components/auth/flow/ConsentDetailField.styles.d.ts.map +1 -0
  131. package/dist/components/auth/flow/ConsentDetailField.styles.js +83 -0
  132. package/dist/components/auth/flow/ConsentDetailField.styles.js.map +1 -0
  133. package/dist/components/auth/flow/ConsentDetailText.d.ts +53 -0
  134. package/dist/components/auth/flow/ConsentDetailText.d.ts.map +1 -0
  135. package/dist/components/auth/flow/ConsentDetailText.js +58 -0
  136. package/dist/components/auth/flow/ConsentDetailText.js.map +1 -0
  137. package/dist/components/auth/flow/ConsentDetailText.styles.d.ts +20 -0
  138. package/dist/components/auth/flow/ConsentDetailText.styles.d.ts.map +1 -0
  139. package/dist/components/auth/flow/ConsentDetailText.styles.js +80 -0
  140. package/dist/components/auth/flow/ConsentDetailText.styles.js.map +1 -0
  141. package/dist/components/auth/flow/FieldFactory.js +1 -1
  142. package/dist/components/auth/flow/resolveConsentText.d.ts +13 -0
  143. package/dist/components/auth/flow/resolveConsentText.d.ts.map +1 -0
  144. package/dist/components/auth/flow/resolveConsentText.js +23 -0
  145. package/dist/components/auth/flow/resolveConsentText.js.map +1 -0
  146. package/dist/components/primitives/KeyValueInput/KeyValueInput.js +1 -1
  147. package/dist/components/primitives/MultiInput/MultiInput.js +1 -1
  148. package/dist/components/primitives/PasswordField/PasswordField.js +1 -1
  149. package/dist/hooks/auth/useConsentDetailField.d.ts +130 -0
  150. package/dist/hooks/auth/useConsentDetailField.d.ts.map +1 -0
  151. package/dist/hooks/auth/useConsentDetailField.js +354 -0
  152. package/dist/hooks/auth/useConsentDetailField.js.map +1 -0
  153. package/dist/hooks/management/useAccountLifecycle.d.ts +49 -0
  154. package/dist/hooks/management/useAccountLifecycle.d.ts.map +1 -0
  155. package/dist/hooks/management/useAccountLifecycle.js +56 -0
  156. package/dist/hooks/management/useAccountLifecycle.js.map +1 -0
  157. package/dist/index.d.ts +16 -4
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +11 -3
  160. package/dist/utils/formatConsentDetailValue.d.ts +16 -0
  161. package/dist/utils/formatConsentDetailValue.d.ts.map +1 -0
  162. package/dist/utils/formatConsentDetailValue.js +25 -0
  163. package/dist/utils/formatConsentDetailValue.js.map +1 -0
  164. package/dist/utils/getConsentDetailLayout.d.ts +32 -0
  165. package/dist/utils/getConsentDetailLayout.d.ts.map +1 -0
  166. package/dist/utils/getConsentDetailLayout.js +43 -0
  167. package/dist/utils/getConsentDetailLayout.js.map +1 -0
  168. package/dist/utils/getConsentDetailListDisplay.d.ts +30 -0
  169. package/dist/utils/getConsentDetailListDisplay.d.ts.map +1 -0
  170. package/dist/utils/getConsentDetailListDisplay.js +30 -0
  171. package/dist/utils/getConsentDetailListDisplay.js.map +1 -0
  172. package/dist/utils/isConsentApprovalBlocked.d.ts +17 -0
  173. package/dist/utils/isConsentApprovalBlocked.d.ts.map +1 -0
  174. package/dist/utils/isConsentApprovalBlocked.js +39 -0
  175. package/dist/utils/isConsentApprovalBlocked.js.map +1 -0
  176. package/dist/utils/isConsentDetailPlainList.d.ts +15 -0
  177. package/dist/utils/isConsentDetailPlainList.d.ts.map +1 -0
  178. package/dist/utils/isConsentDetailPlainList.js +23 -0
  179. package/dist/utils/isConsentDetailPlainList.js.map +1 -0
  180. package/dist/utils/isConsentDetailStructured.d.ts +10 -0
  181. package/dist/utils/isConsentDetailStructured.d.ts.map +1 -0
  182. package/dist/utils/isConsentDetailStructured.js +14 -0
  183. package/dist/utils/isConsentDetailStructured.js.map +1 -0
  184. package/dist/utils/isConsentDetailTextClamped.d.ts +20 -0
  185. package/dist/utils/isConsentDetailTextClamped.d.ts.map +1 -0
  186. package/dist/utils/isConsentDetailTextClamped.js +24 -0
  187. package/dist/utils/isConsentDetailTextClamped.js.map +1 -0
  188. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { Agent, MembershipGroup, Preferences } from '@thunderid/browser';
1
+ import { AccountLifecycleAction, Agent, MembershipGroup, Preferences } from '@thunderid/browser';
2
2
  import { FC, ReactNode } from 'react';
3
3
  /**
4
4
  * What {@link BaseAgentDetails} hands its `children` render function, so the agent can be shown with any UI.
@@ -9,10 +9,19 @@ export interface BaseAgentDetailsRenderProps {
9
9
  groups: MembershipGroup[];
10
10
  isDeleting: boolean;
11
11
  isLoading: boolean;
12
+ isRunningLifecycleAction: boolean;
13
+ /**
14
+ * The account lifecycle actions offered for the agent. Empty when none is allowed.
15
+ */
16
+ lifecycleActions: AccountLifecycleAction[];
12
17
  /**
13
18
  * Deletes the agent. Undefined when deletion is not allowed, including for read-only agents.
14
19
  */
15
20
  onDelete?: () => void;
21
+ /**
22
+ * Runs an account lifecycle action, with the operator's note on a suspension. Undefined when none is allowed.
23
+ */
24
+ onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
16
25
  roles: string[];
17
26
  }
18
27
  export interface BaseAgentDetailsProps {
@@ -34,10 +43,21 @@ export interface BaseAgentDetailsProps {
34
43
  groups?: MembershipGroup[];
35
44
  isDeleting?: boolean;
36
45
  isLoading?: boolean;
46
+ isRunningLifecycleAction?: boolean;
47
+ /**
48
+ * The account lifecycle actions to offer (suspend, unsuspend, unlock), shown only with `onLifecycleAction`. They change
49
+ * only runtime state, so they apply to read-only agents too.
50
+ */
51
+ lifecycleActions?: AccountLifecycleAction[];
37
52
  /**
38
53
  * Deletes the agent. The delete action is hidden when omitted.
39
54
  */
40
55
  onDelete?: () => void;
56
+ /**
57
+ * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first, and a
58
+ * suspension takes an optional operator note.
59
+ */
60
+ onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
41
61
  preferences?: Preferences;
42
62
  /**
43
63
  * The names of the roles the agent holds, directly or through its groups.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseAgentDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/AgentDetails/BaseAgentDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAE,eAAe,EAAE,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAC,EAAE,EAAgB,SAAS,EAAC,MAAM,OAAO,CAAC;AASlD;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,SAAS,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,QAAA,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CA4G/C,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"BaseAgentDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/AgentDetails/BaseAgentDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAC,sBAAsB,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAC,EAAE,EAAgB,SAAS,EAAC,MAAM,OAAO,CAAC;AAUlD;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,wBAAwB,EAAE,OAAO,CAAC;IAClC;;OAEG;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;IAC3C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,SAAS,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,QAAA,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CA+H/C,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -4,6 +4,7 @@ import Alert_default from "../../primitives/Alert/Alert.js";
4
4
  import CopyableText_default from "../../primitives/CopyableText/CopyableText.js";
5
5
  import useAdminPartStyles_default from "../shared/useAdminPartStyles.js";
6
6
  import AdminSection_default from "../shared/AdminSection.js";
7
+ import { AccessStatusBadge, AccountLifecycleButtons } from "../shared/AccountLifecycleParts.js";
7
8
  import { ChipList, ConfirmButton, DetailList, Panel } from "../shared/AdminParts.js";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
 
@@ -12,7 +13,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
13
  * Shows an agent's type, owner, client and the groups and roles it holds. Holds no data of its own:
13
14
  * pass the agent and handle deletion.
14
15
  */
15
- const BaseAgentDetails = ({ actions, agent, children, className, error = null, groups = [], isDeleting = false, isLoading = false, onDelete, preferences, roles = [] }) => {
16
+ const BaseAgentDetails = ({ actions, agent, children, className, error = null, groups = [], isDeleting = false, isLoading = false, isRunningLifecycleAction = false, lifecycleActions = [], onDelete, onLifecycleAction, preferences, roles = [] }) => {
16
17
  const { t } = useTranslation_default(preferences?.i18n);
17
18
  const styles = useAdminPartStyles_default();
18
19
  if (children) return /* @__PURE__ */ jsx("div", {
@@ -23,7 +24,10 @@ const BaseAgentDetails = ({ actions, agent, children, className, error = null, g
23
24
  groups,
24
25
  isDeleting,
25
26
  isLoading,
27
+ isRunningLifecycleAction,
28
+ lifecycleActions: onLifecycleAction && agent ? lifecycleActions : [],
26
29
  onDelete: agent && !agent.isReadOnly ? onDelete : void 0,
30
+ onLifecycleAction: agent ? onLifecycleAction : void 0,
27
31
  roles
28
32
  })
29
33
  });
@@ -65,6 +69,10 @@ const BaseAgentDetails = ({ actions, agent, children, className, error = null, g
65
69
  value: agent.ouHandle ?? agent.ouId
66
70
  }
67
71
  ];
72
+ if (agent.status) details.push({
73
+ label: t("admin.common.columns.status"),
74
+ value: /* @__PURE__ */ jsx(AccessStatusBadge, { status: agent.status.value })
75
+ });
68
76
  if (agent.clientId) details.push({
69
77
  label: t("admin.agents.list.columns.client_id"),
70
78
  value: /* @__PURE__ */ jsx(CopyableText_default, { value: agent.clientId })
@@ -75,12 +83,22 @@ const BaseAgentDetails = ({ actions, agent, children, className, error = null, g
75
83
  title: agent.name,
76
84
  description: agent.description,
77
85
  preferences,
78
- actions: /* @__PURE__ */ jsxs(Fragment, { children: [actions, onDelete && !agent.isReadOnly && /* @__PURE__ */ jsx(ConfirmButton, {
79
- confirmText: t("admin.agents.details.delete.confirm", { name: agent.name }),
80
- isLoading: isDeleting,
81
- onConfirm: onDelete,
82
- children: t("admin.agents.details.delete.action")
83
- })] }),
86
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
87
+ actions,
88
+ onLifecycleAction && /* @__PURE__ */ jsx(AccountLifecycleButtons, {
89
+ actions: lifecycleActions,
90
+ collectsOperatorNote: true,
91
+ isRunning: isRunningLifecycleAction,
92
+ name: agent.name,
93
+ onAction: onLifecycleAction
94
+ }),
95
+ onDelete && !agent.isReadOnly && /* @__PURE__ */ jsx(ConfirmButton, {
96
+ confirmText: t("admin.agents.details.delete.confirm", { name: agent.name }),
97
+ isLoading: isDeleting,
98
+ onConfirm: onDelete,
99
+ children: t("admin.agents.details.delete.action")
100
+ })
101
+ ] }),
84
102
  children: [
85
103
  error && /* @__PURE__ */ jsx(Alert_default, {
86
104
  variant: "error",
@@ -1 +1 @@
1
- {"version":3,"file":"BaseAgentDetails.js","names":["BaseAgentDetails: FC<BaseAgentDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","AdminSection","Spinner","Alert","details: {label: string; value: ReactNode}[]","CopyableText"],"sources":["../../../../src/components/admin/AgentDetails/BaseAgentDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {Agent, MembershipGroup, Preferences} from '@thunderid/browser';\nimport {FC, ReactElement, ReactNode} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {ChipList, ConfirmButton, DetailList, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * What {@link BaseAgentDetails} hands its `children` render function, so the agent can be shown with any UI.\n */\nexport interface BaseAgentDetailsRenderProps {\n agent?: Agent;\n error: string | null;\n groups: MembershipGroup[];\n isDeleting: boolean;\n isLoading: boolean;\n /**\n * Deletes the agent. Undefined when deletion is not allowed, including for read-only agents.\n */\n onDelete?: () => void;\n roles: string[];\n}\n\nexport interface BaseAgentDetailsProps {\n /**\n * Controls rendered next to the agent's name.\n */\n actions?: ReactNode;\n agent?: Agent;\n /**\n * Renders custom UI in place of the default view. Receives the agent, its groups and roles, the\n * loading and error state, and the delete handler.\n */\n children?: (props: BaseAgentDetailsRenderProps) => ReactNode;\n className?: string;\n error?: string | null;\n /**\n * The groups the agent belongs to.\n */\n groups?: MembershipGroup[];\n isDeleting?: boolean;\n isLoading?: boolean;\n /**\n * Deletes the agent. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n preferences?: Preferences;\n /**\n * The names of the roles the agent holds, directly or through its groups.\n */\n roles?: string[];\n}\n\n/**\n * Shows an agent's type, owner, client and the groups and roles it holds. Holds no data of its own:\n * pass the agent and handle deletion.\n */\nconst BaseAgentDetails: FC<BaseAgentDetailsProps> = ({\n actions,\n agent,\n children,\n className,\n error = null,\n groups = [],\n isDeleting = false,\n isLoading = false,\n onDelete,\n preferences,\n roles = [],\n}: BaseAgentDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n\n if (children) {\n return (\n <div className={className}>\n {children({\n agent,\n error,\n groups,\n isDeleting,\n isLoading,\n onDelete: agent && !agent.isReadOnly ? onDelete : undefined,\n roles,\n })}\n </div>\n );\n }\n\n if (isLoading && !agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const details: {label: string; value: ReactNode}[] = [\n {label: t('admin.agents.details.type'), value: agent.type},\n {label: t('admin.agents.details.owner'), value: agent.owner ?? t('admin.agents.details.no_owner')},\n {label: t('admin.common.columns.organization_unit'), value: agent.ouHandle ?? agent.ouId},\n ];\n\n if (agent.clientId) {\n details.push({label: t('admin.agents.list.columns.client_id'), value: <CopyableText value={agent.clientId} />});\n }\n\n return (\n <AdminSection\n block=\"agent-details\"\n className={className}\n title={agent.name}\n description={agent.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onDelete && !agent.isReadOnly && (\n <ConfirmButton\n confirmText={t('admin.agents.details.delete.confirm', {name: agent.name})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.agents.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n <Panel title={t('admin.agents.details.overview')}>\n <DetailList items={details} />\n </Panel>\n <Panel title={t('admin.agents.details.groups')}>\n <ChipList\n label={t('admin.agents.details.groups')}\n emptyText={t('admin.agents.details.no_groups')}\n items={groups.map((group: MembershipGroup) => ({id: group.id, label: group.name}))}\n />\n </Panel>\n <Panel title={t('admin.agents.details.roles')}>\n <ChipList\n label={t('admin.agents.details.roles')}\n emptyText={t('admin.agents.details.no_roles')}\n items={roles.map((role: string) => ({id: role, label: role}))}\n />\n </Panel>\n </AdminSection>\n );\n};\n\nexport default BaseAgentDetails;\n"],"mappings":";;;;;;;;;;;;;;AA+DA,MAAMA,oBAA+C,EACnD,SACA,OACA,UACA,WACA,QAAQ,MACR,SAAS,EAAE,EACX,aAAa,OACb,YAAY,OACZ,UACA,aACA,QAAQ,EAAE,OAC+B;CACzC,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAMC,SAAiCC,4BAAoB;AAE3D,KAAI,SACF,QACE,oBAAC;EAAe;YACb,SAAS;GACR;GACA;GACA;GACA;GACA;GACA,UAAU,SAAS,CAAC,MAAM,aAAa,WAAW;GAClD;GACD,CAAC;GACE;AAIV,KAAI,aAAa,CAAC,MAChB,QACE,oBAACC;EAAa,OAAM;EAA2B;EAAwB;YACrE,qBAAC;GAAK,WAAW,OAAO;GAAU,MAAK;;IACrC,oBAACC,mBAAQ,MAAK,UAAU;;IAAE,EAAE,uBAAuB;;IAC9C;GACM;AAInB,KAAI,CAAC,MACH,QACE,oBAACD;EAAa,OAAM;EAA2B;EAAwB;YACrE,oBAACE;GAAM,SAAQ;GAAQ,MAAK;aACzB,SAAS,EAAE,gCAAgC;IACtC;GACK;CAInB,MAAMC,UAA+C;EACnD;GAAC,OAAO,EAAE,4BAA4B;GAAE,OAAO,MAAM;GAAK;EAC1D;GAAC,OAAO,EAAE,6BAA6B;GAAE,OAAO,MAAM,SAAS,EAAE,gCAAgC;GAAC;EAClG;GAAC,OAAO,EAAE,yCAAyC;GAAE,OAAO,MAAM,YAAY,MAAM;GAAK;EAC1F;AAED,KAAI,MAAM,SACR,SAAQ,KAAK;EAAC,OAAO,EAAE,sCAAsC;EAAE,OAAO,oBAACC,wBAAa,OAAO,MAAM,WAAY;EAAC,CAAC;AAGjH,QACE,qBAACJ;EACC,OAAM;EACK;EACX,OAAO,MAAM;EACb,aAAa,MAAM;EACN;EACb,SACE,4CACG,SACA,YAAY,CAAC,MAAM,cAClB,oBAAC;GACC,aAAa,EAAE,uCAAuC,EAAC,MAAM,MAAM,MAAK,CAAC;GACzE,WAAW;GACX,WAAW;aAEV,EAAE,qCAAqC;IAC1B,IAEjB;;GAGJ,SACC,oBAACE;IAAM,SAAQ;IAAQ,MAAK;cACzB;KACK;GAEV,oBAAC;IAAM,OAAO,EAAE,gCAAgC;cAC9C,oBAAC,cAAW,OAAO,UAAW;KACxB;GACR,oBAAC;IAAM,OAAO,EAAE,8BAA8B;cAC5C,oBAAC;KACC,OAAO,EAAE,8BAA8B;KACvC,WAAW,EAAE,iCAAiC;KAC9C,OAAO,OAAO,KAAK,WAA4B;MAAC,IAAI,MAAM;MAAI,OAAO,MAAM;MAAK,EAAE;MAClF;KACI;GACR,oBAAC;IAAM,OAAO,EAAE,6BAA6B;cAC3C,oBAAC;KACC,OAAO,EAAE,6BAA6B;KACtC,WAAW,EAAE,gCAAgC;KAC7C,OAAO,MAAM,KAAK,UAAkB;MAAC,IAAI;MAAM,OAAO;MAAK,EAAE;MAC7D;KACI;;GACK;;AAInB,+BAAe"}
1
+ {"version":3,"file":"BaseAgentDetails.js","names":["BaseAgentDetails: FC<BaseAgentDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","AdminSection","Spinner","Alert","details: {label: string; value: ReactNode}[]","CopyableText"],"sources":["../../../../src/components/admin/AgentDetails/BaseAgentDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {AccountLifecycleAction, Agent, MembershipGroup, Preferences} from '@thunderid/browser';\nimport {FC, ReactElement, ReactNode} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {AccessStatusBadge, AccountLifecycleButtons} from '../shared/AccountLifecycleParts';\nimport {ChipList, ConfirmButton, DetailList, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * What {@link BaseAgentDetails} hands its `children` render function, so the agent can be shown with any UI.\n */\nexport interface BaseAgentDetailsRenderProps {\n agent?: Agent;\n error: string | null;\n groups: MembershipGroup[];\n isDeleting: boolean;\n isLoading: boolean;\n isRunningLifecycleAction: boolean;\n /**\n * The account lifecycle actions offered for the agent. Empty when none is allowed.\n */\n lifecycleActions: AccountLifecycleAction[];\n /**\n * Deletes the agent. Undefined when deletion is not allowed, including for read-only agents.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action, with the operator's note on a suspension. Undefined when none is allowed.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n roles: string[];\n}\n\nexport interface BaseAgentDetailsProps {\n /**\n * Controls rendered next to the agent's name.\n */\n actions?: ReactNode;\n agent?: Agent;\n /**\n * Renders custom UI in place of the default view. Receives the agent, its groups and roles, the\n * loading and error state, and the delete handler.\n */\n children?: (props: BaseAgentDetailsRenderProps) => ReactNode;\n className?: string;\n error?: string | null;\n /**\n * The groups the agent belongs to.\n */\n groups?: MembershipGroup[];\n isDeleting?: boolean;\n isLoading?: boolean;\n isRunningLifecycleAction?: boolean;\n /**\n * The account lifecycle actions to offer (suspend, unsuspend, unlock), shown only with `onLifecycleAction`. They change\n * only runtime state, so they apply to read-only agents too.\n */\n lifecycleActions?: AccountLifecycleAction[];\n /**\n * Deletes the agent. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first, and a\n * suspension takes an optional operator note.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n preferences?: Preferences;\n /**\n * The names of the roles the agent holds, directly or through its groups.\n */\n roles?: string[];\n}\n\n/**\n * Shows an agent's type, owner, client and the groups and roles it holds. Holds no data of its own:\n * pass the agent and handle deletion.\n */\nconst BaseAgentDetails: FC<BaseAgentDetailsProps> = ({\n actions,\n agent,\n children,\n className,\n error = null,\n groups = [],\n isDeleting = false,\n isLoading = false,\n isRunningLifecycleAction = false,\n lifecycleActions = [],\n onDelete,\n onLifecycleAction,\n preferences,\n roles = [],\n}: BaseAgentDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n\n if (children) {\n return (\n <div className={className}>\n {children({\n agent,\n error,\n groups,\n isDeleting,\n isLoading,\n isRunningLifecycleAction,\n lifecycleActions: onLifecycleAction && agent ? lifecycleActions : [],\n onDelete: agent && !agent.isReadOnly ? onDelete : undefined,\n onLifecycleAction: agent ? onLifecycleAction : undefined,\n roles,\n })}\n </div>\n );\n }\n\n if (isLoading && !agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const details: {label: string; value: ReactNode}[] = [\n {label: t('admin.agents.details.type'), value: agent.type},\n {label: t('admin.agents.details.owner'), value: agent.owner ?? t('admin.agents.details.no_owner')},\n {label: t('admin.common.columns.organization_unit'), value: agent.ouHandle ?? agent.ouId},\n ];\n\n if (agent.status) {\n details.push({label: t('admin.common.columns.status'), value: <AccessStatusBadge status={agent.status.value} />});\n }\n\n if (agent.clientId) {\n details.push({label: t('admin.agents.list.columns.client_id'), value: <CopyableText value={agent.clientId} />});\n }\n\n return (\n <AdminSection\n block=\"agent-details\"\n className={className}\n title={agent.name}\n description={agent.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onLifecycleAction && (\n <AccountLifecycleButtons\n actions={lifecycleActions}\n collectsOperatorNote\n isRunning={isRunningLifecycleAction}\n name={agent.name}\n onAction={onLifecycleAction}\n />\n )}\n {onDelete && !agent.isReadOnly && (\n <ConfirmButton\n confirmText={t('admin.agents.details.delete.confirm', {name: agent.name})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.agents.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n <Panel title={t('admin.agents.details.overview')}>\n <DetailList items={details} />\n </Panel>\n <Panel title={t('admin.agents.details.groups')}>\n <ChipList\n label={t('admin.agents.details.groups')}\n emptyText={t('admin.agents.details.no_groups')}\n items={groups.map((group: MembershipGroup) => ({id: group.id, label: group.name}))}\n />\n </Panel>\n <Panel title={t('admin.agents.details.roles')}>\n <ChipList\n label={t('admin.agents.details.roles')}\n emptyText={t('admin.agents.details.no_roles')}\n items={roles.map((role: string) => ({id: role, label: role}))}\n />\n </Panel>\n </AdminSection>\n );\n};\n\nexport default BaseAgentDetails;\n"],"mappings":";;;;;;;;;;;;;;;AAoFA,MAAMA,oBAA+C,EACnD,SACA,OACA,UACA,WACA,QAAQ,MACR,SAAS,EAAE,EACX,aAAa,OACb,YAAY,OACZ,2BAA2B,OAC3B,mBAAmB,EAAE,EACrB,UACA,mBACA,aACA,QAAQ,EAAE,OAC+B;CACzC,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAMC,SAAiCC,4BAAoB;AAE3D,KAAI,SACF,QACE,oBAAC;EAAe;YACb,SAAS;GACR;GACA;GACA;GACA;GACA;GACA;GACA,kBAAkB,qBAAqB,QAAQ,mBAAmB,EAAE;GACpE,UAAU,SAAS,CAAC,MAAM,aAAa,WAAW;GAClD,mBAAmB,QAAQ,oBAAoB;GAC/C;GACD,CAAC;GACE;AAIV,KAAI,aAAa,CAAC,MAChB,QACE,oBAACC;EAAa,OAAM;EAA2B;EAAwB;YACrE,qBAAC;GAAK,WAAW,OAAO;GAAU,MAAK;;IACrC,oBAACC,mBAAQ,MAAK,UAAU;;IAAE,EAAE,uBAAuB;;IAC9C;GACM;AAInB,KAAI,CAAC,MACH,QACE,oBAACD;EAAa,OAAM;EAA2B;EAAwB;YACrE,oBAACE;GAAM,SAAQ;GAAQ,MAAK;aACzB,SAAS,EAAE,gCAAgC;IACtC;GACK;CAInB,MAAMC,UAA+C;EACnD;GAAC,OAAO,EAAE,4BAA4B;GAAE,OAAO,MAAM;GAAK;EAC1D;GAAC,OAAO,EAAE,6BAA6B;GAAE,OAAO,MAAM,SAAS,EAAE,gCAAgC;GAAC;EAClG;GAAC,OAAO,EAAE,yCAAyC;GAAE,OAAO,MAAM,YAAY,MAAM;GAAK;EAC1F;AAED,KAAI,MAAM,OACR,SAAQ,KAAK;EAAC,OAAO,EAAE,8BAA8B;EAAE,OAAO,oBAAC,qBAAkB,QAAQ,MAAM,OAAO,QAAS;EAAC,CAAC;AAGnH,KAAI,MAAM,SACR,SAAQ,KAAK;EAAC,OAAO,EAAE,sCAAsC;EAAE,OAAO,oBAACC,wBAAa,OAAO,MAAM,WAAY;EAAC,CAAC;AAGjH,QACE,qBAACJ;EACC,OAAM;EACK;EACX,OAAO,MAAM;EACb,aAAa,MAAM;EACN;EACb,SACE;GACG;GACA,qBACC,oBAAC;IACC,SAAS;IACT;IACA,WAAW;IACX,MAAM,MAAM;IACZ,UAAU;KACV;GAEH,YAAY,CAAC,MAAM,cAClB,oBAAC;IACC,aAAa,EAAE,uCAAuC,EAAC,MAAM,MAAM,MAAK,CAAC;IACzE,WAAW;IACX,WAAW;cAEV,EAAE,qCAAqC;KAC1B;MAEjB;;GAGJ,SACC,oBAACE;IAAM,SAAQ;IAAQ,MAAK;cACzB;KACK;GAEV,oBAAC;IAAM,OAAO,EAAE,gCAAgC;cAC9C,oBAAC,cAAW,OAAO,UAAW;KACxB;GACR,oBAAC;IAAM,OAAO,EAAE,8BAA8B;cAC5C,oBAAC;KACC,OAAO,EAAE,8BAA8B;KACvC,WAAW,EAAE,iCAAiC;KAC9C,OAAO,OAAO,KAAK,WAA4B;MAAC,IAAI,MAAM;MAAI,OAAO,MAAM;MAAK,EAAE;MAClF;KACI;GACR,oBAAC;IAAM,OAAO,EAAE,6BAA6B;cAC3C,oBAAC;KACC,OAAO,EAAE,6BAA6B;KACtC,WAAW,EAAE,gCAAgC;KAC7C,OAAO,MAAM,KAAK,UAAkB;MAAC,IAAI;MAAM,OAAO;MAAK,EAAE;MAC7D;KACI;;GACK;;AAInB,+BAAe"}
@@ -1,6 +1,13 @@
1
+ import { AdministrationMode } from '@thunderid/browser';
1
2
  import { FC } from 'react';
2
3
  import { BaseApplicationDetailsProps } from './BaseApplicationDetails';
3
- export type ApplicationDetailsProps = Omit<BaseApplicationDetailsProps, 'application' | 'clientSecret' | 'error' | 'isDeleting' | 'isLoading' | 'isRegeneratingSecret' | 'isUpdating' | 'onDelete' | 'onRegenerateSecret' | 'onSecretDismiss' | 'onUpdate'> & {
4
+ export type ApplicationDetailsProps = Omit<BaseApplicationDetailsProps, 'application' | 'clientSecret' | 'error' | 'isDeleting' | 'isLoading' | 'isRegeneratingSecret' | 'isRunningLifecycleAction' | 'isUpdating' | 'lifecycleActions' | 'onDelete' | 'onLifecycleAction' | 'onRegenerateSecret' | 'onSecretDismiss' | 'onUpdate'> & {
5
+ /**
6
+ * How the delete and the client secret regeneration run. `flow` runs the administration flow the server configures for it, which can revoke
7
+ * grants and end sessions first. It needs `baseUrl`.
8
+ * @default 'native'
9
+ */
10
+ administrationMode?: AdministrationMode;
4
11
  applicationId: string;
5
12
  /**
6
13
  * Whether the application can be deleted from this view.
@@ -12,6 +19,13 @@ export type ApplicationDetailsProps = Omit<BaseApplicationDetailsProps, 'applica
12
19
  * @default false
13
20
  */
14
21
  editable?: boolean;
22
+ /**
23
+ * Whether the application can be suspended and unsuspended from this view. Each action runs through its
24
+ * administration flow and is offered only when the server configures that flow. Reading the configured
25
+ * flows needs a token that may read the server configuration.
26
+ * @default false
27
+ */
28
+ lifecycleActions?: boolean;
15
29
  /**
16
30
  * Called after the application has been deleted.
17
31
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/ApplicationDetails/ApplicationDetails.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAC,EAAE,EAAe,MAAM,OAAO,CAAC;AACvC,OAA+B,EAE7B,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAWlC,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,2BAA2B,EACzB,aAAa,GACb,cAAc,GACd,OAAO,GACP,YAAY,GACZ,WAAW,GACX,sBAAsB,GACtB,YAAY,GACZ,UAAU,GACV,oBAAoB,GACpB,iBAAiB,GACjB,UAAU,CACb,GAAG;IACF,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,kBAAkB,EAAE,EAAE,CAAC,uBAAuB,CAkEnD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ApplicationDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/ApplicationDetails/ApplicationDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,kBAAkB,EAUnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAAe,MAAM,OAAO,CAAC;AACvC,OAA+B,EAE7B,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAYlC,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,2BAA2B,EACzB,aAAa,GACb,cAAc,GACd,OAAO,GACP,YAAY,GACZ,WAAW,GACX,sBAAsB,GACtB,0BAA0B,GAC1B,YAAY,GACZ,kBAAkB,GAClB,UAAU,GACV,mBAAmB,GACnB,oBAAoB,GACpB,iBAAiB,GACjB,UAAU,CACb,GAAG;IACF;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,kBAAkB,EAAE,EAAE,CAAC,uBAAuB,CAiFnD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -2,6 +2,7 @@ import useTranslation_default from "../../../hooks/useTranslation.js";
2
2
  import useManagementEndpoint_default from "../../../hooks/management/useManagementEndpoint.js";
3
3
  import useResourceQuery_default from "../../../hooks/management/useResourceQuery.js";
4
4
  import useResourceMutation_default from "../../../hooks/management/useResourceMutation.js";
5
+ import useAccountLifecycle_default from "../../../hooks/management/useAccountLifecycle.js";
5
6
  import BaseApplicationDetails_default from "./BaseApplicationDetails.js";
6
7
  import { ApplicationQueryKeys, deleteApplication, getApplication, regenerateClientSecret, toManagementErrorMessage, toUpdateApplicationRequest, updateApplication } from "@thunderid/browser";
7
8
  import { jsx } from "react/jsx-runtime";
@@ -29,7 +30,7 @@ import { jsx } from "react/jsx-runtime";
29
30
  * />
30
31
  * ```
31
32
  */
32
- const ApplicationDetails = ({ applicationId, deletable = false, editable = false, onDeleted, preferences, secretRegenerable = false,...rest }) => {
33
+ const ApplicationDetails = ({ administrationMode, applicationId, deletable = false, editable = false, lifecycleActions = false, onDeleted, preferences, secretRegenerable = false,...rest }) => {
33
34
  const { t } = useTranslation_default(preferences?.i18n);
34
35
  const endpoint = useManagementEndpoint_default("applications");
35
36
  const application = useResourceQuery_default([ApplicationQueryKeys.APPLICATION, applicationId], (fetcher) => getApplication({
@@ -39,6 +40,7 @@ const ApplicationDetails = ({ applicationId, deletable = false, editable = false
39
40
  }), { enabled: Boolean(applicationId) });
40
41
  const deletion = useResourceMutation_default((id, fetcher) => deleteApplication({
41
42
  ...endpoint,
43
+ administrationMode,
42
44
  applicationId: id,
43
45
  fetcher
44
46
  }), () => [[ApplicationQueryKeys.APPLICATIONS]], { onSuccess: onDeleted });
@@ -50,15 +52,26 @@ const ApplicationDetails = ({ applicationId, deletable = false, editable = false
50
52
  }), ({ application: current }) => [[ApplicationQueryKeys.APPLICATION, current.id], [ApplicationQueryKeys.APPLICATIONS]]);
51
53
  const regeneration = useResourceMutation_default((id, fetcher) => regenerateClientSecret({
52
54
  ...endpoint,
55
+ administrationMode,
53
56
  applicationId: id,
54
57
  fetcher
55
58
  }), (id) => [[ApplicationQueryKeys.APPLICATION, id], [ApplicationQueryKeys.APPLICATIONS]]);
59
+ const lifecycle = useAccountLifecycle_default({
60
+ enabled: lifecycleActions,
61
+ invalidatedKeys: [[ApplicationQueryKeys.APPLICATION, applicationId], [ApplicationQueryKeys.APPLICATIONS]],
62
+ kind: "application",
63
+ status: application.data?.status?.value,
64
+ subject: applicationId
65
+ });
56
66
  return /* @__PURE__ */ jsx(BaseApplicationDetails_default, {
57
67
  application: application.data,
58
68
  isLoading: application.isLoading,
59
- error: toManagementErrorMessage(t, application.error, "admin.applications.details.error") ?? toManagementErrorMessage(t, deletion.error, "admin.applications.details.delete.error") ?? toManagementErrorMessage(t, update.error, "admin.applications.details.edit.error") ?? toManagementErrorMessage(t, regeneration.error, "admin.applications.details.secret.error"),
69
+ error: toManagementErrorMessage(t, application.error, "admin.applications.details.error") ?? toManagementErrorMessage(t, deletion.error, "admin.applications.details.delete.error") ?? toManagementErrorMessage(t, update.error, "admin.applications.details.edit.error") ?? toManagementErrorMessage(t, regeneration.error, "admin.applications.details.secret.error") ?? toManagementErrorMessage(t, lifecycle.error, "admin.common.lifecycle.error") ?? toManagementErrorMessage(t, lifecycle.loadError, "admin.common.lifecycle.load_error"),
60
70
  isDeleting: deletion.isLoading,
61
71
  onDelete: deletable ? () => void deletion.mutate(applicationId) : void 0,
72
+ lifecycleActions: lifecycle.actions,
73
+ isRunningLifecycleAction: lifecycle.isRunning,
74
+ onLifecycleAction: lifecycleActions ? lifecycle.run : void 0,
62
75
  isUpdating: update.isLoading,
63
76
  onUpdate: editable && application.data ? (changes) => void update.mutate({
64
77
  application: application.data,
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationDetails.js","names":["ApplicationDetails: FC<ApplicationDetailsProps>","useTranslation","endpoint: ManagementEndpoint","useManagementEndpoint","application: ResourceQueryResult<Application>","useResourceQuery","deletion: ResourceMutationResult<void, string>","useResourceMutation","update: ResourceMutationResult<Application, UpdateApplicationVariables>","regeneration: ResourceMutationResult<RegenerateClientSecretResult, string>","BaseApplicationDetails"],"sources":["../../../../src/components/admin/ApplicationDetails/ApplicationDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Application,\n ApplicationQueryKeys,\n deleteApplication,\n getApplication,\n regenerateClientSecret,\n RegenerateClientSecretResult,\n toManagementErrorMessage,\n toUpdateApplicationRequest,\n updateApplication,\n} from '@thunderid/browser';\nimport {FC, ReactElement} from 'react';\nimport BaseApplicationDetails, {\n ApplicationUpdateSubmission,\n BaseApplicationDetailsProps,\n} from './BaseApplicationDetails';\nimport useManagementEndpoint, {ManagementEndpoint} from '../../../hooks/management/useManagementEndpoint';\nimport useResourceMutation, {ResourceMutationResult} from '../../../hooks/management/useResourceMutation';\nimport useResourceQuery, {ResourceQueryResult} from '../../../hooks/management/useResourceQuery';\nimport useTranslation from '../../../hooks/useTranslation';\n\ninterface UpdateApplicationVariables {\n application: Application;\n changes: ApplicationUpdateSubmission;\n}\n\nexport type ApplicationDetailsProps = Omit<\n BaseApplicationDetailsProps,\n | 'application'\n | 'clientSecret'\n | 'error'\n | 'isDeleting'\n | 'isLoading'\n | 'isRegeneratingSecret'\n | 'isUpdating'\n | 'onDelete'\n | 'onRegenerateSecret'\n | 'onSecretDismiss'\n | 'onUpdate'\n> & {\n applicationId: string;\n /**\n * Whether the application can be deleted from this view.\n * @default false\n */\n deletable?: boolean;\n /**\n * Whether the application's general fields, redirect URIs and grant types can be edited from this view.\n * @default false\n */\n editable?: boolean;\n /**\n * Called after the application has been deleted.\n */\n onDeleted?: () => void;\n /**\n * Whether the application's client secret can be regenerated from this view. The new secret is shown\n * once.\n * @default false\n */\n secretRegenerable?: boolean;\n};\n\n/**\n * Shows an application and its OAuth configuration, and optionally lets the signed-in user edit it,\n * regenerate its client secret or delete it.\n *\n * The server guards applications with the root `system` permission, for reading and for changes alike;\n * it defines no application-specific permission. Gate the view and its actions on it with\n * `useGrantedPermissions().can('system')`.\n *\n * @example\n * ```tsx\n * const {can} = useGrantedPermissions();\n * const isAdmin = can('system');\n *\n * <ApplicationDetails\n * applicationId={applicationId}\n * editable={isAdmin}\n * deletable={isAdmin}\n * secretRegenerable={isAdmin}\n * onDeleted={() => navigate('/applications')}\n * />\n * ```\n */\nconst ApplicationDetails: FC<ApplicationDetailsProps> = ({\n applicationId,\n deletable = false,\n editable = false,\n onDeleted,\n preferences,\n secretRegenerable = false,\n ...rest\n}: ApplicationDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const endpoint: ManagementEndpoint = useManagementEndpoint('applications');\n const application: ResourceQueryResult<Application> = useResourceQuery(\n [ApplicationQueryKeys.APPLICATION, applicationId],\n (fetcher) => getApplication({...endpoint, applicationId, fetcher}),\n {enabled: Boolean(applicationId)},\n );\n const deletion: ResourceMutationResult<void, string> = useResourceMutation(\n (id: string, fetcher) => deleteApplication({...endpoint, applicationId: id, fetcher}),\n () => [[ApplicationQueryKeys.APPLICATIONS]],\n {onSuccess: onDeleted},\n );\n const update: ResourceMutationResult<Application, UpdateApplicationVariables> = useResourceMutation(\n ({application: current, changes}: UpdateApplicationVariables, fetcher) =>\n updateApplication({\n ...endpoint,\n applicationId: current.id,\n fetcher,\n payload: toUpdateApplicationRequest(current, changes),\n }),\n ({application: current}: UpdateApplicationVariables) => [\n [ApplicationQueryKeys.APPLICATION, current.id],\n [ApplicationQueryKeys.APPLICATIONS],\n ],\n );\n const regeneration: ResourceMutationResult<RegenerateClientSecretResult, string> = useResourceMutation(\n (id: string, fetcher) => regenerateClientSecret({...endpoint, applicationId: id, fetcher}),\n (id: string) => [[ApplicationQueryKeys.APPLICATION, id], [ApplicationQueryKeys.APPLICATIONS]],\n );\n\n return (\n <BaseApplicationDetails\n application={application.data}\n isLoading={application.isLoading}\n error={\n toManagementErrorMessage(t, application.error, 'admin.applications.details.error') ??\n toManagementErrorMessage(t, deletion.error, 'admin.applications.details.delete.error') ??\n toManagementErrorMessage(t, update.error, 'admin.applications.details.edit.error') ??\n toManagementErrorMessage(t, regeneration.error, 'admin.applications.details.secret.error')\n }\n isDeleting={deletion.isLoading}\n onDelete={deletable ? (): void => void deletion.mutate(applicationId) : undefined}\n isUpdating={update.isLoading}\n onUpdate={\n editable && application.data\n ? (changes: ApplicationUpdateSubmission): void =>\n void update.mutate({application: application.data!, changes})\n : undefined\n }\n isRegeneratingSecret={regeneration.isLoading}\n onRegenerateSecret={secretRegenerable ? (): void => void regeneration.mutate(applicationId) : undefined}\n clientSecret={regeneration.data?.clientSecret ?? null}\n onSecretDismiss={regeneration.reset}\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default ApplicationDetails;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwFA,MAAMA,sBAAmD,EACvD,eACA,YAAY,OACZ,WAAW,OACX,WACA,aACA,oBAAoB,MACpB,GAAG,WACwC;CAC3C,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAMC,WAA+BC,8BAAsB,eAAe;CAC1E,MAAMC,cAAgDC,yBACpD,CAAC,qBAAqB,aAAa,cAAc,GAChD,YAAY,eAAe;EAAC,GAAG;EAAU;EAAe;EAAQ,CAAC,EAClE,EAAC,SAAS,QAAQ,cAAc,EAAC,CAClC;CACD,MAAMC,WAAiDC,6BACpD,IAAY,YAAY,kBAAkB;EAAC,GAAG;EAAU,eAAe;EAAI;EAAQ,CAAC,QAC/E,CAAC,CAAC,qBAAqB,aAAa,CAAC,EAC3C,EAAC,WAAW,WAAU,CACvB;CACD,MAAMC,SAA0ED,6BAC7E,EAAC,aAAa,SAAS,WAAsC,YAC5D,kBAAkB;EAChB,GAAG;EACH,eAAe,QAAQ;EACvB;EACA,SAAS,2BAA2B,SAAS,QAAQ;EACtD,CAAC,GACH,EAAC,aAAa,cAAyC,CACtD,CAAC,qBAAqB,aAAa,QAAQ,GAAG,EAC9C,CAAC,qBAAqB,aAAa,CACpC,CACF;CACD,MAAME,eAA6EF,6BAChF,IAAY,YAAY,uBAAuB;EAAC,GAAG;EAAU,eAAe;EAAI;EAAQ,CAAC,GACzF,OAAe,CAAC,CAAC,qBAAqB,aAAa,GAAG,EAAE,CAAC,qBAAqB,aAAa,CAAC,CAC9F;AAED,QACE,oBAACG;EACC,aAAa,YAAY;EACzB,WAAW,YAAY;EACvB,OACE,yBAAyB,GAAG,YAAY,OAAO,mCAAmC,IAClF,yBAAyB,GAAG,SAAS,OAAO,0CAA0C,IACtF,yBAAyB,GAAG,OAAO,OAAO,wCAAwC,IAClF,yBAAyB,GAAG,aAAa,OAAO,0CAA0C;EAE5F,YAAY,SAAS;EACrB,UAAU,kBAAwB,KAAK,SAAS,OAAO,cAAc,GAAG;EACxE,YAAY,OAAO;EACnB,UACE,YAAY,YAAY,QACnB,YACC,KAAK,OAAO,OAAO;GAAC,aAAa,YAAY;GAAO;GAAQ,CAAC,GAC/D;EAEN,sBAAsB,aAAa;EACnC,oBAAoB,0BAAgC,KAAK,aAAa,OAAO,cAAc,GAAG;EAC9F,cAAc,aAAa,MAAM,gBAAgB;EACjD,iBAAiB,aAAa;EACjB;EACb,GAAI;GACJ;;AAIN,iCAAe"}
1
+ {"version":3,"file":"ApplicationDetails.js","names":["ApplicationDetails: FC<ApplicationDetailsProps>","useTranslation","endpoint: ManagementEndpoint","useManagementEndpoint","application: ResourceQueryResult<Application>","useResourceQuery","deletion: ResourceMutationResult<void, string>","useResourceMutation","update: ResourceMutationResult<Application, UpdateApplicationVariables>","regeneration: ResourceMutationResult<RegenerateClientSecretResult, string>","lifecycle: AccountLifecycleState","useAccountLifecycle","BaseApplicationDetails"],"sources":["../../../../src/components/admin/ApplicationDetails/ApplicationDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AdministrationMode,\n Application,\n ApplicationQueryKeys,\n deleteApplication,\n getApplication,\n regenerateClientSecret,\n RegenerateClientSecretResult,\n toManagementErrorMessage,\n toUpdateApplicationRequest,\n updateApplication,\n} from '@thunderid/browser';\nimport {FC, ReactElement} from 'react';\nimport BaseApplicationDetails, {\n ApplicationUpdateSubmission,\n BaseApplicationDetailsProps,\n} from './BaseApplicationDetails';\nimport useAccountLifecycle, {AccountLifecycleState} from '../../../hooks/management/useAccountLifecycle';\nimport useManagementEndpoint, {ManagementEndpoint} from '../../../hooks/management/useManagementEndpoint';\nimport useResourceMutation, {ResourceMutationResult} from '../../../hooks/management/useResourceMutation';\nimport useResourceQuery, {ResourceQueryResult} from '../../../hooks/management/useResourceQuery';\nimport useTranslation from '../../../hooks/useTranslation';\n\ninterface UpdateApplicationVariables {\n application: Application;\n changes: ApplicationUpdateSubmission;\n}\n\nexport type ApplicationDetailsProps = Omit<\n BaseApplicationDetailsProps,\n | 'application'\n | 'clientSecret'\n | 'error'\n | 'isDeleting'\n | 'isLoading'\n | 'isRegeneratingSecret'\n | 'isRunningLifecycleAction'\n | 'isUpdating'\n | 'lifecycleActions'\n | 'onDelete'\n | 'onLifecycleAction'\n | 'onRegenerateSecret'\n | 'onSecretDismiss'\n | 'onUpdate'\n> & {\n /**\n * How the delete and the client secret regeneration run. `flow` runs the administration flow the server configures for it, which can revoke\n * grants and end sessions first. It needs `baseUrl`.\n * @default 'native'\n */\n administrationMode?: AdministrationMode;\n applicationId: string;\n /**\n * Whether the application can be deleted from this view.\n * @default false\n */\n deletable?: boolean;\n /**\n * Whether the application's general fields, redirect URIs and grant types can be edited from this view.\n * @default false\n */\n editable?: boolean;\n /**\n * Whether the application can be suspended and unsuspended from this view. Each action runs through its\n * administration flow and is offered only when the server configures that flow. Reading the configured\n * flows needs a token that may read the server configuration.\n * @default false\n */\n lifecycleActions?: boolean;\n /**\n * Called after the application has been deleted.\n */\n onDeleted?: () => void;\n /**\n * Whether the application's client secret can be regenerated from this view. The new secret is shown\n * once.\n * @default false\n */\n secretRegenerable?: boolean;\n};\n\n/**\n * Shows an application and its OAuth configuration, and optionally lets the signed-in user edit it,\n * regenerate its client secret or delete it.\n *\n * The server guards applications with the root `system` permission, for reading and for changes alike;\n * it defines no application-specific permission. Gate the view and its actions on it with\n * `useGrantedPermissions().can('system')`.\n *\n * @example\n * ```tsx\n * const {can} = useGrantedPermissions();\n * const isAdmin = can('system');\n *\n * <ApplicationDetails\n * applicationId={applicationId}\n * editable={isAdmin}\n * deletable={isAdmin}\n * secretRegenerable={isAdmin}\n * onDeleted={() => navigate('/applications')}\n * />\n * ```\n */\nconst ApplicationDetails: FC<ApplicationDetailsProps> = ({\n administrationMode,\n applicationId,\n deletable = false,\n editable = false,\n lifecycleActions = false,\n onDeleted,\n preferences,\n secretRegenerable = false,\n ...rest\n}: ApplicationDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const endpoint: ManagementEndpoint = useManagementEndpoint('applications');\n const application: ResourceQueryResult<Application> = useResourceQuery(\n [ApplicationQueryKeys.APPLICATION, applicationId],\n (fetcher) => getApplication({...endpoint, applicationId, fetcher}),\n {enabled: Boolean(applicationId)},\n );\n const deletion: ResourceMutationResult<void, string> = useResourceMutation(\n (id: string, fetcher) => deleteApplication({...endpoint, administrationMode, applicationId: id, fetcher}),\n () => [[ApplicationQueryKeys.APPLICATIONS]],\n {onSuccess: onDeleted},\n );\n const update: ResourceMutationResult<Application, UpdateApplicationVariables> = useResourceMutation(\n ({application: current, changes}: UpdateApplicationVariables, fetcher) =>\n updateApplication({\n ...endpoint,\n applicationId: current.id,\n fetcher,\n payload: toUpdateApplicationRequest(current, changes),\n }),\n ({application: current}: UpdateApplicationVariables) => [\n [ApplicationQueryKeys.APPLICATION, current.id],\n [ApplicationQueryKeys.APPLICATIONS],\n ],\n );\n const regeneration: ResourceMutationResult<RegenerateClientSecretResult, string> = useResourceMutation(\n (id: string, fetcher) => regenerateClientSecret({...endpoint, administrationMode, applicationId: id, fetcher}),\n (id: string) => [[ApplicationQueryKeys.APPLICATION, id], [ApplicationQueryKeys.APPLICATIONS]],\n );\n\n const lifecycle: AccountLifecycleState = useAccountLifecycle({\n enabled: lifecycleActions,\n invalidatedKeys: [[ApplicationQueryKeys.APPLICATION, applicationId], [ApplicationQueryKeys.APPLICATIONS]],\n kind: 'application',\n status: application.data?.status?.value,\n subject: applicationId,\n });\n\n return (\n <BaseApplicationDetails\n application={application.data}\n isLoading={application.isLoading}\n error={\n toManagementErrorMessage(t, application.error, 'admin.applications.details.error') ??\n toManagementErrorMessage(t, deletion.error, 'admin.applications.details.delete.error') ??\n toManagementErrorMessage(t, update.error, 'admin.applications.details.edit.error') ??\n toManagementErrorMessage(t, regeneration.error, 'admin.applications.details.secret.error') ??\n toManagementErrorMessage(t, lifecycle.error, 'admin.common.lifecycle.error') ??\n toManagementErrorMessage(t, lifecycle.loadError, 'admin.common.lifecycle.load_error')\n }\n isDeleting={deletion.isLoading}\n onDelete={deletable ? (): void => void deletion.mutate(applicationId) : undefined}\n lifecycleActions={lifecycle.actions}\n isRunningLifecycleAction={lifecycle.isRunning}\n onLifecycleAction={lifecycleActions ? lifecycle.run : undefined}\n isUpdating={update.isLoading}\n onUpdate={\n editable && application.data\n ? (changes: ApplicationUpdateSubmission): void =>\n void update.mutate({application: application.data!, changes})\n : undefined\n }\n isRegeneratingSecret={regeneration.isLoading}\n onRegenerateSecret={secretRegenerable ? (): void => void regeneration.mutate(applicationId) : undefined}\n clientSecret={regeneration.data?.clientSecret ?? null}\n onSecretDismiss={regeneration.reset}\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default ApplicationDetails;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GA,MAAMA,sBAAmD,EACvD,oBACA,eACA,YAAY,OACZ,WAAW,OACX,mBAAmB,OACnB,WACA,aACA,oBAAoB,MACpB,GAAG,WACwC;CAC3C,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAMC,WAA+BC,8BAAsB,eAAe;CAC1E,MAAMC,cAAgDC,yBACpD,CAAC,qBAAqB,aAAa,cAAc,GAChD,YAAY,eAAe;EAAC,GAAG;EAAU;EAAe;EAAQ,CAAC,EAClE,EAAC,SAAS,QAAQ,cAAc,EAAC,CAClC;CACD,MAAMC,WAAiDC,6BACpD,IAAY,YAAY,kBAAkB;EAAC,GAAG;EAAU;EAAoB,eAAe;EAAI;EAAQ,CAAC,QACnG,CAAC,CAAC,qBAAqB,aAAa,CAAC,EAC3C,EAAC,WAAW,WAAU,CACvB;CACD,MAAMC,SAA0ED,6BAC7E,EAAC,aAAa,SAAS,WAAsC,YAC5D,kBAAkB;EAChB,GAAG;EACH,eAAe,QAAQ;EACvB;EACA,SAAS,2BAA2B,SAAS,QAAQ;EACtD,CAAC,GACH,EAAC,aAAa,cAAyC,CACtD,CAAC,qBAAqB,aAAa,QAAQ,GAAG,EAC9C,CAAC,qBAAqB,aAAa,CACpC,CACF;CACD,MAAME,eAA6EF,6BAChF,IAAY,YAAY,uBAAuB;EAAC,GAAG;EAAU;EAAoB,eAAe;EAAI;EAAQ,CAAC,GAC7G,OAAe,CAAC,CAAC,qBAAqB,aAAa,GAAG,EAAE,CAAC,qBAAqB,aAAa,CAAC,CAC9F;CAED,MAAMG,YAAmCC,4BAAoB;EAC3D,SAAS;EACT,iBAAiB,CAAC,CAAC,qBAAqB,aAAa,cAAc,EAAE,CAAC,qBAAqB,aAAa,CAAC;EACzG,MAAM;EACN,QAAQ,YAAY,MAAM,QAAQ;EAClC,SAAS;EACV,CAAC;AAEF,QACE,oBAACC;EACC,aAAa,YAAY;EACzB,WAAW,YAAY;EACvB,OACE,yBAAyB,GAAG,YAAY,OAAO,mCAAmC,IAClF,yBAAyB,GAAG,SAAS,OAAO,0CAA0C,IACtF,yBAAyB,GAAG,OAAO,OAAO,wCAAwC,IAClF,yBAAyB,GAAG,aAAa,OAAO,0CAA0C,IAC1F,yBAAyB,GAAG,UAAU,OAAO,+BAA+B,IAC5E,yBAAyB,GAAG,UAAU,WAAW,oCAAoC;EAEvF,YAAY,SAAS;EACrB,UAAU,kBAAwB,KAAK,SAAS,OAAO,cAAc,GAAG;EACxE,kBAAkB,UAAU;EAC5B,0BAA0B,UAAU;EACpC,mBAAmB,mBAAmB,UAAU,MAAM;EACtD,YAAY,OAAO;EACnB,UACE,YAAY,YAAY,QACnB,YACC,KAAK,OAAO,OAAO;GAAC,aAAa,YAAY;GAAO;GAAQ,CAAC,GAC/D;EAEN,sBAAsB,aAAa;EACnC,oBAAoB,0BAAgC,KAAK,aAAa,OAAO,cAAc,GAAG;EAC9F,cAAc,aAAa,MAAM,gBAAgB;EACjD,iBAAiB,aAAa;EACjB;EACb,GAAI;GACJ;;AAIN,iCAAe"}
@@ -1,4 +1,4 @@
1
- import { Application, ApplicationSubmission, OAuth2Config, Preferences } from '@thunderid/browser';
1
+ import { AccountLifecycleAction, Application, ApplicationSubmission, OAuth2Config, Preferences } from '@thunderid/browser';
2
2
  import { FC, ReactNode } from 'react';
3
3
  /**
4
4
  * The edited fields {@link BaseApplicationDetails} hands `onUpdate`. Whether the client is public does
@@ -19,7 +19,12 @@ export interface BaseApplicationDetailsRenderProps {
19
19
  isDeleting: boolean;
20
20
  isLoading: boolean;
21
21
  isRegeneratingSecret: boolean;
22
+ isRunningLifecycleAction: boolean;
22
23
  isUpdating: boolean;
24
+ /**
25
+ * The account lifecycle actions offered for the application. Empty when none is allowed.
26
+ */
27
+ lifecycleActions: AccountLifecycleAction[];
23
28
  /**
24
29
  * The application's OAuth configuration, without its client secret. Undefined when it has no OAuth client.
25
30
  */
@@ -28,6 +33,10 @@ export interface BaseApplicationDetailsRenderProps {
28
33
  * Deletes the application. Undefined when deletion is not allowed, including for read-only applications.
29
34
  */
30
35
  onDelete?: () => void;
36
+ /**
37
+ * Runs an account lifecycle action. Undefined when none is allowed.
38
+ */
39
+ onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
31
40
  /**
32
41
  * Gives the application a new client secret. Undefined when not allowed, for read-only applications
33
42
  * and for applications without a confidential OAuth client.
@@ -62,11 +71,21 @@ export interface BaseApplicationDetailsProps {
62
71
  isDeleting?: boolean;
63
72
  isLoading?: boolean;
64
73
  isRegeneratingSecret?: boolean;
74
+ isRunningLifecycleAction?: boolean;
65
75
  isUpdating?: boolean;
76
+ /**
77
+ * The account lifecycle actions to offer (suspend, unsuspend; applications are never locked), shown only with `onLifecycleAction`. They change
78
+ * only runtime state, so they apply to read-only applications too.
79
+ */
80
+ lifecycleActions?: AccountLifecycleAction[];
66
81
  /**
67
82
  * Deletes the application. The delete action is hidden when omitted.
68
83
  */
69
84
  onDelete?: () => void;
85
+ /**
86
+ * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first.
87
+ */
88
+ onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
70
89
  /**
71
90
  * Gives the application a new client secret. The regenerate action is hidden when omitted, and for
72
91
  * public clients, which have no secret.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApplicationDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/ApplicationDetails/BaseApplicationDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,EAEX,qBAAqB,EAGrB,YAAY,EAEZ,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAA2B,SAAS,EAAW,MAAM,OAAO,CAAC;AAYvE;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAC3C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iCAAiC,KAAK,SAAS,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,2BAA2B,KAAK,IAAI,CAAC;IAC9D,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAsBD;;;;GAIG;AACH,QAAA,MAAM,sBAAsB,EAAE,EAAE,CAAC,2BAA2B,CA2O3D,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"BaseApplicationDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/ApplicationDetails/BaseApplicationDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,sBAAsB,EACtB,WAAW,EAEX,qBAAqB,EAGrB,YAAY,EAEZ,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAA2B,SAAS,EAAW,MAAM,OAAO,CAAC;AAavE;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,wBAAwB,EAAE,OAAO,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;IAC3C;;OAEG;IACH,KAAK,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAC3C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iCAAiC,KAAK,SAAS,CAAC;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,2BAA2B,KAAK,IAAI,CAAC;IAC9D,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAsBD;;;;GAIG;AACH,QAAA,MAAM,sBAAsB,EAAE,EAAE,CAAC,2BAA2B,CAgQ3D,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -5,6 +5,7 @@ import Alert_default from "../../primitives/Alert/Alert.js";
5
5
  import CopyableText_default from "../../primitives/CopyableText/CopyableText.js";
6
6
  import useAdminPartStyles_default from "../shared/useAdminPartStyles.js";
7
7
  import AdminSection_default from "../shared/AdminSection.js";
8
+ import { AccessStatusBadge, AccountLifecycleButtons } from "../shared/AccountLifecycleParts.js";
8
9
  import { ChipList, ConfirmButton, DetailList, Panel } from "../shared/AdminParts.js";
9
10
  import ApplicationFormFields_default from "../shared/ApplicationFormFields.js";
10
11
  import ClientSecretNotice_default from "../shared/ClientSecretNotice.js";
@@ -36,7 +37,7 @@ const toSubmission = (values) => ({
36
37
  * application and handle editing, deletion and client secret regeneration, each offered only when its
37
38
  * handler is set.
38
39
  */
39
- const BaseApplicationDetails = ({ actions, application, children, className, clientSecret = null, error = null, isDeleting = false, isLoading = false, isRegeneratingSecret = false, isUpdating = false, onDelete, onRegenerateSecret, onSecretDismiss, onUpdate, preferences }) => {
40
+ const BaseApplicationDetails = ({ actions, application, children, className, clientSecret = null, error = null, isDeleting = false, isLoading = false, isRegeneratingSecret = false, isRunningLifecycleAction = false, isUpdating = false, lifecycleActions = [], onDelete, onLifecycleAction, onRegenerateSecret, onSecretDismiss, onUpdate, preferences }) => {
40
41
  const { t } = useTranslation_default(preferences?.i18n);
41
42
  const styles = useAdminPartStyles_default();
42
43
  const [draft, setDraft] = useState(null);
@@ -58,9 +59,12 @@ const BaseApplicationDetails = ({ actions, application, children, className, cli
58
59
  isDeleting,
59
60
  isLoading,
60
61
  isRegeneratingSecret,
62
+ isRunningLifecycleAction,
61
63
  isUpdating,
64
+ lifecycleActions: onLifecycleAction && application ? lifecycleActions : [],
62
65
  oauth,
63
66
  onDelete: isWritable ? onDelete : void 0,
67
+ onLifecycleAction: application ? onLifecycleAction : void 0,
64
68
  onRegenerateSecret: canRegenerate ? onRegenerateSecret : void 0,
65
69
  onSecretDismiss: clientSecret ? onSecretDismiss : void 0,
66
70
  onUpdate: isWritable ? onUpdate : void 0
@@ -114,6 +118,10 @@ const BaseApplicationDetails = ({ actions, application, children, className, cli
114
118
  value: application.logoUrl ?? notSet
115
119
  }
116
120
  ];
121
+ if (application.status?.value) overview.push({
122
+ label: t("admin.common.columns.status"),
123
+ value: /* @__PURE__ */ jsx(AccessStatusBadge, { status: application.status.value })
124
+ });
117
125
  const handleSave = (event) => {
118
126
  event.preventDefault();
119
127
  if (!draft) return;
@@ -127,12 +135,21 @@ const BaseApplicationDetails = ({ actions, application, children, className, cli
127
135
  title: application.name,
128
136
  description: application.description,
129
137
  preferences,
130
- actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [actions, onDelete && isWritable && /* @__PURE__ */ jsx(ConfirmButton, {
131
- confirmText: t("admin.applications.details.delete.confirm", { name: application.name }),
132
- isLoading: isDeleting,
133
- onConfirm: onDelete,
134
- children: t("admin.applications.details.delete.action")
135
- })] }),
138
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
139
+ actions,
140
+ onLifecycleAction && /* @__PURE__ */ jsx(AccountLifecycleButtons, {
141
+ actions: lifecycleActions,
142
+ isRunning: isRunningLifecycleAction,
143
+ name: application.name,
144
+ onAction: onLifecycleAction
145
+ }),
146
+ onDelete && isWritable && /* @__PURE__ */ jsx(ConfirmButton, {
147
+ confirmText: t("admin.applications.details.delete.confirm", { name: application.name }),
148
+ isLoading: isDeleting,
149
+ onConfirm: onDelete,
150
+ children: t("admin.applications.details.delete.action")
151
+ })
152
+ ] }),
136
153
  children: [error && /* @__PURE__ */ jsx(Alert_default, {
137
154
  variant: "error",
138
155
  role: "alert",
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApplicationDetails.js","names":["BaseApplicationDetails: FC<BaseApplicationDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","oauthConfig: OAuth2Config | undefined","isWritable: boolean","canRegenerate: boolean","oauth: Omit<OAuth2Config, 'clientSecret'> | undefined","AdminSection","Spinner","Alert","notSet: string","overview: DetailListItem[]","CopyableText","ApplicationFormFields","Button","ClientSecretNotice"],"sources":["../../../../src/components/admin/ApplicationDetails/BaseApplicationDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Application,\n APPLICATION_GRANT_TYPES,\n ApplicationSubmission,\n getApplicationOAuthConfig,\n isApplicationSubmissionValid,\n OAuth2Config,\n parseRedirectUris,\n Preferences,\n} from '@thunderid/browser';\nimport {FC, FormEvent, ReactElement, ReactNode, useState} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport Button from '../../primitives/Button/Button';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {ChipList, ConfirmButton, DetailList, DetailListItem, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport ApplicationFormFields, {ApplicationFormValues, ApplicationTextField} from '../shared/ApplicationFormFields';\nimport ClientSecretNotice from '../shared/ClientSecretNotice';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * The edited fields {@link BaseApplicationDetails} hands `onUpdate`. Whether the client is public does\n * not change.\n */\nexport type ApplicationUpdateSubmission = Omit<ApplicationSubmission, 'publicClient'>;\n\n/**\n * What {@link BaseApplicationDetails} hands its `children` render function, so the application can be\n * shown with any UI.\n */\nexport interface BaseApplicationDetailsRenderProps {\n application?: Application;\n /**\n * The new client secret, to show once. `null` while there is none.\n */\n clientSecret: string | null;\n error: string | null;\n isDeleting: boolean;\n isLoading: boolean;\n isRegeneratingSecret: boolean;\n isUpdating: boolean;\n /**\n * The application's OAuth configuration, without its client secret. Undefined when it has no OAuth client.\n */\n oauth?: Omit<OAuth2Config, 'clientSecret'>;\n /**\n * Deletes the application. Undefined when deletion is not allowed, including for read-only applications.\n */\n onDelete?: () => void;\n /**\n * Gives the application a new client secret. Undefined when not allowed, for read-only applications\n * and for applications without a confidential OAuth client.\n */\n onRegenerateSecret?: () => void;\n /**\n * Hides the new client secret once it has been copied. Undefined while there is no secret.\n */\n onSecretDismiss?: () => void;\n /**\n * Saves edited fields. Undefined when editing is not allowed, including for read-only applications.\n */\n onUpdate?: (application: ApplicationUpdateSubmission) => void;\n}\n\nexport interface BaseApplicationDetailsProps {\n /**\n * Controls rendered next to the application's name.\n */\n actions?: ReactNode;\n application?: Application;\n /**\n * Renders custom UI in place of the default view. Receives the application, its OAuth configuration,\n * the loading and error state, the actions the app allows and the new client secret to show.\n */\n children?: (props: BaseApplicationDetailsRenderProps) => ReactNode;\n className?: string;\n /**\n * A client secret just generated by `onRegenerateSecret`, shown once until `onSecretDismiss` clears it.\n */\n clientSecret?: string | null;\n error?: string | null;\n isDeleting?: boolean;\n isLoading?: boolean;\n isRegeneratingSecret?: boolean;\n isUpdating?: boolean;\n /**\n * Deletes the application. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n /**\n * Gives the application a new client secret. The regenerate action is hidden when omitted, and for\n * public clients, which have no secret.\n */\n onRegenerateSecret?: () => void;\n /**\n * Called when the user has copied the new client secret.\n */\n onSecretDismiss?: () => void;\n /**\n * Saves the edited general fields, redirect URIs and grant types. Editing is offered only when set.\n */\n onUpdate?: (application: ApplicationUpdateSubmission) => void;\n preferences?: Preferences;\n}\n\nconst toFormValues = (application: Application, oauth?: OAuth2Config): ApplicationFormValues => ({\n description: application.description ?? '',\n grantTypes: oauth?.grantTypes ?? [],\n logoUrl: application.logoUrl ?? '',\n name: application.name,\n publicClient: Boolean(oauth?.publicClient),\n redirectUris: (oauth?.redirectUris ?? []).join(', '),\n url: application.url ?? '',\n});\n\nconst toSubmission = (values: ApplicationFormValues): ApplicationSubmission => ({\n description: values.description.trim() || undefined,\n grantTypes: values.grantTypes,\n logoUrl: values.logoUrl.trim() || undefined,\n name: values.name.trim(),\n publicClient: values.publicClient,\n redirectUris: parseRedirectUris(values.redirectUris),\n url: values.url.trim() || undefined,\n});\n\n/**\n * Shows an application's general fields and OAuth configuration. Holds no data of its own: pass the\n * application and handle editing, deletion and client secret regeneration, each offered only when its\n * handler is set.\n */\nconst BaseApplicationDetails: FC<BaseApplicationDetailsProps> = ({\n actions,\n application,\n children,\n className,\n clientSecret = null,\n error = null,\n isDeleting = false,\n isLoading = false,\n isRegeneratingSecret = false,\n isUpdating = false,\n onDelete,\n onRegenerateSecret,\n onSecretDismiss,\n onUpdate,\n preferences,\n}: BaseApplicationDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n const [draft, setDraft] = useState<ApplicationFormValues | null>(null);\n\n const oauthConfig: OAuth2Config | undefined = getApplicationOAuthConfig(application);\n const isWritable: boolean = Boolean(application) && !application?.isReadOnly;\n const canRegenerate: boolean = isWritable && Boolean(oauthConfig) && !oauthConfig?.publicClient;\n\n if (children) {\n let oauth: Omit<OAuth2Config, 'clientSecret'> | undefined;\n\n if (oauthConfig) {\n const {clientSecret: storedSecret, ...rest}: OAuth2Config = oauthConfig;\n void storedSecret;\n oauth = rest;\n }\n\n return (\n <div className={className}>\n {children({\n application,\n clientSecret,\n error,\n isDeleting,\n isLoading,\n isRegeneratingSecret,\n isUpdating,\n oauth,\n onDelete: isWritable ? onDelete : undefined,\n onRegenerateSecret: canRegenerate ? onRegenerateSecret : undefined,\n onSecretDismiss: clientSecret ? onSecretDismiss : undefined,\n onUpdate: isWritable ? onUpdate : undefined,\n })}\n </div>\n );\n }\n\n if (isLoading && !application) {\n return (\n <AdminSection block=\"application-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!application) {\n return (\n <AdminSection block=\"application-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const notSet: string = t('admin.applications.details.not_set');\n const overview: DetailListItem[] = [\n {\n label: t('admin.applications.list.columns.client_id'),\n value: oauthConfig?.clientId ? <CopyableText value={oauthConfig.clientId} /> : notSet,\n },\n {label: t('admin.applications.details.type'), value: application.type ?? application.template ?? notSet},\n {label: t('admin.common.columns.organization_unit'), value: application.ouId ?? notSet},\n {label: t('admin.applications.fields.url'), value: application.url ?? notSet},\n {label: t('admin.applications.fields.logo_url'), value: application.logoUrl ?? notSet},\n ];\n\n const handleSave = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault();\n\n if (!draft) {\n return;\n }\n\n const {publicClient, ...submission}: ApplicationSubmission = toSubmission(draft);\n void publicClient;\n onUpdate?.(submission);\n setDraft(null);\n };\n\n return (\n <AdminSection\n block=\"application-details\"\n className={className}\n title={application.name}\n description={application.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onDelete && isWritable && (\n <ConfirmButton\n confirmText={t('admin.applications.details.delete.confirm', {name: application.name})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.applications.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n {draft ? (\n <Panel title={t('admin.applications.details.edit.action')}>\n <form\n className={styles['row']}\n style={{alignItems: 'stretch', flexDirection: 'column'}}\n onSubmit={handleSave}\n noValidate\n >\n <ApplicationFormFields\n preferences={preferences}\n values={draft}\n onInputChange={(field: ApplicationTextField, value: string): void =>\n setDraft((previous: ApplicationFormValues | null) => previous && {...previous, [field]: value})\n }\n onGrantTypesChange={(grantTypes: string[]): void =>\n setDraft((previous: ApplicationFormValues | null) => previous && {...previous, grantTypes})\n }\n />\n <div className={styles['row']} style={{justifyContent: 'flex-end'}}>\n <Button type=\"button\" variant=\"text\" disabled={isUpdating} onClick={(): void => setDraft(null)}>\n {t('admin.common.actions.cancel')}\n </Button>\n <Button\n type=\"submit\"\n loading={isUpdating}\n disabled={isUpdating || !isApplicationSubmissionValid(toSubmission(draft))}\n >\n {t('admin.applications.details.edit.save')}\n </Button>\n </div>\n </form>\n </Panel>\n ) : (\n <>\n <Panel\n title={t('admin.applications.details.overview')}\n actions={\n onUpdate && isWritable ? (\n <Button\n variant=\"outline\"\n size=\"small\"\n onClick={(): void => setDraft(toFormValues(application, oauthConfig))}\n >\n {t('admin.applications.details.edit.action')}\n </Button>\n ) : undefined\n }\n >\n <DetailList items={overview} />\n </Panel>\n {oauthConfig && (\n <Panel\n title={t('admin.applications.details.oauth')}\n actions={\n onRegenerateSecret && canRegenerate && !clientSecret ? (\n <ConfirmButton\n confirmText={t('admin.applications.details.secret.confirm', {name: application.name})}\n isLoading={isRegeneratingSecret}\n onConfirm={onRegenerateSecret}\n >\n {t('admin.applications.details.secret.action')}\n </ConfirmButton>\n ) : undefined\n }\n >\n {clientSecret && (\n <ClientSecretNotice secret={clientSecret} onDismiss={onSecretDismiss} preferences={preferences} />\n )}\n <DetailList\n items={[\n {\n label: t('admin.applications.fields.grant_types'),\n value: (\n <ChipList\n label={t('admin.applications.fields.grant_types')}\n emptyText={t('admin.applications.details.no_grant_types')}\n items={(oauthConfig.grantTypes ?? []).map((grantType: string) => ({\n id: grantType,\n label: APPLICATION_GRANT_TYPES.includes(grantType)\n ? t(`admin.applications.grant_types.${grantType}`)\n : grantType,\n }))}\n />\n ),\n },\n {\n label: t('admin.applications.fields.redirect_uris'),\n value: (\n <ChipList\n label={t('admin.applications.fields.redirect_uris')}\n emptyText={t('admin.applications.details.no_redirect_uris')}\n items={(oauthConfig.redirectUris ?? []).map((uri: string) => ({id: uri, label: uri}))}\n />\n ),\n },\n {\n label: t('admin.applications.details.public_client'),\n value: oauthConfig.publicClient\n ? t('admin.applications.details.yes')\n : t('admin.applications.details.no'),\n },\n ]}\n />\n </Panel>\n )}\n </>\n )}\n </AdminSection>\n );\n};\n\nexport default BaseApplicationDetails;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BaseApplicationDetails.js","names":["BaseApplicationDetails: FC<BaseApplicationDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","oauthConfig: OAuth2Config | undefined","isWritable: boolean","canRegenerate: boolean","oauth: Omit<OAuth2Config, 'clientSecret'> | undefined","AdminSection","Spinner","Alert","notSet: string","overview: DetailListItem[]","CopyableText","ApplicationFormFields","Button","ClientSecretNotice"],"sources":["../../../../src/components/admin/ApplicationDetails/BaseApplicationDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AccountLifecycleAction,\n Application,\n APPLICATION_GRANT_TYPES,\n ApplicationSubmission,\n getApplicationOAuthConfig,\n isApplicationSubmissionValid,\n OAuth2Config,\n parseRedirectUris,\n Preferences,\n} from '@thunderid/browser';\nimport {FC, FormEvent, ReactElement, ReactNode, useState} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport Button from '../../primitives/Button/Button';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {AccessStatusBadge, AccountLifecycleButtons} from '../shared/AccountLifecycleParts';\nimport {ChipList, ConfirmButton, DetailList, DetailListItem, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport ApplicationFormFields, {ApplicationFormValues, ApplicationTextField} from '../shared/ApplicationFormFields';\nimport ClientSecretNotice from '../shared/ClientSecretNotice';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * The edited fields {@link BaseApplicationDetails} hands `onUpdate`. Whether the client is public does\n * not change.\n */\nexport type ApplicationUpdateSubmission = Omit<ApplicationSubmission, 'publicClient'>;\n\n/**\n * What {@link BaseApplicationDetails} hands its `children` render function, so the application can be\n * shown with any UI.\n */\nexport interface BaseApplicationDetailsRenderProps {\n application?: Application;\n /**\n * The new client secret, to show once. `null` while there is none.\n */\n clientSecret: string | null;\n error: string | null;\n isDeleting: boolean;\n isLoading: boolean;\n isRegeneratingSecret: boolean;\n isRunningLifecycleAction: boolean;\n isUpdating: boolean;\n /**\n * The account lifecycle actions offered for the application. Empty when none is allowed.\n */\n lifecycleActions: AccountLifecycleAction[];\n /**\n * The application's OAuth configuration, without its client secret. Undefined when it has no OAuth client.\n */\n oauth?: Omit<OAuth2Config, 'clientSecret'>;\n /**\n * Deletes the application. Undefined when deletion is not allowed, including for read-only applications.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action. Undefined when none is allowed.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n /**\n * Gives the application a new client secret. Undefined when not allowed, for read-only applications\n * and for applications without a confidential OAuth client.\n */\n onRegenerateSecret?: () => void;\n /**\n * Hides the new client secret once it has been copied. Undefined while there is no secret.\n */\n onSecretDismiss?: () => void;\n /**\n * Saves edited fields. Undefined when editing is not allowed, including for read-only applications.\n */\n onUpdate?: (application: ApplicationUpdateSubmission) => void;\n}\n\nexport interface BaseApplicationDetailsProps {\n /**\n * Controls rendered next to the application's name.\n */\n actions?: ReactNode;\n application?: Application;\n /**\n * Renders custom UI in place of the default view. Receives the application, its OAuth configuration,\n * the loading and error state, the actions the app allows and the new client secret to show.\n */\n children?: (props: BaseApplicationDetailsRenderProps) => ReactNode;\n className?: string;\n /**\n * A client secret just generated by `onRegenerateSecret`, shown once until `onSecretDismiss` clears it.\n */\n clientSecret?: string | null;\n error?: string | null;\n isDeleting?: boolean;\n isLoading?: boolean;\n isRegeneratingSecret?: boolean;\n isRunningLifecycleAction?: boolean;\n isUpdating?: boolean;\n /**\n * The account lifecycle actions to offer (suspend, unsuspend; applications are never locked), shown only with `onLifecycleAction`. They change\n * only runtime state, so they apply to read-only applications too.\n */\n lifecycleActions?: AccountLifecycleAction[];\n /**\n * Deletes the application. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n /**\n * Gives the application a new client secret. The regenerate action is hidden when omitted, and for\n * public clients, which have no secret.\n */\n onRegenerateSecret?: () => void;\n /**\n * Called when the user has copied the new client secret.\n */\n onSecretDismiss?: () => void;\n /**\n * Saves the edited general fields, redirect URIs and grant types. Editing is offered only when set.\n */\n onUpdate?: (application: ApplicationUpdateSubmission) => void;\n preferences?: Preferences;\n}\n\nconst toFormValues = (application: Application, oauth?: OAuth2Config): ApplicationFormValues => ({\n description: application.description ?? '',\n grantTypes: oauth?.grantTypes ?? [],\n logoUrl: application.logoUrl ?? '',\n name: application.name,\n publicClient: Boolean(oauth?.publicClient),\n redirectUris: (oauth?.redirectUris ?? []).join(', '),\n url: application.url ?? '',\n});\n\nconst toSubmission = (values: ApplicationFormValues): ApplicationSubmission => ({\n description: values.description.trim() || undefined,\n grantTypes: values.grantTypes,\n logoUrl: values.logoUrl.trim() || undefined,\n name: values.name.trim(),\n publicClient: values.publicClient,\n redirectUris: parseRedirectUris(values.redirectUris),\n url: values.url.trim() || undefined,\n});\n\n/**\n * Shows an application's general fields and OAuth configuration. Holds no data of its own: pass the\n * application and handle editing, deletion and client secret regeneration, each offered only when its\n * handler is set.\n */\nconst BaseApplicationDetails: FC<BaseApplicationDetailsProps> = ({\n actions,\n application,\n children,\n className,\n clientSecret = null,\n error = null,\n isDeleting = false,\n isLoading = false,\n isRegeneratingSecret = false,\n isRunningLifecycleAction = false,\n isUpdating = false,\n lifecycleActions = [],\n onDelete,\n onLifecycleAction,\n onRegenerateSecret,\n onSecretDismiss,\n onUpdate,\n preferences,\n}: BaseApplicationDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n const [draft, setDraft] = useState<ApplicationFormValues | null>(null);\n\n const oauthConfig: OAuth2Config | undefined = getApplicationOAuthConfig(application);\n const isWritable: boolean = Boolean(application) && !application?.isReadOnly;\n const canRegenerate: boolean = isWritable && Boolean(oauthConfig) && !oauthConfig?.publicClient;\n\n if (children) {\n let oauth: Omit<OAuth2Config, 'clientSecret'> | undefined;\n\n if (oauthConfig) {\n const {clientSecret: storedSecret, ...rest}: OAuth2Config = oauthConfig;\n void storedSecret;\n oauth = rest;\n }\n\n return (\n <div className={className}>\n {children({\n application,\n clientSecret,\n error,\n isDeleting,\n isLoading,\n isRegeneratingSecret,\n isRunningLifecycleAction,\n isUpdating,\n lifecycleActions: onLifecycleAction && application ? lifecycleActions : [],\n oauth,\n onDelete: isWritable ? onDelete : undefined,\n onLifecycleAction: application ? onLifecycleAction : undefined,\n onRegenerateSecret: canRegenerate ? onRegenerateSecret : undefined,\n onSecretDismiss: clientSecret ? onSecretDismiss : undefined,\n onUpdate: isWritable ? onUpdate : undefined,\n })}\n </div>\n );\n }\n\n if (isLoading && !application) {\n return (\n <AdminSection block=\"application-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!application) {\n return (\n <AdminSection block=\"application-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const notSet: string = t('admin.applications.details.not_set');\n const overview: DetailListItem[] = [\n {\n label: t('admin.applications.list.columns.client_id'),\n value: oauthConfig?.clientId ? <CopyableText value={oauthConfig.clientId} /> : notSet,\n },\n {label: t('admin.applications.details.type'), value: application.type ?? application.template ?? notSet},\n {label: t('admin.common.columns.organization_unit'), value: application.ouId ?? notSet},\n {label: t('admin.applications.fields.url'), value: application.url ?? notSet},\n {label: t('admin.applications.fields.logo_url'), value: application.logoUrl ?? notSet},\n ];\n\n if (application.status?.value) {\n overview.push({\n label: t('admin.common.columns.status'),\n value: <AccessStatusBadge status={application.status.value} />,\n });\n }\n\n const handleSave = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault();\n\n if (!draft) {\n return;\n }\n\n const {publicClient, ...submission}: ApplicationSubmission = toSubmission(draft);\n void publicClient;\n onUpdate?.(submission);\n setDraft(null);\n };\n\n return (\n <AdminSection\n block=\"application-details\"\n className={className}\n title={application.name}\n description={application.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onLifecycleAction && (\n <AccountLifecycleButtons\n actions={lifecycleActions}\n isRunning={isRunningLifecycleAction}\n name={application.name}\n onAction={onLifecycleAction}\n />\n )}\n {onDelete && isWritable && (\n <ConfirmButton\n confirmText={t('admin.applications.details.delete.confirm', {name: application.name})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.applications.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n {draft ? (\n <Panel title={t('admin.applications.details.edit.action')}>\n <form\n className={styles['row']}\n style={{alignItems: 'stretch', flexDirection: 'column'}}\n onSubmit={handleSave}\n noValidate\n >\n <ApplicationFormFields\n preferences={preferences}\n values={draft}\n onInputChange={(field: ApplicationTextField, value: string): void =>\n setDraft((previous: ApplicationFormValues | null) => previous && {...previous, [field]: value})\n }\n onGrantTypesChange={(grantTypes: string[]): void =>\n setDraft((previous: ApplicationFormValues | null) => previous && {...previous, grantTypes})\n }\n />\n <div className={styles['row']} style={{justifyContent: 'flex-end'}}>\n <Button type=\"button\" variant=\"text\" disabled={isUpdating} onClick={(): void => setDraft(null)}>\n {t('admin.common.actions.cancel')}\n </Button>\n <Button\n type=\"submit\"\n loading={isUpdating}\n disabled={isUpdating || !isApplicationSubmissionValid(toSubmission(draft))}\n >\n {t('admin.applications.details.edit.save')}\n </Button>\n </div>\n </form>\n </Panel>\n ) : (\n <>\n <Panel\n title={t('admin.applications.details.overview')}\n actions={\n onUpdate && isWritable ? (\n <Button\n variant=\"outline\"\n size=\"small\"\n onClick={(): void => setDraft(toFormValues(application, oauthConfig))}\n >\n {t('admin.applications.details.edit.action')}\n </Button>\n ) : undefined\n }\n >\n <DetailList items={overview} />\n </Panel>\n {oauthConfig && (\n <Panel\n title={t('admin.applications.details.oauth')}\n actions={\n onRegenerateSecret && canRegenerate && !clientSecret ? (\n <ConfirmButton\n confirmText={t('admin.applications.details.secret.confirm', {name: application.name})}\n isLoading={isRegeneratingSecret}\n onConfirm={onRegenerateSecret}\n >\n {t('admin.applications.details.secret.action')}\n </ConfirmButton>\n ) : undefined\n }\n >\n {clientSecret && (\n <ClientSecretNotice secret={clientSecret} onDismiss={onSecretDismiss} preferences={preferences} />\n )}\n <DetailList\n items={[\n {\n label: t('admin.applications.fields.grant_types'),\n value: (\n <ChipList\n label={t('admin.applications.fields.grant_types')}\n emptyText={t('admin.applications.details.no_grant_types')}\n items={(oauthConfig.grantTypes ?? []).map((grantType: string) => ({\n id: grantType,\n label: APPLICATION_GRANT_TYPES.includes(grantType)\n ? t(`admin.applications.grant_types.${grantType}`)\n : grantType,\n }))}\n />\n ),\n },\n {\n label: t('admin.applications.fields.redirect_uris'),\n value: (\n <ChipList\n label={t('admin.applications.fields.redirect_uris')}\n emptyText={t('admin.applications.details.no_redirect_uris')}\n items={(oauthConfig.redirectUris ?? []).map((uri: string) => ({id: uri, label: uri}))}\n />\n ),\n },\n {\n label: t('admin.applications.details.public_client'),\n value: oauthConfig.publicClient\n ? t('admin.applications.details.yes')\n : t('admin.applications.details.no'),\n },\n ]}\n />\n </Panel>\n )}\n </>\n )}\n </AdminSection>\n );\n};\n\nexport default BaseApplicationDetails;\n"],"mappings":"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"}
@@ -1,6 +1,13 @@
1
+ import { AdministrationMode } from '@thunderid/browser';
1
2
  import { FC } from 'react';
2
3
  import { BaseGroupMembersProps } from './BaseGroupMembers';
3
4
  export type GroupMembersProps = Omit<BaseGroupMembersProps, 'candidates' | 'error' | 'isLoading' | 'isMutating' | 'members' | 'onAdd' | 'onRemove'> & {
5
+ /**
6
+ * How a member is removed. `flow` runs the administration flow the server configures for it, which can revoke
7
+ * grants and end sessions first. It needs `baseUrl`.
8
+ * @default 'native'
9
+ */
10
+ administrationMode?: AdministrationMode;
4
11
  /**
5
12
  * Whether members can be added and removed from this view.
6
13
  * @default false
@@ -1 +1 @@
1
- {"version":3,"file":"GroupMembers.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/GroupMembers/GroupMembers.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAC,EAAE,EAAwB,MAAM,OAAO,CAAC;AAChD,OAAyB,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAsB3E,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,qBAAqB,EACrB,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CACvF,GAAG;IACF;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,QAAA,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAuEvC,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"GroupMembers.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/GroupMembers/GroupMembers.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,kBAAkB,EAoBnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAAwB,MAAM,OAAO,CAAC;AAChD,OAAyB,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAsB3E,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,qBAAqB,EACrB,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CACvF,GAAG;IACF;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,QAAA,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAwEvC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -25,7 +25,7 @@ const getMembershipInvalidatedKeys = ({ groupId }) => [
25
25
  * <GroupMembers groupId={groupId} />
26
26
  * ```
27
27
  */
28
- const GroupMembers = ({ editable = false, groupId, preferences,...rest }) => {
28
+ const GroupMembers = ({ administrationMode, editable = false, groupId, preferences,...rest }) => {
29
29
  const { t } = useTranslation_default(preferences?.i18n);
30
30
  const groupsEndpoint = useManagementEndpoint_default("groups");
31
31
  const usersEndpoint = useManagementEndpoint_default("users");
@@ -58,6 +58,7 @@ const GroupMembers = ({ editable = false, groupId, preferences,...rest }) => {
58
58
  }), getMembershipInvalidatedKeys);
59
59
  const remove = useResourceMutation_default(({ groupId: id, members: changes }, fetcher) => removeGroupMembers({
60
60
  ...groupsEndpoint,
61
+ administrationMode,
61
62
  fetcher,
62
63
  groupId: id,
63
64
  members: changes