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 improvements #4327

Merged
merged 10 commits into from
Jul 30, 2024
4 changes: 4 additions & 0 deletions frontend/common/stores/feature-list-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ const controller = {
})
.then((res) => {
// onComplete calls back preserving the order of multivariate_options with their updated ids
if (res.error) {
store.saved({ error: res.error })
return
}
if (onComplete) {
onComplete(res)
}
Expand Down
33 changes: 24 additions & 9 deletions frontend/common/stores/project-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,30 @@ const controller = {

editEnv: (env) => {
API.trackEvent(Constants.events.EDIT_ENVIRONMENT)
data.put(`${Project.api}environments/${env.api_key}/`, env).then((res) => {
const index = _.findIndex(store.model.environments, { id: env.id })
store.model.environments[index] = res
store.saved()
getStore().dispatch(
environmentService.util.invalidateTags(['Environment']),
)
AppActions.refreshOrganisation()
})
data
.put(`${Project.api}environments/${env.api_key}/`, env)
.then((res) => {
const index = _.findIndex(store.model.environments, { id: env.id })
store.model.environments[index] = res
store.saved()
getStore().dispatch(
environmentService.util.invalidateTags(['Environment']),
)
AppActions.refreshOrganisation()
})
.catch((e) => {
e.json()
.then((result) => {
if (result?.metadata?.[0]) {
toast(result.metadata[0], 'danger')
} else {
toast('Error updating the environment', 'danger')
}
})
.catch((e) => {
API.ajaxHandler(store, e)
})
})
},
editProject: (project) => {
store.saving()
Expand Down
16 changes: 6 additions & 10 deletions frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ const Utils = Object.assign({}, require('./base/_utils'), {
valid = isEnterprise
break
}
case 'METADATA': {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: this affects #4281

valid = isEnterprise
break
}
default:
valid = true
break
Expand Down Expand Up @@ -536,16 +540,8 @@ const Utils = Object.assign({}, require('./base/_utils'), {
return /^-?\d*\.?\d+$/.test(`${value}`)
},
isValidURL(value: any) {
const pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$',
'i',
)
return !!pattern.test(value)
const regex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i
return regex.test(value)
},
loadScriptPromise(url: string) {
return new Promise((resolve) => {
Expand Down
8 changes: 5 additions & 3 deletions frontend/web/components/base/forms/InputGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const InputGroup = class extends Component {
const { inputProps, size } = this.props
return (
<div
className={`${
this.props.className ? this.props.className : ''
} form-group ${this.props.isInvalid ? 'invalid' : ''}`}
className={`${this.props.className ? this.props.className : ''} ${
this.props.noMargin ? '' : 'form-group'
} ${this.props.isInvalid ? 'invalid' : ''}`}
>
{this.props.tooltip ? (
<Tooltip
Expand Down Expand Up @@ -83,6 +83,7 @@ const InputGroup = class extends Component {
type={props.type || 'text'}
id={id}
placeholder={props.placeholder}
onBlur={props.onBlur}
/>
) : (
<Input
Expand All @@ -96,6 +97,7 @@ const InputGroup = class extends Component {
onChange={props.onChange}
type={props.type || 'text'}
id={id}
onBlur={props.onBlur}
placeholder={props.placeholder}
size={size}
/>
Expand Down
55 changes: 25 additions & 30 deletions frontend/web/components/metadata/AddMetadataToEntity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import {
useUpdateEnvironmentMutation,
} from 'common/services/useEnvironment'
import { MetadataField, Metadata } from 'common/types/responses'
import Input from 'components/base/forms/Input'
import Utils from 'common/utils/utils'
import { useGetProjectFlagQuery } from 'common/services/useProjectFlag'
import Tooltip from 'components/Tooltip'
import { sortBy } from 'lodash'
import Switch from 'components/Switch'
import InputGroup from 'components/base/forms/InputGroup'

export type CustomMetadataField = MetadataField & {
metadataModelFieldId: number | string | null
Expand Down Expand Up @@ -247,8 +246,8 @@ const AddMetadataToEntity: FC<AddMetadataToEntityType> = ({
}}
renderNoResults={
<FormGroup>
No custom fields configured for {entity} entity. Add custom fields in
your{' '}
No custom fields configured for {entity} entity. Add custom fields
in your{' '}
<a
href={`/project/${projectId}/settings?tab=metadata`}
target='_blank'
Expand Down Expand Up @@ -334,32 +333,7 @@ const MetadataRow: FC<MetadataRowType> = ({
<Flex className='table-column'>{`${metadata?.name} ${
metadata?.isRequiredFor ? '*' : ''
}`}</Flex>
{metadata?.type !== 'bool' ? (
<Flex className='flex-row' style={{ minWidth: '300px' }}>
<Tooltip
title={
<Input
value={metadataValue}
onBlur={saveMetadata}
onChange={(e: InputEvent) => {
setMetadataValue(Utils.safeParseEventValue(e))
setMetadataValueChanged(true)
}}
className='mr-2'
style={{ width: '250px' }}
placeholder='Field Value'
isValid={Utils.validateMetadataType(
metadata?.type,
metadataValue,
)}
/>
}
place='top'
>
{`This value has to be of type ${metadata?.type}`}
</Tooltip>
</Flex>
) : (
{metadata?.type === 'bool' ? (
<Flex className='flex-row'>
<Switch
checked={!!metadataValue}
Expand All @@ -370,6 +344,27 @@ const MetadataRow: FC<MetadataRowType> = ({
}}
/>
</Flex>
) : (
<Flex className='flex-row mt-1' style={{ minWidth: '300px' }}>
<InputGroup
textarea={metadata?.type === 'multiline_str'}
onBlur={saveMetadata}
value={metadataValue}
inputProps={{
style: {
height: metadata?.type === 'multiline_str' ? '65px' : '44px',
width: '250px',
},
}}
noMargin
isValid={Utils.validateMetadataType(metadata?.type, metadataValue)}
onChange={(e: InputEvent) => {
setMetadataValue(Utils.safeParseEventValue(e))
setMetadataValueChanged(true)
}}
type='text'
/>
</Flex>
)}
</Row>
)
Expand Down
73 changes: 40 additions & 33 deletions frontend/web/components/modals/CreateFlag.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import { getSupportedContentType } from 'common/services/useSupportedContentType
import { getGithubIntegration } from 'common/services/useGithubIntegration'
import { removeUserOverride } from 'components/RemoveUserOverride'
import ExternalResourcesLinkTab from 'components/ExternalResourcesLinkTab'
import MetadataTitle from 'components/metadata/MetadataTitle'
import { saveFeatureWithValidation } from 'components/saveFeatureWithValidation'

const CreateFlag = class extends Component {
Expand Down Expand Up @@ -182,7 +181,10 @@ const CreateFlag = class extends Component {
) {
this.getFeatureUsage()
}
if (Utils.getFlagsmithHasFeature('enable_metadata')) {
if (
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')
) {
getSupportedContentType(getStore(), {
organisation_id: AccountStore.getOrganisation().id,
}).then((res) => {
Expand Down Expand Up @@ -550,7 +552,9 @@ const CreateFlag = class extends Component {
const hideIdentityOverridesTab = Utils.getShouldHideIdentityOverridesTab()
const noPermissions = this.props.noPermissions
let regexValid = true
const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata')
const metadataEnable =
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')
try {
if (!isEdit && name && regex) {
regexValid = name.match(new RegExp(regex))
Expand Down Expand Up @@ -580,32 +584,24 @@ const CreateFlag = class extends Component {
)}
{metadataEnable && featureContentType?.id && (
<>
<MetadataTitle
visible={this.state.visible}
onVisibleChange={(v) => {
this.setState({ visible: v })
<label className='mt-1'>Custom Fields</label>
<AddMetadataToEntity
organisationId={AccountStore.getOrganisation().id}
projectId={this.props.projectId}
entityId={projectFlag?.id}
entityContentType={featureContentType?.id}
entity={featureContentType?.model}
setHasMetadataRequired={(b) => {
this.setState({
hasMetadataRequired: b,
})
}}
onChange={(m) => {
this.setState({
metadata: m,
})
}}
hasRequiredMetadata={this.state.hasMetadataRequired}
/>
{(this.state.hasMetadataRequired || this.state.visible) && (
<AddMetadataToEntity
organisationId={AccountStore.getOrganisation().id}
projectId={this.props.projectId}
entityId={projectFlag?.id}
entityContentType={featureContentType?.id}
entity={featureContentType?.model}
setHasMetadataRequired={(b) => {
this.setState({
hasMetadataRequired: b,
})
}}
onChange={(m) => {
this.setState({
metadata: m,
})
}}
/>
)}
</>
)}
{!identity && projectFlag && (
Expand Down Expand Up @@ -1961,12 +1957,23 @@ const FeatureProvider = (WrappedComponent) => {
this.listenTo(
FeatureListStore,
'saved',
({ changeRequest, createdFlag, isCreate } = {}) => {
toast(
`${createdFlag || isCreate ? 'Created' : 'Updated'} ${
changeRequest ? 'Change Request' : 'Feature'
}`,
)
({ changeRequest, createdFlag, error, isCreate } = {}) => {
if (error?.data?.metadata) {
Copy link
Member

@kyle-ssg kyle-ssg Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error could be defined but not error?.data?.metadata. With this code any other error will show a success message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

error.data.metadata?.forEach((m) => {
if (Object.keys(m).length > 0) {
toast(m.non_field_errors[0], 'danger')
}
})
} else if (error?.data) {
toast('Error updating the Flag', 'danger')
return
} else {
toast(
`${createdFlag || isCreate ? 'Created' : 'Updated'} ${
changeRequest ? 'Change Request' : 'Feature'
}`,
)
}
const envFlags = FeatureListStore.getEnvironmentFlags()

if (createdFlag) {
Expand Down
8 changes: 6 additions & 2 deletions frontend/web/components/modals/CreateSegment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ const CreateSegment: FC<CreateSegmentType> = ({
const [metadata, setMetadata] = useState<CustomMetadataField[]>(
segment.metadata,
)
const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata')
const metadataEnable =
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')

const error = createError || updateError
const totalSegments = ProjectStore.getTotalSegments() ?? 0
Expand Down Expand Up @@ -763,7 +765,9 @@ const CreateSegment: FC<CreateSegmentType> = ({
</TabItem>
<TabItem
tabLabelString='Custom Fields'
tabLabel={<Row className='justify-content-center'>Custom Fields</Row>}
tabLabel={
<Row className='justify-content-center'>Custom Fields</Row>
}
>
<div className={className || 'my-3 mx-4'}>{MetadataTab}</div>
</TabItem>
Expand Down
8 changes: 6 additions & 2 deletions frontend/web/components/pages/CreateEnvironmentPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ const CreateEnvironmentPage = class extends Component {
componentDidMount = () => {
API.trackPage(Constants.pages.CREATE_ENVIRONMENT)

if (Utils.getFlagsmithHasFeature('enable_metadata')) {
if (
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')
) {
getSupportedContentType(getStore(), {
organisation_id: AccountStore.getOrganisation().id,
}).then((res) => {
Expand Down Expand Up @@ -196,7 +199,8 @@ const CreateEnvironmentPage = class extends Component {
</CondensedRow>
)}
</div>
{Utils.getFlagsmithHasFeature('enable_metadata') &&
{Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata') &&
envContentType?.id && (
<CondensedRow>
<FormGroup className='mt-5 setting'>
Expand Down
9 changes: 7 additions & 2 deletions frontend/web/components/pages/EnvironmentSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ const EnvironmentSettingsPage = class extends Component {
})
})

if (Utils.getFlagsmithHasFeature('enable_metadata')) {
if (
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')
) {
getSupportedContentType(getStore(), {
organisation_id: AccountStore.getOrganisation().id,
}).then((res) => {
Expand Down Expand Up @@ -260,7 +263,9 @@ const EnvironmentSettingsPage = class extends Component {
},
} = this
const has4EyesPermission = Utils.getPlansPermission('4_EYES')
const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata')
const metadataEnable =
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')

return (
<div className='app-container container'>
Expand Down
4 changes: 3 additions & 1 deletion frontend/web/components/pages/ProjectSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ const ProjectSettingsPage = class extends Component {
const { name, stale_flags_limit_days } = this.state
const hasStaleFlagsPermission = Utils.getPlansPermission('STALE_FLAGS')

const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata')
const metadataEnable =
Utils.getPlansPermission('METADATA') &&
Utils.getFlagsmithHasFeature('enable_metadata')

return (
<div className='app-container container'>
Expand Down
2 changes: 1 addition & 1 deletion frontend/web/components/pages/SegmentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import PageTitle from 'components/PageTitle'
import Switch from 'components/Switch'
import { setModalTitle } from 'components/modals/base/ModalDefault'
import classNames from 'classnames'
import InfoMessage from 'components/InfoMessage';
import InfoMessage from 'components/InfoMessage'

const CodeHelp = require('../../components/CodeHelp')
type SegmentsPageType = {
Expand Down
Loading