diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 431736f34c..69b80c040e 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -1290,6 +1290,14 @@ boxui.metadataInstanceEditor.customErrorDuplicateKey = A field with that key alr boxui.metadataInstanceEditor.customErrorInternalKey = Keys cannot begin with a $. # Error enforcing required key for custom metadata boxui.metadataInstanceEditor.customErrorRequired = A key is required. +# Label for the button that navigates the user to manage the custom Box AI extract agent +boxui.metadataInstanceEditor.customExtractAgentManageButton = Manage agent +# Body of the notice shown when a metadata instance is managed by a custom Box AI extract agent +boxui.metadataInstanceEditor.customExtractAgentNoticeDescription = This policy is managed by an agent. Manage the agent to change the configuration. +# Aria label for the info icon on the custom extract agent notice +boxui.metadataInstanceEditor.customExtractAgentNoticeIconAriaLabel = Extract agent managed information +# Bold title of the notice shown when a metadata instance is managed by a custom Box AI extract agent +boxui.metadataInstanceEditor.customExtractAgentNoticeTitle = This Metadata can't be edited here. # Label for the key field for custom metadata boxui.metadataInstanceEditor.customKey = Key # Placeholder for the key field for custom metadata diff --git a/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.js b/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.js new file mode 100644 index 0000000000..14d17c7bbc --- /dev/null +++ b/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.js @@ -0,0 +1,81 @@ +// @flow +import * as React from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { ActionableInlineNotice } from '@box/blueprint-web'; +// $FlowFixMe - blueprint-web-assets icons not typed for Flow +import { Lock } from '@box/blueprint-web-assets/icons/Line'; + +import TemplatedInstance from './TemplatedInstance'; +import CustomInstance from './CustomInstance'; +import messages from './messages'; +import { TEMPLATE_CUSTOM_PROPERTIES } from './constants'; +import { getCustomExtractAgentId } from './metadataUtil'; +import type { MetadataFields, MetadataTemplate } from '../../common/types/metadata'; +import './CustomExtractAgentInstanceBody.scss'; + +type Props = { + // Raw agent configuration value from the cascade policy (e.g. `extract_agent_`). + // The navigable numeric id is resolved from this here. + agentConfiguration?: string, + data: MetadataFields, + isEditing: boolean, + onManageExtractAgent?: (agentId: string) => void, + template: MetadataTemplate, +}; + +/** + * Presentational interior for a metadata instance managed by a custom Box AI + * extract agent. When collapsed it shows read-only field values; when the user + * enters edit mode it replaces the form with an informational notice and a button + * to manage the agent (instead of allowing inline edits). + * + * The "manage agent" button is only shown when a navigable numeric agent id can be + * resolved from the configuration; otherwise the notice is shown without an action. + */ +const CustomExtractAgentInstanceBody = ({ + agentConfiguration, + data, + isEditing, + onManageExtractAgent, + template, +}: Props) => { + const { formatMessage } = useIntl(); + const isProperties = template.templateKey === TEMPLATE_CUSTOM_PROPERTIES; + const customExtractAgentId = getCustomExtractAgentId(agentConfiguration); + + if (isEditing) { + return ( +
+ + {!!customExtractAgentId && onManageExtractAgent && ( + onManageExtractAgent(customExtractAgentId)} + > + {formatMessage(messages.customExtractAgentManageButton)} + + )} + +
+ ); + } + + return ( +
+
+ +
+ {isProperties ? ( + + ) : ( + + )} +
+ ); +}; + +export default CustomExtractAgentInstanceBody; diff --git a/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.scss b/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.scss new file mode 100644 index 0000000000..064dde73f4 --- /dev/null +++ b/src/features/metadata-instance-editor/CustomExtractAgentInstanceBody.scss @@ -0,0 +1,3 @@ +.metadata-instance-editor-custom-extract-agent { + padding: 8px; +} diff --git a/src/features/metadata-instance-editor/EditableInstanceBody.js b/src/features/metadata-instance-editor/EditableInstanceBody.js new file mode 100644 index 0000000000..972afdf24a --- /dev/null +++ b/src/features/metadata-instance-editor/EditableInstanceBody.js @@ -0,0 +1,145 @@ +// @flow +import * as React from 'react'; +import noop from 'lodash/noop'; + +import type { AgentType } from '@box/box-ai-agent-selector'; +import Form from '../../components/form-elements/form/Form'; +import LoadingIndicatorWrapper from '../../components/loading-indicator/LoadingIndicatorWrapper'; + +import CascadePolicy from './CascadePolicy'; +import TemplatedInstance from './TemplatedInstance'; +import CustomInstance from './CustomInstance'; +import MetadataInstanceConfirmDialog from './MetadataInstanceConfirmDialog'; +import Footer from './Footer'; +import type { + MetadataCascadePolicy, + MetadataFields, + MetadataTemplate, + MetadataFieldValue, +} from '../../common/types/metadata'; + +type Props = { + canUseAIFolderExtraction: boolean, + cascadePolicy: MetadataCascadePolicy, + confirmationMessage: React.Node, + data: MetadataFields, + errors: { [string]: React.Node }, + isAIFolderExtractionEnabled: boolean, + isBusy: boolean, + isCascadingEnabled: boolean, + isCascadingOverwritten: boolean, + isCascadingPolicyApplicable?: boolean, + isDirty: boolean, + isEditing: boolean, + isExistingCascadePolicy: boolean, + isProperties: boolean, + onAIAgentSelect: (agent: AgentType | null) => void, + onAIFolderExtractionToggle: (value: boolean) => void, + onCancel: () => void, + onCascadeModeChange: (value: boolean) => void, + onCascadeToggle: (value: boolean) => void, + onConfirmCancel: () => void, + onConfirmRemove: () => void, + onFieldChange: (key: string, value: MetadataFieldValue, type: string) => void, + onFieldRemove: (key: string) => void, + onRemove: () => void, + onSave: () => void, + shouldConfirmRemove: boolean, + shouldShowCascadeOptions: boolean, + template: MetadataTemplate, +}; + +/** + * Presentational interior for an editable metadata instance: the confirm-remove + * dialog, the form with cascade policy + fields, and the save/remove footer. + * All state and handlers are owned by the parent Instance and supplied via props. + */ +const EditableInstanceBody = ({ + canUseAIFolderExtraction, + cascadePolicy = {}, + confirmationMessage, + data, + errors, + isAIFolderExtractionEnabled, + isBusy, + isCascadingEnabled, + isCascadingOverwritten, + isCascadingPolicyApplicable, + isDirty, + isEditing, + isExistingCascadePolicy, + isProperties, + onAIAgentSelect, + onAIFolderExtractionToggle, + onCancel, + onCascadeModeChange, + onCascadeToggle, + onConfirmCancel, + onConfirmRemove, + onFieldChange, + onFieldRemove, + onRemove, + onSave, + shouldConfirmRemove, + shouldShowCascadeOptions, + template, +}: Props) => { + if (shouldConfirmRemove) { + return ( + + + + ); + } + + return ( + +
+
+ {isCascadingPolicyApplicable && ( + + )} + {isProperties ? ( + + ) : ( + + )} +
+ {isEditing &&