Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Metadata UI issues #4069

Merged
merged 12 commits into from
Jun 20, 2024
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
Loading