Skip to content

Commit 5f1a8c0

Browse files
authored
feat: show-redirections-links-in-feature-health-tab (#6103)
1 parent efb8aef commit 5f1a8c0

File tree

14 files changed

+394
-272
lines changed

14 files changed

+394
-272
lines changed

frontend/web/components/EditHealthProvider.tsx

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import {
1212
useGetHealthProvidersQuery,
1313
} from 'common/services/useHealthProvider'
1414
import { components } from 'react-select'
15-
import InfoMessage from './InfoMessage'
1615
import InteractiveDemo from './InteractiveDemo'
16+
import FeatureHealthProviderDocumentationNote from './feature-health/components/FeatureHealthProviderDocumentationNote'
1717

1818
type EditHealthProviderType = {
1919
projectId: number
@@ -147,32 +147,7 @@ const EditHealthProvider: FC<EditHealthProviderType> = ({
147147
Learn about Feature Health.
148148
</Button>
149149
</p>
150-
<InfoMessage>
151-
<div>
152-
<strong>
153-
Follow the documentation to configure alerting using the supported
154-
providers.
155-
</strong>
156-
</div>
157-
<div>
158-
<span>
159-
Sample provider:{' '}
160-
<a href='https://docs.flagsmith.com/advanced-use/feature-health#sample-provider'>
161-
https://docs.flagsmith.com/advanced-use/feature-health#sample-provider
162-
</a>
163-
</span>
164-
</div>
165-
<div>
166-
<span>
167-
Grafana provider:{' '}
168-
<a href='https://docs.flagsmith.com/integrations/apm/grafana/#in-grafana-1'>
169-
{' '}
170-
https://docs.flagsmith.com/integrations/apm/grafana/#in-grafana-1
171-
</a>
172-
</span>
173-
</div>
174-
</InfoMessage>
175-
150+
<FeatureHealthProviderDocumentationNote />
176151
<label>Provider Name</label>
177152
<CreateHealthProviderForm projectId={projectId} />
178153
<hr className='py-0 my-4' />

frontend/web/components/InfoMessage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ type InfoMessageType = {
1414
title?: string
1515
url?: string
1616
close?: () => void
17+
defaultClosed?: boolean
1718
}
1819

1920
const InfoMessage: FC<InfoMessageType> = ({
2021
buttonText,
2122
children,
2223
close,
2324
collapseId,
25+
defaultClosed = false,
2426
icon,
2527
isClosable,
2628
title = 'NOTE',
@@ -33,7 +35,7 @@ const InfoMessage: FC<InfoMessageType> = ({
3335
localStorage.getItem(`infoMessageCollapsed_${collapseId}`) || 'false',
3436
)
3537
}
36-
return false
38+
return defaultClosed
3739
})
3840

3941
useEffect(() => {

frontend/web/components/PermissionsTabs.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ const PermissionsTabs: FC<PermissionsTabsType> = ({
8282
uncontrolled={uncontrolled}
8383
value={value}
8484
onChange={onChange}
85-
theme='pill m-0'
85+
theme='pill'
86+
className='m-0'
8687
isRoles={true}
8788
>
8889
<TabItem
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react'
2+
import { useGetHealthEventsQuery } from 'common/services/useHealthEvents'
3+
import {} from 'common/types/responses'
4+
import { useGetHealthProvidersQuery } from 'common/services/useHealthProvider'
5+
import FeatureHealthEventsList from './components/FeatureHealthEventsList'
6+
import EmptyFeatureHealthProviders from './components/EmptyFeatureHealthProviders'
7+
8+
type FeatureHealthTabContentProps = {
9+
projectId: number
10+
environmentId: number
11+
featureId: number
12+
}
13+
14+
const FeatureHealthTabContent: React.FC<FeatureHealthTabContentProps> = ({
15+
environmentId,
16+
featureId,
17+
projectId,
18+
}) => {
19+
const { data: healthEvents, isLoading } = useGetHealthEventsQuery(
20+
{ projectId: String(projectId) },
21+
{ skip: !projectId },
22+
)
23+
24+
const { data: providers, isLoading: isLoadingProviders } =
25+
useGetHealthProvidersQuery({ projectId: projectId })
26+
27+
if (isLoading || isLoadingProviders) {
28+
return (
29+
<div className='text-center'>
30+
<Loader />
31+
</div>
32+
)
33+
}
34+
35+
const hasFeatureHealthConfigured = providers && providers?.length > 0
36+
37+
return (
38+
<div>
39+
{hasFeatureHealthConfigured ? (
40+
<FeatureHealthEventsList
41+
featureHealthEvents={healthEvents || []}
42+
projectId={projectId}
43+
environmentId={environmentId}
44+
featureId={featureId}
45+
/>
46+
) : (
47+
<EmptyFeatureHealthProviders projectId={projectId} />
48+
)}
49+
</div>
50+
)
51+
}
52+
53+
export default FeatureHealthTabContent
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react'
2+
import AccountStore from 'common/stores/account-store'
3+
4+
interface EmptyFeatureHealthProvidersProps {
5+
projectId: number
6+
}
7+
8+
const EmptyFeatureHealthProviders: React.FC<
9+
EmptyFeatureHealthProvidersProps
10+
> = ({ projectId }) => {
11+
const isAdmin = AccountStore.isAdmin()
12+
13+
return (
14+
<>
15+
<div className='mb-4'>
16+
<h5>No Provider Configured</h5>
17+
</div>
18+
<div className='d-flex flex-column gap-4'>
19+
<div className='text-center'>
20+
{isAdmin ? (
21+
<p className='modal-caption fs-small lh-sm'>
22+
Configure a health provider in your{' '}
23+
<a
24+
target='_blank'
25+
rel='noreferrer'
26+
className='fw-normal btn-link'
27+
href={`/project/${projectId}/settings?tab=feature-health`}
28+
>
29+
project settings
30+
</a>{' '}
31+
to start monitoring your feature health, or read about the
32+
functionality{' '}
33+
<a
34+
target='_blank'
35+
rel='noreferrer'
36+
className='fw-normal btn-link'
37+
href='https://docs.flagsmith.com/advanced-use/feature-health'
38+
>
39+
here
40+
</a>
41+
.
42+
</p>
43+
) : (
44+
<p>
45+
Contact your Flagsmith administrators to configure a feature
46+
health provider, or read about the functionality{' '}
47+
<a
48+
target='_blank'
49+
rel='noreferrer'
50+
className='fw-normal btn-link'
51+
href='https://docs.flagsmith.com/advanced-use/feature-health'
52+
>
53+
here
54+
</a>
55+
.
56+
</p>
57+
)}
58+
</div>
59+
</div>
60+
</>
61+
)
62+
}
63+
64+
export default EmptyFeatureHealthProviders
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { useState } from 'react'
2+
import Constants from 'common/constants'
3+
import Collapse from '@material-ui/core/Collapse'
4+
import { IonIcon } from '@ionic/react'
5+
import { chevronDown, chevronUp } from 'ionicons/icons'
6+
import { FeatureHealthEventReasonTextBlock } from 'common/types/responses'
7+
8+
interface EventTextBlocksProps {
9+
textBlocks: FeatureHealthEventReasonTextBlock[] | undefined
10+
}
11+
12+
const EventTextBlocks: React.FC<EventTextBlocksProps> = ({ textBlocks }) => {
13+
// Index is used here only because the data is read only.
14+
// Backend sorts created_at in descending order.
15+
const initialValue =
16+
textBlocks?.map((_, index) => ({ collapsed: index !== 0, id: index })) ?? []
17+
const [collapsibleItems, setCollapsibleItems] =
18+
useState<{ id: number; collapsed: boolean }[]>(initialValue)
19+
20+
const handleCollapse = (index: number) => {
21+
if (!collapsibleItems?.[index]) {
22+
return null
23+
}
24+
25+
setCollapsibleItems((prev) => {
26+
const updatedItems = [...prev]
27+
updatedItems[index].collapsed = !updatedItems?.[index]?.collapsed
28+
return updatedItems
29+
})
30+
}
31+
32+
const color = Constants.featureHealth.unhealthyColor
33+
34+
if (!textBlocks?.length) {
35+
return <></>
36+
}
37+
38+
return (
39+
<div className='d-flex flex-column m-0 gap-2 flex-1'>
40+
<strong className='text-body'>Incident Insights</strong>
41+
{textBlocks.map((textBlock, index) => (
42+
<div key={`${textBlock.text}-${index}`}>
43+
{textBlock.title && (
44+
<div className='mb-2 text-body'>
45+
<strong style={{ color }}>{textBlock.title ?? 'Event'}</strong>
46+
{!!textBlock.text && (
47+
<IonIcon
48+
style={{ color, marginBottom: -2 }}
49+
className='ms-1'
50+
icon={
51+
collapsibleItems?.[index]?.collapsed
52+
? chevronDown
53+
: chevronUp
54+
}
55+
onClick={() => handleCollapse(index)}
56+
/>
57+
)}
58+
</div>
59+
)}
60+
<Collapse key={index} in={!collapsibleItems?.[index]?.collapsed}>
61+
{textBlock.text}
62+
</Collapse>
63+
</div>
64+
))}
65+
</div>
66+
)
67+
}
68+
export default EventTextBlocks
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react'
2+
import { FeatureHealthEventReasonUrlBlock } from 'common/types/responses'
3+
import Icon from 'components/Icon'
4+
import Button from 'components/base/forms/Button'
5+
6+
interface EventURLBlocksProps {
7+
urlBlocks: FeatureHealthEventReasonUrlBlock[] | undefined
8+
}
9+
const EventURLBlocks: React.FC<EventURLBlocksProps> = ({ urlBlocks }) => {
10+
if (!urlBlocks?.length) {
11+
return <></>
12+
}
13+
14+
return (
15+
<div className='d-flex flex-column m-0 gap-2 align-items-start'>
16+
<div>
17+
<strong className='text-body'>Provider Links</strong>
18+
</div>
19+
{urlBlocks.map((urlBlock, index) => (
20+
<Button
21+
key={`${urlBlock.url}-${index}`}
22+
theme='text'
23+
onClick={() => window.open(urlBlock.url, '_blank')}
24+
>
25+
{urlBlock.title ?? 'Link'}{' '}
26+
<Icon name='open-external-link' width={14} fill='#6837fc' />
27+
</Button>
28+
))}
29+
</div>
30+
)
31+
}
32+
33+
export default EventURLBlocks

0 commit comments

Comments
 (0)