Merge pull request #949 from pranshuchittora/pranshuchittora/feat/ttl-s3

feat(FE): TTL/s3 integration
This commit is contained in:
palash-signoz 2022-04-05 16:28:41 +05:30 committed by GitHub
commit 6e446dc0ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 408 additions and 255 deletions

View File

@ -1,3 +1,18 @@
{ {
"monitor_signup": "Monitor your applications. Find what is causing issues." "monitor_signup": "Monitor your applications. Find what is causing issues.",
"routes": {
"general": "General",
"alert_channels": "Alert Channels"
},
"settings": {
"total_retention_period": "Total Retention Period",
"move_to_s3": "Move to S3\n(should be lower than total retention period)",
"retention_success_message": "Congrats. The retention periods for {{name}} has been updated successfully.",
"retention_error_message": "There was an issue in changing the retention period for {{name}}. Please try again or reach out to support@signoz.io",
"retention_failed_message": "There was an issue in changing the retention period. Please try again or reach out to support@signoz.io",
"retention_comparison_error": "Total retention period for {{name}} cant be lower or equal to the period after which data is moved to s3.",
"retention_null_value_error": "Retention Period for {{name}} is not set yet. Please set by choosing below",
"retention_confirmation": "Are you sure you want to change the retention period?",
"retention_confirmation_description": "This will change the amount of storage needed for saving metrics & traces."
}
} }

