@thunderid/react 1.8.0 → 1.9.1
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.
- package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs +1 -1
- package/dist/cjs/components/admin/AgentDetails/AgentDetails.cjs +13 -2
- package/dist/cjs/components/admin/AgentDetails/AgentDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/AgentDetails/BaseAgentDetails.cjs +25 -7
- package/dist/cjs/components/admin/AgentDetails/BaseAgentDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/ApplicationDetails/ApplicationDetails.cjs +15 -2
- package/dist/cjs/components/admin/ApplicationDetails/ApplicationDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/ApplicationDetails/BaseApplicationDetails.cjs +24 -7
- package/dist/cjs/components/admin/ApplicationDetails/BaseApplicationDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/GroupMembers/GroupMembers.cjs +2 -1
- package/dist/cjs/components/admin/GroupMembers/GroupMembers.cjs.map +1 -1
- package/dist/cjs/components/admin/RoleAssignments/RoleAssignments.cjs +2 -1
- package/dist/cjs/components/admin/RoleAssignments/RoleAssignments.cjs.map +1 -1
- package/dist/cjs/components/admin/UserDetails/BaseUserDetails.cjs +32 -14
- package/dist/cjs/components/admin/UserDetails/BaseUserDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/UserDetails/UserDetails.cjs +14 -2
- package/dist/cjs/components/admin/UserDetails/UserDetails.cjs.map +1 -1
- package/dist/cjs/components/admin/shared/AccountLifecycleParts.cjs +105 -0
- package/dist/cjs/components/admin/shared/AccountLifecycleParts.cjs.map +1 -0
- package/dist/cjs/components/admin/shared/ApplicationFormFields.cjs +1 -1
- package/dist/cjs/components/auth/AcceptInvite/BaseAcceptInvite.cjs +4 -5
- package/dist/cjs/components/auth/AcceptInvite/BaseAcceptInvite.cjs.map +1 -1
- package/dist/cjs/components/auth/SignIn/SignIn.cjs +5 -5
- package/dist/cjs/components/auth/SignIn/SignIn.cjs.map +1 -1
- package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs +4 -5
- package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs.map +1 -1
- package/dist/cjs/components/auth/flow/Consent.cjs +53 -28
- package/dist/cjs/components/auth/flow/Consent.cjs.map +1 -1
- package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.cjs +104 -0
- package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.styles.cjs +59 -0
- package/dist/cjs/components/auth/flow/ConsentAuthorizationDetail.styles.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/ConsentDetailField.cjs +171 -0
- package/dist/cjs/components/auth/flow/ConsentDetailField.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/ConsentDetailField.styles.cjs +85 -0
- package/dist/cjs/components/auth/flow/ConsentDetailField.styles.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/ConsentDetailText.cjs +63 -0
- package/dist/cjs/components/auth/flow/ConsentDetailText.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/ConsentDetailText.styles.cjs +83 -0
- package/dist/cjs/components/auth/flow/ConsentDetailText.styles.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/FieldFactory.cjs +1 -1
- package/dist/cjs/components/auth/flow/resolveConsentText.cjs +25 -0
- package/dist/cjs/components/auth/flow/resolveConsentText.cjs.map +1 -0
- package/dist/cjs/components/primitives/KeyValueInput/KeyValueInput.cjs +1 -1
- package/dist/cjs/components/primitives/MultiInput/MultiInput.cjs +1 -1
- package/dist/cjs/components/primitives/PasswordField/PasswordField.cjs +1 -1
- package/dist/cjs/contexts/Theme/ThemeProvider.cjs +36 -6
- package/dist/cjs/contexts/Theme/ThemeProvider.cjs.map +1 -1
- package/dist/cjs/contexts/Theme/ThemeRootContext.cjs +12 -0
- package/dist/cjs/contexts/Theme/ThemeRootContext.cjs.map +1 -0
- package/dist/cjs/hooks/auth/useConsentDetailField.cjs +357 -0
- package/dist/cjs/hooks/auth/useConsentDetailField.cjs.map +1 -0
- package/dist/cjs/hooks/management/useAccountLifecycle.cjs +59 -0
- package/dist/cjs/hooks/management/useAccountLifecycle.cjs.map +1 -0
- package/dist/cjs/index.cjs +66 -1
- package/dist/cjs/utils/formatConsentDetailValue.cjs +26 -0
- package/dist/cjs/utils/formatConsentDetailValue.cjs.map +1 -0
- package/dist/cjs/utils/getConsentDetailLayout.cjs +44 -0
- package/dist/cjs/utils/getConsentDetailLayout.cjs.map +1 -0
- package/dist/cjs/utils/getConsentDetailListDisplay.cjs +30 -0
- package/dist/cjs/utils/getConsentDetailListDisplay.cjs.map +1 -0
- package/dist/cjs/utils/isConsentApprovalBlocked.cjs +41 -0
- package/dist/cjs/utils/isConsentApprovalBlocked.cjs.map +1 -0
- package/dist/cjs/utils/isConsentDetailPlainList.cjs +24 -0
- package/dist/cjs/utils/isConsentDetailPlainList.cjs.map +1 -0
- package/dist/cjs/utils/isConsentDetailStructured.cjs +15 -0
- package/dist/cjs/utils/isConsentDetailStructured.cjs.map +1 -0
- package/dist/cjs/utils/isConsentDetailTextClamped.cjs +25 -0
- package/dist/cjs/utils/isConsentDetailTextClamped.cjs.map +1 -0
- package/dist/components/account/UserProfile/BaseUserProfile.js +1 -1
- package/dist/components/admin/AgentDetails/AgentDetails.d.ts +8 -1
- package/dist/components/admin/AgentDetails/AgentDetails.d.ts.map +1 -1
- package/dist/components/admin/AgentDetails/AgentDetails.js +13 -2
- package/dist/components/admin/AgentDetails/AgentDetails.js.map +1 -1
- package/dist/components/admin/AgentDetails/BaseAgentDetails.d.ts +21 -1
- package/dist/components/admin/AgentDetails/BaseAgentDetails.d.ts.map +1 -1
- package/dist/components/admin/AgentDetails/BaseAgentDetails.js +25 -7
- package/dist/components/admin/AgentDetails/BaseAgentDetails.js.map +1 -1
- package/dist/components/admin/ApplicationDetails/ApplicationDetails.d.ts +15 -1
- package/dist/components/admin/ApplicationDetails/ApplicationDetails.d.ts.map +1 -1
- package/dist/components/admin/ApplicationDetails/ApplicationDetails.js +15 -2
- package/dist/components/admin/ApplicationDetails/ApplicationDetails.js.map +1 -1
- package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.d.ts +20 -1
- package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.d.ts.map +1 -1
- package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.js +24 -7
- package/dist/components/admin/ApplicationDetails/BaseApplicationDetails.js.map +1 -1
- package/dist/components/admin/GroupMembers/GroupMembers.d.ts +7 -0
- package/dist/components/admin/GroupMembers/GroupMembers.d.ts.map +1 -1
- package/dist/components/admin/GroupMembers/GroupMembers.js +2 -1
- package/dist/components/admin/GroupMembers/GroupMembers.js.map +1 -1
- package/dist/components/admin/RoleAssignments/RoleAssignments.d.ts +7 -0
- package/dist/components/admin/RoleAssignments/RoleAssignments.d.ts.map +1 -1
- package/dist/components/admin/RoleAssignments/RoleAssignments.js +2 -1
- package/dist/components/admin/RoleAssignments/RoleAssignments.js.map +1 -1
- package/dist/components/admin/UserDetails/BaseUserDetails.d.ts +22 -1
- package/dist/components/admin/UserDetails/BaseUserDetails.d.ts.map +1 -1
- package/dist/components/admin/UserDetails/BaseUserDetails.js +32 -14
- package/dist/components/admin/UserDetails/BaseUserDetails.js.map +1 -1
- package/dist/components/admin/UserDetails/UserDetails.d.ts +15 -1
- package/dist/components/admin/UserDetails/UserDetails.d.ts.map +1 -1
- package/dist/components/admin/UserDetails/UserDetails.js +14 -2
- package/dist/components/admin/UserDetails/UserDetails.js.map +1 -1
- package/dist/components/admin/shared/AccountLifecycleParts.d.ts +32 -0
- package/dist/components/admin/shared/AccountLifecycleParts.d.ts.map +1 -0
- package/dist/components/admin/shared/AccountLifecycleParts.js +101 -0
- package/dist/components/admin/shared/AccountLifecycleParts.js.map +1 -0
- package/dist/components/admin/shared/ApplicationFormFields.js +1 -1
- package/dist/components/auth/AcceptInvite/BaseAcceptInvite.d.ts.map +1 -1
- package/dist/components/auth/AcceptInvite/BaseAcceptInvite.js +5 -6
- package/dist/components/auth/AcceptInvite/BaseAcceptInvite.js.map +1 -1
- package/dist/components/auth/SignIn/SignIn.d.ts.map +1 -1
- package/dist/components/auth/SignIn/SignIn.js +6 -6
- package/dist/components/auth/SignIn/SignIn.js.map +1 -1
- package/dist/components/auth/flow/AuthOptionFactory.d.ts.map +1 -1
- package/dist/components/auth/flow/AuthOptionFactory.js +5 -6
- package/dist/components/auth/flow/AuthOptionFactory.js.map +1 -1
- package/dist/components/auth/flow/Consent.d.ts +6 -0
- package/dist/components/auth/flow/Consent.d.ts.map +1 -1
- package/dist/components/auth/flow/Consent.js +54 -29
- package/dist/components/auth/flow/Consent.js.map +1 -1
- package/dist/components/auth/flow/ConsentAuthorizationDetail.d.ts +68 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.js +100 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.js.map +1 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.d.ts +10 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.js +57 -0
- package/dist/components/auth/flow/ConsentAuthorizationDetail.styles.js.map +1 -0
- package/dist/components/auth/flow/ConsentDetailField.d.ts +39 -0
- package/dist/components/auth/flow/ConsentDetailField.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentDetailField.js +168 -0
- package/dist/components/auth/flow/ConsentDetailField.js.map +1 -0
- package/dist/components/auth/flow/ConsentDetailField.styles.d.ts +11 -0
- package/dist/components/auth/flow/ConsentDetailField.styles.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentDetailField.styles.js +83 -0
- package/dist/components/auth/flow/ConsentDetailField.styles.js.map +1 -0
- package/dist/components/auth/flow/ConsentDetailText.d.ts +53 -0
- package/dist/components/auth/flow/ConsentDetailText.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentDetailText.js +58 -0
- package/dist/components/auth/flow/ConsentDetailText.js.map +1 -0
- package/dist/components/auth/flow/ConsentDetailText.styles.d.ts +20 -0
- package/dist/components/auth/flow/ConsentDetailText.styles.d.ts.map +1 -0
- package/dist/components/auth/flow/ConsentDetailText.styles.js +80 -0
- package/dist/components/auth/flow/ConsentDetailText.styles.js.map +1 -0
- package/dist/components/auth/flow/FieldFactory.js +1 -1
- package/dist/components/auth/flow/resolveConsentText.d.ts +13 -0
- package/dist/components/auth/flow/resolveConsentText.d.ts.map +1 -0
- package/dist/components/auth/flow/resolveConsentText.js +23 -0
- package/dist/components/auth/flow/resolveConsentText.js.map +1 -0
- package/dist/components/primitives/KeyValueInput/KeyValueInput.js +1 -1
- package/dist/components/primitives/MultiInput/MultiInput.js +1 -1
- package/dist/components/primitives/PasswordField/PasswordField.js +1 -1
- package/dist/contexts/Theme/ThemeProvider.d.ts +2 -1
- package/dist/contexts/Theme/ThemeProvider.d.ts.map +1 -1
- package/dist/contexts/Theme/ThemeProvider.js +37 -7
- package/dist/contexts/Theme/ThemeProvider.js.map +1 -1
- package/dist/contexts/Theme/ThemeRootContext.d.ts +11 -0
- package/dist/contexts/Theme/ThemeRootContext.d.ts.map +1 -0
- package/dist/contexts/Theme/ThemeRootContext.js +10 -0
- package/dist/contexts/Theme/ThemeRootContext.js.map +1 -0
- package/dist/hooks/auth/useConsentDetailField.d.ts +130 -0
- package/dist/hooks/auth/useConsentDetailField.d.ts.map +1 -0
- package/dist/hooks/auth/useConsentDetailField.js +354 -0
- package/dist/hooks/auth/useConsentDetailField.js.map +1 -0
- package/dist/hooks/management/useAccountLifecycle.d.ts +49 -0
- package/dist/hooks/management/useAccountLifecycle.d.ts.map +1 -0
- package/dist/hooks/management/useAccountLifecycle.js +56 -0
- package/dist/hooks/management/useAccountLifecycle.js.map +1 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/utils/formatConsentDetailValue.d.ts +16 -0
- package/dist/utils/formatConsentDetailValue.d.ts.map +1 -0
- package/dist/utils/formatConsentDetailValue.js +25 -0
- package/dist/utils/formatConsentDetailValue.js.map +1 -0
- package/dist/utils/getConsentDetailLayout.d.ts +32 -0
- package/dist/utils/getConsentDetailLayout.d.ts.map +1 -0
- package/dist/utils/getConsentDetailLayout.js +43 -0
- package/dist/utils/getConsentDetailLayout.js.map +1 -0
- package/dist/utils/getConsentDetailListDisplay.d.ts +30 -0
- package/dist/utils/getConsentDetailListDisplay.d.ts.map +1 -0
- package/dist/utils/getConsentDetailListDisplay.js +30 -0
- package/dist/utils/getConsentDetailListDisplay.js.map +1 -0
- package/dist/utils/isConsentApprovalBlocked.d.ts +17 -0
- package/dist/utils/isConsentApprovalBlocked.d.ts.map +1 -0
- package/dist/utils/isConsentApprovalBlocked.js +39 -0
- package/dist/utils/isConsentApprovalBlocked.js.map +1 -0
- package/dist/utils/isConsentDetailPlainList.d.ts +15 -0
- package/dist/utils/isConsentDetailPlainList.d.ts.map +1 -0
- package/dist/utils/isConsentDetailPlainList.js +23 -0
- package/dist/utils/isConsentDetailPlainList.js.map +1 -0
- package/dist/utils/isConsentDetailStructured.d.ts +10 -0
- package/dist/utils/isConsentDetailStructured.d.ts.map +1 -0
- package/dist/utils/isConsentDetailStructured.js +14 -0
- package/dist/utils/isConsentDetailStructured.js.map +1 -0
- package/dist/utils/isConsentDetailTextClamped.d.ts +20 -0
- package/dist/utils/isConsentDetailTextClamped.d.ts.map +1 -0
- package/dist/utils/isConsentDetailTextClamped.js +24 -0
- package/dist/utils/isConsentDetailTextClamped.js.map +1 -0
- package/package.json +2 -2
|
@@ -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":"
|
|
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;
|
|
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: [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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":"
|
|
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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupMembers.js","names":["GroupMembers: FC<GroupMembersProps>","useTranslation","groupsEndpoint: ManagementEndpoint","useManagementEndpoint","usersEndpoint: ManagementEndpoint","agentsEndpoint: ManagementEndpoint","members: ResourceQueryResult<GroupMemberListResponse>","useResourceQuery","users: ResourceQueryResult<ManagedUserListResponse>","agents: ResourceQueryResult<AgentListResponse>","add: ResourceMutationResult<Group, GroupMembersVariables>","useResourceMutation","remove: ResourceMutationResult<Group, GroupMembersVariables>","candidates: Partial<Record<'user' | 'agent', IdentityCandidate[]>>","BaseGroupMembers"],"sources":["../../../../src/components/admin/GroupMembers/GroupMembers.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AgentListResponse,\n AgentQueryKeys,\n BasicAgent,\n Group,\n GroupMember,\n GroupMemberListResponse,\n GroupQueryKeys,\n ManagedUser,\n ManagedUserListResponse,\n ResourceQueryKey,\n RoleQueryKeys,\n UserQueryKeys,\n addGroupMembers,\n getAgents,\n getGroupMembers,\n getUsers,\n removeGroupMembers,\n resolveManagedIdentityName,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo} from 'react';\nimport BaseGroupMembers, {BaseGroupMembersProps} from './BaseGroupMembers';\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';\nimport {IdentityCandidate} from '../shared/IdentityPicker';\n\ninterface GroupMembersVariables {\n groupId: string;\n members: Pick<GroupMember, 'id' | 'type'>[];\n}\n\n/**\n * Query keys to refetch after a group's membership changes.\n */\nconst getMembershipInvalidatedKeys = ({groupId}: GroupMembersVariables): ResourceQueryKey[] => [\n [GroupQueryKeys.GROUP, groupId],\n [GroupQueryKeys.GROUP_MEMBERS, groupId],\n [GroupQueryKeys.MEMBERSHIPS],\n [RoleQueryKeys.ROLE_ASSIGNMENTS],\n];\n\nexport type GroupMembersProps = Omit<\n BaseGroupMembersProps,\n 'candidates' | 'error' | 'isLoading' | 'isMutating' | 'members' | 'onAdd' | 'onRemove'\n> & {\n /**\n * Whether members can be added and removed from this view.\n * @default false\n */\n editable?: boolean;\n groupId: string;\n};\n\n/**\n * Lists the users and agents in a group, and lets the signed-in user add and remove them.\n *\n * @example\n * ```tsx\n * <GroupMembers groupId={groupId} />\n * ```\n */\nconst GroupMembers: FC<GroupMembersProps> = ({\n editable = false,\n groupId,\n preferences,\n ...rest\n}: GroupMembersProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const groupsEndpoint: ManagementEndpoint = useManagementEndpoint('groups');\n const usersEndpoint: ManagementEndpoint = useManagementEndpoint('users');\n const agentsEndpoint: ManagementEndpoint = useManagementEndpoint('agents');\n const members: ResourceQueryResult<GroupMemberListResponse> = useResourceQuery(\n [GroupQueryKeys.GROUP_MEMBERS, groupId, {limit: 100}],\n (fetcher) => getGroupMembers({...groupsEndpoint, fetcher, groupId, limit: 100}),\n {enabled: Boolean(groupId)},\n );\n const users: ResourceQueryResult<ManagedUserListResponse> = useResourceQuery(\n [UserQueryKeys.USERS, {limit: 100}],\n (fetcher) => getUsers({...usersEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const agents: ResourceQueryResult<AgentListResponse> = useResourceQuery(\n [AgentQueryKeys.AGENTS, {limit: 100}],\n (fetcher) => getAgents({...agentsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const add: ResourceMutationResult<Group, GroupMembersVariables> = useResourceMutation(\n ({groupId: id, members: changes}: GroupMembersVariables, fetcher) =>\n addGroupMembers({...groupsEndpoint, fetcher, groupId: id, members: changes}),\n getMembershipInvalidatedKeys,\n );\n const remove: ResourceMutationResult<Group, GroupMembersVariables> = useResourceMutation(\n ({groupId: id, members: changes}: GroupMembersVariables, fetcher) =>\n removeGroupMembers({...groupsEndpoint, fetcher, groupId: id, members: changes}),\n getMembershipInvalidatedKeys,\n );\n\n const candidates: Partial<Record<'user' | 'agent', IdentityCandidate[]>> = useMemo(\n () => ({\n agent: (agents.data?.agents ?? []).map((agent: BasicAgent) => ({id: agent.id, label: agent.name})),\n user: (users.data?.users ?? []).map((user: ManagedUser) => ({\n id: user.id,\n label: resolveManagedIdentityName(user),\n })),\n }),\n [agents.data, users.data],\n );\n\n return (\n <BaseGroupMembers\n members={members.data?.members ?? []}\n isLoading={members.isLoading}\n isMutating={add.isLoading || remove.isLoading}\n error={\n toManagementErrorMessage(t, members.error, 'admin.groups.members.error') ??\n toManagementErrorMessage(t, add.error ?? remove.error, 'admin.groups.members.update_error')\n }\n candidates={editable ? candidates : undefined}\n onAdd={\n editable\n ? (member: Pick<GroupMember, 'id' | 'type'>): void => void add.mutate({groupId, members: [member]})\n : undefined\n }\n onRemove={\n editable\n ? (member: GroupMember): void => void remove.mutate({groupId, members: [{id: member.id, type: member.type}]})\n : undefined\n }\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default GroupMembers;\n"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"GroupMembers.js","names":["GroupMembers: FC<GroupMembersProps>","useTranslation","groupsEndpoint: ManagementEndpoint","useManagementEndpoint","usersEndpoint: ManagementEndpoint","agentsEndpoint: ManagementEndpoint","members: ResourceQueryResult<GroupMemberListResponse>","useResourceQuery","users: ResourceQueryResult<ManagedUserListResponse>","agents: ResourceQueryResult<AgentListResponse>","add: ResourceMutationResult<Group, GroupMembersVariables>","useResourceMutation","remove: ResourceMutationResult<Group, GroupMembersVariables>","candidates: Partial<Record<'user' | 'agent', IdentityCandidate[]>>","BaseGroupMembers"],"sources":["../../../../src/components/admin/GroupMembers/GroupMembers.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AdministrationMode,\n AgentListResponse,\n AgentQueryKeys,\n BasicAgent,\n Group,\n GroupMember,\n GroupMemberListResponse,\n GroupQueryKeys,\n ManagedUser,\n ManagedUserListResponse,\n ResourceQueryKey,\n RoleQueryKeys,\n UserQueryKeys,\n addGroupMembers,\n getAgents,\n getGroupMembers,\n getUsers,\n removeGroupMembers,\n resolveManagedIdentityName,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo} from 'react';\nimport BaseGroupMembers, {BaseGroupMembersProps} from './BaseGroupMembers';\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';\nimport {IdentityCandidate} from '../shared/IdentityPicker';\n\ninterface GroupMembersVariables {\n groupId: string;\n members: Pick<GroupMember, 'id' | 'type'>[];\n}\n\n/**\n * Query keys to refetch after a group's membership changes.\n */\nconst getMembershipInvalidatedKeys = ({groupId}: GroupMembersVariables): ResourceQueryKey[] => [\n [GroupQueryKeys.GROUP, groupId],\n [GroupQueryKeys.GROUP_MEMBERS, groupId],\n [GroupQueryKeys.MEMBERSHIPS],\n [RoleQueryKeys.ROLE_ASSIGNMENTS],\n];\n\nexport type GroupMembersProps = Omit<\n BaseGroupMembersProps,\n 'candidates' | 'error' | 'isLoading' | 'isMutating' | 'members' | 'onAdd' | 'onRemove'\n> & {\n /**\n * How a member is removed. `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 /**\n * Whether members can be added and removed from this view.\n * @default false\n */\n editable?: boolean;\n groupId: string;\n};\n\n/**\n * Lists the users and agents in a group, and lets the signed-in user add and remove them.\n *\n * @example\n * ```tsx\n * <GroupMembers groupId={groupId} />\n * ```\n */\nconst GroupMembers: FC<GroupMembersProps> = ({\n administrationMode,\n editable = false,\n groupId,\n preferences,\n ...rest\n}: GroupMembersProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const groupsEndpoint: ManagementEndpoint = useManagementEndpoint('groups');\n const usersEndpoint: ManagementEndpoint = useManagementEndpoint('users');\n const agentsEndpoint: ManagementEndpoint = useManagementEndpoint('agents');\n const members: ResourceQueryResult<GroupMemberListResponse> = useResourceQuery(\n [GroupQueryKeys.GROUP_MEMBERS, groupId, {limit: 100}],\n (fetcher) => getGroupMembers({...groupsEndpoint, fetcher, groupId, limit: 100}),\n {enabled: Boolean(groupId)},\n );\n const users: ResourceQueryResult<ManagedUserListResponse> = useResourceQuery(\n [UserQueryKeys.USERS, {limit: 100}],\n (fetcher) => getUsers({...usersEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const agents: ResourceQueryResult<AgentListResponse> = useResourceQuery(\n [AgentQueryKeys.AGENTS, {limit: 100}],\n (fetcher) => getAgents({...agentsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const add: ResourceMutationResult<Group, GroupMembersVariables> = useResourceMutation(\n ({groupId: id, members: changes}: GroupMembersVariables, fetcher) =>\n addGroupMembers({...groupsEndpoint, fetcher, groupId: id, members: changes}),\n getMembershipInvalidatedKeys,\n );\n const remove: ResourceMutationResult<Group, GroupMembersVariables> = useResourceMutation(\n ({groupId: id, members: changes}: GroupMembersVariables, fetcher) =>\n removeGroupMembers({...groupsEndpoint, administrationMode, fetcher, groupId: id, members: changes}),\n getMembershipInvalidatedKeys,\n );\n\n const candidates: Partial<Record<'user' | 'agent', IdentityCandidate[]>> = useMemo(\n () => ({\n agent: (agents.data?.agents ?? []).map((agent: BasicAgent) => ({id: agent.id, label: agent.name})),\n user: (users.data?.users ?? []).map((user: ManagedUser) => ({\n id: user.id,\n label: resolveManagedIdentityName(user),\n })),\n }),\n [agents.data, users.data],\n );\n\n return (\n <BaseGroupMembers\n members={members.data?.members ?? []}\n isLoading={members.isLoading}\n isMutating={add.isLoading || remove.isLoading}\n error={\n toManagementErrorMessage(t, members.error, 'admin.groups.members.error') ??\n toManagementErrorMessage(t, add.error ?? remove.error, 'admin.groups.members.update_error')\n }\n candidates={editable ? candidates : undefined}\n onAdd={\n editable\n ? (member: Pick<GroupMember, 'id' | 'type'>): void => void add.mutate({groupId, members: [member]})\n : undefined\n }\n onRemove={\n editable\n ? (member: GroupMember): void => void remove.mutate({groupId, members: [{id: member.id, type: member.type}]})\n : undefined\n }\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default GroupMembers;\n"],"mappings":";;;;;;;;;;;;;AAyCA,MAAM,gCAAgC,EAAC,cAAwD;CAC7F,CAAC,eAAe,OAAO,QAAQ;CAC/B,CAAC,eAAe,eAAe,QAAQ;CACvC,CAAC,eAAe,YAAY;CAC5B,CAAC,cAAc,iBAAiB;CACjC;;;;;;;;;AA4BD,MAAMA,gBAAuC,EAC3C,oBACA,WAAW,OACX,SACA,YACA,GAAG,WACkC;CACrC,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAMC,iBAAqCC,8BAAsB,SAAS;CAC1E,MAAMC,gBAAoCD,8BAAsB,QAAQ;CACxE,MAAME,iBAAqCF,8BAAsB,SAAS;CAC1E,MAAMG,UAAwDC,yBAC5D;EAAC,eAAe;EAAe;EAAS,EAAC,OAAO,KAAI;EAAC,GACpD,YAAY,gBAAgB;EAAC,GAAG;EAAgB;EAAS;EAAS,OAAO;EAAI,CAAC,EAC/E,EAAC,SAAS,QAAQ,QAAQ,EAAC,CAC5B;CACD,MAAMC,QAAsDD,yBAC1D,CAAC,cAAc,OAAO,EAAC,OAAO,KAAI,CAAC,GAClC,YAAY,SAAS;EAAC,GAAG;EAAe;EAAS,OAAO;EAAI,CAAC,EAC9D,EAAC,SAAS,UAAS,CACpB;CACD,MAAME,SAAiDF,yBACrD,CAAC,eAAe,QAAQ,EAAC,OAAO,KAAI,CAAC,GACpC,YAAY,UAAU;EAAC,GAAG;EAAgB;EAAS,OAAO;EAAI,CAAC,EAChE,EAAC,SAAS,UAAS,CACpB;CACD,MAAMG,MAA4DC,6BAC/D,EAAC,SAAS,IAAI,SAAS,WAAiC,YACvD,gBAAgB;EAAC,GAAG;EAAgB;EAAS,SAAS;EAAI,SAAS;EAAQ,CAAC,EAC9E,6BACD;CACD,MAAMC,SAA+DD,6BAClE,EAAC,SAAS,IAAI,SAAS,WAAiC,YACvD,mBAAmB;EAAC,GAAG;EAAgB;EAAoB;EAAS,SAAS;EAAI,SAAS;EAAQ,CAAC,EACrG,6BACD;CAED,MAAME,aAAqE,eAClE;EACL,QAAQ,OAAO,MAAM,UAAU,EAAE,EAAE,KAAK,WAAuB;GAAC,IAAI,MAAM;GAAI,OAAO,MAAM;GAAK,EAAE;EAClG,OAAO,MAAM,MAAM,SAAS,EAAE,EAAE,KAAK,UAAuB;GAC1D,IAAI,KAAK;GACT,OAAO,2BAA2B,KAAK;GACxC,EAAE;EACJ,GACD,CAAC,OAAO,MAAM,MAAM,KAAK,CAC1B;AAED,QACE,oBAACC;EACC,SAAS,QAAQ,MAAM,WAAW,EAAE;EACpC,WAAW,QAAQ;EACnB,YAAY,IAAI,aAAa,OAAO;EACpC,OACE,yBAAyB,GAAG,QAAQ,OAAO,6BAA6B,IACxE,yBAAyB,GAAG,IAAI,SAAS,OAAO,OAAO,oCAAoC;EAE7F,YAAY,WAAW,aAAa;EACpC,OACE,YACK,WAAmD,KAAK,IAAI,OAAO;GAAC;GAAS,SAAS,CAAC,OAAO;GAAC,CAAC,GACjG;EAEN,UACE,YACK,WAA8B,KAAK,OAAO,OAAO;GAAC;GAAS,SAAS,CAAC;IAAC,IAAI,OAAO;IAAI,MAAM,OAAO;IAAK,CAAC;GAAC,CAAC,GAC3G;EAEO;EACb,GAAI;GACJ;;AAIN,2BAAe"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { AdministrationMode } from '@thunderid/browser';
|
|
1
2
|
import { FC } from 'react';
|
|
2
3
|
import { BaseRoleAssignmentsProps } from './BaseRoleAssignments';
|
|
3
4
|
export type RoleAssignmentsProps = Omit<BaseRoleAssignmentsProps, 'assignments' | 'candidates' | 'error' | 'isLoading' | 'isMutating' | 'onAssign' | 'onTypeFilterChange' | 'onUnassign' | 'typeFilter'> & {
|
|
5
|
+
/**
|
|
6
|
+
* How an assignment 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 assignments can be added and removed from this view.
|
|
6
13
|
* @default false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoleAssignments.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/RoleAssignments/RoleAssignments.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RoleAssignments.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/RoleAssignments/RoleAssignments.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,kBAAkB,EAuBnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAAkC,MAAM,OAAO,CAAC;AAC1D,OAA4B,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AAYpF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CACrC,wBAAwB,EACtB,aAAa,GACb,YAAY,GACZ,OAAO,GACP,WAAW,GACX,YAAY,GACZ,UAAU,GACV,oBAAoB,GACpB,YAAY,GACZ,YAAY,CACf,GAAG;IACF;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;GAQG;AACH,QAAA,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CAqF7C,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -17,7 +17,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
17
17
|
* <RoleAssignments roleId={roleId} />
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
|
-
const RoleAssignments = ({ editable = false, filterable = true, preferences, roleId,...rest }) => {
|
|
20
|
+
const RoleAssignments = ({ administrationMode, editable = false, filterable = true, preferences, roleId,...rest }) => {
|
|
21
21
|
const { t } = useTranslation_default(preferences?.i18n);
|
|
22
22
|
const [typeFilter, setTypeFilter] = useState(null);
|
|
23
23
|
const rolesEndpoint = useManagementEndpoint_default("roles");
|
|
@@ -62,6 +62,7 @@ const RoleAssignments = ({ editable = false, filterable = true, preferences, rol
|
|
|
62
62
|
}), () => [[RoleQueryKeys.ROLE_ASSIGNMENTS]]);
|
|
63
63
|
const unassign = useResourceMutation_default(({ assignments: changes, roleId: id }, fetcher) => removeRoleAssignments({
|
|
64
64
|
...rolesEndpoint,
|
|
65
|
+
administrationMode,
|
|
65
66
|
assignments: changes,
|
|
66
67
|
fetcher,
|
|
67
68
|
roleId: id
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoleAssignments.js","names":["RoleAssignments: FC<RoleAssignmentsProps>","useTranslation","rolesEndpoint: ManagementEndpoint","useManagementEndpoint","usersEndpoint: ManagementEndpoint","groupsEndpoint: ManagementEndpoint","agentsEndpoint: ManagementEndpoint","type: RoleAssigneeType | undefined","assignments: ResourceQueryResult<RoleAssignmentListResponse>","useResourceQuery","users: ResourceQueryResult<ManagedUserListResponse>","groups: ResourceQueryResult<GroupListResponse>","agents: ResourceQueryResult<AgentListResponse>","assign: ResourceMutationResult<void, RoleAssignmentsVariables>","useResourceMutation","unassign: ResourceMutationResult<void, RoleAssignmentsVariables>","candidates: Partial<Record<'user' | 'group' | 'agent', IdentityCandidate[]>>","BaseRoleAssignments"],"sources":["../../../../src/components/admin/RoleAssignments/RoleAssignments.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AgentListResponse,\n AgentQueryKeys,\n BasicAgent,\n Group,\n GroupListResponse,\n GroupQueryKeys,\n ManagedUser,\n ManagedUserListResponse,\n RoleAssigneeType,\n RoleAssignment,\n RoleAssignmentInput,\n RoleAssignmentListResponse,\n RoleQueryKeys,\n UserQueryKeys,\n addRoleAssignments,\n getAgents,\n getGroups,\n getRoleAssignments,\n getUsers,\n removeRoleAssignments,\n resolveManagedIdentityName,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo, useState} from 'react';\nimport BaseRoleAssignments, {BaseRoleAssignmentsProps} from './BaseRoleAssignments';\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';\nimport {IdentityCandidate} from '../shared/IdentityPicker';\n\ninterface RoleAssignmentsVariables {\n assignments: RoleAssignmentInput[];\n roleId: string;\n}\n\nexport type RoleAssignmentsProps = Omit<\n BaseRoleAssignmentsProps,\n | 'assignments'\n | 'candidates'\n | 'error'\n | 'isLoading'\n | 'isMutating'\n | 'onAssign'\n | 'onTypeFilterChange'\n | 'onUnassign'\n | 'typeFilter'\n> & {\n /**\n * Whether assignments can be added and removed from this view.\n * @default false\n */\n editable?: boolean;\n /**\n * Whether to show the assignee type filter, which narrows the list on the server.\n * @default true\n */\n filterable?: boolean;\n roleId: string;\n};\n\n/**\n * Lists who holds a role, and lets the signed-in user assign it to users, groups and agents or\n * remove it from them.\n *\n * @example\n * ```tsx\n * <RoleAssignments roleId={roleId} />\n * ```\n */\nconst RoleAssignments: FC<RoleAssignmentsProps> = ({\n editable = false,\n filterable = true,\n preferences,\n roleId,\n ...rest\n}: RoleAssignmentsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const [typeFilter, setTypeFilter] = useState<RoleAssigneeType | null>(null);\n const rolesEndpoint: ManagementEndpoint = useManagementEndpoint('roles');\n const usersEndpoint: ManagementEndpoint = useManagementEndpoint('users');\n const groupsEndpoint: ManagementEndpoint = useManagementEndpoint('groups');\n const agentsEndpoint: ManagementEndpoint = useManagementEndpoint('agents');\n const type: RoleAssigneeType | undefined = typeFilter ?? undefined;\n const assignments: ResourceQueryResult<RoleAssignmentListResponse> = useResourceQuery(\n [RoleQueryKeys.ROLE_ASSIGNMENTS, roleId, {limit: 100, type}],\n (fetcher) => getRoleAssignments({...rolesEndpoint, fetcher, limit: 100, roleId, type}),\n {enabled: Boolean(roleId)},\n );\n const users: ResourceQueryResult<ManagedUserListResponse> = useResourceQuery(\n [UserQueryKeys.USERS, {limit: 100}],\n (fetcher) => getUsers({...usersEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const groups: ResourceQueryResult<GroupListResponse> = useResourceQuery(\n [GroupQueryKeys.GROUPS, {limit: 100}],\n (fetcher) => getGroups({...groupsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const agents: ResourceQueryResult<AgentListResponse> = useResourceQuery(\n [AgentQueryKeys.AGENTS, {limit: 100}],\n (fetcher) => getAgents({...agentsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const assign: ResourceMutationResult<void, RoleAssignmentsVariables> = useResourceMutation(\n ({assignments: changes, roleId: id}: RoleAssignmentsVariables, fetcher) =>\n addRoleAssignments({...rolesEndpoint, assignments: changes, fetcher, roleId: id}),\n () => [[RoleQueryKeys.ROLE_ASSIGNMENTS]],\n );\n const unassign: ResourceMutationResult<void, RoleAssignmentsVariables> = useResourceMutation(\n ({assignments: changes, roleId: id}: RoleAssignmentsVariables, fetcher) =>\n removeRoleAssignments({...rolesEndpoint, assignments: changes, fetcher, roleId: id}),\n () => [[RoleQueryKeys.ROLE_ASSIGNMENTS]],\n );\n\n const candidates: Partial<Record<'user' | 'group' | 'agent', IdentityCandidate[]>> = useMemo(\n () => ({\n agent: (agents.data?.agents ?? []).map((agent: BasicAgent) => ({id: agent.id, label: agent.name})),\n group: (groups.data?.groups ?? []).map((group: Group) => ({id: group.id, label: group.name})),\n user: (users.data?.users ?? []).map((user: ManagedUser) => ({\n id: user.id,\n label: resolveManagedIdentityName(user),\n })),\n }),\n [agents.data, groups.data, users.data],\n );\n\n return (\n <BaseRoleAssignments\n assignments={assignments.data?.assignments ?? []}\n isLoading={assignments.isLoading}\n isMutating={assign.isLoading || unassign.isLoading}\n error={\n toManagementErrorMessage(t, assignments.error, 'admin.roles.assignments.error') ??\n toManagementErrorMessage(t, assign.error ?? unassign.error, 'admin.roles.assignments.update_error')\n }\n candidates={editable ? candidates : undefined}\n onAssign={\n editable\n ? (assignment: RoleAssignmentInput): void => void assign.mutate({assignments: [assignment], roleId})\n : undefined\n }\n onUnassign={\n editable\n ? (assignment: RoleAssignment): void =>\n void unassign.mutate({assignments: [{id: assignment.id, type: assignment.type}], roleId})\n : undefined\n }\n typeFilter={typeFilter}\n onTypeFilterChange={filterable ? setTypeFilter : undefined}\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default RoleAssignments;\n"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"RoleAssignments.js","names":["RoleAssignments: FC<RoleAssignmentsProps>","useTranslation","rolesEndpoint: ManagementEndpoint","useManagementEndpoint","usersEndpoint: ManagementEndpoint","groupsEndpoint: ManagementEndpoint","agentsEndpoint: ManagementEndpoint","type: RoleAssigneeType | undefined","assignments: ResourceQueryResult<RoleAssignmentListResponse>","useResourceQuery","users: ResourceQueryResult<ManagedUserListResponse>","groups: ResourceQueryResult<GroupListResponse>","agents: ResourceQueryResult<AgentListResponse>","assign: ResourceMutationResult<void, RoleAssignmentsVariables>","useResourceMutation","unassign: ResourceMutationResult<void, RoleAssignmentsVariables>","candidates: Partial<Record<'user' | 'group' | 'agent', IdentityCandidate[]>>","BaseRoleAssignments"],"sources":["../../../../src/components/admin/RoleAssignments/RoleAssignments.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AdministrationMode,\n AgentListResponse,\n AgentQueryKeys,\n BasicAgent,\n Group,\n GroupListResponse,\n GroupQueryKeys,\n ManagedUser,\n ManagedUserListResponse,\n RoleAssigneeType,\n RoleAssignment,\n RoleAssignmentInput,\n RoleAssignmentListResponse,\n RoleQueryKeys,\n UserQueryKeys,\n addRoleAssignments,\n getAgents,\n getGroups,\n getRoleAssignments,\n getUsers,\n removeRoleAssignments,\n resolveManagedIdentityName,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo, useState} from 'react';\nimport BaseRoleAssignments, {BaseRoleAssignmentsProps} from './BaseRoleAssignments';\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';\nimport {IdentityCandidate} from '../shared/IdentityPicker';\n\ninterface RoleAssignmentsVariables {\n assignments: RoleAssignmentInput[];\n roleId: string;\n}\n\nexport type RoleAssignmentsProps = Omit<\n BaseRoleAssignmentsProps,\n | 'assignments'\n | 'candidates'\n | 'error'\n | 'isLoading'\n | 'isMutating'\n | 'onAssign'\n | 'onTypeFilterChange'\n | 'onUnassign'\n | 'typeFilter'\n> & {\n /**\n * How an assignment is removed. `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 /**\n * Whether assignments can be added and removed from this view.\n * @default false\n */\n editable?: boolean;\n /**\n * Whether to show the assignee type filter, which narrows the list on the server.\n * @default true\n */\n filterable?: boolean;\n roleId: string;\n};\n\n/**\n * Lists who holds a role, and lets the signed-in user assign it to users, groups and agents or\n * remove it from them.\n *\n * @example\n * ```tsx\n * <RoleAssignments roleId={roleId} />\n * ```\n */\nconst RoleAssignments: FC<RoleAssignmentsProps> = ({\n administrationMode,\n editable = false,\n filterable = true,\n preferences,\n roleId,\n ...rest\n}: RoleAssignmentsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const [typeFilter, setTypeFilter] = useState<RoleAssigneeType | null>(null);\n const rolesEndpoint: ManagementEndpoint = useManagementEndpoint('roles');\n const usersEndpoint: ManagementEndpoint = useManagementEndpoint('users');\n const groupsEndpoint: ManagementEndpoint = useManagementEndpoint('groups');\n const agentsEndpoint: ManagementEndpoint = useManagementEndpoint('agents');\n const type: RoleAssigneeType | undefined = typeFilter ?? undefined;\n const assignments: ResourceQueryResult<RoleAssignmentListResponse> = useResourceQuery(\n [RoleQueryKeys.ROLE_ASSIGNMENTS, roleId, {limit: 100, type}],\n (fetcher) => getRoleAssignments({...rolesEndpoint, fetcher, limit: 100, roleId, type}),\n {enabled: Boolean(roleId)},\n );\n const users: ResourceQueryResult<ManagedUserListResponse> = useResourceQuery(\n [UserQueryKeys.USERS, {limit: 100}],\n (fetcher) => getUsers({...usersEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const groups: ResourceQueryResult<GroupListResponse> = useResourceQuery(\n [GroupQueryKeys.GROUPS, {limit: 100}],\n (fetcher) => getGroups({...groupsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const agents: ResourceQueryResult<AgentListResponse> = useResourceQuery(\n [AgentQueryKeys.AGENTS, {limit: 100}],\n (fetcher) => getAgents({...agentsEndpoint, fetcher, limit: 100}),\n {enabled: editable},\n );\n const assign: ResourceMutationResult<void, RoleAssignmentsVariables> = useResourceMutation(\n ({assignments: changes, roleId: id}: RoleAssignmentsVariables, fetcher) =>\n addRoleAssignments({...rolesEndpoint, assignments: changes, fetcher, roleId: id}),\n () => [[RoleQueryKeys.ROLE_ASSIGNMENTS]],\n );\n const unassign: ResourceMutationResult<void, RoleAssignmentsVariables> = useResourceMutation(\n ({assignments: changes, roleId: id}: RoleAssignmentsVariables, fetcher) =>\n removeRoleAssignments({...rolesEndpoint, administrationMode, assignments: changes, fetcher, roleId: id}),\n () => [[RoleQueryKeys.ROLE_ASSIGNMENTS]],\n );\n\n const candidates: Partial<Record<'user' | 'group' | 'agent', IdentityCandidate[]>> = useMemo(\n () => ({\n agent: (agents.data?.agents ?? []).map((agent: BasicAgent) => ({id: agent.id, label: agent.name})),\n group: (groups.data?.groups ?? []).map((group: Group) => ({id: group.id, label: group.name})),\n user: (users.data?.users ?? []).map((user: ManagedUser) => ({\n id: user.id,\n label: resolveManagedIdentityName(user),\n })),\n }),\n [agents.data, groups.data, users.data],\n );\n\n return (\n <BaseRoleAssignments\n assignments={assignments.data?.assignments ?? []}\n isLoading={assignments.isLoading}\n isMutating={assign.isLoading || unassign.isLoading}\n error={\n toManagementErrorMessage(t, assignments.error, 'admin.roles.assignments.error') ??\n toManagementErrorMessage(t, assign.error ?? unassign.error, 'admin.roles.assignments.update_error')\n }\n candidates={editable ? candidates : undefined}\n onAssign={\n editable\n ? (assignment: RoleAssignmentInput): void => void assign.mutate({assignments: [assignment], roleId})\n : undefined\n }\n onUnassign={\n editable\n ? (assignment: RoleAssignment): void =>\n void unassign.mutate({assignments: [{id: assignment.id, type: assignment.type}], roleId})\n : undefined\n }\n typeFilter={typeFilter}\n onTypeFilterChange={filterable ? setTypeFilter : undefined}\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default RoleAssignments;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiFA,MAAMA,mBAA6C,EACjD,oBACA,WAAW,OACX,aAAa,MACb,aACA,OACA,GAAG,WACqC;CACxC,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAkC,KAAK;CAC3E,MAAMC,gBAAoCC,8BAAsB,QAAQ;CACxE,MAAMC,gBAAoCD,8BAAsB,QAAQ;CACxE,MAAME,iBAAqCF,8BAAsB,SAAS;CAC1E,MAAMG,iBAAqCH,8BAAsB,SAAS;CAC1E,MAAMI,OAAqC,cAAc;CACzD,MAAMC,cAA+DC,yBACnE;EAAC,cAAc;EAAkB;EAAQ;GAAC,OAAO;GAAK;GAAK;EAAC,GAC3D,YAAY,mBAAmB;EAAC,GAAG;EAAe;EAAS,OAAO;EAAK;EAAQ;EAAK,CAAC,EACtF,EAAC,SAAS,QAAQ,OAAO,EAAC,CAC3B;CACD,MAAMC,QAAsDD,yBAC1D,CAAC,cAAc,OAAO,EAAC,OAAO,KAAI,CAAC,GAClC,YAAY,SAAS;EAAC,GAAG;EAAe;EAAS,OAAO;EAAI,CAAC,EAC9D,EAAC,SAAS,UAAS,CACpB;CACD,MAAME,SAAiDF,yBACrD,CAAC,eAAe,QAAQ,EAAC,OAAO,KAAI,CAAC,GACpC,YAAY,UAAU;EAAC,GAAG;EAAgB;EAAS,OAAO;EAAI,CAAC,EAChE,EAAC,SAAS,UAAS,CACpB;CACD,MAAMG,SAAiDH,yBACrD,CAAC,eAAe,QAAQ,EAAC,OAAO,KAAI,CAAC,GACpC,YAAY,UAAU;EAAC,GAAG;EAAgB;EAAS,OAAO;EAAI,CAAC,EAChE,EAAC,SAAS,UAAS,CACpB;CACD,MAAMI,SAAiEC,6BACpE,EAAC,aAAa,SAAS,QAAQ,MAA+B,YAC7D,mBAAmB;EAAC,GAAG;EAAe,aAAa;EAAS;EAAS,QAAQ;EAAG,CAAC,QAC7E,CAAC,CAAC,cAAc,iBAAiB,CAAC,CACzC;CACD,MAAMC,WAAmED,6BACtE,EAAC,aAAa,SAAS,QAAQ,MAA+B,YAC7D,sBAAsB;EAAC,GAAG;EAAe;EAAoB,aAAa;EAAS;EAAS,QAAQ;EAAG,CAAC,QACpG,CAAC,CAAC,cAAc,iBAAiB,CAAC,CACzC;CAED,MAAME,aAA+E,eAC5E;EACL,QAAQ,OAAO,MAAM,UAAU,EAAE,EAAE,KAAK,WAAuB;GAAC,IAAI,MAAM;GAAI,OAAO,MAAM;GAAK,EAAE;EAClG,QAAQ,OAAO,MAAM,UAAU,EAAE,EAAE,KAAK,WAAkB;GAAC,IAAI,MAAM;GAAI,OAAO,MAAM;GAAK,EAAE;EAC7F,OAAO,MAAM,MAAM,SAAS,EAAE,EAAE,KAAK,UAAuB;GAC1D,IAAI,KAAK;GACT,OAAO,2BAA2B,KAAK;GACxC,EAAE;EACJ,GACD;EAAC,OAAO;EAAM,OAAO;EAAM,MAAM;EAAK,CACvC;AAED,QACE,oBAACC;EACC,aAAa,YAAY,MAAM,eAAe,EAAE;EAChD,WAAW,YAAY;EACvB,YAAY,OAAO,aAAa,SAAS;EACzC,OACE,yBAAyB,GAAG,YAAY,OAAO,gCAAgC,IAC/E,yBAAyB,GAAG,OAAO,SAAS,SAAS,OAAO,uCAAuC;EAErG,YAAY,WAAW,aAAa;EACpC,UACE,YACK,eAA0C,KAAK,OAAO,OAAO;GAAC,aAAa,CAAC,WAAW;GAAE;GAAO,CAAC,GAClG;EAEN,YACE,YACK,eACC,KAAK,SAAS,OAAO;GAAC,aAAa,CAAC;IAAC,IAAI,WAAW;IAAI,MAAM,WAAW;IAAK,CAAC;GAAE;GAAO,CAAC,GAC3F;EAEM;EACZ,oBAAoB,aAAa,gBAAgB;EACpC;EACb,GAAI;GACJ;;AAIN,8BAAe"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ManagedUser, MembershipGroup, Preferences } from '@thunderid/browser';
|
|
1
|
+
import { AccountLifecycleAction, ManagedUser, MembershipGroup, Preferences } from '@thunderid/browser';
|
|
2
2
|
import { FC, ReactNode } from 'react';
|
|
3
3
|
/**
|
|
4
4
|
* What {@link BaseUserDetails} hands its `children` render function, so the user can be shown with any UI.
|
|
@@ -16,11 +16,21 @@ export interface BaseUserDetailsRenderProps {
|
|
|
16
16
|
groups: MembershipGroup[];
|
|
17
17
|
isDeleting: boolean;
|
|
18
18
|
isLoading: boolean;
|
|
19
|
+
isRunningLifecycleAction: boolean;
|
|
19
20
|
isUpdating: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The account lifecycle actions offered for the user. Empty when none is allowed.
|
|
23
|
+
*/
|
|
24
|
+
lifecycleActions: AccountLifecycleAction[];
|
|
20
25
|
/**
|
|
21
26
|
* Deletes the user. Undefined when deletion is not allowed, including for read-only users.
|
|
22
27
|
*/
|
|
23
28
|
onDelete?: () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Runs an account lifecycle action, with the operator's note on a suspension. Undefined when none is
|
|
31
|
+
* allowed.
|
|
32
|
+
*/
|
|
33
|
+
onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
|
|
24
34
|
/**
|
|
25
35
|
* Saves the given attributes. Undefined when editing is not allowed, including for read-only users.
|
|
26
36
|
*/
|
|
@@ -50,11 +60,22 @@ export interface BaseUserDetailsProps {
|
|
|
50
60
|
hiddenAttributes?: string[];
|
|
51
61
|
isDeleting?: boolean;
|
|
52
62
|
isLoading?: boolean;
|
|
63
|
+
isRunningLifecycleAction?: boolean;
|
|
53
64
|
isUpdating?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The account lifecycle actions to offer (suspend, unsuspend, unlock), shown only with
|
|
67
|
+
* `onLifecycleAction`. They change only runtime state, so they apply to read-only users too.
|
|
68
|
+
*/
|
|
69
|
+
lifecycleActions?: AccountLifecycleAction[];
|
|
54
70
|
/**
|
|
55
71
|
* Deletes the user. The delete action is hidden when omitted.
|
|
56
72
|
*/
|
|
57
73
|
onDelete?: () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first, and a
|
|
76
|
+
* suspension takes an optional operator note.
|
|
77
|
+
*/
|
|
78
|
+
onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;
|
|
58
79
|
/**
|
|
59
80
|
* Saves edited attributes. Text attributes become editable only when set. Receives every
|
|
60
81
|
* attribute, edited or not.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseUserDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/UserDetails/BaseUserDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"BaseUserDetails.d.ts","sourceRoot":"","sources":["../../../../src/components/admin/UserDetails/BaseUserDetails.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,sBAAsB,EAEtB,WAAW,EACX,eAAe,EACf,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAc,EAAE,EAAgB,SAAS,EAAW,MAAM,OAAO,CAAC;AAYzE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,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,UAAU,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;IAC3C;;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;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAC;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,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;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACzD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAUD;;;GAGG;AACH,QAAA,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CAqL7C,CAAC;AAEF,eAAe,eAAe,CAAC"}
|