mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 07:29:02 +08:00
Feat(FE): retention UI (#353)
* feat: get set retention api is updated * feat: Settings retention UI is updated
This commit is contained in:
parent
53d52254cb
commit
510815655f
24
frontend/src/api/settings/getRetention.ts
Normal file
24
frontend/src/api/settings/getRetention.ts
Normal file
@ -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<PayloadProps> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get<PayloadProps>(`/settings/ttl`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getRetention;
|
26
frontend/src/api/settings/setRetention.ts
Normal file
26
frontend/src/api/settings/setRetention.ts
Normal file
@ -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<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post<PayloadProps>(
|
||||||
|
`/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;
|
108
frontend/src/container/GeneralSettings/Retention.tsx
Normal file
108
frontend/src/container/GeneralSettings/Retention.tsx
Normal file
@ -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<React.SetStateAction<SettingPeroid>>,
|
||||||
|
): void => {
|
||||||
|
const selected = e.key as SettingPeroid;
|
||||||
|
func(selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu onClick={(e): void => onClickHandler(e, setSelectedRetentionPeroid)}>
|
||||||
|
{options.map((option) => (
|
||||||
|
<Menu.Item key={option.key}>{option.value}</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentSelectedOption = (option: SettingPeroid): string | undefined => {
|
||||||
|
return options.find((e) => e.key === option)?.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeHandler = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
func: React.Dispatch<React.SetStateAction<number>>,
|
||||||
|
): 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 (
|
||||||
|
<RetentionContainer>
|
||||||
|
<TextContainer>
|
||||||
|
<Typography>{text}</Typography>
|
||||||
|
</TextContainer>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
value={retentionValue}
|
||||||
|
onChange={(e): void => onChangeHandler(e, setRentionValue)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown overlay={menu}>
|
||||||
|
<Button>
|
||||||
|
{currentSelectedOption(selectedRetentionPeroid)} <DownOutlined />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</RetentionContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
key: SettingPeroid;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RetentionProps {
|
||||||
|
retentionValue: number;
|
||||||
|
text: string;
|
||||||
|
setRentionValue: React.Dispatch<React.SetStateAction<number>>;
|
||||||
|
selectedRetentionPeroid: SettingPeroid;
|
||||||
|
setSelectedRetentionPeroid: React.Dispatch<
|
||||||
|
React.SetStateAction<SettingPeroid>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Retention;
|
163
frontend/src/container/GeneralSettings/index.tsx
Normal file
163
frontend/src/container/GeneralSettings/index.tsx
Normal file
@ -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<SettingPeroid>('month');
|
||||||
|
const [notifications, Element] = notification.useNotification();
|
||||||
|
|
||||||
|
const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState<number>(
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const [modal, setModal] = useState<boolean>(false);
|
||||||
|
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [selectedTracePeroid, setSelectedTracePeroid] = useState<SettingPeroid>(
|
||||||
|
'hr',
|
||||||
|
);
|
||||||
|
|
||||||
|
const [retentionPeroidTrace, setRetentionPeroidTrace] = useState<number>(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<void> => {
|
||||||
|
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 <Typography>{errorMessage}</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading || payload === undefined) {
|
||||||
|
return <Spinner tip="Loading.." height="70vh" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{Element}
|
||||||
|
|
||||||
|
<Retention
|
||||||
|
text={'Retention Period for Metrics'}
|
||||||
|
selectedRetentionPeroid={selectedMetricsPeroid}
|
||||||
|
setRentionValue={setRetentionPeroidMetrics}
|
||||||
|
retentionValue={retentionPeroidMetrics}
|
||||||
|
setSelectedRetentionPeroid={setSelectedMetricsPeroid}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Retention
|
||||||
|
text={'Retention Period for Traces'}
|
||||||
|
selectedRetentionPeroid={selectedTracePeroid}
|
||||||
|
setRentionValue={setRetentionPeroidTrace}
|
||||||
|
retentionValue={retentionPeroidTrace}
|
||||||
|
setSelectedRetentionPeroid={setSelectedTracePeroid}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title="Are you sure you want to change the retention period?"
|
||||||
|
focusTriggerAfterClose
|
||||||
|
forceRender
|
||||||
|
destroyOnClose
|
||||||
|
closable
|
||||||
|
onCancel={onModalToggleHandler}
|
||||||
|
onOk={onOkHandler}
|
||||||
|
centered
|
||||||
|
visible={modal}
|
||||||
|
confirmLoading={postApiLoading}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
This will change the amount of storage needed for saving metrics & traces.
|
||||||
|
</Typography>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<ButtonContainer>
|
||||||
|
<Button onClick={onClickSaveHandler} type="primary">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</ButtonContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SettingPeroid = 'hr' | 'day' | 'month';
|
||||||
|
|
||||||
|
export default GeneralSettings;
|
61
frontend/src/container/GeneralSettings/styles.ts
Normal file
61
frontend/src/container/GeneralSettings/styles.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
`;
|
15
frontend/src/lib/convertIntoHr.ts
Normal file
15
frontend/src/lib/convertIntoHr.ts
Normal file
@ -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;
|
36
frontend/src/lib/getSettingsPeroid.ts
Normal file
36
frontend/src/lib/getSettingsPeroid.ts
Normal file
@ -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;
|
@ -1,36 +1,22 @@
|
|||||||
import { Form, Input, Space } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
import { Alert } from 'antd';
|
import React from 'react';
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
|
|
||||||
const SettingsPage = (): JSX.Element => {
|
const SettingsPage = (): JSX.Element => {
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
form.setFieldsValue({
|
|
||||||
retention_period: '3 days',
|
|
||||||
});
|
|
||||||
}, [form]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Tabs defaultActiveKey="1">
|
||||||
<Form name="basic" initialValues={{ remember: true }} form={form}>
|
<TabPane tab="General" key="1">
|
||||||
<Form.Item
|
<GeneralSettings />
|
||||||
label="Retention Period"
|
</TabPane>
|
||||||
name="retention_period"
|
{/* <TabPane tab="Alert Channels" key="2">
|
||||||
rules={[{ required: false }]}
|
Alerts
|
||||||
style={{ maxWidth: '40%' }}
|
</TabPane>
|
||||||
>
|
<TabPane tab="Users" key="3">
|
||||||
<Input disabled={true} />
|
Users
|
||||||
</Form.Item>
|
</TabPane> */}
|
||||||
</Form>
|
</Tabs>
|
||||||
|
|
||||||
<Space>
|
|
||||||
<Alert
|
|
||||||
message="Mail us at support@signoz.io to get instructions on how to change your retention period"
|
|
||||||
type="info"
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
4
frontend/src/types/api/settings/getRetention.ts
Normal file
4
frontend/src/types/api/settings/getRetention.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface PayloadProps {
|
||||||
|
metrics_ttl_duration_hrs: number;
|
||||||
|
traces_ttl_duration_hrs: number;
|
||||||
|
}
|
8
frontend/src/types/api/settings/setRetention.ts
Normal file
8
frontend/src/types/api/settings/setRetention.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface Props {
|
||||||
|
type: 'metrics' | 'traces';
|
||||||
|
duration: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayloadProps {
|
||||||
|
success: 'message';
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user