View 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/disks/getDisks';
const getDisks = async (): Promise<
SuccessResponse<PayloadProps> | ErrorResponse
> => {
try {
const response = await axios.get(`/disks`);
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default getDisks;

View File

@ -9,7 +9,11 @@ const setRetention = async (
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => { ): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try { try {
const response = await axios.post<PayloadProps>( const response = await axios.post<PayloadProps>(
`/settings/ttl?duration=${props.duration}&type=${props.type}`, `/settings/ttl?duration=${props.totalDuration}&type=${props.type}${
props.coldStorage
? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}`
: ''
}`,
); );
return { return {

View File

@ -1,108 +1,118 @@
import { DownOutlined } from '@ant-design/icons'; import { Col, Row, Select } from 'antd';
import { Button, Menu } from 'antd'; import { find } from 'lodash-es';
import React from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { SettingPeroid } from '.';
import { import {
Dropdown,
Input, Input,
RetentionContainer, RetentionContainer,
TextContainer, RetentionFieldInputContainer,
Typography, RetentionFieldLabel,
} from './styles'; } from './styles';
import {
convertHoursValueToRelevantUnit,
SettingPeriod,
TimeUnits,
} from './utils';
const { Option } = Select;
function Retention({ function Retention({
retentionValue, retentionValue,
setRentionValue, setRetentionValue,
selectedRetentionPeroid,
setSelectedRetentionPeroid,
text, text,
}: RetentionProps): JSX.Element { hide,
const options: Option[] = [ }: RetentionProps): JSX.Element | null {
{ const {
key: 'hr', value: initialValue,
value: 'Hrs', timeUnitValue: initialTimeUnitValue,
}, } = convertHoursValueToRelevantUnit(Number(retentionValue));
{ const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue);
key: 'day', const [selectedValue, setSelectedValue] = useState<number | null>(
value: 'Days', initialValue,
},
{
key: 'month',
value: 'Months',
},
];
const onClickHandler = (
e: { key: string },
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 interacted = useRef(false);
useEffect(() => {
if (!interacted.current) setSelectedValue(initialValue);
}, [initialValue]);
const currentSelectedOption = (option: SettingPeroid): string | undefined => { useEffect(() => {
return options.find((e) => e.key === option)?.value; if (!interacted.current) setSelectTimeUnit(initialTimeUnitValue);
}, [initialTimeUnitValue]);
const menuItems = TimeUnits.map((option) => (
<Option key={option.value} value={option.value}>
{option.key}
</Option>
));
const currentSelectedOption = (option: SettingPeriod): void => {
const selectedValue = find(TimeUnits, (e) => e.value === option)?.value;
if (selectedValue) setSelectTimeUnit(selectedValue);
}; };
useEffect(() => {
const inverseMultiplier = find(
TimeUnits,
(timeUnit) => timeUnit.value === selectedTimeUnit,
)?.multiplier;
if (!selectedValue) setRetentionValue(null);
if (selectedValue && inverseMultiplier) {
setRetentionValue(selectedValue * (1 / inverseMultiplier));
}
}, [selectedTimeUnit, selectedValue, setRetentionValue]);
const onChangeHandler = ( const onChangeHandler = (
e: React.ChangeEvent<HTMLInputElement>, e: React.ChangeEvent<HTMLInputElement>,
func: React.Dispatch<React.SetStateAction<string>>, func: React.Dispatch<React.SetStateAction<number | null>>,
): void => { ): void => {
interacted.current = true;
const { value } = e.target; const { value } = e.target;
const integerValue = parseInt(value, 10); const integerValue = parseInt(value, 10);
if (value.length > 0 && integerValue.toString() === value) { if (value.length > 0 && integerValue.toString() === value) {
const parsedValue = Math.abs(integerValue).toString(); const parsedValue = Math.abs(integerValue);
func(parsedValue); func(parsedValue);
} }
if (value.length === 0) { if (value.length === 0) {
func(''); func(null);
} }
}; };
if (hide) {
return null;
}
return ( return (
<RetentionContainer> <RetentionContainer>
<TextContainer> <Row justify="space-between">
<Typography>{text}</Typography> <Col flex={1} style={{ display: 'flex' }}>
</TextContainer> <RetentionFieldLabel>{text}</RetentionFieldLabel>
</Col>
<Input <Col flex="150px">
value={retentionValue} <RetentionFieldInputContainer>
onChange={(e): void => onChangeHandler(e, setRentionValue)} <Input
/> value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
onChange={(e): void => onChangeHandler(e, setSelectedValue)}
<Dropdown overlay={menu}> style={{ width: 75 }}
<Button> />
{currentSelectedOption(selectedRetentionPeroid)} <DownOutlined /> <Select
</Button> value={selectedTimeUnit}
</Dropdown> onChange={currentSelectedOption}
style={{ width: 100 }}
>
{menuItems}
</Select>
</RetentionFieldInputContainer>
</Col>
</Row>
</RetentionContainer> </RetentionContainer>
); );
} }
interface Option {
key: SettingPeroid;
value: string;
}
interface RetentionProps { interface RetentionProps {
retentionValue: string; retentionValue: number | null;
text: string; text: string;
setRentionValue: React.Dispatch<React.SetStateAction<string>>; setRetentionValue: React.Dispatch<React.SetStateAction<number | null>>;
selectedRetentionPeroid: SettingPeroid; hide: boolean;
setSelectedRetentionPeroid: React.Dispatch<
React.SetStateAction<SettingPeroid>
>;
} }
export default Retention; export default Retention;

View File

@ -1,43 +1,69 @@
import { Button, Modal, notification, Typography } from 'antd'; import { Button, Col, Modal, notification, Row, Typography } from 'antd';
import getRetentionperoidApi from 'api/settings/getRetention'; import getDisks from 'api/disks/getDisks';
import getRetentionPeriodApi from 'api/settings/getRetention';
import setRetentionApi from 'api/settings/setRetention'; import setRetentionApi from 'api/settings/setRetention';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import TextToolTip from 'components/TextToolTip'; import TextToolTip from 'components/TextToolTip';
import useFetch from 'hooks/useFetch'; import useFetch from 'hooks/useFetch';
import convertIntoHr from 'lib/convertIntoHr'; import { find } from 'lodash-es';
import getSettingsPeroid from 'lib/getSettingsPeroid'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next';
import { IDiskType } from 'types/api/disks/getDisks';
import { PayloadProps } from 'types/api/settings/getRetention'; import { PayloadProps } from 'types/api/settings/getRetention';
import Retention from './Retention'; import Retention from './Retention';
import { import {
ButtonContainer, ButtonContainer,
Container,
ErrorText, ErrorText,
ErrorTextContainer, ErrorTextContainer,
ToolTipContainer, ToolTipContainer,
} from './styles'; } from './styles';
function GeneralSettings(): JSX.Element { function GeneralSettings(): JSX.Element {
const [ const { t } = useTranslation();
selectedMetricsPeroid,
setSelectedMetricsPeroid,
] = useState<SettingPeroid>('month');
const [notifications, Element] = notification.useNotification(); const [notifications, Element] = notification.useNotification();
const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState<string>(
'',
);
const [modal, setModal] = useState<boolean>(false); const [modal, setModal] = useState<boolean>(false);
const [postApiLoading, setPostApiLoading] = useState<boolean>(false); const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
const [selectedTracePeroid, setSelectedTracePeroid] = useState<SettingPeroid>( const [availableDisks, setAvailableDisks] = useState<IDiskType[] | null>(null);
'hr',
);
const [retentionPeroidTrace, setRetentionPeroidTrace] = useState<string>(''); useEffect(() => {
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false); getDisks().then((response) => setAvailableDisks(response.payload));
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false); }, []);
const { payload: currentTTLValues, loading, error, errorMessage } = useFetch<
PayloadProps,
undefined
>(getRetentionPeriodApi, undefined);
const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState<
number | null
>(null);
const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState<
number | null
>(null);
const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState<
number | null
>(null);
const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState<
number | null
>(null);
useEffect(() => {
if (currentTTLValues) {
setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs);
setMetricsS3RetentionPeriod(
currentTTLValues.metrics_move_ttl_duration_hrs
? currentTTLValues.metrics_move_ttl_duration_hrs
: null,
);
setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs);
setTracesS3RetentionPeriod(
currentTTLValues.traces_move_ttl_duration_hrs
? currentTTLValues.traces_move_ttl_duration_hrs
: null,
);
}
}, [currentTTLValues]);
const onModalToggleHandler = (): void => { const onModalToggleHandler = (): void => {
setModal((modal) => !modal); setModal((modal) => !modal);
@ -47,142 +73,182 @@ function GeneralSettings(): JSX.Element {
onModalToggleHandler(); onModalToggleHandler();
}, []); }, []);
const { payload, loading, error, errorMessage } = useFetch< const s3Enabled = useMemo(
PayloadProps, () => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
undefined [availableDisks],
>(getRetentionperoidApi, undefined); );
const checkMetricTraceDefault = (trace: number, metric: number): void => { const renderConfig = [
if (metric === -1) { {
setIsDefaultMetrics(true); name: 'Metrics',
} else { retentionFields: [
setIsDefaultMetrics(false); {
name: t('settings.total_retention_period'),
value: metricsTotalRetentionPeriod,
setValue: setMetricsTotalRetentionPeriod,
},
{
name: t('settings.move_to_s3'),
value: metricsS3RetentionPeriod,
setValue: setMetricsS3RetentionPeriod,
hide: !s3Enabled,
},
],
},
{
name: 'Traces',
retentionFields: [
{
name: t('settings.total_retention_period'),
value: tracesTotalRetentionPeriod,
setValue: setTracesTotalRetentionPeriod,
},
{
name: t('settings.move_to_s3'),
value: tracesS3RetentionPeriod,
setValue: setTracesS3RetentionPeriod,
hide: !s3Enabled,
},
],
},
].map((category): JSX.Element | null => {
if (
Array.isArray(category.retentionFields) &&
category.retentionFields.length > 0
) {
return (
<Col flex="40%" style={{ minWidth: 475 }} key={category.name}>
<Typography.Title level={3}>{category.name}</Typography.Title>
{category.retentionFields.map((retentionField) => (
<Retention
key={retentionField.name}
text={retentionField.name}
retentionValue={retentionField.value}
setRetentionValue={retentionField.setValue}
hide={!!retentionField.hide}
/>
))}
</Col>
);
} }
return null;
if (trace === -1) { });
setIsDefaultTrace(true);
} else {
setIsDefaultTrace(false);
}
};
useEffect(() => {
if (!loading && payload !== undefined) {
const {
metrics_ttl_duration_hrs: metricTllDuration,
traces_ttl_duration_hrs: traceTllDuration,
} = payload;
checkMetricTraceDefault(traceTllDuration, metricTllDuration);
const traceValue = getSettingsPeroid(traceTllDuration);
const metricsValue = getSettingsPeroid(metricTllDuration);
setRetentionPeroidTrace(traceValue.value.toString());
setSelectedTracePeroid(traceValue.peroid);
setRetentionPeroidMetrics(metricsValue.value.toString());
setSelectedMetricsPeroid(metricsValue.peroid);
}
}, [setSelectedMetricsPeroid, loading, payload]);
const onOkHandler = async (): Promise<void> => { const onOkHandler = async (): Promise<void> => {
try { try {
setPostApiLoading(true); setPostApiLoading(true);
const retentionTraceValue = const [metricsTTLApiResponse, tracesTTLApiResponse] = await Promise.all([
retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0
? payload?.traces_ttl_duration_hrs || 0
: parseInt(retentionPeroidTrace, 10);
const retentionMetricsValue =
retentionPeroidMetrics === '0' &&
(payload?.metrics_ttl_duration_hrs || 0) < 0
? payload?.metrics_ttl_duration_hrs || 0
: parseInt(retentionPeroidMetrics, 10);
const [tracesResponse, metricsResponse] = await Promise.all([
setRetentionApi({ setRetentionApi({
duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`, type: 'metrics',
type: 'traces', totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
coldStorage: s3Enabled ? 's3' : null,
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
}), }),
setRetentionApi({ setRetentionApi({
duration: `${convertIntoHr( type: 'traces',
retentionMetricsValue, totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
selectedMetricsPeroid, coldStorage: s3Enabled ? 's3' : null,
)}h`, toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
type: 'metrics',
}), }),
]); ]);
[
{
apiResponse: metricsTTLApiResponse,
name: 'metrics',
},
{
apiResponse: tracesTTLApiResponse,
name: 'traces',
},
].forEach(({ apiResponse, name }) => {
if (apiResponse.statusCode === 200) {
notifications.success({
message: 'Success!',
placement: 'topRight',
if ( description: t('settings.retention_success_message', { name }),
tracesResponse.statusCode === 200 && });
metricsResponse.statusCode === 200 } else {
) { notifications.error({
notifications.success({ message: 'Error',
message: 'Success!', description: t('settings.retention_error_message', { name }),
placement: 'topRight', placement: 'topRight',
description: 'Congrats. The retention periods were updated correctly.', });
}); }
});
checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue); onModalToggleHandler();
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); setPostApiLoading(false);
} catch (error) { } catch (error) {
notifications.error({ notifications.error({
message: 'Error', message: 'Error',
description: description: t('settings.retention_failed_message'),
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
placement: 'topRight', placement: 'topRight',
}); });
} }
setModal(false);
}; };
const [isDisabled, errorText] = useMemo(() => {
// Various methods to return dynamic error message text.
const messages = {
compareError: (name: string | number): string =>
t('settings.retention_comparison_error', { name }),
nullValueError: (name: string | number): string =>
t('settings.retention_null_value_error', { name }),
};
// Defaults to button not disabled and empty error message text.
let isDisabled = false;
let errorText = '';
if (s3Enabled) {
if (
(metricsTotalRetentionPeriod || metricsS3RetentionPeriod) &&
Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod)
) {
isDisabled = true;
errorText = messages.compareError('metrics');
} else if (
(tracesTotalRetentionPeriod || tracesS3RetentionPeriod) &&
Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod)
) {
isDisabled = true;
errorText = messages.compareError('traces');
}
}
if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
isDisabled = true;
if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
errorText = messages.nullValueError('metrics and traces');
} else if (!metricsTotalRetentionPeriod) {
errorText = messages.nullValueError('metrics');
} else if (!tracesTotalRetentionPeriod) {
errorText = messages.nullValueError('traces');
}
}
return [isDisabled, errorText];
}, [
metricsS3RetentionPeriod,
metricsTotalRetentionPeriod,
s3Enabled,
t,
tracesS3RetentionPeriod,
tracesTotalRetentionPeriod,
]);
if (error) { if (error) {
return <Typography>{errorMessage}</Typography>; return <Typography>{errorMessage}</Typography>;
} }
if (loading || payload === undefined) { if (loading || currentTTLValues === undefined) {
return <Spinner tip="Loading.." height="70vh" />; return <Spinner tip="Loading.." height="70vh" />;
} }
const getErrorText = (): string => {
const getValue = (value: string): string =>
`Retention Peroid for ${value} is not set yet. Please set by choosing below`;
if (!isDefaultMetrics && !isDefaultTrace) {
return '';
}
if (isDefaultMetrics && !isDefaultTrace) {
return `${getValue('Metrics')}`;
}
if (!isDefaultMetrics && isDefaultTrace) {
return `${getValue('Trace')}`;
}
return `${getValue('Trace , Metrics')}`;
};
const isDisabledHandler = (): boolean => {
return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === '');
};
const errorText = getErrorText();
return ( return (
<Container> <Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
{Element} {Element}
{errorText ? ( {errorText ? (
<ErrorTextContainer> <ErrorTextContainer>
<ErrorText>{errorText}</ErrorText> <ErrorText>{errorText}</ErrorText>
@ -204,25 +270,10 @@ function GeneralSettings(): JSX.Element {
/> />
</ToolTipContainer> </ToolTipContainer>
)} )}
<Row justify="space-around">{renderConfig}</Row>
<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 <Modal
title="Are you sure you want to change the retention period?" title={t('settings.retention_confirmation')}
focusTriggerAfterClose focusTriggerAfterClose
forceRender forceRender
destroyOnClose destroyOnClose
@ -233,24 +284,18 @@ function GeneralSettings(): JSX.Element {
visible={modal} visible={modal}
confirmLoading={postApiLoading} confirmLoading={postApiLoading}
> >
<Typography> <Typography>{t('settings.retention_confirmation_description')}</Typography>
This will change the amount of storage needed for saving metrics & traces.
</Typography>
</Modal> </Modal>
<ButtonContainer> <ButtonContainer>
<Button <Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
onClick={onClickSaveHandler}
disabled={isDisabledHandler()}
type="primary"
>
Save Save
</Button> </Button>
</ButtonContainer> </ButtonContainer>
</Container> </Col>
); );
} }
export type SettingPeroid = 'hr' | 'day' | 'month'; export type SettingPeriod = 'hr' | 'day' | 'month';
export default GeneralSettings; export default GeneralSettings;

