@thunderid/react 1.15.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/management/AgentCreate/AgentCreate.cjs.map +1 -1
- package/dist/cjs/components/management/AgentDetails/BaseAgentDetails.cjs +1 -1
- package/dist/cjs/components/management/AgentDetails/BaseAgentDetails.cjs.map +1 -1
- package/dist/cjs/components/management/FlowVariables/BaseFlowVariables.cjs +210 -0
- package/dist/cjs/components/management/FlowVariables/BaseFlowVariables.cjs.map +1 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariableField.cjs +162 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariableField.cjs.map +1 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariables.styles.cjs +123 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariables.styles.cjs.map +1 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariablesForm.cjs +50 -0
- package/dist/cjs/components/management/FlowVariables/FlowVariablesForm.cjs.map +1 -0
- package/dist/cjs/components/management/SharedFlowList/BaseSharedFlowList.cjs +78 -0
- package/dist/cjs/components/management/SharedFlowList/BaseSharedFlowList.cjs.map +1 -0
- package/dist/cjs/components/management/SharedFlowList/SharedFlowList.cjs +69 -0
- package/dist/cjs/components/management/SharedFlowList/SharedFlowList.cjs.map +1 -0
- package/dist/cjs/hooks/management/useFlowVariables.cjs +107 -0
- package/dist/cjs/hooks/management/useFlowVariables.cjs.map +1 -0
- package/dist/cjs/index.cjs +48 -0
- package/dist/components/management/AgentCreate/AgentCreate.d.ts +2 -2
- package/dist/components/management/AgentCreate/AgentCreate.d.ts.map +1 -1
- package/dist/components/management/AgentCreate/AgentCreate.js.map +1 -1
- package/dist/components/management/AgentDetails/BaseAgentDetails.js +1 -1
- package/dist/components/management/AgentDetails/BaseAgentDetails.js.map +1 -1
- package/dist/components/management/FlowVariables/BaseFlowVariables.d.ts +124 -0
- package/dist/components/management/FlowVariables/BaseFlowVariables.d.ts.map +1 -0
- package/dist/components/management/FlowVariables/BaseFlowVariables.js +206 -0
- package/dist/components/management/FlowVariables/BaseFlowVariables.js.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariableField.d.ts +46 -0
- package/dist/components/management/FlowVariables/FlowVariableField.d.ts.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariableField.js +158 -0
- package/dist/components/management/FlowVariables/FlowVariableField.js.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariables.styles.d.ts +11 -0
- package/dist/components/management/FlowVariables/FlowVariables.styles.d.ts.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariables.styles.js +121 -0
- package/dist/components/management/FlowVariables/FlowVariables.styles.js.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariablesForm.d.ts +30 -0
- package/dist/components/management/FlowVariables/FlowVariablesForm.d.ts.map +1 -0
- package/dist/components/management/FlowVariables/FlowVariablesForm.js +47 -0
- package/dist/components/management/FlowVariables/FlowVariablesForm.js.map +1 -0
- package/dist/components/management/SharedFlowList/BaseSharedFlowList.d.ts +53 -0
- package/dist/components/management/SharedFlowList/BaseSharedFlowList.d.ts.map +1 -0
- package/dist/components/management/SharedFlowList/BaseSharedFlowList.js +76 -0
- package/dist/components/management/SharedFlowList/BaseSharedFlowList.js.map +1 -0
- package/dist/components/management/SharedFlowList/SharedFlowList.d.ts +32 -0
- package/dist/components/management/SharedFlowList/SharedFlowList.d.ts.map +1 -0
- package/dist/components/management/SharedFlowList/SharedFlowList.js +65 -0
- package/dist/components/management/SharedFlowList/SharedFlowList.js.map +1 -0
- package/dist/hooks/management/useFlowVariables.d.ts +84 -0
- package/dist/hooks/management/useFlowVariables.d.ts.map +1 -0
- package/dist/hooks/management/useFlowVariables.js +104 -0
- package/dist/hooks/management/useFlowVariables.js.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentCreate.cjs","names":["AgentCreate: FC<AgentCreateProps>","useTranslation","placement: ProjectPlacement","useProjectPlacement","agentsEndpoint: ManagementEndpoint","useManagementEndpoint","agentTypesEndpoint: ManagementEndpoint","useResourceQuery","AgentQueryKeys","useResourceMutation","options: SelectOption[]","BaseAgentCreate"],"sources":["../../../../../src/components/management/AgentCreate/AgentCreate.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Agent,\n AgentQueryKeys,\n AgentType,\n AgentTypeListResponse,\n CreateAgentRequest,\n SelectOption,\n createAgent,\n getAgentTypes,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo} from 'react';\nimport BaseAgentCreate, {AgentCreateSubmission, BaseAgentCreateProps} from './BaseAgentCreate';\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 useProjectPlacement, {ProjectPlacement} from '../shared/useProjectPlacement';\n\nexport type AgentCreateProps = Omit<BaseAgentCreateProps, 'agentTypes' | 'error' | 'isSubmitting' | 'onSubmit'> & {\n /**\n * Called with the created agent.\n */\n onCreated?: (agent: Agent) => void;\n /**\n * Organization unit the agent is created in. Without it, the resource is created in the\n * current project (sent as `projectId`) when one is chosen, otherwise in the signed-in user's organization unit.\n */\n ouId?: string;\n /**\n * The person responsible for the agent. Defaults to the server's choice, usually the creator.\n */\n owner?:
|
|
1
|
+
{"version":3,"file":"AgentCreate.cjs","names":["AgentCreate: FC<AgentCreateProps>","useTranslation","placement: ProjectPlacement","useProjectPlacement","agentsEndpoint: ManagementEndpoint","useManagementEndpoint","agentTypesEndpoint: ManagementEndpoint","useResourceQuery","AgentQueryKeys","useResourceMutation","options: SelectOption[]","BaseAgentCreate"],"sources":["../../../../../src/components/management/AgentCreate/AgentCreate.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Agent,\n AgentOwner,\n AgentQueryKeys,\n AgentType,\n AgentTypeListResponse,\n CreateAgentRequest,\n SelectOption,\n createAgent,\n getAgentTypes,\n toManagementErrorMessage,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useMemo} from 'react';\nimport BaseAgentCreate, {AgentCreateSubmission, BaseAgentCreateProps} from './BaseAgentCreate';\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 useProjectPlacement, {ProjectPlacement} from '../shared/useProjectPlacement';\n\nexport type AgentCreateProps = Omit<BaseAgentCreateProps, 'agentTypes' | 'error' | 'isSubmitting' | 'onSubmit'> & {\n /**\n * Called with the created agent.\n */\n onCreated?: (agent: Agent) => void;\n /**\n * Organization unit the agent is created in. Without it, the resource is created in the\n * current project (sent as `projectId`) when one is chosen, otherwise in the signed-in user's organization unit.\n */\n ouId?: string;\n /**\n * The person responsible for the agent. Defaults to the server's choice, usually the creator.\n */\n owner?: AgentOwner;\n};\n\n/**\n * Registers an agent through the management API.\n *\n * @example\n * ```tsx\n * <AgentCreate onCreated={(agent) => navigate(`/agents/${agent.id}`)} />\n * ```\n */\nconst AgentCreate: FC<AgentCreateProps> = ({\n onCreated,\n ouId,\n owner,\n preferences,\n ...rest\n}: AgentCreateProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const placement: ProjectPlacement = useProjectPlacement(ouId);\n const canPlace = Boolean(placement.ouId ?? placement.projectId);\n const agentsEndpoint: ManagementEndpoint = useManagementEndpoint('agents');\n const agentTypesEndpoint: ManagementEndpoint = useManagementEndpoint('agentTypes');\n const {data: agentTypes}: ResourceQueryResult<AgentTypeListResponse> = useResourceQuery(\n [AgentQueryKeys.AGENT_TYPES, {limit: 100}],\n (fetcher) => getAgentTypes({...agentTypesEndpoint, fetcher, limit: 100}),\n );\n const {error, isLoading, mutate}: ResourceMutationResult<Agent, CreateAgentRequest> = useResourceMutation(\n (payload: CreateAgentRequest, fetcher) => createAgent({...agentsEndpoint, fetcher, payload}),\n () => [[AgentQueryKeys.AGENTS]],\n {onSuccess: onCreated},\n );\n\n const options: SelectOption[] = useMemo(\n () =>\n (agentTypes?.types ?? []).map((agentType: AgentType) => ({\n label: agentType.displayName,\n value: agentType.handle,\n })),\n [agentTypes],\n );\n\n const handleSubmit = ({description, name, type}: AgentCreateSubmission): void => {\n if (canPlace) {\n void mutate({attributes: {name}, description, owner, type, ...placement});\n }\n };\n\n return (\n <BaseAgentCreate\n agentTypes={options}\n onSubmit={handleSubmit}\n isSubmitting={isLoading}\n error={\n canPlace ? toManagementErrorMessage(t, error, 'admin.agents.create.error') : t('admin.common.errors.no_ou')\n }\n preferences={preferences}\n {...rest}\n />\n );\n};\n\nexport default AgentCreate;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAMA,eAAqC,EACzC,WACA,MACA,OACA,YACA,GAAG,WACiC;CACpC,MAAM,EAAC,MAAKC,+BAAe,aAAa,KAAK;CAC7C,MAAMC,YAA8BC,oCAAoB,KAAK;CAC7D,MAAM,WAAW,QAAQ,UAAU,QAAQ,UAAU,UAAU;CAC/D,MAAMC,iBAAqCC,sCAAsB,SAAS;CAC1E,MAAMC,qBAAyCD,sCAAsB,aAAa;CAClF,MAAM,EAAC,MAAM,eAA0DE,iCACrE,CAACC,mCAAe,aAAa,EAAC,OAAO,KAAI,CAAC,GACzC,mDAA0B;EAAC,GAAG;EAAoB;EAAS,OAAO;EAAI,CAAC,CACzE;CACD,MAAM,EAAC,OAAO,WAAW,WAA6DC,qCACnF,SAA6B,iDAAwB;EAAC,GAAG;EAAgB;EAAS;EAAQ,CAAC,QACtF,CAAC,CAACD,mCAAe,OAAO,CAAC,EAC/B,EAAC,WAAW,WAAU,CACvB;CAED,MAAME,oCAED,YAAY,SAAS,EAAE,EAAE,KAAK,eAA0B;EACvD,OAAO,UAAU;EACjB,OAAO,UAAU;EAClB,EAAE,EACL,CAAC,WAAW,CACb;CAED,MAAM,gBAAgB,EAAC,aAAa,MAAM,WAAuC;AAC/E,MAAI,SACF,CAAK,OAAO;GAAC,YAAY,EAAC,MAAK;GAAE;GAAa;GAAO;GAAM,GAAG;GAAU,CAAC;;AAI7E,QACE,2CAACC;EACC,YAAY;EACZ,UAAU;EACV,cAAc;EACd,OACE,6DAAoC,GAAG,OAAO,4BAA4B,GAAG,EAAE,4BAA4B;EAEhG;EACb,GAAI;GACJ;;AAIN,0BAAe"}
|
|
@@ -66,7 +66,7 @@ const BaseAgentDetails = ({ actions, agent, children, className, error = null, g
|
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
label: t("admin.agents.details.owner"),
|
|
69
|
-
value: agent.owner ?? t("admin.agents.details.no_owner")
|
|
69
|
+
value: agent.owner?.id ?? t("admin.agents.details.no_owner")
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
label: t("admin.common.columns.organization_unit"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseAgentDetails.cjs","names":["BaseAgentDetails: FC<BaseAgentDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","AdminSection","Spinner","Alert","details: {label: string; value: ReactNode}[]","AccessStatusBadge","CopyableText","AccountLifecycleButtons","ConfirmButton","Panel","DetailList","ChipList"],"sources":["../../../../../src/components/management/AgentDetails/BaseAgentDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {AccountLifecycleAction, Agent, MembershipGroup, Preferences, getAgentName} from '@thunderid/browser';\nimport {FC, ReactElement, ReactNode} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {AccessStatusBadge, AccountLifecycleButtons} from '../shared/AccountLifecycleParts';\nimport {ChipList, ConfirmButton, DetailList, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * What {@link BaseAgentDetails} hands its `children` render function, so the agent can be shown with any UI.\n */\nexport interface BaseAgentDetailsRenderProps {\n agent?: Agent;\n error: string | null;\n groups: MembershipGroup[];\n isDeleting: boolean;\n isLoading: boolean;\n isRunningLifecycleAction: boolean;\n /**\n * The account lifecycle actions offered for the agent. Empty when none is allowed.\n */\n lifecycleActions: AccountLifecycleAction[];\n /**\n * Deletes the agent. Undefined when deletion is not allowed, including for read-only agents.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action, with the operator's note on a suspension. Undefined when none is allowed.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n roles: string[];\n}\n\nexport interface BaseAgentDetailsProps {\n /**\n * Controls rendered next to the agent's name.\n */\n actions?: ReactNode;\n agent?: Agent;\n /**\n * Renders custom UI in place of the default view. Receives the agent, its groups and roles, the\n * loading and error state, and the delete handler.\n */\n children?: (props: BaseAgentDetailsRenderProps) => ReactNode;\n className?: string;\n error?: string | null;\n /**\n * The groups the agent belongs to.\n */\n groups?: MembershipGroup[];\n isDeleting?: boolean;\n isLoading?: boolean;\n isRunningLifecycleAction?: boolean;\n /**\n * The account lifecycle actions to offer (suspend, unsuspend, unlock), shown only with `onLifecycleAction`. They change\n * only runtime state, so they apply to read-only agents too.\n */\n lifecycleActions?: AccountLifecycleAction[];\n /**\n * Deletes the agent. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first, and a\n * suspension takes an optional operator note.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n preferences?: Preferences;\n /**\n * The names of the roles the agent holds, directly or through its groups.\n */\n roles?: string[];\n}\n\n/**\n * Shows an agent's type, owner, client and the groups and roles it holds. Holds no data of its own:\n * pass the agent and handle deletion.\n */\nconst BaseAgentDetails: FC<BaseAgentDetailsProps> = ({\n actions,\n agent,\n children,\n className,\n error = null,\n groups = [],\n isDeleting = false,\n isLoading = false,\n isRunningLifecycleAction = false,\n lifecycleActions = [],\n onDelete,\n onLifecycleAction,\n preferences,\n roles = [],\n}: BaseAgentDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n\n if (children) {\n return (\n <div className={className}>\n {children({\n agent,\n error,\n groups,\n isDeleting,\n isLoading,\n isRunningLifecycleAction,\n lifecycleActions: onLifecycleAction && agent ? lifecycleActions : [],\n onDelete: agent && !agent.isReadOnly ? onDelete : undefined,\n onLifecycleAction: agent ? onLifecycleAction : undefined,\n roles,\n })}\n </div>\n );\n }\n\n if (isLoading && !agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const details: {label: string; value: ReactNode}[] = [\n {label: t('admin.agents.details.type'), value: agent.type},\n {label: t('admin.agents.details.owner'), value: agent.owner ?? t('admin.agents.details.no_owner')},\n {label: t('admin.common.columns.organization_unit'), value: agent.ouHandle ?? agent.ouId},\n ];\n\n if (agent.status) {\n details.push({label: t('admin.common.columns.status'), value: <AccessStatusBadge status={agent.status.value} />});\n }\n\n if (agent.clientId) {\n details.push({label: t('admin.agents.list.columns.client_id'), value: <CopyableText value={agent.clientId} />});\n }\n\n return (\n <AdminSection\n block=\"agent-details\"\n className={className}\n title={getAgentName(agent)}\n description={agent.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onLifecycleAction && (\n <AccountLifecycleButtons\n actions={lifecycleActions}\n collectsOperatorNote\n isRunning={isRunningLifecycleAction}\n name={getAgentName(agent)}\n onAction={onLifecycleAction}\n />\n )}\n {onDelete && !agent.isReadOnly && (\n <ConfirmButton\n confirmText={t('admin.agents.details.delete.confirm', {name: getAgentName(agent)})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.agents.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n <Panel title={t('admin.agents.details.overview')}>\n <DetailList items={details} />\n </Panel>\n <Panel title={t('admin.agents.details.groups')}>\n <ChipList\n label={t('admin.agents.details.groups')}\n emptyText={t('admin.agents.details.no_groups')}\n items={groups.map((group: MembershipGroup) => ({id: group.id, label: group.name}))}\n />\n </Panel>\n <Panel title={t('admin.agents.details.roles')}>\n <ChipList\n label={t('admin.agents.details.roles')}\n emptyText={t('admin.agents.details.no_roles')}\n items={roles.map((role: string) => ({id: role, label: role}))}\n />\n </Panel>\n </AdminSection>\n );\n};\n\nexport default BaseAgentDetails;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoFA,MAAMA,oBAA+C,EACnD,SACA,OACA,UACA,WACA,QAAQ,MACR,SAAS,EAAE,EACX,aAAa,OACb,YAAY,OACZ,2BAA2B,OAC3B,mBAAmB,EAAE,EACrB,UACA,mBACA,aACA,QAAQ,EAAE,OAC+B;CACzC,MAAM,EAAC,MAAKC,+BAAe,aAAa,KAAK;CAC7C,MAAMC,SAAiCC,oCAAoB;AAE3D,KAAI,SACF,QACE,2CAAC;EAAe;YACb,SAAS;GACR;GACA;GACA;GACA;GACA;GACA;GACA,kBAAkB,qBAAqB,QAAQ,mBAAmB,EAAE;GACpE,UAAU,SAAS,CAAC,MAAM,aAAa,WAAW;GAClD,mBAAmB,QAAQ,oBAAoB;GAC/C;GACD,CAAC;GACE;AAIV,KAAI,aAAa,CAAC,MAChB,QACE,2CAACC;EAAa,OAAM;EAA2B;EAAwB;YACrE,4CAAC;GAAK,WAAW,OAAO;GAAU,MAAK;;IACrC,2CAACC,2BAAQ,MAAK,UAAU;;IAAE,EAAE,uBAAuB;;IAC9C;GACM;AAInB,KAAI,CAAC,MACH,QACE,2CAACD;EAAa,OAAM;EAA2B;EAAwB;YACrE,2CAACE;GAAM,SAAQ;GAAQ,MAAK;aACzB,SAAS,EAAE,gCAAgC;IACtC;GACK;CAInB,MAAMC,UAA+C;EACnD;GAAC,OAAO,EAAE,4BAA4B;GAAE,OAAO,MAAM;GAAK;EAC1D;GAAC,OAAO,EAAE,6BAA6B;GAAE,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"BaseAgentDetails.cjs","names":["BaseAgentDetails: FC<BaseAgentDetailsProps>","useTranslation","styles: Record<string, string>","useAdminPartStyles","AdminSection","Spinner","Alert","details: {label: string; value: ReactNode}[]","AccessStatusBadge","CopyableText","AccountLifecycleButtons","ConfirmButton","Panel","DetailList","ChipList"],"sources":["../../../../../src/components/management/AgentDetails/BaseAgentDetails.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {AccountLifecycleAction, Agent, MembershipGroup, Preferences, getAgentName} from '@thunderid/browser';\nimport {FC, ReactElement, ReactNode} from 'react';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport CopyableText from '../../primitives/CopyableText/CopyableText';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport {AccessStatusBadge, AccountLifecycleButtons} from '../shared/AccountLifecycleParts';\nimport {ChipList, ConfirmButton, DetailList, Panel} from '../shared/AdminParts';\nimport AdminSection from '../shared/AdminSection';\nimport useAdminPartStyles from '../shared/useAdminPartStyles';\n\n/**\n * What {@link BaseAgentDetails} hands its `children` render function, so the agent can be shown with any UI.\n */\nexport interface BaseAgentDetailsRenderProps {\n agent?: Agent;\n error: string | null;\n groups: MembershipGroup[];\n isDeleting: boolean;\n isLoading: boolean;\n isRunningLifecycleAction: boolean;\n /**\n * The account lifecycle actions offered for the agent. Empty when none is allowed.\n */\n lifecycleActions: AccountLifecycleAction[];\n /**\n * Deletes the agent. Undefined when deletion is not allowed, including for read-only agents.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action, with the operator's note on a suspension. Undefined when none is allowed.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n roles: string[];\n}\n\nexport interface BaseAgentDetailsProps {\n /**\n * Controls rendered next to the agent's name.\n */\n actions?: ReactNode;\n agent?: Agent;\n /**\n * Renders custom UI in place of the default view. Receives the agent, its groups and roles, the\n * loading and error state, and the delete handler.\n */\n children?: (props: BaseAgentDetailsRenderProps) => ReactNode;\n className?: string;\n error?: string | null;\n /**\n * The groups the agent belongs to.\n */\n groups?: MembershipGroup[];\n isDeleting?: boolean;\n isLoading?: boolean;\n isRunningLifecycleAction?: boolean;\n /**\n * The account lifecycle actions to offer (suspend, unsuspend, unlock), shown only with `onLifecycleAction`. They change\n * only runtime state, so they apply to read-only agents too.\n */\n lifecycleActions?: AccountLifecycleAction[];\n /**\n * Deletes the agent. The delete action is hidden when omitted.\n */\n onDelete?: () => void;\n /**\n * Runs an account lifecycle action. Suspending and unsuspending ask for confirmation first, and a\n * suspension takes an optional operator note.\n */\n onLifecycleAction?: (action: AccountLifecycleAction, operatorNote?: string) => void;\n preferences?: Preferences;\n /**\n * The names of the roles the agent holds, directly or through its groups.\n */\n roles?: string[];\n}\n\n/**\n * Shows an agent's type, owner, client and the groups and roles it holds. Holds no data of its own:\n * pass the agent and handle deletion.\n */\nconst BaseAgentDetails: FC<BaseAgentDetailsProps> = ({\n actions,\n agent,\n children,\n className,\n error = null,\n groups = [],\n isDeleting = false,\n isLoading = false,\n isRunningLifecycleAction = false,\n lifecycleActions = [],\n onDelete,\n onLifecycleAction,\n preferences,\n roles = [],\n}: BaseAgentDetailsProps): ReactElement => {\n const {t} = useTranslation(preferences?.i18n);\n const styles: Record<string, string> = useAdminPartStyles();\n\n if (children) {\n return (\n <div className={className}>\n {children({\n agent,\n error,\n groups,\n isDeleting,\n isLoading,\n isRunningLifecycleAction,\n lifecycleActions: onLifecycleAction && agent ? lifecycleActions : [],\n onDelete: agent && !agent.isReadOnly ? onDelete : undefined,\n onLifecycleAction: agent ? onLifecycleAction : undefined,\n roles,\n })}\n </div>\n );\n }\n\n if (isLoading && !agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <span className={styles['muted']} role=\"status\">\n <Spinner size=\"small\" /> {t('admin.common.loading')}\n </span>\n </AdminSection>\n );\n }\n\n if (!agent) {\n return (\n <AdminSection block=\"agent-details\" className={className} preferences={preferences}>\n <Alert variant=\"error\" role=\"alert\">\n {error ?? t('admin.common.errors.not_found')}\n </Alert>\n </AdminSection>\n );\n }\n\n const details: {label: string; value: ReactNode}[] = [\n {label: t('admin.agents.details.type'), value: agent.type},\n {label: t('admin.agents.details.owner'), value: agent.owner?.id ?? t('admin.agents.details.no_owner')},\n {label: t('admin.common.columns.organization_unit'), value: agent.ouHandle ?? agent.ouId},\n ];\n\n if (agent.status) {\n details.push({label: t('admin.common.columns.status'), value: <AccessStatusBadge status={agent.status.value} />});\n }\n\n if (agent.clientId) {\n details.push({label: t('admin.agents.list.columns.client_id'), value: <CopyableText value={agent.clientId} />});\n }\n\n return (\n <AdminSection\n block=\"agent-details\"\n className={className}\n title={getAgentName(agent)}\n description={agent.description}\n preferences={preferences}\n actions={\n <>\n {actions}\n {onLifecycleAction && (\n <AccountLifecycleButtons\n actions={lifecycleActions}\n collectsOperatorNote\n isRunning={isRunningLifecycleAction}\n name={getAgentName(agent)}\n onAction={onLifecycleAction}\n />\n )}\n {onDelete && !agent.isReadOnly && (\n <ConfirmButton\n confirmText={t('admin.agents.details.delete.confirm', {name: getAgentName(agent)})}\n isLoading={isDeleting}\n onConfirm={onDelete}\n >\n {t('admin.agents.details.delete.action')}\n </ConfirmButton>\n )}\n </>\n }\n >\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n <Panel title={t('admin.agents.details.overview')}>\n <DetailList items={details} />\n </Panel>\n <Panel title={t('admin.agents.details.groups')}>\n <ChipList\n label={t('admin.agents.details.groups')}\n emptyText={t('admin.agents.details.no_groups')}\n items={groups.map((group: MembershipGroup) => ({id: group.id, label: group.name}))}\n />\n </Panel>\n <Panel title={t('admin.agents.details.roles')}>\n <ChipList\n label={t('admin.agents.details.roles')}\n emptyText={t('admin.agents.details.no_roles')}\n items={roles.map((role: string) => ({id: role, label: role}))}\n />\n </Panel>\n </AdminSection>\n );\n};\n\nexport default BaseAgentDetails;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoFA,MAAMA,oBAA+C,EACnD,SACA,OACA,UACA,WACA,QAAQ,MACR,SAAS,EAAE,EACX,aAAa,OACb,YAAY,OACZ,2BAA2B,OAC3B,mBAAmB,EAAE,EACrB,UACA,mBACA,aACA,QAAQ,EAAE,OAC+B;CACzC,MAAM,EAAC,MAAKC,+BAAe,aAAa,KAAK;CAC7C,MAAMC,SAAiCC,oCAAoB;AAE3D,KAAI,SACF,QACE,2CAAC;EAAe;YACb,SAAS;GACR;GACA;GACA;GACA;GACA;GACA;GACA,kBAAkB,qBAAqB,QAAQ,mBAAmB,EAAE;GACpE,UAAU,SAAS,CAAC,MAAM,aAAa,WAAW;GAClD,mBAAmB,QAAQ,oBAAoB;GAC/C;GACD,CAAC;GACE;AAIV,KAAI,aAAa,CAAC,MAChB,QACE,2CAACC;EAAa,OAAM;EAA2B;EAAwB;YACrE,4CAAC;GAAK,WAAW,OAAO;GAAU,MAAK;;IACrC,2CAACC,2BAAQ,MAAK,UAAU;;IAAE,EAAE,uBAAuB;;IAC9C;GACM;AAInB,KAAI,CAAC,MACH,QACE,2CAACD;EAAa,OAAM;EAA2B;EAAwB;YACrE,2CAACE;GAAM,SAAQ;GAAQ,MAAK;aACzB,SAAS,EAAE,gCAAgC;IACtC;GACK;CAInB,MAAMC,UAA+C;EACnD;GAAC,OAAO,EAAE,4BAA4B;GAAE,OAAO,MAAM;GAAK;EAC1D;GAAC,OAAO,EAAE,6BAA6B;GAAE,OAAO,MAAM,OAAO,MAAM,EAAE,gCAAgC;GAAC;EACtG;GAAC,OAAO,EAAE,yCAAyC;GAAE,OAAO,MAAM,YAAY,MAAM;GAAK;EAC1F;AAED,KAAI,MAAM,OACR,SAAQ,KAAK;EAAC,OAAO,EAAE,8BAA8B;EAAE,OAAO,2CAACC,mDAAkB,QAAQ,MAAM,OAAO,QAAS;EAAC,CAAC;AAGnH,KAAI,MAAM,SACR,SAAQ,KAAK;EAAC,OAAO,EAAE,sCAAsC;EAAE,OAAO,2CAACC,gCAAa,OAAO,MAAM,WAAY;EAAC,CAAC;AAGjH,QACE,4CAACL;EACC,OAAM;EACK;EACX,6CAAoB,MAAM;EAC1B,aAAa,MAAM;EACN;EACb,SACE;GACG;GACA,qBACC,2CAACM;IACC,SAAS;IACT;IACA,WAAW;IACX,4CAAmB,MAAM;IACzB,UAAU;KACV;GAEH,YAAY,CAAC,MAAM,cAClB,2CAACC;IACC,aAAa,EAAE,uCAAuC,EAAC,4CAAmB,MAAM,EAAC,CAAC;IAClF,WAAW;IACX,WAAW;cAEV,EAAE,qCAAqC;KAC1B;MAEjB;;GAGJ,SACC,2CAACL;IAAM,SAAQ;IAAQ,MAAK;cACzB;KACK;GAEV,2CAACM;IAAM,OAAO,EAAE,gCAAgC;cAC9C,2CAACC,iCAAW,OAAO,UAAW;KACxB;GACR,2CAACD;IAAM,OAAO,EAAE,8BAA8B;cAC5C,2CAACE;KACC,OAAO,EAAE,8BAA8B;KACvC,WAAW,EAAE,iCAAiC;KAC9C,OAAO,OAAO,KAAK,WAA4B;MAAC,IAAI,MAAM;MAAI,OAAO,MAAM;MAAK,EAAE;MAClF;KACI;GACR,2CAACF;IAAM,OAAO,EAAE,6BAA6B;cAC3C,2CAACE;KACC,OAAO,EAAE,6BAA6B;KACtC,WAAW,EAAE,gCAAgC;KAC7C,OAAO,MAAM,KAAK,UAAkB;MAAC,IAAI;MAAM,OAAO;MAAK,EAAE;MAC7D;KACI;;GACK;;AAInB,+BAAe"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_useTheme = require('../../../contexts/Theme/useTheme.cjs');
|
|
3
|
+
const require_useTranslation = require('../../../hooks/useTranslation.cjs');
|
|
4
|
+
const require_Button = require('../../primitives/Button/Button.cjs');
|
|
5
|
+
const require_Alert = require('../../primitives/Alert/Alert.cjs');
|
|
6
|
+
const require_AdminSection = require('../shared/AdminSection.cjs');
|
|
7
|
+
const require_FlowVariables_styles = require('./FlowVariables.styles.cjs');
|
|
8
|
+
const require_FlowVariableField = require('./FlowVariableField.cjs');
|
|
9
|
+
let __thunderid_browser = require("@thunderid/browser");
|
|
10
|
+
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
11
|
+
let react = require("react");
|
|
12
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
13
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
14
|
+
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
15
|
+
|
|
16
|
+
//#region src/components/management/FlowVariables/BaseFlowVariables.tsx
|
|
17
|
+
const startingValuesOf = (variables, overlay, initialValues) => variables.reduce((values, definition) => {
|
|
18
|
+
const { defaultValue, editable } = (0, __thunderid_browser.resolveFlowVariableRule)(definition, overlay);
|
|
19
|
+
const chosen = editable ? initialValues?.[definition.identifier] ?? defaultValue : defaultValue;
|
|
20
|
+
if (chosen !== void 0 && chosen !== null) values[definition.identifier] = chosen;
|
|
21
|
+
return values;
|
|
22
|
+
}, {});
|
|
23
|
+
/**
|
|
24
|
+
* A form for the variables a flow declares, each in a card with its label, its description, whether it is
|
|
25
|
+
* customized or follows the default, and the input its type calls for. It is what a delegated admin sees when
|
|
26
|
+
* overriding a flow, and what the flow editor previews. Holds no data of its own: pass the variables and handle
|
|
27
|
+
* the submission.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <BaseFlowVariables variables={variables} connections={connections} overlay={overlay} onSubmit={save} />
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
const BaseFlowVariables = ({ children, className, connections = [], description, error = null, initialValues, isSubmitting = false, onCancel, onChange, onCreateConnection, onSubmit, ouName, overlay, ownerName, preferences, readOnly = false, renderField, renderFieldAction, showScopeNotice = false, title, variables }) => {
|
|
35
|
+
const { theme, colorScheme } = require_useTheme.default();
|
|
36
|
+
const styles = require_FlowVariables_styles.default(theme, colorScheme);
|
|
37
|
+
const { t } = require_useTranslation.default(preferences?.i18n);
|
|
38
|
+
const startingValues = (0, react.useMemo)(() => startingValuesOf(variables, overlay, initialValues), [
|
|
39
|
+
JSON.stringify(variables),
|
|
40
|
+
JSON.stringify(overlay),
|
|
41
|
+
JSON.stringify(initialValues)
|
|
42
|
+
]);
|
|
43
|
+
const [values, setValues] = (0, react.useState)(startingValues);
|
|
44
|
+
const [touched, setTouched] = (0, react.useState)({});
|
|
45
|
+
const [submitted, setSubmitted] = (0, react.useState)(false);
|
|
46
|
+
const appliedValues = (0, react.useRef)(startingValues);
|
|
47
|
+
(0, react.useEffect)(() => {
|
|
48
|
+
if (appliedValues.current !== startingValues) {
|
|
49
|
+
appliedValues.current = startingValues;
|
|
50
|
+
setValues(startingValues);
|
|
51
|
+
setTouched({});
|
|
52
|
+
setSubmitted(false);
|
|
53
|
+
}
|
|
54
|
+
}, [startingValues]);
|
|
55
|
+
const getRule = (definition) => (0, __thunderid_browser.resolveFlowVariableRule)(definition, overlay);
|
|
56
|
+
const getOptions = (definition) => {
|
|
57
|
+
const { allowedValues, editable, excludedValues } = getRule(definition);
|
|
58
|
+
return (0, __thunderid_browser.getFlowVariableOptions)(definition, connections, editable ? {
|
|
59
|
+
allowedValues,
|
|
60
|
+
excludedValues
|
|
61
|
+
} : {});
|
|
62
|
+
};
|
|
63
|
+
const errors = {};
|
|
64
|
+
let isValid = true;
|
|
65
|
+
variables.forEach((definition) => {
|
|
66
|
+
if (!getRule(definition).editable) return;
|
|
67
|
+
const problem = (0, __thunderid_browser.validateFlowVariableValue)(definition, values[definition.identifier]);
|
|
68
|
+
if (problem) {
|
|
69
|
+
isValid = false;
|
|
70
|
+
if (submitted || touched[definition.identifier]) errors[definition.identifier] = problem.message ?? t(`admin.flowVariables.errors.${problem.code}`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const handleChange = (identifier, value) => {
|
|
74
|
+
const next = {
|
|
75
|
+
...values,
|
|
76
|
+
[identifier]: value
|
|
77
|
+
};
|
|
78
|
+
setValues(next);
|
|
79
|
+
setTouched((previous) => ({
|
|
80
|
+
...previous,
|
|
81
|
+
[identifier]: true
|
|
82
|
+
}));
|
|
83
|
+
onChange?.(next);
|
|
84
|
+
};
|
|
85
|
+
const resetToDefaults = () => {
|
|
86
|
+
const next = variables.reduce((accumulated, definition) => {
|
|
87
|
+
const { defaultValue } = getRule(definition);
|
|
88
|
+
if (defaultValue !== void 0) accumulated[definition.identifier] = defaultValue;
|
|
89
|
+
return accumulated;
|
|
90
|
+
}, {});
|
|
91
|
+
setValues(next);
|
|
92
|
+
setTouched({});
|
|
93
|
+
setSubmitted(false);
|
|
94
|
+
onChange?.(next);
|
|
95
|
+
};
|
|
96
|
+
const submit = () => {
|
|
97
|
+
setSubmitted(true);
|
|
98
|
+
if (!isValid) return;
|
|
99
|
+
const overrides = variables.reduce((accumulated, definition) => {
|
|
100
|
+
const { defaultValue, editable } = getRule(definition);
|
|
101
|
+
const value = values[definition.identifier];
|
|
102
|
+
if (editable && value !== void 0 && value !== defaultValue) accumulated[definition.identifier] = value;
|
|
103
|
+
return accumulated;
|
|
104
|
+
}, {});
|
|
105
|
+
onSubmit?.(values, overrides);
|
|
106
|
+
};
|
|
107
|
+
const handleSubmit = (event) => {
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
submit();
|
|
110
|
+
};
|
|
111
|
+
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
112
|
+
className,
|
|
113
|
+
children: children({
|
|
114
|
+
error,
|
|
115
|
+
errors,
|
|
116
|
+
getOptions,
|
|
117
|
+
getRule,
|
|
118
|
+
handleChange,
|
|
119
|
+
handleSubmit: submit,
|
|
120
|
+
isSubmitting,
|
|
121
|
+
isValid,
|
|
122
|
+
onCancel,
|
|
123
|
+
resetToDefaults,
|
|
124
|
+
values,
|
|
125
|
+
variables
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
const ou = ouName ?? t("admin.flowVariables.ou_fallback");
|
|
129
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AdminSection.default, {
|
|
130
|
+
block: "flow-variables",
|
|
131
|
+
className,
|
|
132
|
+
title,
|
|
133
|
+
description,
|
|
134
|
+
preferences,
|
|
135
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
136
|
+
className: styles["form"],
|
|
137
|
+
onSubmit: handleSubmit,
|
|
138
|
+
noValidate: true,
|
|
139
|
+
children: [
|
|
140
|
+
showScopeNotice && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Alert.default, {
|
|
141
|
+
variant: "info",
|
|
142
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default.Title, { children: t("admin.flowVariables.notice_title", { ou }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default.Description, { children: t("admin.flowVariables.notice_body", { ou }) })]
|
|
143
|
+
}),
|
|
144
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Alert.default, {
|
|
145
|
+
variant: "error",
|
|
146
|
+
role: "alert",
|
|
147
|
+
children: error
|
|
148
|
+
}),
|
|
149
|
+
variables.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
150
|
+
className: styles["empty"],
|
|
151
|
+
children: t("admin.flowVariables.empty")
|
|
152
|
+
}),
|
|
153
|
+
variables.map((definition, index) => {
|
|
154
|
+
const rule = getRule(definition);
|
|
155
|
+
const key = `${index}-${definition.identifier}`;
|
|
156
|
+
const field = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_FlowVariableField.default, {
|
|
157
|
+
action: renderFieldAction?.(definition, index),
|
|
158
|
+
definition,
|
|
159
|
+
defaultValue: rule.defaultValue,
|
|
160
|
+
disabled: readOnly || isSubmitting,
|
|
161
|
+
editable: rule.editable,
|
|
162
|
+
error: errors[definition.identifier],
|
|
163
|
+
hint: definition.type === "CONNECTION_SELECT" && rule.editable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("admin.flowVariables.connections_hint", { ou }) }), onCreateConnection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
164
|
+
type: "button",
|
|
165
|
+
className: styles["link"],
|
|
166
|
+
onClick: onCreateConnection,
|
|
167
|
+
children: t("admin.flowVariables.create_connection")
|
|
168
|
+
})] }) : void 0,
|
|
169
|
+
options: getOptions(definition),
|
|
170
|
+
ownerName,
|
|
171
|
+
value: values[definition.identifier],
|
|
172
|
+
onChange: (value) => handleChange(definition.identifier, value)
|
|
173
|
+
});
|
|
174
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderField ? renderField(field, definition, index) : field }, key);
|
|
175
|
+
}),
|
|
176
|
+
onSubmit && !readOnly && variables.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
177
|
+
className: styles["actions"],
|
|
178
|
+
children: [
|
|
179
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
|
|
180
|
+
type: "button",
|
|
181
|
+
variant: "text",
|
|
182
|
+
onClick: resetToDefaults,
|
|
183
|
+
disabled: isSubmitting,
|
|
184
|
+
children: t("admin.flowVariables.reset_defaults")
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: styles["actionsSpacer"] }),
|
|
187
|
+
onCancel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
|
|
188
|
+
type: "button",
|
|
189
|
+
variant: "text",
|
|
190
|
+
onClick: onCancel,
|
|
191
|
+
disabled: isSubmitting,
|
|
192
|
+
children: t("admin.common.actions.cancel")
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Button.default, {
|
|
195
|
+
type: "submit",
|
|
196
|
+
loading: isSubmitting,
|
|
197
|
+
disabled: isSubmitting,
|
|
198
|
+
children: t("admin.flowVariables.submit")
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
]
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
var BaseFlowVariables_default = BaseFlowVariables;
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
exports.default = BaseFlowVariables_default;
|
|
210
|
+
//# sourceMappingURL=BaseFlowVariables.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseFlowVariables.cjs","names":["chosen: FlowVariableValue | undefined","BaseFlowVariables: FC<BaseFlowVariablesProps>","useTheme","styles: Record<string, string>","useStyles","useTranslation","startingValues: FlowVariableValues","appliedValues: {current: FlowVariableValues}","errors: Record<string, string>","problem: FlowVariableValidationError | undefined","next: FlowVariableValues","overrides: FlowVariableValues","value: FlowVariableValue | undefined","ou: string","AdminSection","Alert","rule: ResolvedFlowVariableRule","field: ReactElement","FlowVariableField","Fragment","Button"],"sources":["../../../../../src/components/management/FlowVariables/BaseFlowVariables.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n ConnectionInstance,\n FlowVariableDefinition,\n FlowVariableOption,\n FlowVariableValidationError,\n FlowVariableValue,\n FlowVariableValues,\n Preferences,\n ResolvedFlowVariableRule,\n ResolvedOverlay,\n getFlowVariableOptions,\n resolveFlowVariableRule,\n validateFlowVariableValue,\n} from '@thunderid/browser';\nimport {FC, Fragment, FormEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState} from 'react';\nimport FlowVariableField from './FlowVariableField';\nimport useStyles from './FlowVariables.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport useTranslation from '../../../hooks/useTranslation';\nimport Alert from '../../primitives/Alert/Alert';\nimport Button from '../../primitives/Button/Button';\nimport AdminSection from '../shared/AdminSection';\n\n/**\n * What {@link BaseFlowVariables} hands its `children` render function, so the form can be built with any UI.\n */\nexport interface BaseFlowVariablesRenderProps {\n error: string | null;\n /**\n * The message to show under each variable that is not valid, keyed by identifier.\n */\n errors: Record<string, string>;\n /**\n * The choices of a select variable, narrowed to what the unit may pick.\n */\n getOptions: (definition: FlowVariableDefinition) => FlowVariableOption[];\n /**\n * What the unit may do with a variable: whether it can set its own value, and the default it starts from.\n */\n getRule: (definition: FlowVariableDefinition) => ResolvedFlowVariableRule;\n /**\n * Sets the value of one variable.\n */\n handleChange: (identifier: string, value: FlowVariableValue) => void;\n /**\n * Submits the current values. Does nothing while they are not valid.\n */\n handleSubmit: () => void;\n isSubmitting: boolean;\n /**\n * Whether the values can be submitted.\n */\n isValid: boolean;\n onCancel?: () => void;\n /**\n * Returns every variable the unit may set to its default.\n */\n resetToDefaults: () => void;\n values: FlowVariableValues;\n variables: FlowVariableDefinition[];\n}\n\nexport interface BaseFlowVariablesProps {\n /**\n * Renders custom UI in place of the default form. Receives the variables, their values and setters, and the\n * submit handler.\n */\n children?: (props: BaseFlowVariablesRenderProps) => ReactNode;\n className?: string;\n /**\n * The connections a `CONNECTION_SELECT` variable can take.\n */\n connections?: Pick<ConnectionInstance, 'categories' | 'id' | 'name' | 'type'>[];\n description?: string;\n error?: string | null;\n /**\n * The values the form starts with, over each variable's default. A variable left out starts at its default.\n */\n initialValues?: FlowVariableValues;\n isSubmitting?: boolean;\n onCancel?: () => void;\n /**\n * Called when the user asks to create a connection, from the hint under a connection variable. Without it, the\n * hint does not offer one.\n */\n onCreateConnection?: () => void;\n /**\n * Called with every value after one changes.\n */\n onChange?: (values: FlowVariableValues) => void;\n /**\n * Called when the form is submitted and valid, with every value and the overrides: the values of the variables\n * the unit may set that differ from their default. Without it the form has no save button, which suits a preview.\n */\n onSubmit?: (values: FlowVariableValues, overrides: FlowVariableValues) => void;\n /**\n * What the unit may do with each variable, as `getFlowOverlayRules` returns it. Without it every variable is\n * editable, which suits a preview.\n */\n overlay?: ResolvedOverlay;\n /**\n * The name of the organization unit the variables are set for, used in the notice and the connection hint.\n */\n ouName?: string;\n /**\n * The name of whoever owns the flow, shown on a variable the unit may not set.\n */\n ownerName?: string;\n preferences?: Preferences;\n /**\n * Shows the variables without letting them be changed.\n */\n readOnly?: boolean;\n /**\n * Wraps the card of each variable, so a host can add its own controls around it, such as selecting or removing the\n * variable while it is being designed. Receives the card, the variable and its position.\n */\n renderField?: (field: ReactElement, definition: FlowVariableDefinition, index: number) => ReactNode;\n /**\n * Content at the end of the label row of each variable, for a control a host adds while designing the variables.\n */\n renderFieldAction?: (definition: FlowVariableDefinition, index: number) => ReactNode;\n /**\n * Shows a notice that the settings apply to the organization unit only. Needs `ouName` to be useful.\n */\n showScopeNotice?: boolean;\n title?: string;\n /**\n * The variables to render, as the flow declares them.\n */\n variables: FlowVariableDefinition[];\n}\n\nconst startingValuesOf = (\n variables: FlowVariableDefinition[],\n overlay: ResolvedOverlay | undefined,\n initialValues: FlowVariableValues | undefined,\n): FlowVariableValues =>\n variables.reduce((values: FlowVariableValues, definition: FlowVariableDefinition) => {\n const {defaultValue, editable}: ResolvedFlowVariableRule = resolveFlowVariableRule(definition, overlay);\n const chosen: FlowVariableValue | undefined = editable\n ? (initialValues?.[definition.identifier] ?? defaultValue)\n : defaultValue;\n\n if (chosen !== undefined && chosen !== null) {\n values[definition.identifier] = chosen;\n }\n\n return values;\n }, {});\n\n/**\n * A form for the variables a flow declares, each in a card with its label, its description, whether it is\n * customized or follows the default, and the input its type calls for. It is what a delegated admin sees when\n * overriding a flow, and what the flow editor previews. Holds no data of its own: pass the variables and handle\n * the submission.\n *\n * @example\n * ```tsx\n * <BaseFlowVariables variables={variables} connections={connections} overlay={overlay} onSubmit={save} />\n * ```\n */\nconst BaseFlowVariables: FC<BaseFlowVariablesProps> = ({\n children,\n className,\n connections = [],\n description,\n error = null,\n initialValues,\n isSubmitting = false,\n onCancel,\n onChange,\n onCreateConnection,\n onSubmit,\n ouName,\n overlay,\n ownerName,\n preferences,\n readOnly = false,\n renderField,\n renderFieldAction,\n showScopeNotice = false,\n title,\n variables,\n}: BaseFlowVariablesProps): ReactElement => {\n const {theme, colorScheme} = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme);\n const {t} = useTranslation(preferences?.i18n);\n\n const startingValues: FlowVariableValues = useMemo(\n () => startingValuesOf(variables, overlay, initialValues),\n // The identity of the inputs can change on every render while their content does not.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(variables), JSON.stringify(overlay), JSON.stringify(initialValues)],\n );\n const [values, setValues] = useState<FlowVariableValues>(startingValues);\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n const [submitted, setSubmitted] = useState<boolean>(false);\n const appliedValues: {current: FlowVariableValues} = useRef(startingValues);\n\n // The form already holds the starting values on mount; only a change to them replaces what was typed.\n useEffect(() => {\n if (appliedValues.current !== startingValues) {\n appliedValues.current = startingValues;\n setValues(startingValues);\n setTouched({});\n setSubmitted(false);\n }\n }, [startingValues]);\n\n const getRule = (definition: FlowVariableDefinition): ResolvedFlowVariableRule =>\n resolveFlowVariableRule(definition, overlay);\n\n const getOptions = (definition: FlowVariableDefinition): FlowVariableOption[] => {\n const {allowedValues, editable, excludedValues}: ResolvedFlowVariableRule = getRule(definition);\n\n // A variable the unit cannot set shows its pinned value, which a restriction might hide from the choices.\n return getFlowVariableOptions(definition, connections, editable ? {allowedValues, excludedValues} : {});\n };\n\n const errors: Record<string, string> = {};\n let isValid = true;\n\n variables.forEach((definition: FlowVariableDefinition) => {\n if (!getRule(definition).editable) {\n return;\n }\n\n const problem: FlowVariableValidationError | undefined = validateFlowVariableValue(\n definition,\n values[definition.identifier],\n );\n\n if (problem) {\n isValid = false;\n\n if (submitted || touched[definition.identifier]) {\n errors[definition.identifier] = problem.message ?? t(`admin.flowVariables.errors.${problem.code}`);\n }\n }\n });\n\n const handleChange = (identifier: string, value: FlowVariableValue): void => {\n const next: FlowVariableValues = {...values, [identifier]: value};\n\n setValues(next);\n setTouched((previous: Record<string, boolean>) => ({...previous, [identifier]: true}));\n onChange?.(next);\n };\n\n const resetToDefaults = (): void => {\n const next: FlowVariableValues = variables.reduce(\n (accumulated: FlowVariableValues, definition: FlowVariableDefinition) => {\n const {defaultValue}: ResolvedFlowVariableRule = getRule(definition);\n\n if (defaultValue !== undefined) {\n accumulated[definition.identifier] = defaultValue;\n }\n\n return accumulated;\n },\n {},\n );\n\n setValues(next);\n setTouched({});\n setSubmitted(false);\n onChange?.(next);\n };\n\n const submit = (): void => {\n setSubmitted(true);\n\n if (!isValid) {\n return;\n }\n\n const overrides: FlowVariableValues = variables.reduce(\n (accumulated: FlowVariableValues, definition: FlowVariableDefinition) => {\n const {defaultValue, editable}: ResolvedFlowVariableRule = getRule(definition);\n const value: FlowVariableValue | undefined = values[definition.identifier];\n\n if (editable && value !== undefined && value !== defaultValue) {\n accumulated[definition.identifier] = value;\n }\n\n return accumulated;\n },\n {},\n );\n\n onSubmit?.(values, overrides);\n };\n\n const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {\n event.preventDefault();\n submit();\n };\n\n if (children) {\n return (\n <div className={className}>\n {children({\n error,\n errors,\n getOptions,\n getRule,\n handleChange,\n handleSubmit: submit,\n isSubmitting,\n isValid,\n onCancel,\n resetToDefaults,\n values,\n variables,\n })}\n </div>\n );\n }\n\n const ou: string = ouName ?? t('admin.flowVariables.ou_fallback');\n\n return (\n <AdminSection\n block=\"flow-variables\"\n className={className}\n title={title}\n description={description}\n preferences={preferences}\n >\n <form className={styles['form']} onSubmit={handleSubmit} noValidate>\n {showScopeNotice && (\n <Alert variant=\"info\">\n <Alert.Title>{t('admin.flowVariables.notice_title', {ou})}</Alert.Title>\n <Alert.Description>{t('admin.flowVariables.notice_body', {ou})}</Alert.Description>\n </Alert>\n )}\n {error && (\n <Alert variant=\"error\" role=\"alert\">\n {error}\n </Alert>\n )}\n {variables.length === 0 && <p className={styles['empty']}>{t('admin.flowVariables.empty')}</p>}\n {variables.map((definition: FlowVariableDefinition, index: number) => {\n const rule: ResolvedFlowVariableRule = getRule(definition);\n // An identifier can be repeated while a flow's variables are being edited, so it cannot be the key alone.\n const key = `${index}-${definition.identifier}`;\n\n const field: ReactElement = (\n <FlowVariableField\n action={renderFieldAction?.(definition, index)}\n definition={definition}\n defaultValue={rule.defaultValue}\n disabled={readOnly || isSubmitting}\n editable={rule.editable}\n error={errors[definition.identifier]}\n hint={\n definition.type === 'CONNECTION_SELECT' && rule.editable ? (\n <>\n <span>{t('admin.flowVariables.connections_hint', {ou})}</span>\n {onCreateConnection && (\n <button type=\"button\" className={styles['link']} onClick={onCreateConnection}>\n {t('admin.flowVariables.create_connection')}\n </button>\n )}\n </>\n ) : undefined\n }\n options={getOptions(definition)}\n ownerName={ownerName}\n value={values[definition.identifier]}\n onChange={(value: FlowVariableValue): void => handleChange(definition.identifier, value)}\n />\n );\n\n return <Fragment key={key}>{renderField ? renderField(field, definition, index) : field}</Fragment>;\n })}\n {onSubmit && !readOnly && variables.length > 0 && (\n <div className={styles['actions']}>\n <Button type=\"button\" variant=\"text\" onClick={resetToDefaults} disabled={isSubmitting}>\n {t('admin.flowVariables.reset_defaults')}\n </Button>\n <div className={styles['actionsSpacer']} />\n {onCancel && (\n <Button type=\"button\" variant=\"text\" onClick={onCancel} disabled={isSubmitting}>\n {t('admin.common.actions.cancel')}\n </Button>\n )}\n <Button type=\"submit\" loading={isSubmitting} disabled={isSubmitting}>\n {t('admin.flowVariables.submit')}\n </Button>\n </div>\n )}\n </form>\n </AdminSection>\n );\n};\n\nexport default BaseFlowVariables;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_emotion = require('../../../styles/emotion.cjs');
|
|
3
|
+
const require_useTheme = require('../../../contexts/Theme/useTheme.cjs');
|
|
4
|
+
const require_useTranslation = require('../../../hooks/useTranslation.cjs');
|
|
5
|
+
const require_Toggle = require('../../primitives/Toggle/Toggle.cjs');
|
|
6
|
+
const require_TextField = require('../../primitives/TextField/TextField.cjs');
|
|
7
|
+
const require_Select = require('../../primitives/Select/Select.cjs');
|
|
8
|
+
const require_FlowVariables_styles = require('./FlowVariables.styles.cjs');
|
|
9
|
+
let __thunderid_browser = require("@thunderid/browser");
|
|
10
|
+
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
11
|
+
let react = require("react");
|
|
12
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
13
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
14
|
+
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
15
|
+
|
|
16
|
+
//#region src/components/management/FlowVariables/FlowVariableField.tsx
|
|
17
|
+
const INPUT_TYPES = {
|
|
18
|
+
DATE_INPUT: "date",
|
|
19
|
+
EMAIL_INPUT: "email",
|
|
20
|
+
NUMBER_INPUT: "number",
|
|
21
|
+
PHONE_INPUT: "tel",
|
|
22
|
+
TEXT_INPUT: "text"
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* One variable as the delegated admin sees it, in a card: its label and description, whether it is customized
|
|
26
|
+
* or follows the default, the input its type calls for, and what the default is. A variable the unit may not set
|
|
27
|
+
* is shown as set by its owner.
|
|
28
|
+
*/
|
|
29
|
+
const FlowVariableField = ({ action, className, defaultValue, definition, disabled, editable = true, error, hint, onChange, options = [], ownerName, value }) => {
|
|
30
|
+
const { theme, colorScheme } = require_useTheme.default();
|
|
31
|
+
const styles = require_FlowVariables_styles.default(theme, colorScheme);
|
|
32
|
+
const { t } = require_useTranslation.default();
|
|
33
|
+
const id = (0, react.useId)();
|
|
34
|
+
const descriptionId = definition.description ? `${id}-description` : void 0;
|
|
35
|
+
const label = definition.label ?? definition.identifier;
|
|
36
|
+
const isSelect = definition.type === "SELECT" || definition.type === "CONNECTION_SELECT";
|
|
37
|
+
const isDefault = value === defaultValue;
|
|
38
|
+
const display = (shown) => {
|
|
39
|
+
if (shown === void 0) return "";
|
|
40
|
+
if (typeof shown === "boolean") return t(shown ? "admin.flowVariables.on" : "admin.flowVariables.off");
|
|
41
|
+
return isSelect ? options.find((option) => String(option.value) === String(shown))?.label ?? String(shown) : String(shown);
|
|
42
|
+
};
|
|
43
|
+
const renderControl = () => {
|
|
44
|
+
switch (definition.type) {
|
|
45
|
+
case "BOOLEAN_INPUT": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Toggle.default, {
|
|
46
|
+
id,
|
|
47
|
+
"aria-label": label,
|
|
48
|
+
"aria-describedby": descriptionId,
|
|
49
|
+
checked: value === true,
|
|
50
|
+
disabled: disabled === true || !editable,
|
|
51
|
+
error,
|
|
52
|
+
onChange: (event) => onChange(event.target.checked)
|
|
53
|
+
});
|
|
54
|
+
case "SELECT":
|
|
55
|
+
case "CONNECTION_SELECT": {
|
|
56
|
+
const handleSelect = (event) => {
|
|
57
|
+
const chosen = (definition.options ?? []).find((option) => String(option) === event.target.value);
|
|
58
|
+
onChange(event.target.value === "" && defaultValue !== void 0 ? defaultValue : chosen ?? event.target.value);
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Select.default, {
|
|
61
|
+
id,
|
|
62
|
+
"aria-label": label,
|
|
63
|
+
"aria-describedby": descriptionId,
|
|
64
|
+
disabled,
|
|
65
|
+
emptyText: t("admin.flowVariables.no_options"),
|
|
66
|
+
error,
|
|
67
|
+
options,
|
|
68
|
+
placeholder: defaultValue === void 0 || defaultValue === "" ? t("admin.flowVariables.select_placeholder") : t("admin.flowVariables.use_default", { default: display(defaultValue) }),
|
|
69
|
+
value: isDefault || value === void 0 ? "" : String(value),
|
|
70
|
+
onChange: handleSelect
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
default: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_TextField.default, {
|
|
74
|
+
id,
|
|
75
|
+
"aria-label": label,
|
|
76
|
+
"aria-describedby": descriptionId,
|
|
77
|
+
disabled,
|
|
78
|
+
error,
|
|
79
|
+
max: definition.max,
|
|
80
|
+
min: definition.min,
|
|
81
|
+
placeholder: defaultValue === void 0 || defaultValue === "" ? t("admin.flowVariables.enter_value") : display(defaultValue),
|
|
82
|
+
type: INPUT_TYPES[definition.type] ?? "text",
|
|
83
|
+
value: value === void 0 ? "" : String(value),
|
|
84
|
+
onChange: (event) => {
|
|
85
|
+
const raw = event.target.value;
|
|
86
|
+
onChange(definition.type === "NUMBER_INPUT" && raw !== "" && !Number.isNaN(Number(raw)) ? Number(raw) : raw);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
92
|
+
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("flow-variables", "field")), (0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("flow-variables", "field", definition.type.toLowerCase())), styles["card"], !editable && styles["cardLocked"], className),
|
|
93
|
+
"data-variable": definition.identifier,
|
|
94
|
+
"data-editable": editable,
|
|
95
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
96
|
+
className: styles["header"],
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
99
|
+
className: styles["headerText"],
|
|
100
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
101
|
+
htmlFor: id,
|
|
102
|
+
className: styles["label"],
|
|
103
|
+
children: label
|
|
104
|
+
}), definition.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
105
|
+
id: descriptionId,
|
|
106
|
+
className: styles["description"],
|
|
107
|
+
children: definition.description
|
|
108
|
+
})]
|
|
109
|
+
}),
|
|
110
|
+
editable && !isDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
111
|
+
className: styles["chipCustomized"],
|
|
112
|
+
"data-testid": "flow-variable-status",
|
|
113
|
+
children: t("admin.flowVariables.status.customized")
|
|
114
|
+
}),
|
|
115
|
+
action
|
|
116
|
+
]
|
|
117
|
+
}), editable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
118
|
+
renderControl(),
|
|
119
|
+
hint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
120
|
+
className: styles["hint"],
|
|
121
|
+
children: hint
|
|
122
|
+
}),
|
|
123
|
+
!isDefault && defaultValue !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
124
|
+
className: styles["hint"],
|
|
125
|
+
children: t("admin.flowVariables.default_hint", { default: display(defaultValue) })
|
|
126
|
+
})
|
|
127
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
128
|
+
className: styles["lockedLine"],
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
131
|
+
width: "16",
|
|
132
|
+
height: "16",
|
|
133
|
+
viewBox: "0 0 24 24",
|
|
134
|
+
fill: "none",
|
|
135
|
+
stroke: "currentColor",
|
|
136
|
+
strokeWidth: "1.7",
|
|
137
|
+
strokeLinecap: "round",
|
|
138
|
+
strokeLinejoin: "round",
|
|
139
|
+
"aria-hidden": "true",
|
|
140
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
141
|
+
x: "5",
|
|
142
|
+
y: "11",
|
|
143
|
+
width: "14",
|
|
144
|
+
height: "9",
|
|
145
|
+
rx: "2"
|
|
146
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 11V8a4 4 0 018 0v3" })]
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
149
|
+
t("admin.flowVariables.locked", { owner: ownerName ?? t("admin.flowVariables.owner_fallback") }),
|
|
150
|
+
" · ",
|
|
151
|
+
display(value)
|
|
152
|
+
] }),
|
|
153
|
+
definition.type === "BOOLEAN_INPUT" && renderControl()
|
|
154
|
+
]
|
|
155
|
+
})]
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
var FlowVariableField_default = FlowVariableField;
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
exports.default = FlowVariableField_default;
|
|
162
|
+
//# sourceMappingURL=FlowVariableField.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlowVariableField.cjs","names":["INPUT_TYPES: Partial<Record<FlowVariableDefinition['type'], string>>","FlowVariableField: FC<FlowVariableFieldProps>","useTheme","styles: Record<string, string>","useStyles","useTranslation","id: string","descriptionId: string | undefined","label: string","isSelect: boolean","isDefault: boolean","Toggle","chosen: FlowVariableValue | undefined","Select","TextField","raw: string","cx"],"sources":["../../../../../src/components/management/FlowVariables/FlowVariableField.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n FlowVariableDefinition,\n FlowVariableValue,\n SelectOption,\n bem,\n withVendorCSSClassPrefix,\n} from '@thunderid/browser';\nimport {ChangeEvent, FC, ReactElement, ReactNode, useId} from 'react';\nimport useStyles from './FlowVariables.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {cx} from '../../../styles/emotion';\nimport Select from '../../primitives/Select/Select';\nimport TextField from '../../primitives/TextField/TextField';\nimport Toggle from '../../primitives/Toggle/Toggle';\n\nexport interface FlowVariableFieldProps {\n /**\n * Extra content at the end of the label row, such as a control a host adds while the variable is being designed.\n */\n action?: ReactNode;\n className?: string;\n /**\n * The value the variable has until the unit sets its own, which a sharing policy may have set.\n */\n defaultValue?: FlowVariableValue;\n definition: FlowVariableDefinition;\n disabled?: boolean;\n /**\n * Whether the unit may set its own value. A variable it may not set is shown as set by its owner.\n * @default true\n */\n editable?: boolean;\n /**\n * The error to show under the field.\n */\n error?: string;\n /**\n * Extra text under the input, such as where the choices come from.\n */\n hint?: ReactNode;\n onChange: (value: FlowVariableValue) => void;\n /**\n * The choices of a `SELECT` or `CONNECTION_SELECT`, as {@link getFlowVariableOptions} returns them.\n */\n options?: SelectOption[];\n /**\n * Who set the variable, shown while it is not editable.\n */\n ownerName?: string;\n value: FlowVariableValue | undefined;\n}\n\nconst INPUT_TYPES: Partial<Record<FlowVariableDefinition['type'], string>> = {\n DATE_INPUT: 'date',\n EMAIL_INPUT: 'email',\n NUMBER_INPUT: 'number',\n PHONE_INPUT: 'tel',\n TEXT_INPUT: 'text',\n};\n\n/**\n * One variable as the delegated admin sees it, in a card: its label and description, whether it is customized\n * or follows the default, the input its type calls for, and what the default is. A variable the unit may not set\n * is shown as set by its owner.\n */\nconst FlowVariableField: FC<FlowVariableFieldProps> = ({\n action,\n className,\n defaultValue,\n definition,\n disabled,\n editable = true,\n error,\n hint,\n onChange,\n options = [],\n ownerName,\n value,\n}: FlowVariableFieldProps): ReactElement => {\n const {theme, colorScheme} = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme);\n const {t} = useTranslation();\n const id: string = useId();\n const descriptionId: string | undefined = definition.description ? `${id}-description` : undefined;\n const label: string = definition.label ?? definition.identifier;\n const isSelect: boolean = definition.type === 'SELECT' || definition.type === 'CONNECTION_SELECT';\n const isDefault: boolean = value === defaultValue;\n\n const display = (shown: FlowVariableValue | undefined): string => {\n if (shown === undefined) {\n return '';\n }\n if (typeof shown === 'boolean') {\n return t(shown ? 'admin.flowVariables.on' : 'admin.flowVariables.off');\n }\n\n return isSelect\n ? (options.find((option: SelectOption) => String(option.value) === String(shown))?.label ?? String(shown))\n : String(shown);\n };\n\n const renderControl = (): ReactElement => {\n switch (definition.type) {\n case 'BOOLEAN_INPUT':\n return (\n <Toggle\n id={id}\n aria-label={label}\n aria-describedby={descriptionId}\n checked={value === true}\n disabled={disabled === true || !editable}\n error={error}\n onChange={(event: ChangeEvent<HTMLInputElement>): void => onChange(event.target.checked)}\n />\n );\n case 'SELECT':\n case 'CONNECTION_SELECT': {\n const handleSelect = (event: ChangeEvent<HTMLSelectElement>): void => {\n const chosen: FlowVariableValue | undefined = (definition.options ?? []).find(\n (option: FlowVariableValue) => String(option) === event.target.value,\n );\n\n // The first entry, the default, clears the choice.\n onChange(\n event.target.value === '' && defaultValue !== undefined ? defaultValue : (chosen ?? event.target.value),\n );\n };\n\n return (\n <Select\n id={id}\n aria-label={label}\n aria-describedby={descriptionId}\n disabled={disabled}\n emptyText={t('admin.flowVariables.no_options')}\n error={error}\n options={options}\n placeholder={\n defaultValue === undefined || defaultValue === ''\n ? t('admin.flowVariables.select_placeholder')\n : t('admin.flowVariables.use_default', {default: display(defaultValue)})\n }\n value={isDefault || value === undefined ? '' : String(value)}\n onChange={handleSelect}\n />\n );\n }\n default:\n return (\n <TextField\n id={id}\n aria-label={label}\n aria-describedby={descriptionId}\n disabled={disabled}\n error={error}\n max={definition.max}\n min={definition.min}\n placeholder={\n defaultValue === undefined || defaultValue === ''\n ? t('admin.flowVariables.enter_value')\n : display(defaultValue)\n }\n type={INPUT_TYPES[definition.type] ?? 'text'}\n value={value === undefined ? '' : String(value)}\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n const raw: string = event.target.value;\n\n onChange(\n definition.type === 'NUMBER_INPUT' && raw !== '' && !Number.isNaN(Number(raw)) ? Number(raw) : raw,\n );\n }}\n />\n );\n }\n };\n\n return (\n <section\n className={cx(\n withVendorCSSClassPrefix(bem('flow-variables', 'field')),\n withVendorCSSClassPrefix(bem('flow-variables', 'field', definition.type.toLowerCase())),\n styles['card'],\n !editable && styles['cardLocked'],\n className,\n )}\n data-variable={definition.identifier}\n data-editable={editable}\n >\n <div className={styles['header']}>\n <div className={styles['headerText']}>\n <label htmlFor={id} className={styles['label']}>\n {label}\n </label>\n {definition.description && (\n <p id={descriptionId} className={styles['description']}>\n {definition.description}\n </p>\n )}\n </div>\n {editable && !isDefault && (\n <span className={styles['chipCustomized']} data-testid=\"flow-variable-status\">\n {t('admin.flowVariables.status.customized')}\n </span>\n )}\n {action}\n </div>\n {editable ? (\n <>\n {renderControl()}\n {hint && <p className={styles['hint']}>{hint}</p>}\n {!isDefault && defaultValue !== undefined && (\n <p className={styles['hint']}>{t('admin.flowVariables.default_hint', {default: display(defaultValue)})}</p>\n )}\n </>\n ) : (\n <div className={styles['lockedLine']}>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\" />\n <path d=\"M8 11V8a4 4 0 018 0v3\" />\n </svg>\n <span>\n {t('admin.flowVariables.locked', {owner: ownerName ?? t('admin.flowVariables.owner_fallback')})}\n {' · '}\n {display(value)}\n </span>\n {definition.type === 'BOOLEAN_INPUT' && renderControl()}\n </div>\n )}\n </section>\n );\n};\n\nexport default FlowVariableField;\n"],"mappings":"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"}
|