diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 12ecb6822af1..d25aa0ce6288 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -29,9 +29,8 @@ module.exports = { 'CodeHelp': true, 'Column': true, 'Cookies': true, - 'Dispatcher': true, 'DYNATRACE_URL': true, - 'dtrum': true, + 'Dispatcher': true, 'E2E': true, 'ES6Component': true, 'FB': true, @@ -52,6 +51,7 @@ module.exports = { 'OptionalNumber': true, 'OptionalObject': true, 'OptionalString': true, + 'dtrum': true, 'OrganisationProvider': true, 'OrganisationSelect': true, 'Paging': true, diff --git a/frontend/common/services/useIdentity.ts b/frontend/common/services/useIdentity.ts index 20cbb05e1497..d80cd2ae1b5d 100644 --- a/frontend/common/services/useIdentity.ts +++ b/frontend/common/services/useIdentity.ts @@ -58,12 +58,13 @@ export const identityService = service page_size = 10, pageType, pages, + q, search, } = baseQuery let url = `${getIdentityEndpoint( environmentId, isEdge, - )}/?q=${encodeURIComponent(search || '')}&page_size=${page_size}` + )}/?q=${encodeURIComponent(search || q || '')}&page_size=${page_size}` let last_evaluated_key = null if (!isEdge) { url += `&page=${page}` diff --git a/frontend/common/services/useIdentityFeatureState.ts b/frontend/common/services/useIdentityFeatureState.ts new file mode 100644 index 000000000000..da795094d45c --- /dev/null +++ b/frontend/common/services/useIdentityFeatureState.ts @@ -0,0 +1,54 @@ +import { Res } from 'common/types/responses' +import { Req } from 'common/types/requests' +import { service } from 'common/service' +import Utils from 'common/utils/utils' + +export const identityFeatureStateService = service + .enhanceEndpoints({ addTagTypes: ['IdentityFeatureState'] }) + .injectEndpoints({ + endpoints: (builder) => ({ + getIdentityFeatureStates: builder.query< + Res['identityFeatureStates'], + Req['getIdentityFeatureStates'] + >({ + providesTags: (res, _, req) => [ + { id: req.user, type: 'IdentityFeatureState' }, + ], + query: (query: Req['getIdentityFeatureStates']) => ({ + url: `environments/${ + query.environment + }/${Utils.getIdentitiesEndpoint()}/${ + query.user + }/${Utils.getFeatureStatesEndpoint()}/all/`, + }), + }), + // END OF ENDPOINTS + }), + }) + +export async function getIdentityFeatureStates( + store: any, + data: Req['getIdentityFeatureStates'], + options?: Parameters< + typeof identityFeatureStateService.endpoints.getIdentityFeatureStates.initiate + >[1], +) { + return store.dispatch( + identityFeatureStateService.endpoints.getIdentityFeatureStates.initiate( + data, + options, + ), + ) +} +// END OF FUNCTION_EXPORTS + +export const { + useGetIdentityFeatureStatesQuery, + // END OF EXPORTS +} = identityFeatureStateService + +/* Usage examples: +const { data, isLoading } = useGetIdentityFeatureStatesQuery({ id: 2 }, {}) //get hook +const [createIdentityFeatureStates, { isLoading, data, isSuccess }] = useCreateIdentityFeatureStatesMutation() //create hook +identityFeatureStateService.endpoints.getIdentityFeatureStates.select({id: 2})(store.getState()) //access data from any function +*/ diff --git a/frontend/common/services/useProjectFlag.ts b/frontend/common/services/useProjectFlag.ts new file mode 100644 index 000000000000..bcae22887c05 --- /dev/null +++ b/frontend/common/services/useProjectFlag.ts @@ -0,0 +1,76 @@ +import { PagedResponse, ProjectFlag, Res } from 'common/types/responses' +import { Req } from 'common/types/requests' +import { service } from 'common/service' +import data from 'common/data/base/_data' +import { BaseQueryFn } from '@reduxjs/toolkit/query' + +function recursivePageGet( + url: string, + parentRes: null | PagedResponse, + baseQuery: (arg: unknown) => any, // matches rtk types, +) { + return baseQuery({ + method: 'GET', + url, + }).then((res: Res['projectFlags']) => { + let response + if (parentRes) { + response = { + ...parentRes, + results: parentRes.results.concat(res.results), + } + } else { + response = res + } + if (res.next) { + return recursivePageGet(res.next, response, baseQuery) + } + return Promise.resolve(response) + }) +} +export const projectFlagService = service + .enhanceEndpoints({ addTagTypes: ['ProjectFlag'] }) + .injectEndpoints({ + endpoints: (builder) => ({ + getProjectFlags: builder.query< + Res['projectFlags'], + Req['getProjectFlags'] + >({ + providesTags: (res, _, req) => [ + { id: req?.project, type: 'ProjectFlag' }, + ], + queryFn: async (args, _, _2, baseQuery) => { + return await recursivePageGet( + `projects/${args.project}/features/?page_size=999`, + null, + baseQuery, + ) + }, + }), + // END OF ENDPOINTS + }), + }) + +export async function getProjectFlags( + store: any, + data: Req['getProjectFlags'], + options?: Parameters< + typeof projectFlagService.endpoints.getProjectFlags.initiate + >[1], +) { + return store.dispatch( + projectFlagService.endpoints.getProjectFlags.initiate(data, options), + ) +} +// END OF FUNCTION_EXPORTS + +export const { + useGetProjectFlagsQuery, + // END OF EXPORTS +} = projectFlagService + +/* Usage examples: +const { data, isLoading } = useGetProjectFlagsQuery({ id: 2 }, {}) //get hook +const [createProjectFlags, { isLoading, data, isSuccess }] = useCreateProjectFlagsMutation() //create hook +projectFlagService.endpoints.getProjectFlags.select({id: 2})(store.getState()) //access data from any function +*/ diff --git a/frontend/common/stores/feature-list-store.js b/frontend/common/stores/feature-list-store.js index 630bd86da44f..7ca5f05c3240 100644 --- a/frontend/common/stores/feature-list-store.js +++ b/frontend/common/stores/feature-list-store.js @@ -214,9 +214,9 @@ const controller = { return createSegmentOverride( getStore(), { + enabled: !!v.enabled, environmentId, featureId: featureFlagId, - enabled: !!v.enabled, feature_segment: { segment: v.segment, }, @@ -235,10 +235,6 @@ const controller = { const newValue = { environment: segmentOverride.data.environment, feature: featureFlagId, - id: segmentOverride.data.feature_segment.id, - priority: segmentOverride.data.feature_segment.priority, - segment: segmentOverride.data.feature_segment.segment, - uuid: segmentOverride.data.feature_segment.uuid, feature_segment_value: { change_request: segmentOverride.data.change_request, created_at: segmentOverride.data.created_at, @@ -253,8 +249,12 @@ const controller = { updated_at: segmentOverride.data.updated_at, uuid: segmentOverride.data.uuid, }, + id: segmentOverride.data.feature_segment.id, multivariate_options: segmentOverrides[i].multivariate_options, + priority: segmentOverride.data.feature_segment.priority, + segment: segmentOverride.data.feature_segment.segment, segment_name: v.segment_name, + uuid: segmentOverride.data.feature_segment.uuid, value: segmentOverrides[i].value, } segmentOverrides[i] = newValue diff --git a/frontend/common/types/requests.ts b/frontend/common/types/requests.ts index 06350c1db73d..c7a6d2d98bb0 100644 --- a/frontend/common/types/requests.ts +++ b/frontend/common/types/requests.ts @@ -3,6 +3,7 @@ import { Account, Segment, Tag, FeatureStateValue } from './responses' export type PagedRequest = T & { page?: number page_size?: number + q?: string } export type OAuthType = 'github' | 'saml' | 'google' export type PermissionLevel = 'organisation' | 'project' | 'environment' @@ -99,5 +100,10 @@ export type Req = { feature_segment: featureSegment feature_state_value: FeatureStateValue } + getIdentityFeatureStates: { + environment: string + user: string + } + getProjectFlags: { project: string } // END OF TYPES } diff --git a/frontend/common/types/responses.ts b/frontend/common/types/responses.ts index 02efcfa45b16..8ad3aacf5e70 100644 --- a/frontend/common/types/responses.ts +++ b/frontend/common/types/responses.ts @@ -1,6 +1,4 @@ // eslint-disable-next-line @typescript-eslint/no-empty-interface -import { type } from 'common/data/base/_data' -import UserGroupList from 'components/UserGroupList' export type EdgePagedResponse = PagedResponse & { last_evaluated_key?: string @@ -188,6 +186,25 @@ export type MultivariateOption = { default_percentage_allocation: number } +export type FeatureType = 'STANDARD' | 'MULTIVARIATE' + +export type IdentityFeatureState = { + feature: { + id: number + name: string + type: FeatureType + } + enabled: boolean + feature_state_value: FlagsmithValue + segment: null + multivariate_feature_state_values?: { + multivariate_feature_option: { + value: number + } + percentage_allocation: number + }[] +} + export type FeatureState = { id: number feature_state_value: string @@ -316,5 +333,8 @@ export type Res = { } value: string } + + projectFlags: PagedResponse + identityFeatureStates: IdentityFeatureState[] // END OF TYPES } diff --git a/frontend/web/components/AlertBar.js b/frontend/web/components/AlertBar.js index 8afaa0a8c37f..9e69aca3a8a6 100644 --- a/frontend/web/components/AlertBar.js +++ b/frontend/web/components/AlertBar.js @@ -1,5 +1,5 @@ import React from 'react' -import ModalClose from './modals/base/ModalClose'; +import ModalClose from './modals/base/ModalClose' const AlertBar = class extends React.Component { state = {} diff --git a/frontend/web/components/CompareEnvironments.js b/frontend/web/components/CompareEnvironments.js index b79acb225e3b..8e7f7d8787e1 100644 --- a/frontend/web/components/CompareEnvironments.js +++ b/frontend/web/components/CompareEnvironments.js @@ -8,6 +8,8 @@ import FeatureListStore from 'common/stores/feature-list-store' import ConfigProvider from 'common/providers/ConfigProvider' import Permission from 'common/providers/Permission' import Tag from './tags/Tag' +import { getProjectFlags } from 'common/services/useProjectFlag' +import { getStore } from 'common/store' const featureNameWidth = 300 @@ -45,8 +47,8 @@ class CompareEnvironments extends Component { return Promise.all([ this.state.projectFlags ? Promise.resolve({ results: this.state.projectFlags }) - : data.get( - `${Project.api}projects/${this.props.projectId}/features/?page_size=999`, + : getProjectFlags(getStore(), { project: this.props.projectId }).then( + (res) => res.data, ), data.get( `${Project.api}environments/${this.state.environmentLeft}/featurestates/?page_size=999`, @@ -122,7 +124,12 @@ class CompareEnvironments extends Component {
this.setState({ environmentLeft }) } @@ -136,7 +143,12 @@ class CompareEnvironments extends Component {
this.setState({ environmentRight }) } diff --git a/frontend/web/components/CompareIdentities.tsx b/frontend/web/components/CompareIdentities.tsx new file mode 100644 index 000000000000..e8f174cbd619 --- /dev/null +++ b/frontend/web/components/CompareIdentities.tsx @@ -0,0 +1,289 @@ +import React, { FC, useEffect, useMemo, useState } from 'react' +import IdentitySelect, { IdentitySelectType } from './IdentitySelect' +import Utils from 'common/utils/utils' +import EnvironmentSelect from './EnvironmentSelect' +import { useGetIdentityFeatureStatesQuery } from 'common/services/useIdentityFeatureState' +import { useGetProjectFlagsQuery } from 'common/services/useProjectFlag' +import Tag from './tags/Tag' +import PanelSearch from './PanelSearch' +import { ProjectFlag, Res } from 'common/types/responses' +import Icon from './Icon' +import Switch from './Switch' +import FeatureValue from './FeatureValue' +import { sortBy } from 'lodash' +import { useHasPermission } from 'common/providers/Permission' +import Constants from 'common/constants' + +type CompareIdentitiesType = { + projectId: string + environmentId: string +} +const selectWidth = 300 +const featureNameWidth = 300 + +const calculateFeatureDifference = ( + projectFlagId: number, + leftUser: Res['identityFeatureStates'] | undefined, + rightUser: Res['identityFeatureStates'] | undefined, +) => { + const featureStateLeft = leftUser?.find((v) => v.feature.id === projectFlagId) + const featureStateRight = rightUser?.find( + (v) => v.feature.id === projectFlagId, + ) + const enabledDifferent = + featureStateLeft?.enabled !== featureStateRight?.enabled + const valueDifferent = + featureStateLeft?.feature_state_value !== + featureStateRight?.feature_state_value + + return { + enabledDifferent, + featureStateLeft, + featureStateRight, + valueDifferent, + } +} +const CompareIdentities: FC = ({ + environmentId: _environmentId, + projectId, +}) => { + const [leftId, setLeftId] = useState() + const [rightId, setRightId] = useState() + const { data: projectFlags } = useGetProjectFlagsQuery({ project: projectId }) + const [environmentId, setEnvironmentId] = useState(_environmentId) + const [showArchived, setShowArchived] = useState(false) + + const { isLoading: permissionLoading, permission } = useHasPermission({ + id: environmentId, + level: 'environment', + permission: Utils.getViewIdentitiesPermission(), + }) + + const { data: leftUser } = useGetIdentityFeatureStatesQuery( + { environment: environmentId, user: `${leftId?.value}` }, + { skip: !leftId }, + ) + const { data: rightUser } = useGetIdentityFeatureStatesQuery( + { environment: environmentId, user: `${rightId?.value}` }, + { skip: !rightId }, + ) + + useEffect(() => { + // Clear users whenever environment or project is changed + setLeftId(null) + setRightId(null) + }, [environmentId, projectId]) + + const filteredItems = useMemo(() => { + if (!projectFlags?.results) return projectFlags?.results + + return sortBy( + projectFlags.results.filter((v) => { + if (showArchived) { + return true + } + return !v.is_archived + }), + (projectFlag) => { + const { id, name } = projectFlag + const { enabledDifferent, valueDifferent } = calculateFeatureDifference( + id, + leftUser, + rightUser, + ) + const isDifferent = enabledDifferent || valueDifferent ? 0 : 1 + return `${isDifferent}${name}` + }, + ) + }, [projectFlags, showArchived, leftUser, rightUser]) + + const isReady = + !!leftId && !!rightId && !!leftUser && !!rightUser && !!projectFlags + + const isEdge = Utils.getIsEdge() + + const goUser = (user: IdentitySelectType['value'], feature: string) => { + window.open( + `${ + document.location.origin + }/project/${projectId}/environment/${environmentId}/users/${encodeURIComponent( + user!.label, + )}/${user!.value}?flag=${encodeURIComponent(feature)}`, + '_blank', + ) + } + + return ( +
+

Compare Identities

+

Compare feature states between 2 identities

+
+ { + setRightId(null) + setLeftId(null) + setEnvironmentId(v) + }} + /> +
+ {!permission && !permissionLoading ? ( +
+ ) : ( + +
+ +
+
+ +
+
+ +
+
+ )} + + {isReady && ( + <> + + { + setShowArchived(!showArchived) + }} + className='px-2 py-2 ml-2 mr-2' + tag={{ color: '#0AADDF', label: 'Archived' }} + /> + + } + items={filteredItems} + renderRow={(data: ProjectFlag) => { + const { description, id, name } = data + const { + enabledDifferent, + featureStateLeft, + featureStateRight, + valueDifferent, + } = calculateFeatureDifference(id, leftUser, rightUser) + const goUserLeft = () => { + goUser(leftId, data.name) + } + const goUserRight = () => { + goUser(leftId, data.name) + } + + return ( + +
+ + {description ? ( + + {name} + + + + } + > + {description} + + ) : ( + name + )} + +
+
+ +
+ + {featureStateLeft && ( + + )} + +
+ +
+ + {featureStateRight && ( + + )} + +
+ ) + }} + header={ + +
+ Name +
+ +
+ {leftId?.label} +
+ +
+ +
+ {rightId?.label} +
+ +
+
+ } + /> + + )} +
+ ) +} + +export default CompareIdentities diff --git a/frontend/web/components/DateSelect.js b/frontend/web/components/DateSelect.js index a009b61e1d65..2aa3fe36e0f9 100644 --- a/frontend/web/components/DateSelect.js +++ b/frontend/web/components/DateSelect.js @@ -2,7 +2,7 @@ import DatePicker from 'react-datepicker' import Icon from './Icon' import { useState } from 'react' -const DateSelect = ({ onChange, selected, value,onSelect }) => { +const DateSelect = ({ onChange, onSelect, selected, value }) => { const [isMonthPicker, setMonthPicker] = useState(false) const [isYearPicker, setYearPicker] = useState(false) const [isOpen, setOpen] = useState(false) @@ -12,8 +12,8 @@ const DateSelect = ({ onChange, selected, value,onSelect }) => { renderCustomHeader={({ date, decreaseMonth, - increaseMonth, decreaseYear, + increaseMonth, increaseYear, }) => ( diff --git a/frontend/web/components/EditPermissions.tsx b/frontend/web/components/EditPermissions.tsx index ebc243ed9cc1..658bdb9bfe84 100644 --- a/frontend/web/components/EditPermissions.tsx +++ b/frontend/web/components/EditPermissions.tsx @@ -1,22 +1,28 @@ -import React, { FC, useEffect, useState } from 'react'; -import { find } from 'lodash'; -import _data from 'common/data/base/_data'; -import { AvailablePermission, GroupPermission, User, UserGroup, UserPermission } from 'common/types/responses'; -import Utils from 'common/utils/utils'; -import AccountStore from 'common/stores/account-store'; -import Format from 'common/utils/format'; -import PanelSearch from './PanelSearch'; -import Button from './base/forms/Button'; -import InfoMessage from './InfoMessage'; -import Switch from './Switch'; -import TabItem from './base/forms/TabItem'; -import Tabs from './base/forms/Tabs'; -import UserGroupList from './UserGroupList'; -import { PermissionLevel } from 'common/types/requests'; -import { RouterChildContext } from 'react-router'; -import { useGetAvailablePermissionsQuery } from 'common/services/useAvailablePermissions'; -import ConfigProvider from 'common/providers/ConfigProvider'; -import Icon from './Icon'; +import React, { FC, useEffect, useState } from 'react' +import { find } from 'lodash' +import _data from 'common/data/base/_data' +import { + AvailablePermission, + GroupPermission, + User, + UserGroup, + UserPermission, +} from 'common/types/responses' +import Utils from 'common/utils/utils' +import AccountStore from 'common/stores/account-store' +import Format from 'common/utils/format' +import PanelSearch from './PanelSearch' +import Button from './base/forms/Button' +import InfoMessage from './InfoMessage' +import Switch from './Switch' +import TabItem from './base/forms/TabItem' +import Tabs from './base/forms/Tabs' +import UserGroupList from './UserGroupList' +import { PermissionLevel } from 'common/types/requests' +import { RouterChildContext } from 'react-router' +import { useGetAvailablePermissionsQuery } from 'common/services/useAvailablePermissions' +import ConfigProvider from 'common/providers/ConfigProvider' +import Icon from './Icon' const OrganisationProvider = require('common/providers/OrganisationProvider') const Project = require('common/project') diff --git a/frontend/web/components/EnvironmentSelect.js b/frontend/web/components/EnvironmentSelect.js deleted file mode 100644 index fd6f72487fb5..000000000000 --- a/frontend/web/components/EnvironmentSelect.js +++ /dev/null @@ -1,51 +0,0 @@ -import React, { Component } from 'react' - -const EnvironmentSelect = class extends Component { - static displayName = 'EnvironmentSelect' - - constructor(props, context) { - super(props, context) - this.state = {} - } - render() { - return ( - - {({ project }) => { - const selectedEnv = - this.props.value && - _.find(project.environments, { api_key: this.props.value }) - if (this.props.readOnly) { - return
{selectedEnv && selectedEnv.name}
- } - return ( -
- + onChange(value?.value || '') + } + /> +
+ ) +} + +export default EnvironmentSelect diff --git a/frontend/web/components/FeatureRow.js b/frontend/web/components/FeatureRow.js index 9506d9ce3381..6c9a95174076 100644 --- a/frontend/web/components/FeatureRow.js +++ b/frontend/web/components/FeatureRow.js @@ -10,6 +10,7 @@ import { hasProtectedTag } from 'common/utils/hasProtectedTag' import SegmentsIcon from './svg/SegmentsIcon' import UsersIcon from './svg/UsersIcon' // we need this to make JSX compile import Icon from './Icon' +import FeatureValue from './FeatureValue' export const width = [190, 65, 48, 75] class TheComponent extends Component { diff --git a/frontend/web/components/FeatureValue.js b/frontend/web/components/FeatureValue.js deleted file mode 100644 index 21c0061cc036..000000000000 --- a/frontend/web/components/FeatureValue.js +++ /dev/null @@ -1,40 +0,0 @@ -import React, { Component } from 'react' -import Format from 'common/utils/format' - -const FeatureValue = class extends Component { - static displayName = 'FeatureValue' - - constructor(props, context) { - super(props, context) - this.state = {} - } - - render() { - if (this.props.value === null || this.props.value === undefined) { - return null - } - const type = typeof this.props.value - if ( - type === 'string' && - this.props.value === '' && - !this.props.includeEmpty - ) { - return null - } - return ( - - {type == 'string' && "} - {Format.truncateText(`${this.props.value}`, 16)} - {type == 'string' && "} - - ) - } -} - -FeatureValue.propTypes = {} - -module.exports = FeatureValue diff --git a/frontend/web/components/FeatureValue.tsx b/frontend/web/components/FeatureValue.tsx new file mode 100644 index 000000000000..7105d8e9647a --- /dev/null +++ b/frontend/web/components/FeatureValue.tsx @@ -0,0 +1,31 @@ +import { FC } from 'react' +import { FlagsmithValue } from 'common/types/responses' +import Format from 'common/utils/format' // we need this to make JSX compile + +type FeatureValueType = { + value: FlagsmithValue + includeEmpty?: boolean // whether to show empty values + className?: string + onClick?: () => void + 'data-test'?: string +} + +const FeatureValue: FC = (props) => { + const type = typeof props.value + + return ( + + {type == 'string' && "} + + {Format.truncateText(`${props.value}`, 20)} + + {type == 'string' && "} + + ) +} + +export default FeatureValue diff --git a/frontend/web/components/IdentitySelect.tsx b/frontend/web/components/IdentitySelect.tsx index b5d9b58b8ba4..b351730564ec 100644 --- a/frontend/web/components/IdentitySelect.tsx +++ b/frontend/web/components/IdentitySelect.tsx @@ -8,12 +8,13 @@ import { components } from 'react-select' import Utils from 'common/utils/utils' import Button from './base/forms/Button' -type IdentitySelectType = { - value: Identity['id'] | null | undefined +export type IdentitySelectType = { + value: { value: string; label: string } | null | undefined ignoreIds?: Identity['id'][] isEdge: boolean - onChange: (v: Identity) => void + onChange: (v: { value: string; label: string }) => void environmentId: string + placeholder?: string } const IdentitySelect: FC = ({ @@ -21,6 +22,7 @@ const IdentitySelect: FC = ({ ignoreIds, isEdge, onChange, + placeholder = 'Search Identities...', value, }) => { const { data, isLoading, loadMore, searchItems } = useInfiniteScroll< @@ -35,7 +37,8 @@ const IdentitySelect: FC = ({ return filter( data?.results, (identity) => - !ignoreIds?.length || !find(ignoreIds, (v) => v === identity.id), + !ignoreIds?.length || + !find(ignoreIds, (v) => `${v}` === `${identity.id}`), ) .map(({ id: value, identifier: label }) => ({ label, value })) .slice(0, 10) @@ -47,7 +50,7 @@ const IdentitySelect: FC = ({ searchItems(Utils.safeParseEventValue(e)) }} data-test='select-identity' - placeholder='Create an Identity Override...' + placeholder={placeholder} value={value} components={{ Menu: ({ ...props }: any) => { diff --git a/frontend/web/components/InlineModal.js b/frontend/web/components/InlineModal.js index bd1d8abddcba..a3acf51edfa7 100644 --- a/frontend/web/components/InlineModal.js +++ b/frontend/web/components/InlineModal.js @@ -11,13 +11,13 @@ class InlineModal extends PureComponent { static displayName = 'Popover' static propTypes = { + bottom: propTypes.node, children: propTypes.node, isOpen: propTypes.bool, onBack: propTypes.func, onClose: propTypes.func, showBack: propTypes.bool, title: propTypes.string, - bottom: propTypes.node, } handleClickOutside() { diff --git a/frontend/web/components/SegmentOverrides.js b/frontend/web/components/SegmentOverrides.js index 41ebed921fbc..7be5cd003a70 100644 --- a/frontend/web/components/SegmentOverrides.js +++ b/frontend/web/components/SegmentOverrides.js @@ -1,5 +1,5 @@ // import propTypes from 'prop-types'; -import React, { Component, Fragment } from 'react'; +import React, { Component, Fragment } from 'react' import { SortableContainer, SortableElement } from 'react-sortable-hoc' import ProjectStore from 'common/stores/project-store' import ValueEditor from './ValueEditor' diff --git a/frontend/web/components/ServerSideSDKKeys.js b/frontend/web/components/ServerSideSDKKeys.js index ce68d5a69962..8f99fd824544 100644 --- a/frontend/web/components/ServerSideSDKKeys.js +++ b/frontend/web/components/ServerSideSDKKeys.js @@ -3,7 +3,7 @@ import _data from 'common/data/base/_data' import ProjectStore from 'common/stores/project-store' import Token from './Token' import ModalHR from './modals/ModalHR' -import Icon from 'components/Icon' +import Icon from './Icon' class CreateServerSideKeyModal extends Component { state = {} diff --git a/frontend/web/components/base/forms/InputGroup.js b/frontend/web/components/base/forms/InputGroup.js index cec2357e65b7..8a59cec7f097 100644 --- a/frontend/web/components/base/forms/InputGroup.js +++ b/frontend/web/components/base/forms/InputGroup.js @@ -31,8 +31,7 @@ const InputGroup = class extends Component { title={ @@ -44,7 +43,7 @@ const InputGroup = class extends Component { ) : ( {!!props.title && ( - +
diff --git a/frontend/web/components/tags/CreateEditTag.tsx b/frontend/web/components/tags/CreateEditTag.tsx index 8475c302532e..00f2813d3dc6 100644 --- a/frontend/web/components/tags/CreateEditTag.tsx +++ b/frontend/web/components/tags/CreateEditTag.tsx @@ -24,12 +24,12 @@ type CreateEditTagType = { } const CreateEditTag: FC = ({ - onComplete: _onComplete, - projectId, - tag: _tag, isOpen, onBack, onClose, + onComplete: _onComplete, + projectId, + tag: _tag, title, }) => { const [tag, setTag] = useState | undefined>(_tag) @@ -100,7 +100,7 @@ const CreateEditTag: FC = ({ onClose={onClose} className='inline-modal--tags pb-0' bottom={ - + diff --git a/frontend/web/project/api.js b/frontend/web/project/api.js index 79fa2ab40cbc..748f257b0036 100644 --- a/frontend/web/project/api.js +++ b/frontend/web/project/api.js @@ -39,14 +39,14 @@ global.API = { } }) }, - alias(id, user={}) { + alias(id, user = {}) { if (id === Project.excludeAnalytics) return if (Project.mixpanel) { mixpanel.alias(id) } if (enableDynatrace && user?.id) { - dtrum.identifyUser( `${user.id}`) + dtrum.identifyUser(`${user.id}`) } if (Project.heap) { @@ -147,7 +147,7 @@ global.API = { } if (enableDynatrace && user?.id) { - dtrum.identifyUser( `${user.id}`) + dtrum.identifyUser(`${user.id}`) } if (Project.heap) { diff --git a/frontend/web/project/project-components.js b/frontend/web/project/project-components.js index a458301d4cab..43808b4db40b 100644 --- a/frontend/web/project/project-components.js +++ b/frontend/web/project/project-components.js @@ -40,7 +40,6 @@ window.Panel = require('../components/base/grid/Panel') window.FormGroup = require('../components/base/grid/FormGroup') window.PanelSearch = PanelSearch -window.FeatureValue = require('../components/FeatureValue') window.CodeHelp = require('../components/CodeHelp') // Useful for components used all the time within a project diff --git a/frontend/web/routes.js b/frontend/web/routes.js index 3d280172f17c..2c41effcfbf5 100644 --- a/frontend/web/routes.js +++ b/frontend/web/routes.js @@ -28,7 +28,7 @@ import ScheduledChangesPage from './components/pages/ScheduledChangesPage' import AuditLogPage from './components/pages/AuditLogPage' import CompareEnvironmentsPage from './components/pages/CompareEnvironmentsPage' import WidgetPage from './components/pages/WidgetPage' -import BrokenPage from './components/pages/BrokenPage'; +import BrokenPage from './components/pages/BrokenPage' export default (