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 { 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 (
|
||||
<>
|
||||
<Form name="basic" initialValues={{ remember: true }} form={form}>
|
||||
<Form.Item
|
||||
label="Retention Period"
|
||||
name="retention_period"
|
||||
rules={[{ required: false }]}
|
||||
style={{ maxWidth: '40%' }}
|
||||
>
|
||||
<Input disabled={true} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Space>
|
||||
<Alert
|
||||
message="Mail us at support@signoz.io to get instructions on how to change your retention period"
|
||||
type="info"
|
||||
/>
|
||||
</Space>
|
||||
</>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab="General" key="1">
|
||||
<GeneralSettings />
|
||||
</TabPane>
|
||||
{/* <TabPane tab="Alert Channels" key="2">
|
||||
Alerts
|
||||
</TabPane>
|
||||
<TabPane tab="Users" key="3">
|
||||
Users
|
||||
</TabPane> */}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
|
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