View File

@ -1,16 +1,13 @@
import { import {
Col,
Dropdown as DropDownComponent, Dropdown as DropDownComponent,
Input as InputComponent, Input as InputComponent,
Typography as TypographyComponent, Typography as TypographyComponent,
} from 'antd'; } from 'antd';
import styled from 'styled-components'; import styled from 'styled-components';
export const RetentionContainer = styled.div` export const RetentionContainer = styled(Col)`
width: 50%; margin: 0.75rem 0;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
`; `;
export const Input = styled(InputComponent)` export const Input = styled(InputComponent)`
@ -37,13 +34,6 @@ export const ButtonContainer = styled.div`
} }
`; `;
export const Container = styled.div`
&&& {
display: flex;
flex-direction: column;
}
`;
export const Dropdown = styled(DropDownComponent)` export const Dropdown = styled(DropDownComponent)`
&&& { &&& {
display: flex; display: flex;
@ -90,3 +80,12 @@ export const ErrorText = styled(TypographyComponent)`
font-style: italic; font-style: italic;
} }
`; `;
export const RetentionFieldLabel = styled(TypographyComponent)`
vertical-align: middle;
white-space: pre-wrap;
`;
export const RetentionFieldInputContainer = styled.div`
display: inline-flex;
`;

View File

@ -0,0 +1,42 @@
export type SettingPeriod = 'hr' | 'day' | 'month';
export interface ITimeUnit {
value: SettingPeriod;
key: string;
multiplier: number;
}
export const TimeUnits: ITimeUnit[] = [
{
value: 'hr',
key: 'Hours',
multiplier: 1,
},
{
value: 'day',
key: 'Days',
multiplier: 1 / 24,
},
{
value: 'month',
key: 'Months',
multiplier: 1 / (24 * 30),
},
];
export const convertHoursValueToRelevantUnit = (
value: number,
): { value: number; timeUnitValue: SettingPeriod } => {
if (value)
for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) {
const timeUnit = TimeUnits[idx];
const convertedValue = timeUnit.multiplier * value;
if (
convertedValue >= 1 &&
convertedValue === parseInt(`${convertedValue}`, 10)
) {
return { value: convertedValue, timeUnitValue: timeUnit.value };
}
}
return { value, timeUnitValue: TimeUnits[0].value };
};

