From 510815655fe6cc9ac3e86b62e218132d8dc47c51 Mon Sep 17 00:00:00 2001 From: pal-sig <88981777+pal-sig@users.noreply.github.com> Date: Tue, 9 Nov 2021 17:10:15 +0530 Subject: [PATCH] Feat(FE): retention UI (#353) * feat: get set retention api is updated * feat: Settings retention UI is updated --- frontend/src/api/settings/getRetention.ts | 24 +++ frontend/src/api/settings/setRetention.ts | 26 +++ .../container/GeneralSettings/Retention.tsx | 108 ++++++++++++ .../src/container/GeneralSettings/index.tsx | 163 ++++++++++++++++++ .../src/container/GeneralSettings/styles.ts | 61 +++++++ frontend/src/lib/convertIntoHr.ts | 15 ++ frontend/src/lib/getSettingsPeroid.ts | 36 ++++ frontend/src/pages/Settings/index.tsx | 46 ++--- .../src/types/api/settings/getRetention.ts | 4 + .../src/types/api/settings/setRetention.ts | 8 + 10 files changed, 461 insertions(+), 30 deletions(-) create mode 100644 frontend/src/api/settings/getRetention.ts create mode 100644 frontend/src/api/settings/setRetention.ts create mode 100644 frontend/src/container/GeneralSettings/Retention.tsx create mode 100644 frontend/src/container/GeneralSettings/index.tsx create mode 100644 frontend/src/container/GeneralSettings/styles.ts create mode 100644 frontend/src/lib/convertIntoHr.ts create mode 100644 frontend/src/lib/getSettingsPeroid.ts create mode 100644 frontend/src/types/api/settings/getRetention.ts create mode 100644 frontend/src/types/api/settings/setRetention.ts diff --git a/frontend/src/api/settings/getRetention.ts b/frontend/src/api/settings/getRetention.ts new file mode 100644 index 0000000000..4e5ad3d6f1 --- /dev/null +++ b/frontend/src/api/settings/getRetention.ts @@ -0,0 +1,24 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/settings/getRetention'; + +const getRetention = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/settings/ttl`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getRetention; diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts new file mode 100644 index 0000000000..dcf0a0f2c3 --- /dev/null +++ b/frontend/src/api/settings/setRetention.ts @@ -0,0 +1,26 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/settings/setRetention'; + +const setRetention = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post( + `/settings/ttl?duration=${props.duration}&type=${props.type}`, + ); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default setRetention; diff --git a/frontend/src/container/GeneralSettings/Retention.tsx b/frontend/src/container/GeneralSettings/Retention.tsx new file mode 100644 index 0000000000..c98098e4ca --- /dev/null +++ b/frontend/src/container/GeneralSettings/Retention.tsx @@ -0,0 +1,108 @@ +import { DownOutlined } from '@ant-design/icons'; +import { Button, Menu } from 'antd'; +import { MenuInfo } from 'rc-menu/lib/interface'; +import React from 'react'; + +import { SettingPeroid } from '.'; +import { + Dropdown, + Input, + RetentionContainer, + TextContainer, + Typography, +} from './styles'; + +const Retention = ({ + retentionValue, + setRentionValue, + selectedRetentionPeroid, + setSelectedRetentionPeroid, + text, +}: RetentionProps): JSX.Element => { + const options: Option[] = [ + { + key: 'hr', + value: 'Hrs', + }, + { + key: 'day', + value: 'Days', + }, + { + key: 'month', + value: 'Months', + }, + ]; + + const onClickHandler = ( + e: MenuInfo, + func: React.Dispatch>, + ): void => { + const selected = e.key as SettingPeroid; + func(selected); + }; + + const menu = ( + onClickHandler(e, setSelectedRetentionPeroid)}> + {options.map((option) => ( + {option.value} + ))} + + ); + + const currentSelectedOption = (option: SettingPeroid): string | undefined => { + return options.find((e) => e.key === option)?.value; + }; + + const onChangeHandler = ( + e: React.ChangeEvent, + func: React.Dispatch>, + ): void => { + const value = e.target.value; + + if (value.length > 0) { + const parsedValue = Math.abs(parseInt(value, 10)); + func(parsedValue); + } + + if (value.length === 0) { + func(0); + } + }; + + return ( + + + {text} + + + onChangeHandler(e, setRentionValue)} + /> + + + + + + ); +}; + +interface Option { + key: SettingPeroid; + value: string; +} + +interface RetentionProps { + retentionValue: number; + text: string; + setRentionValue: React.Dispatch>; + selectedRetentionPeroid: SettingPeroid; + setSelectedRetentionPeroid: React.Dispatch< + React.SetStateAction + >; +} + +export default Retention; diff --git a/frontend/src/container/GeneralSettings/index.tsx b/frontend/src/container/GeneralSettings/index.tsx new file mode 100644 index 0000000000..35c2473248 --- /dev/null +++ b/frontend/src/container/GeneralSettings/index.tsx @@ -0,0 +1,163 @@ +import { Button, Modal, notification, Typography } from 'antd'; +import getRetentionperoidApi from 'api/settings/getRetention'; +import setRetentionApi from 'api/settings/setRetention'; +import Spinner from 'components/Spinner'; +import useFetch from 'hooks/useFetch'; +import convertIntoHr from 'lib/convertIntoHr'; +import getSettingsPeroid from 'lib/getSettingsPeroid'; +import React, { useCallback, useEffect, useState } from 'react'; +import { PayloadProps } from 'types/api/settings/getRetention'; + +import Retention from './Retention'; +import { ButtonContainer, Container } from './styles'; + +const GeneralSettings = (): JSX.Element => { + const [ + selectedMetricsPeroid, + setSelectedMetricsPeroid, + ] = useState('month'); + const [notifications, Element] = notification.useNotification(); + + const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState( + 0, + ); + const [modal, setModal] = useState(false); + const [postApiLoading, setPostApiLoading] = useState(false); + + const [selectedTracePeroid, setSelectedTracePeroid] = useState( + 'hr', + ); + + const [retentionPeroidTrace, setRetentionPeroidTrace] = useState(0); + + const onClickSaveHandler = useCallback(() => { + onModalToggleHandler(); + }, []); + + const { payload, loading, error, errorMessage } = useFetch< + PayloadProps, + undefined + >(getRetentionperoidApi, undefined); + + const onModalToggleHandler = (): void => { + setModal((modal) => !modal); + }; + + useEffect(() => { + if (!loading && payload !== undefined) { + const { metrics_ttl_duration_hrs, traces_ttl_duration_hrs } = payload; + + const traceValue = getSettingsPeroid(traces_ttl_duration_hrs); + const metricsValue = getSettingsPeroid(metrics_ttl_duration_hrs); + + setRetentionPeroidTrace(traceValue.value); + setSelectedTracePeroid(traceValue.peroid); + + setRetentionPeroidMetrics(metricsValue.value); + setSelectedMetricsPeroid(metricsValue.peroid); + } + }, [setSelectedMetricsPeroid, loading, payload]); + + const onOkHandler = async (): Promise => { + try { + setPostApiLoading(true); + const [tracesResponse, metricsResponse] = await Promise.all([ + setRetentionApi({ + duration: `${convertIntoHr(retentionPeroidTrace, selectedTracePeroid)}h`, + type: 'traces', + }), + setRetentionApi({ + duration: `${convertIntoHr( + retentionPeroidMetrics, + selectedMetricsPeroid, + )}h`, + type: 'metrics', + }), + ]); + + if ( + tracesResponse.statusCode === 200 && + metricsResponse.statusCode === 200 + ) { + notifications.success({ + message: 'Success!', + placement: 'topRight', + description: 'Congrats. The retention periods were updated correctly.', + }); + onModalToggleHandler(); + } else { + notifications.error({ + message: 'Error', + description: + 'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io', + placement: 'topRight', + }); + } + setPostApiLoading(false); + } catch (error) { + notifications.error({ + message: 'Error', + description: + 'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io', + placement: 'topRight', + }); + } + }; + + if (error) { + return {errorMessage}; + } + + if (loading || payload === undefined) { + return ; + } + + return ( + + {Element} + + + + + + + + This will change the amount of storage needed for saving metrics & traces. + + + + + + + + ); +}; + +export type SettingPeroid = 'hr' | 'day' | 'month'; + +export default GeneralSettings; diff --git a/frontend/src/container/GeneralSettings/styles.ts b/frontend/src/container/GeneralSettings/styles.ts new file mode 100644 index 0000000000..2bddbde144 --- /dev/null +++ b/frontend/src/container/GeneralSettings/styles.ts @@ -0,0 +1,61 @@ +import { + Dropdown as DropDownComponent, + Input as InputComponent, + Typography as TypographyComponent, +} from 'antd'; +import styled from 'styled-components'; + +export const RetentionContainer = styled.div` + width: 50%; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +`; + +export const Input = styled(InputComponent)` + &&& { + height: 2rem; + max-width: 150px; + } +`; + +export const Typography = styled(TypographyComponent)` + &&& { + display: flex; + align-items: center; + } +`; + +export const ButtonContainer = styled.div` + &&& { + display: flex; + justify-content: flex-end; + width: 50%; + align-items: center; + margin-top: 3rem; + } +`; + +export const Container = styled.div` + &&& { + display: flex; + flex-direction: column; + } +`; + +export const Dropdown = styled(DropDownComponent)` + &&& { + display: flex; + justify-content: center; + align-items: center; + max-width: 150px; + min-width: 150px; + } +`; + +export const TextContainer = styled.div` + &&& { + min-width: 100px; + } +`; diff --git a/frontend/src/lib/convertIntoHr.ts b/frontend/src/lib/convertIntoHr.ts new file mode 100644 index 0000000000..cd2d059e94 --- /dev/null +++ b/frontend/src/lib/convertIntoHr.ts @@ -0,0 +1,15 @@ +import { SettingPeroid } from 'container/GeneralSettings'; + +const converIntoHr = (value: number, peroid: SettingPeroid): number => { + if (peroid === 'day') { + return value * 24; + } + + if (peroid === 'hr') { + return value; + } + + return value * 720; +}; + +export default converIntoHr; diff --git a/frontend/src/lib/getSettingsPeroid.ts b/frontend/src/lib/getSettingsPeroid.ts new file mode 100644 index 0000000000..72da4c3abb --- /dev/null +++ b/frontend/src/lib/getSettingsPeroid.ts @@ -0,0 +1,36 @@ +import { SettingPeroid } from 'container/GeneralSettings'; + +const getSettingsPeroid = (hr: number): PayloadProps => { + if (hr <= 0) { + return { + peroid: 'hr', + value: 0, + }; + } + + if (hr < 24) { + return { + peroid: 'hr', + value: hr, + }; + } + + if (hr < 720) { + return { + peroid: 'day', + value: hr / 24, + }; + } + + return { + peroid: 'month', + value: hr / 720, + }; +}; + +interface PayloadProps { + value: number; + peroid: SettingPeroid; +} + +export default getSettingsPeroid; diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index cfaf996138..1fa12cea22 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -1,36 +1,22 @@ -import { Form, Input, Space } from 'antd'; -import { Alert } from 'antd'; -import React, { useEffect } from 'react'; +import { Tabs } from 'antd'; +import React from 'react'; + +const { TabPane } = Tabs; +import GeneralSettings from 'container/GeneralSettings'; const SettingsPage = (): JSX.Element => { - const [form] = Form.useForm(); - - useEffect(() => { - form.setFieldsValue({ - retention_period: '3 days', - }); - }, [form]); - return ( - <> -
- - - -
- - - - - + + + + + {/* + Alerts + + + Users + */} + ); }; diff --git a/frontend/src/types/api/settings/getRetention.ts b/frontend/src/types/api/settings/getRetention.ts new file mode 100644 index 0000000000..e6fd1eae43 --- /dev/null +++ b/frontend/src/types/api/settings/getRetention.ts @@ -0,0 +1,4 @@ +export interface PayloadProps { + metrics_ttl_duration_hrs: number; + traces_ttl_duration_hrs: number; +} diff --git a/frontend/src/types/api/settings/setRetention.ts b/frontend/src/types/api/settings/setRetention.ts new file mode 100644 index 0000000000..6e82f8ef2a --- /dev/null +++ b/frontend/src/types/api/settings/setRetention.ts @@ -0,0 +1,8 @@ +export interface Props { + type: 'metrics' | 'traces'; + duration: string; +} + +export interface PayloadProps { + success: 'message'; +}