Skip to content

Commit

Permalink
fix: Metadata UI issues (#4069)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthew Elwell <matthew.elwell@flagsmith.com>
  • Loading branch information
novakzaballa and matthewelwell authored Jun 20, 2024
1 parent b0bc87a commit 36c8bb3
Show file tree
Hide file tree
Showing 23 changed files with 335 additions and 166 deletions.
3 changes: 2 additions & 1 deletion frontend/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<strong>protected</strong>" will prevent them from accidentally being deleted.',
TOOLTIP_METADATA_DESCRIPTION: 'Add metadata in your',
TOOLTIP_METADATA_DESCRIPTION: (entity: string) =>
`Add Metadata in your <strong>${entity}</strong>, 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:
Expand Down
3 changes: 2 additions & 1 deletion frontend/common/dispatcher/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down
4 changes: 2 additions & 2 deletions frontend/common/providers/ProjectProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/common/services/useFeatureVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/common/stores/feature-list-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand Down
5 changes: 3 additions & 2 deletions frontend/common/stores/project-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
})
Expand Down Expand Up @@ -247,6 +247,7 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
action.projectId,
action.cloneId,
action.description,
action.metadata,
)
break
case Actions.EDIT_ENVIRONMENT:
Expand Down
16 changes: 8 additions & 8 deletions frontend/web/components/AuditLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const AuditLog: FC<AuditLogType> = (props) => {
search,
},
{
refetchOnMountOrArgChange: true
refetchOnMountOrArgChange: true,
},
)

Expand Down Expand Up @@ -128,13 +128,13 @@ const AuditLog: FC<AuditLogType> = (props) => {
style={{ width: widths[2] }}
to={`/project/${props.projectId}/environment/${environment?.api_key}/features/`}
>
<Tag
tag={{
color: Utils.getTagColour(colour),
label: environment?.name,
}}
className='chip--sm'
/>
<Tag
tag={{
color: Utils.getTagColour(colour),
label: environment?.name,
}}
className='chip--sm'
/>
</Link>
) : (
<div className='table-column' style={{ width: widths[2] }} />
Expand Down
9 changes: 8 additions & 1 deletion frontend/web/components/ErrorMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? (
<div
className={errorMessageClassName}
Expand Down
4 changes: 1 addition & 3 deletions frontend/web/components/FeatureVersion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import DiffFeature from './diff/DiffFeature'
import { useGetVersionFeatureStateQuery } from 'common/services/useVersionFeatureState'
import InfoMessage from './InfoMessage'
import moment from 'moment'
import {
useGetFeatureVersionQuery,
} from 'common/services/useFeatureVersion'
import { useGetFeatureVersionQuery } from 'common/services/useFeatureVersion'

type VersionDiffType = {
oldUUID: string
Expand Down
2 changes: 1 addition & 1 deletion frontend/web/components/IntegrationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ class IntegrationList extends Component {
}
githubMeta={{ githubId: githubId, installationId: installationId }}
projectId={this.props.projectId}
onComplete={githubId ? this.fetchGithubIntegration : this.fetch }
onComplete={githubId ? this.fetchGithubIntegration : this.fetch}
/>,
'side-modal',
)
Expand Down
49 changes: 36 additions & 13 deletions frontend/web/components/metadata/AddMetadataToEntity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,6 +43,7 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
entityContentType,
entityId,
envName,
isCloningEnvironment,
onChange,
organisationId,
projectId,
Expand Down Expand Up @@ -102,9 +104,6 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
}, [metadataFieldsAssociatedtoEntity])

const [metadataChanged, setMetadataChanged] = useState<boolean>(false)
useEffect(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [metadataFieldsAssociatedtoEntity])

const mergeMetadataEntityWithMetadataField = (
metadata: Metadata[], // Metadata array
Expand Down Expand Up @@ -160,9 +159,7 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
})
// 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,
Expand Down Expand Up @@ -215,7 +212,7 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
])
return (
<>
<FormGroup className='mt-4 setting'>
<FormGroup className='setting'>
<PanelSearch
className='mt-1 no-pad'
header={
Expand All @@ -229,6 +226,7 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
return (
<MetadataRow
metadata={m}
entity={entity}
getMetadataValue={(m: CustomMetadata) => {
setMetadataFieldsAssociatedtoEntity((prevState) =>
prevState?.map((metadata) => {
Expand All @@ -247,8 +245,22 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
/>
)
}}
renderNoResults={
<FormGroup>
No metadata configured for {entity} entity. Add metadata field in
your{' '}
<a
href={`/project/${projectId}/settings?tab=metadata`}
target='_blank'
rel='noreferrer'
>
Project Settings
</a>
.
</FormGroup>
}
/>
{entity === 'environment' && (
{entity === 'environment' && !isCloningEnvironment && (
<div className='text-right'>
<Button
theme='primary'
Expand All @@ -270,8 +282,12 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
project: parseInt(`${projectId}`),
},
id: entityId,
}).then(() => {
toast('Environment Metadata Updated')
}).then((res) => {
if (res?.error) {
toast(res?.error?.data.metadata[0], 'danger')
} else {
toast('Environment Metadata Updated')
}
})
}}
>
Expand All @@ -287,8 +303,13 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
type MetadataRowType = {
metadata: CustomMetadata
getMetadataValue?: (metadata: CustomMetadata) => void
entity: string
}
const MetadataRow: FC<MetadataRowType> = ({ getMetadataValue, metadata }) => {
const MetadataRow: FC<MetadataRowType> = ({
entity,
getMetadataValue,
metadata,
}) => {
const [metadataValue, setMetadataValue] = useState<string | boolean>(() => {
if (metadata?.type === 'bool') {
return metadata?.field_value === 'true' ? true : false
Expand All @@ -307,7 +328,9 @@ const MetadataRow: FC<MetadataRowType> = ({ getMetadataValue, metadata }) => {
useState<boolean>(false)
return (
<Row className='space list-item clickable py-2'>
{metadataValueChanged && <div className='unread ml-2 px-1'>{'*'}</div>}
{metadataValueChanged && entity !== 'segment' && (
<div className='unread ml-2 px-1'>{'*'}</div>
)}
<Flex className='table-column'>{`${metadata?.name} ${
metadata?.isRequiredFor ? '*' : ''
}`}</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const ContentTypesMetadataFieldTable: FC<
Entity
</Flex>
<div className='table-column text-center' style={{ width: '80px' }}>
Requeried
Required
</div>
<div className='table-column text-center' style={{ width: '80px' }}>
Remove
Expand Down
20 changes: 6 additions & 14 deletions frontend/web/components/metadata/MetadataPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ const MetadataPage: FC<MetadataPageType> = ({ organisationId, projectId }) => {
</div>
),
destructive: true,
onYes: () => deleteMetadata({ id }),
onYes: () =>
deleteMetadata({ id }).then(() => toast('Metadata Field Deleted')),
title: 'Delete Metadata Field',
yesText: 'Confirm',
})
Expand All @@ -105,18 +106,9 @@ const MetadataPage: FC<MetadataPageType> = ({ organisationId, projectId }) => {
return (
<div>
<Row space className='mb-2'>
<Tooltip
title={
<Row>
<h5 className='mt-2'>Metadata Fields</h5>
<div>
<Icon name='info-outlined' />
</div>
</Row>
}
>
{'Create or Update the Metadata Fields Project'}
</Tooltip>
<Row>
<h5 className='mt-2'>Metadata Fields</h5>
</Row>
<Button className='mt-2' onClick={() => createMetadataField()}>
{'Create Metadata Field'}
</Button>
Expand All @@ -126,7 +118,7 @@ const MetadataPage: FC<MetadataPageType> = ({ organisationId, projectId }) => {
<Button
theme='text'
target='_blank'
href='http://localhost:3000/system-administration/metadata/#metadata-fields'
href='https://docs.flagsmith.com/system-administration/metadata/'
className='fw-normal'
>
Learn more.
Expand Down
32 changes: 32 additions & 0 deletions frontend/web/components/metadata/MetadataTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Icon from 'components/Icon'
import React, { FC } from 'react'

type MetadataContainerType = {
hasRequiredMetadata: boolean
visible: boolean
onVisibleChange: (v: boolean) => void
}

const MetadataContainer: FC<MetadataContainerType> = ({
hasRequiredMetadata,
onVisibleChange,
visible,
}) => {
return (
<div
style={{ cursor: 'pointer' }}
onClick={() => {
onVisibleChange(!visible)
}}
>
<Row>
<label className='mt-1'>Metadata</label>
{!hasRequiredMetadata && (
<Icon name={visible ? 'chevron-down' : 'chevron-right'} />
)}
</Row>
</div>
)
}

export default MetadataContainer
Loading

0 comments on commit 36c8bb3

Please sign in to comment.