View File

@ -1,6 +1,6 @@
import { SettingPeroid } from 'container/GeneralSettings'; import { SettingPeriod } from 'container/GeneralSettings';
const converIntoHr = (value: number, peroid: SettingPeroid): number => { const converIntoHr = (value: number, peroid: SettingPeriod): number => {
if (peroid === 'day') { if (peroid === 'day') {
return value * 24; return value * 24;
} }

View File

@ -1,4 +1,4 @@
import { SettingPeroid } from 'container/GeneralSettings'; import { SettingPeriod } from 'container/GeneralSettings';
const getSettingsPeroid = (hr: number): PayloadProps => { const getSettingsPeroid = (hr: number): PayloadProps => {
if (hr <= 0) { if (hr <= 0) {
@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => {
interface PayloadProps { interface PayloadProps {
value: number; value: number;
peroid: SettingPeroid; peroid: SettingPeriod;
} }
export default getSettingsPeroid; export default getSettingsPeroid;

View File

@ -5,29 +5,32 @@ import CreateAlertChannels from 'container/CreateAlertChannels';
import GeneralSettings from 'container/GeneralSettings'; import GeneralSettings from 'container/GeneralSettings';
import history from 'lib/history'; import history from 'lib/history';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
function SettingsPage(): JSX.Element { function SettingsPage(): JSX.Element {
const pathName = history.location.pathname; const pathName = history.location.pathname;
const { t } = useTranslation();
return ( return (
<RouteTab <RouteTab
{...{ {...{
routes: [ routes: [
{ {
Component: GeneralSettings, Component: GeneralSettings,
name: 'General Settings', name: t('routes.general'),
route: ROUTES.SETTINGS, route: ROUTES.SETTINGS,
}, },
{ {
Component: (): JSX.Element => { Component: (): JSX.Element => {
return <CreateAlertChannels preType="slack" />; return <CreateAlertChannels preType="slack" />;
}, },
name: 'Alert Channels', name: t('routes.alert_channels'),
route: ROUTES.ALL_CHANNELS, route: ROUTES.ALL_CHANNELS,
}, },
], ],
activeKey: activeKey:
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels', pathName === ROUTES.SETTINGS
? t('routes.general')
: t('routes.alert_channels'),
}} }}
/> />
); );

View File

@ -4,27 +4,30 @@ import AlertChannels from 'container/AllAlertChannels';
import GeneralSettings from 'container/GeneralSettings'; import GeneralSettings from 'container/GeneralSettings';
import history from 'lib/history'; import history from 'lib/history';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
function AllAlertChannels(): JSX.Element { function AllAlertChannels(): JSX.Element {
const pathName = history.location.pathname; const pathName = history.location.pathname;
const { t } = useTranslation();
return ( return (
<RouteTab <RouteTab
{...{ {...{
routes: [ routes: [
{ {
Component: GeneralSettings, Component: GeneralSettings,
name: 'General Settings', name: t('routes.general'),
route: ROUTES.SETTINGS, route: ROUTES.SETTINGS,
}, },
{ {
Component: AlertChannels, Component: AlertChannels,
name: 'Alert Channels', name: t('routes.alert_channels'),
route: ROUTES.ALL_CHANNELS, route: ROUTES.ALL_CHANNELS,
}, },
], ],
activeKey: activeKey:
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels', pathName === ROUTES.SETTINGS
? t('routes.general')
: t('routes.alert_channels'),
}} }}
/> />
); );

View File

@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element {
routes: [ routes: [
{ {
Component: GeneralSettings, Component: GeneralSettings,
name: 'General Settings', name: 'General',
route: ROUTES.SETTINGS, route: ROUTES.SETTINGS,
}, },
{ {
@ -23,8 +23,7 @@ function SettingsPage(): JSX.Element {
route: ROUTES.ALL_CHANNELS, route: ROUTES.ALL_CHANNELS,
}, },
], ],
activeKey: activeKey: pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General',
pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings',
}} }}
/> />
); );

View File

@ -0,0 +1,5 @@
export type PayloadProps = IDiskType[];
export interface IDiskType {
name: string;
type: string;
}

View File

@ -1,4 +1,6 @@
export interface PayloadProps { export interface PayloadProps {
metrics_ttl_duration_hrs: number; metrics_ttl_duration_hrs: number;
metrics_move_ttl_duration_hrs?: number;
traces_ttl_duration_hrs: number; traces_ttl_duration_hrs: number;
traces_move_ttl_duration_hrs?: number;
} }

View File

@ -1,6 +1,8 @@
export interface Props { export interface Props {
type: 'metrics' | 'traces'; type: 'metrics' | 'traces';
duration: string; totalDuration: string;
coldStorage?: 's3' | null;
toColdDuration?: string;
} }
export interface PayloadProps { export interface PayloadProps {