From 36c8bb3c401ed153dcc0e866e0edab49746a4ff9 Mon Sep 17 00:00:00 2001 From: Novak Zaballa <41410593+novakzaballa@users.noreply.github.com> Date: Thu, 20 Jun 2024 10:17:32 -0400 Subject: [PATCH] fix: Metadata UI issues (#4069) Co-authored-by: Matthew Elwell --- frontend/common/constants.ts | 3 +- frontend/common/dispatcher/app-actions.js | 3 +- frontend/common/providers/ProjectProvider.js | 4 +- frontend/common/services/useFeatureVersion.ts | 2 +- frontend/common/stores/feature-list-store.ts | 2 +- frontend/common/stores/project-store.js | 5 +- frontend/web/components/AuditLog.tsx | 16 +-- frontend/web/components/ErrorMessage.js | 9 +- frontend/web/components/FeatureVersion.tsx | 4 +- frontend/web/components/IntegrationList.js | 2 +- .../metadata/AddMetadataToEntity.tsx | 49 +++++-- .../ContentTypesMetadataFieldTable.tsx | 2 +- .../web/components/metadata/MetadataPage.tsx | 20 +-- .../web/components/metadata/MetadataTitle.tsx | 32 +++++ frontend/web/components/modals/CreateFlag.js | 59 ++++---- .../components/modals/CreateMetadataField.tsx | 135 ++++++++++-------- .../web/components/modals/CreateSegment.tsx | 78 +++++++--- .../web/components/pages/AuditLogItemPage.tsx | 6 +- .../components/pages/CreateEnvironmentPage.js | 60 +++++++- .../pages/EnvironmentSettingsPage.js | 5 +- .../pages/FeatureHistoryDetailPage.tsx | 2 +- frontend/web/components/pages/HomePage.js | 2 +- .../web/components/pages/SegmentsPage.tsx | 1 - 23 files changed, 335 insertions(+), 166 deletions(-) create mode 100644 frontend/web/components/metadata/MetadataTitle.tsx diff --git a/frontend/common/constants.ts b/frontend/common/constants.ts index b4e75f6bda8b..12a7c62c0222 100644 --- a/frontend/common/constants.ts +++ b/frontend/common/constants.ts @@ -518,7 +518,8 @@ export default { 'Set different values for your feature based on what segments users are in. Identity overrides will take priority over any segment override.', TAGS_DESCRIPTION: 'Organise your flags with tags, tagging your features as "protected" will prevent them from accidentally being deleted.', - TOOLTIP_METADATA_DESCRIPTION: 'Add metadata in your', + TOOLTIP_METADATA_DESCRIPTION: (entity: string) => + `Add Metadata in your ${entity}, you can create the Metadata Fields in the project settings.`, USER_PROPERTY_DESCRIPTION: 'The name of the user trait or custom property belonging to the user, e.g. firstName', WEBHOOKS_DESCRIPTION: diff --git a/frontend/common/dispatcher/app-actions.js b/frontend/common/dispatcher/app-actions.js index 1012667780b0..90adb94f4f96 100644 --- a/frontend/common/dispatcher/app-actions.js +++ b/frontend/common/dispatcher/app-actions.js @@ -29,11 +29,12 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), { pin, }) }, - createEnv(name, projectId, cloneId, description) { + createEnv(name, projectId, cloneId, description, metadata) { Dispatcher.handleViewAction({ actionType: Actions.CREATE_ENV, cloneId, description, + metadata, name, projectId, }) diff --git a/frontend/common/providers/ProjectProvider.js b/frontend/common/providers/ProjectProvider.js index 2f29fd73b75c..cf91d54a0c1a 100644 --- a/frontend/common/providers/ProjectProvider.js +++ b/frontend/common/providers/ProjectProvider.js @@ -40,8 +40,8 @@ const ProjectProvider = class extends React.Component { }) } - createEnv = (env, projectId, cloneId, description) => { - AppActions.createEnv(env, projectId, cloneId, description) + createEnv = (env, projectId, cloneId, description, metadata) => { + AppActions.createEnv(env, projectId, cloneId, description, metadata) } editEnv = (env) => { diff --git a/frontend/common/services/useFeatureVersion.ts b/frontend/common/services/useFeatureVersion.ts index f389d6ce09ab..91fa9449d740 100644 --- a/frontend/common/services/useFeatureVersion.ts +++ b/frontend/common/services/useFeatureVersion.ts @@ -136,11 +136,11 @@ export const featureVersionService = service } const ret = { + error: res.find((v) => !!v.error)?.error, feature_states: res.map((item) => ({ ...item, version_sha: versionRes.data.uuid, })), - error: res.find((v) => !!v.error)?.error, version_sha: versionRes.data.uuid, } diff --git a/frontend/common/stores/feature-list-store.ts b/frontend/common/stores/feature-list-store.ts index b7248ceb4ead..22325753c37d 100644 --- a/frontend/common/stores/feature-list-store.ts +++ b/frontend/common/stores/feature-list-store.ts @@ -481,8 +481,8 @@ const controller = { environmentId: env.id, featureId: projectFlag.id, featureStates, - skipPublish: true, liveFrom: changeRequest.live_from, + skipPublish: true, }) environment_feature_versions = [version.version_sha] } diff --git a/frontend/common/stores/project-store.js b/frontend/common/stores/project-store.js index 13f597615fdf..a174fd7a77eb 100644 --- a/frontend/common/stores/project-store.js +++ b/frontend/common/stores/project-store.js @@ -13,7 +13,7 @@ const BaseStore = require('./base/_store') const data = require('../data/base/_data') const controller = { - createEnv: (name, projectId, cloneId, description) => { + createEnv: (name, projectId, cloneId, description, metadata) => { API.trackEvent(Constants.events.CREATE_ENVIRONMENT) store.saving() const req = cloneId @@ -26,12 +26,12 @@ const controller = { name, project: projectId, }) - req .then((res) => data .put(`${Project.api}environments/${res.api_key}/`, { description, + metadata: metadata || [], name, project: projectId, }) @@ -247,6 +247,7 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => { action.projectId, action.cloneId, action.description, + action.metadata, ) break case Actions.EDIT_ENVIRONMENT: diff --git a/frontend/web/components/AuditLog.tsx b/frontend/web/components/AuditLog.tsx index 8a77f28f5a3f..8242acff616b 100644 --- a/frontend/web/components/AuditLog.tsx +++ b/frontend/web/components/AuditLog.tsx @@ -65,7 +65,7 @@ const AuditLog: FC = (props) => { search, }, { - refetchOnMountOrArgChange: true + refetchOnMountOrArgChange: true, }, ) @@ -128,13 +128,13 @@ const AuditLog: FC = (props) => { style={{ width: widths[2] }} to={`/project/${props.projectId}/environment/${environment?.api_key}/features/`} > - + ) : (
diff --git a/frontend/web/components/ErrorMessage.js b/frontend/web/components/ErrorMessage.js index 50c6e0f9e11b..cbf1832f6c7e 100644 --- a/frontend/web/components/ErrorMessage.js +++ b/frontend/web/components/ErrorMessage.js @@ -12,7 +12,14 @@ export default class ErrorMessage extends PureComponent { const errorMessageClassName = `alert alert-danger ${ this.props.errorMessageClass || 'flex-1 align-items-center' }` - const error = this.props.error?.data || this.props.error?.message || this.props.error + const error = + this.props.error?.data?.metadata?.find((item) => + // eslint-disable-next-line no-prototype-builtins + item.hasOwnProperty('non_field_errors'), + )?.non_field_errors[0] || + this.props.error?.data || + this.props.error?.message || + this.props.error return this.props.error ? (
, 'side-modal', ) diff --git a/frontend/web/components/metadata/AddMetadataToEntity.tsx b/frontend/web/components/metadata/AddMetadataToEntity.tsx index 9b451daa7f36..ca3191d06543 100644 --- a/frontend/web/components/metadata/AddMetadataToEntity.tsx +++ b/frontend/web/components/metadata/AddMetadataToEntity.tsx @@ -27,11 +27,12 @@ export type CustomMetadataField = MetadataField & { type CustomMetadata = (Metadata & CustomMetadataField) | null type AddMetadataToEntityType = { + isCloningEnvironment?: boolean organisationId: string projectId: string | number entityContentType: number entityId: string - entity: number | string + entity: string envName?: string onChange?: (m: CustomMetadataField[]) => void setHasMetadataRequired?: (b: boolean) => void @@ -42,6 +43,7 @@ const AddMetadataToEntity: FC = ({ entityContentType, entityId, envName, + isCloningEnvironment, onChange, organisationId, projectId, @@ -102,9 +104,6 @@ const AddMetadataToEntity: FC = ({ }, [metadataFieldsAssociatedtoEntity]) const [metadataChanged, setMetadataChanged] = useState(false) - useEffect(() => { - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [metadataFieldsAssociatedtoEntity]) const mergeMetadataEntityWithMetadataField = ( metadata: Metadata[], // Metadata array @@ -160,9 +159,7 @@ const AddMetadataToEntity: FC = ({ }) // Determine if isRequiredFor should be true or false based on is_required_for array const isRequiredFor = !!matchingItem?.is_required_for.length - if (isRequiredFor) { - setHasMetadataRequired?.() - } + setHasMetadataRequired?.(isRequiredFor) // Return the metadata field with additional metadata model field information including isRequiredFor return { ...meta, @@ -215,7 +212,7 @@ const AddMetadataToEntity: FC = ({ ]) return ( <> - + = ({ return ( { setMetadataFieldsAssociatedtoEntity((prevState) => prevState?.map((metadata) => { @@ -247,8 +245,22 @@ const AddMetadataToEntity: FC = ({ /> ) }} + renderNoResults={ + + No metadata configured for {entity} entity. Add metadata field in + your{' '} + + Project Settings + + . + + } /> - {entity === 'environment' && ( + {entity === 'environment' && !isCloningEnvironment && (
@@ -126,7 +118,7 @@ const MetadataPage: FC = ({ organisationId, projectId }) => {
+ {metadataEnable && segmentContentType?.id && ( + + Metadata + {metadataValueChanged && ( +
{'*'}
+ )} + + } + > +
{MetadataTab}
+
+ )} + + ) : metadataEnable && segmentContentType?.id ? ( + setTab(tab)}> + +
{Tab1}
+
+ Metadata} + > +
{MetadataTab}
+
) : (
{Tab1}
diff --git a/frontend/web/components/pages/AuditLogItemPage.tsx b/frontend/web/components/pages/AuditLogItemPage.tsx index 701dd4e02302..9d9334eb3cf8 100644 --- a/frontend/web/components/pages/AuditLogItemPage.tsx +++ b/frontend/web/components/pages/AuditLogItemPage.tsx @@ -12,8 +12,8 @@ import DiffString from 'components/diff/DiffString' import DiffEnabled from 'components/diff/DiffEnabled' import Format from 'common/utils/format' import { Environment } from 'common/types/responses' -import { Link } from 'react-router-dom'; -import Button from 'components/base/forms/Button'; +import { Link } from 'react-router-dom' +import Button from 'components/base/forms/Button' type AuditLogItemPageType = { match: { params: { @@ -122,7 +122,7 @@ const AuditLogItemPage: FC = ({ match }) => { > - )} + )} )}
diff --git a/frontend/web/components/pages/CreateEnvironmentPage.js b/frontend/web/components/pages/CreateEnvironmentPage.js index 9ed8d36e0a85..f61a6c657cab 100644 --- a/frontend/web/components/pages/CreateEnvironmentPage.js +++ b/frontend/web/components/pages/CreateEnvironmentPage.js @@ -5,13 +5,19 @@ import Constants from 'common/constants' import ErrorMessage from 'components/ErrorMessage' import PageTitle from 'components/PageTitle' import CondensedRow from 'components/CondensedRow' +import AddMetadataToEntity from 'components/metadata/AddMetadataToEntity' +import { getSupportedContentType } from 'common/services/useSupportedContentType' +import { getStore } from 'common/store' const CreateEnvironmentPage = class extends Component { static displayName = 'CreateEnvironmentPage' constructor(props, context) { super(props, context) - this.state = {} + this.state = { + envContentType: {}, + metadata: [], + } } static contextTypes = { @@ -27,6 +33,19 @@ const CreateEnvironmentPage = class extends Component { componentDidMount = () => { API.trackPage(Constants.pages.CREATE_ENVIRONMENT) + if (Utils.getFlagsmithHasFeature('enable_metadata')) { + getSupportedContentType(getStore(), { + organisation_id: AccountStore.getOrganisation().id, + }).then((res) => { + const envContentType = Utils.getContentType( + res.data, + 'model', + 'environment', + ) + this.setState({ envContentType: envContentType }) + }) + } + this.focusTimeout = setTimeout(() => { this.input.focus() this.focusTimeout = null @@ -40,7 +59,7 @@ const CreateEnvironmentPage = class extends Component { } render() { - const { name } = this.state + const { envContentType, metadata, name } = this.state return (
@@ -84,6 +103,7 @@ const CreateEnvironmentPage = class extends Component { this.state.selectedEnv && this.state.selectedEnv.api_key, this.state.description, + metadata, ) }} > @@ -176,6 +196,42 @@ const CreateEnvironmentPage = class extends Component { )}
+ {Utils.getFlagsmithHasFeature('enable_metadata') && + envContentType?.id && ( + + + { + this.setState({ + metadata: m, + }) + }} + /> + } + /> + + + )}
diff --git a/frontend/web/components/pages/EnvironmentSettingsPage.js b/frontend/web/components/pages/EnvironmentSettingsPage.js index a22edd351e27..b7b009444bf9 100644 --- a/frontend/web/components/pages/EnvironmentSettingsPage.js +++ b/frontend/web/components/pages/EnvironmentSettingsPage.js @@ -7,7 +7,6 @@ import CreateWebhookModal from 'components/modals/CreateWebhook' import ConfirmRemoveWebhook from 'components/modals/ConfirmRemoveWebhook' import ConfirmToggleEnvFeature from 'components/modals/ConfirmToggleEnvFeature' import EditPermissions from 'components/EditPermissions' -import ServerSideSDKKeys from 'components/ServerSideSDKKeys' import Tabs from 'components/base/forms/Tabs' import TabItem from 'components/base/forms/TabItem' import JSONReference from 'components/JSONReference' @@ -897,7 +896,9 @@ const EnvironmentSettingsPage = class extends Component { = ({ match, router }) => { isLoading: versionsLoading, } = useGetFeatureVersionsQuery( { - is_live: true, environmentId, featureId: featureId as any, + is_live: true, }, { skip: !featureId, diff --git a/frontend/web/components/pages/HomePage.js b/frontend/web/components/pages/HomePage.js index 7a3f96f3ce2d..bb32be331c91 100644 --- a/frontend/web/components/pages/HomePage.js +++ b/frontend/web/components/pages/HomePage.js @@ -366,8 +366,8 @@ const HomePage = class extends React.Component {