diff --git a/frontend/src/container/FormAlertRules/BasicInfo.tsx b/frontend/src/container/FormAlertRules/BasicInfo.tsx index 6e686d1188..9f04a07924 100644 --- a/frontend/src/container/FormAlertRules/BasicInfo.tsx +++ b/frontend/src/container/FormAlertRules/BasicInfo.tsx @@ -8,7 +8,7 @@ import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; import ROUTES from 'constants/routes'; import useComponentPermission from 'hooks/useComponentPermission'; import useFetch from 'hooks/useFetch'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -83,16 +83,22 @@ function BasicInfo({ window.open(ROUTES.CHANNELS_NEW, '_blank'); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const hasLoggedEvent = useRef(false); useEffect(() => { - if (!channels.loading && isNewRule) { + if (!channels.loading && isNewRule && !hasLoggedEvent.current) { logEvent('Alert: New alert creation page visited', { dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], numberOfChannels: channels?.payload?.length, }); + hasLoggedEvent.current = true; } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [channels.payload, channels.loading]); + }, [channels.loading]); + + const refetchChannels = async (): Promise => { + await channels.refetch(); + }; return ( <> @@ -197,7 +203,7 @@ function BasicInfo({ {!shouldBroadCastToAllChannels && ( { setAlertDef({ diff --git a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx index 209369c229..86c717396d 100644 --- a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx +++ b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx @@ -1,24 +1,33 @@ -import { Select } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; +import { Select, Spin } from 'antd'; +import useComponentPermission from 'hooks/useComponentPermission'; import { State } from 'hooks/useFetch'; import { useNotifications } from 'hooks/useNotifications'; import { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; import { PayloadProps } from 'types/api/channels/getAll'; +import AppReducer from 'types/reducer/app'; -import { StyledSelect } from './styles'; +import { StyledCreateChannelOption, StyledSelect } from './styles'; export interface ChannelSelectProps { disabled?: boolean; currentValue?: string[]; onSelectChannels: (s: string[]) => void; + onDropdownOpen: () => void; channels: State; + handleCreateNewChannels: () => void; } function ChannelSelect({ disabled, currentValue, onSelectChannels, + onDropdownOpen, channels, + handleCreateNewChannels, }: ChannelSelectProps): JSX.Element | null { // init namespace for translations const { t } = useTranslation('alerts'); @@ -26,6 +35,10 @@ function ChannelSelect({ const { notifications } = useNotifications(); const handleChange = (value: string[]): void => { + if (value.includes('add-new-channel')) { + handleCreateNewChannels(); + return; + } onSelectChannels(value); }; @@ -35,9 +48,27 @@ function ChannelSelect({ description: channels.errorMessage, }); } + + const { role } = useSelector((state) => state.app); + const [addNewChannelPermission] = useComponentPermission( + ['add_new_channel'], + role, + ); + const renderOptions = (): ReactNode[] => { const children: ReactNode[] = []; + if (!channels.loading && addNewChannelPermission) { + children.push( + + + + Create a new channel + + , + ); + } + if ( channels.loading || channels.payload === undefined || @@ -56,6 +87,7 @@ function ChannelSelect({ return children; }; + return ( } + onDropdownVisibleChange={(open): void => { + if (open) { + onDropdownOpen(); + } + }} onChange={(value): void => { handleChange(value as string[]); }} diff --git a/frontend/src/container/FormAlertRules/ChannelSelect/styles.ts b/frontend/src/container/FormAlertRules/ChannelSelect/styles.ts index 7a59e38767..33d4376ba5 100644 --- a/frontend/src/container/FormAlertRules/ChannelSelect/styles.ts +++ b/frontend/src/container/FormAlertRules/ChannelSelect/styles.ts @@ -4,3 +4,10 @@ import styled from 'styled-components'; export const StyledSelect = styled(Select)` border-radius: 4px; `; + +export const StyledCreateChannelOption = styled.div` + color: var(--bg-robin-500); + display: flex; + align-items: center; + gap: 8px; +`; diff --git a/frontend/src/hooks/useFetch.ts b/frontend/src/hooks/useFetch.ts index 7d67ff2a34..56377799d4 100644 --- a/frontend/src/hooks/useFetch.ts +++ b/frontend/src/hooks/useFetch.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { ErrorResponse, SuccessResponse } from 'types/api'; function useFetch( @@ -10,7 +10,7 @@ function useFetch( (arg0: any): Promise | ErrorResponse>; }, param?: FunctionParams, -): State { +): State & { refetch: () => Promise } { const [state, setStates] = useState>({ loading: true, success: null, @@ -19,37 +19,28 @@ function useFetch( payload: undefined, }); - const loadingRef = useRef(0); - - useEffect(() => { + const fetchData = useCallback(async (): Promise => { + setStates((prev) => ({ ...prev, loading: true })); try { - (async (): Promise => { - if (state.loading) { - const response = await functions(param); + const response = await functions(param); - if (loadingRef.current === 0) { - loadingRef.current = 1; - - if (response.statusCode === 200) { - setStates({ - loading: false, - error: false, - success: true, - payload: response.payload, - errorMessage: '', - }); - } else { - setStates({ - loading: false, - error: true, - success: false, - payload: undefined, - errorMessage: response.error as string, - }); - } - } - } - })(); + if (response.statusCode === 200) { + setStates({ + loading: false, + error: false, + success: true, + payload: response.payload, + errorMessage: '', + }); + } else { + setStates({ + loading: false, + error: true, + success: false, + payload: undefined, + errorMessage: response.error as string, + }); + } } catch (error) { setStates({ payload: undefined, @@ -59,13 +50,16 @@ function useFetch( errorMessage: error as string, }); } - return (): void => { - loadingRef.current = 1; - }; - }, [functions, param, state.loading]); + }, [functions, param]); + + // Initial fetch + useEffect(() => { + fetchData(); + }, [fetchData]); return { ...state, + refetch: fetchData, }; }