Skip to content

Commit 9182c41

Browse files
Zaimwa9tiagoapolo
andauthored
feat: frontend-report-environment-metrics (#5447)
Co-authored-by: Tiago Paiva <tiago@tiagopaiva.me>
1 parent 68f6847 commit 9182c41

File tree

19 files changed

+397
-21
lines changed

19 files changed

+397
-21
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useEffect, useState } from 'react'
2+
3+
export default function useMediaQuery(query: string): boolean {
4+
const [matches, setMatches] = useState(() =>
5+
typeof window !== 'undefined' ? window.matchMedia(query).matches : false,
6+
)
7+
8+
useEffect(() => {
9+
const mediaQuery = window.matchMedia(query)
10+
11+
const updateMatch = () => setMatches(mediaQuery.matches)
12+
mediaQuery.addEventListener('change', updateMatch)
13+
14+
updateMatch()
15+
16+
return () => mediaQuery.removeEventListener('change', updateMatch)
17+
}, [query])
18+
19+
return matches
20+
}

frontend/common/services/useEnvironment.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ export const environmentService = service
1212
url: `environments/${query.id}/`,
1313
}),
1414
}),
15+
getEnvironmentMetrics: builder.query<
16+
Res['environmentMetrics'],
17+
Req['getEnvironmentMetrics']
18+
>({
19+
providesTags: () => [{ id: 'METRICS', type: 'Environment' }],
20+
query: (query: Req['getEnvironmentMetrics']) => ({
21+
url: `environments/${query.id}/metrics/`,
22+
}),
23+
}),
1524
getEnvironments: builder.query<
1625
Res['environments'],
1726
Req['getEnvironments']
@@ -75,6 +84,7 @@ export async function updateEnvironment(
7584
// END OF FUNCTION_EXPORTS
7685

7786
export const {
87+
useGetEnvironmentMetricsQuery,
7888
useGetEnvironmentQuery,
7989
useGetEnvironmentsQuery,
8090
useUpdateEnvironmentMutation,

frontend/common/services/useFeatureVersion.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,17 @@ export const getFeatureStateCrud = (
103103
}
104104
}
105105
export const featureVersionService = service
106-
.enhanceEndpoints({ addTagTypes: ['FeatureVersion'] })
106+
.enhanceEndpoints({ addTagTypes: ['FeatureVersion', 'Environment'] })
107107
.injectEndpoints({
108108
endpoints: (builder) => ({
109109
createAndSetFeatureVersion: builder.mutation<
110110
Res['featureVersion'],
111111
Req['createAndSetFeatureVersion']
112112
>({
113-
invalidatesTags: [{ id: 'LIST', type: 'FeatureVersion' }],
113+
invalidatesTags: [
114+
{ id: 'LIST', type: 'FeatureVersion' },
115+
{ id: 'METRICS', type: 'Environment' },
116+
],
114117
queryFn: async (query: Req['createAndSetFeatureVersion']) => {
115118
// todo: this will be removed when we combine saving value and segment overrides
116119
const mode = query.featureStates.find(

frontend/common/services/useIdentity.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const getIdentityEndpoint = (environmentId: string, isEdge: boolean) => {
1010
}
1111

1212
export const identityService = service
13-
.enhanceEndpoints({ addTagTypes: ['Identity'] })
13+
.enhanceEndpoints({ addTagTypes: ['Identity', 'Environment'] })
1414
.injectEndpoints({
1515
endpoints: (builder) => ({
1616
createIdentities: builder.mutation<
@@ -133,6 +133,7 @@ export const identityService = service
133133
invalidatesTags: (res) => [
134134
{ id: 'LIST', type: 'Identity' },
135135
{ id: res?.id, type: 'Identity' },
136+
{ id: 'METRICS', type: 'Environment' },
136137
],
137138
query: (query: Req['updateIdentity']) => ({
138139
body: query.data,

frontend/common/types/requests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ export type Req = {
684684
}
685685
getBuildVersion: {}
686686
createOnboardingSupportOptIn: {}
687+
getEnvironmentMetrics: { id: string }
687688
getUserEnvironmentPermissions: {
688689
environmentId: string
689690
userId: string

frontend/common/types/responses.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,15 @@ export type Res = {
995995
conversionEvents: PagedResponse<ConversionEvent>
996996
splitTest: PagedResponse<SplitTestResult>
997997
onboardingSupportOptIn: { id: string }
998+
environmentMetrics: {
999+
metrics: {
1000+
value: number
1001+
description: string
1002+
name: string
1003+
entity: 'features' | 'identities' | 'segments' | 'workflows'
1004+
rank: number
1005+
}[]
1006+
}
9981007
profile: User
9991008
onboarding: {}
10001009
userPermissions: UserPermissions

frontend/web/components/App.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import GithubStar from './GithubStar'
2424
import Tooltip from './Tooltip'
2525
import classNames from 'classnames'
2626
import { apps, gitBranch, gitCompare, statsChart } from 'ionicons/icons'
27-
import NavSubLink from './NavSubLink'
27+
import NavSubLink from './navigation/NavSubLink'
2828
import SettingsIcon from './svg/SettingsIcon'
2929
import UsersIcon from './svg/UsersIcon'
3030
import BreadcrumbSeparator from './BreadcrumbSeparator'
@@ -344,6 +344,12 @@ const App = class extends Component {
344344
}
345345
const isOrganisationSelect = document.location.pathname === '/organisations'
346346
const integrations = Object.keys(Utils.getIntegrationData())
347+
const environmentMetricsEnabled = Utils.getFlagsmithHasFeature(
348+
'environment_metrics',
349+
)
350+
const projectMetricsTooltipEnabled = Utils.getFlagsmithHasFeature(
351+
'project_metrics_tooltip',
352+
)
347353
return (
348354
<Provider store={getStore()}>
349355
<AccountProvider
@@ -610,6 +616,21 @@ const App = class extends Component {
610616
>
611617
Compare
612618
</NavSubLink>
619+
{projectMetricsTooltipEnabled && (
620+
<NavSubLink
621+
icon={gitCompare}
622+
to=''
623+
id='reporting-link'
624+
disabled
625+
tooltip={
626+
Utils.getFlagsmithValue(
627+
'project_metrics_tooltip',
628+
) || 'Coming soon - fallback'
629+
}
630+
>
631+
Reporting
632+
</NavSubLink>
633+
)}
613634
<Permission
614635
level='project'
615636
permission='ADMIN'

frontend/web/components/Tooltip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { sanitize } from 'dompurify'
66
import { createPortal } from 'react-dom'
77

88
export type TooltipProps = {
9-
title: ReactNode
9+
title: ReactNode | React.ReactNode
1010
children: string | undefined | null
1111
place?: _TooltipProps['place']
1212
plainText?: boolean
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Icon from 'components/Icon'
2+
import { FC } from 'react'
3+
4+
interface LabelWithTooltipProps {
5+
label: string
6+
tooltip?: string
7+
}
8+
9+
const LabelWithTooltip: FC<LabelWithTooltipProps> = ({ label, tooltip }) => {
10+
return (
11+
<>
12+
{label}{' '}
13+
{tooltip && (
14+
<Tooltip
15+
title={<Icon name='info-outlined' width={12} height={12} />}
16+
place='top'
17+
titleClassName='cursor-pointer'
18+
>
19+
{tooltip}
20+
</Tooltip>
21+
)}
22+
</>
23+
)
24+
}
25+
26+
export default LabelWithTooltip
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React, { useState, FC } from 'react'
2+
import { IconButton, Collapse } from '@material-ui/core'
3+
import { chevronDown, chevronUp } from 'ionicons/icons'
4+
import { IonIcon } from '@ionic/react'
5+
6+
interface AccordionCardProps {
7+
children?: React.ReactNode
8+
title?: string
9+
className?: string
10+
}
11+
12+
const AccordionCard: FC<AccordionCardProps> = ({
13+
children,
14+
title = 'Summary',
15+
}) => {
16+
const [open, setOpen] = useState(false)
17+
18+
return (
19+
<div className='d-flex flex-column px-3 py-3 accordion-card m-0'>
20+
<div
21+
style={{
22+
alignItems: 'center',
23+
cursor: 'pointer',
24+
display: 'flex',
25+
justifyContent: 'space-between',
26+
}}
27+
onClick={() => setOpen(!open)}
28+
className='d-flex flex-row justify-content-between font-weight-medium'
29+
>
30+
{title}
31+
<IconButton size='small'>
32+
<IonIcon
33+
className='fs-small me-2 text-muted'
34+
icon={open ? chevronUp : chevronDown}
35+
/>
36+
</IconButton>
37+
</div>
38+
<Collapse in={open}>
39+
<div className='mt-2 mb-2'>{children}</div>
40+
</Collapse>
41+
</div>
42+
)
43+
}
44+
45+
export default AccordionCard

0 commit comments

Comments
 (0)