feat: show retention info for cloud users and show count next to team… (#4536)

* feat: show retention info for cloud users and show count next to team members and pending invites

* feat: add safety check for saved views response
This commit is contained in:
Yunus M 2024-02-14 12:42:36 +05:30 committed by GitHub
parent 0dffd86287
commit d6b7587bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 99 additions and 52 deletions

View File

@ -107,7 +107,7 @@ function ExplorerOptions({
const viewName = useGetSearchQueryParam(QueryParams.viewName) || ''; const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || ''; const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || '';
const extraData = viewsData?.data.data.find((view) => view.uuid === viewKey) const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey)
?.extraData; ?.extraData;
const extraDataColor = extraData ? JSON.parse(extraData).color : ''; const extraDataColor = extraData ? JSON.parse(extraData).color : '';
@ -134,7 +134,7 @@ function ExplorerOptions({
}; };
const onUpdateQueryHandler = (): void => { const onUpdateQueryHandler = (): void => {
const extraData = viewsData?.data.data.find((view) => view.uuid === viewKey) const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey)
?.extraData; ?.extraData;
updateViewAsync( updateViewAsync(
{ {
@ -166,7 +166,7 @@ function ExplorerOptions({
({ key }: { key: string }): void => { ({ key }: { key: string }): void => {
const currentViewDetails = getViewDetailsUsingViewKey( const currentViewDetails = getViewDetailsUsingViewKey(
key, key,
viewsData?.data.data, viewsData?.data?.data,
); );
if (!currentViewDetails) return; if (!currentViewDetails) return;
const { const {
@ -296,7 +296,7 @@ function ExplorerOptions({
onClear={handleClearSelect} onClear={handleClearSelect}
ref={ref} ref={ref}
> >
{viewsData?.data.data.map((view) => { {viewsData?.data?.data?.map((view) => {
const extraData = const extraData =
view.extraData !== '' ? JSON.parse(view.extraData) : ''; view.extraData !== '' ? JSON.parse(view.extraData) : '';
let bgColor = getRandomColor(); let bgColor = getRandomColor();

View File

@ -3,6 +3,7 @@ import { LoadingOutlined } from '@ant-design/icons';
import { Button, Card, Col, Divider, Modal, Row, Spin, Typography } from 'antd'; import { Button, Card, Col, Divider, Modal, Row, Spin, Typography } from 'antd';
import setRetentionApi from 'api/settings/setRetention'; import setRetentionApi from 'api/settings/setRetention';
import TextToolTip from 'components/TextToolTip'; import TextToolTip from 'components/TextToolTip';
import GeneralSettingsCloud from 'container/GeneralSettingsCloud';
import useComponentPermission from 'hooks/useComponentPermission'; import useComponentPermission from 'hooks/useComponentPermission';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';
import find from 'lodash-es/find'; import find from 'lodash-es/find';
@ -24,6 +25,7 @@ import {
PayloadPropsTraces as GetRetentionPeriodTracesPayload, PayloadPropsTraces as GetRetentionPeriodTracesPayload,
} from 'types/api/settings/getRetention'; } from 'types/api/settings/getRetention';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { isCloudUser } from 'utils/app';
import Retention from './Retention'; import Retention from './Retention';
import StatusMessage from './StatusMessage'; import StatusMessage from './StatusMessage';
@ -394,6 +396,8 @@ function GeneralSettings({
onModalToggleHandler(type); onModalToggleHandler(type);
}; };
const isCloudUserVal = isCloudUser();
const renderConfig = [ const renderConfig = [
{ {
name: 'Metrics', name: 'Metrics',
@ -521,7 +525,7 @@ function GeneralSettings({
return ( return (
<Fragment key={category.name}> <Fragment key={category.name}>
<Col xs={22} xl={11} key={category.name} style={{ margin: '0.5rem' }}> <Col xs={22} xl={11} key={category.name} style={{ margin: '0.5rem' }}>
<Card style={{ height: '100%', minHeight: 300 }}> <Card style={{ height: '100%' }}>
<Typography.Title style={{ margin: 0 }} level={3}> <Typography.Title style={{ margin: 0 }} level={3}>
{category.name} {category.name}
</Typography.Title> </Typography.Title>
@ -542,38 +546,43 @@ function GeneralSettings({
hide={!!retentionField.hide} hide={!!retentionField.hide}
/> />
))} ))}
<ActionItemsContainer>
<Button {!isCloudUserVal && (
type="primary" <>
onClick={category.save.modalOpen} <ActionItemsContainer>
disabled={category.save.isDisabled} <Button
> type="primary"
{category.save.saveButtonText} onClick={category.save.modalOpen}
</Button> disabled={category.save.isDisabled}
{category.statusComponent} >
</ActionItemsContainer> {category.save.saveButtonText}
<Modal </Button>
title={t('retention_confirmation')} {category.statusComponent}
focusTriggerAfterClose </ActionItemsContainer>
forceRender <Modal
destroyOnClose title={t('retention_confirmation')}
closable focusTriggerAfterClose
onCancel={(): void => forceRender
onModalToggleHandler(category.name.toLowerCase() as TTTLType) destroyOnClose
} closable
onOk={(): Promise<void> => onCancel={(): void =>
onOkHandler(category.name.toLowerCase() as TTTLType) onModalToggleHandler(category.name.toLowerCase() as TTTLType)
} }
centered onOk={(): Promise<void> =>
open={category.save.modal} onOkHandler(category.name.toLowerCase() as TTTLType)
confirmLoading={category.save.apiLoading} }
> centered
<Typography> open={category.save.modal}
{t('retention_confirmation_description', { confirmLoading={category.save.apiLoading}
name: category.name.toLowerCase(), >
})} <Typography>
</Typography> {t('retention_confirmation_description', {
</Modal> name: category.name.toLowerCase(),
})}
</Typography>
</Modal>
</>
)}
</Card> </Card>
</Col> </Col>
</Fragment> </Fragment>
@ -587,16 +596,20 @@ function GeneralSettings({
{Element} {Element}
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}> <Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
<ErrorTextContainer> <ErrorTextContainer>
<TextToolTip {!isCloudUserVal && (
{...{ <TextToolTip
text: `More details on how to set retention period`, {...{
url: 'https://signoz.io/docs/userguide/retention-period/', text: `More details on how to set retention period`,
}} url: 'https://signoz.io/docs/userguide/retention-period/',
/> }}
/>
)}
{errorText && <ErrorText>{errorText}</ErrorText>} {errorText && <ErrorText>{errorText}</ErrorText>}
</ErrorTextContainer> </ErrorTextContainer>
<Row justify="start">{renderConfig}</Row> <Row justify="start">{renderConfig}</Row>
{isCloudUserVal && <GeneralSettingsCloud />}
</Col> </Col>
</> </>
); );

View File

@ -8,6 +8,7 @@ import {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import { isCloudUser } from 'utils/app';
import { import {
Input, Input,
@ -85,9 +86,13 @@ function Retention({
func(null); func(null);
} }
}; };
if (hide) { if (hide) {
return null; return null;
} }
const isCloudUserVal = isCloudUser();
return ( return (
<RetentionContainer> <RetentionContainer>
<Row justify="space-between"> <Row justify="space-between">
@ -98,12 +103,14 @@ function Retention({
<RetentionFieldInputContainer> <RetentionFieldInputContainer>
<Input <Input
value={selectedValue && selectedValue >= 0 ? selectedValue : ''} value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
disabled={isCloudUserVal}
onChange={(e): void => onChangeHandler(e, setSelectedValue)} onChange={(e): void => onChangeHandler(e, setSelectedValue)}
style={{ width: 75 }} style={{ width: 75 }}
/> />
<Select <Select
value={selectedTimeUnit} value={selectedTimeUnit}
onChange={currentSelectedOption} onChange={currentSelectedOption}
disabled={isCloudUserVal}
style={{ width: 100 }} style={{ width: 100 }}
> >
{menuItems} {menuItems}

View File

@ -1,6 +1,6 @@
import { green, orange, volcano } from '@ant-design/colors'; import { green, orange, volcano } from '@ant-design/colors';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { Card, Col, Row } from 'antd'; import { Card, Col } from 'antd';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { TStatus } from 'types/api/settings/getRetention'; import { TStatus } from 'types/api/settings/getRetention';
@ -43,9 +43,16 @@ function StatusMessage({
width: '100%', width: '100%',
}} }}
> >
<Row style={{ gap: '1rem', alignItems: 'center', justifyContent: 'center' }}> <div
style={{
display: 'flex',
gap: '1rem',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Col xs={1}> <Col xs={1}>
<InfoCircleOutlined style={{ fontSize: '1.5rem' }} /> <InfoCircleOutlined style={{ fontSize: '1rem' }} />
</Col> </Col>
<Col <Col
@ -56,7 +63,7 @@ function StatusMessage({
> >
{statusMessage} {statusMessage}
</Col> </Col>
</Row> </div>
</Card> </Card>
) : null; ) : null;
} }

View File

@ -1,7 +1,11 @@
.general-settings-container { .general-settings-container {
margin: 16px 8px;
.ant-card-body { .ant-card-body {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
padding: 8px;
margin: 16px 0;
} }
} }

View File

@ -231,7 +231,7 @@ function UserFunction({
function Members(): JSX.Element { function Members(): JSX.Element {
const { org } = useSelector<AppState, AppReducer>((state) => state.app); const { org } = useSelector<AppState, AppReducer>((state) => state.app);
const { status, data } = useQuery({ const { status, data, isLoading } = useQuery({
queryFn: () => queryFn: () =>
getOrgUser({ getOrgUser({
orgId: (org || [])[0].id, orgId: (org || [])[0].id,
@ -308,7 +308,12 @@ function Members(): JSX.Element {
return ( return (
<Space direction="vertical" size="middle"> <Space direction="vertical" size="middle">
<Typography.Title level={3}>Members</Typography.Title> <Typography.Title level={3}>
Members{' '}
{!isLoading && dataSource && (
<div className="members-count"> ({dataSource.length}) </div>
)}
</Typography.Title>
<ResizeTable <ResizeTable
columns={columns} columns={columns}
tableLayout="fixed" tableLayout="fixed"

View File

@ -271,7 +271,12 @@ function PendingInvitesContainer(): JSX.Element {
<Space direction="vertical" size="middle"> <Space direction="vertical" size="middle">
<TitleWrapper> <TitleWrapper>
<Typography.Title level={3}>{t('pending_invites')}</Typography.Title> <Typography.Title level={3}>
{t('pending_invites')}
{getPendingInvitesResponse.status !== 'loading' && dataSource && (
<div className="members-count"> ({dataSource.length})</div>
)}
</Typography.Title>
<Space> <Space>
<Typography.Text type="warning"> <Typography.Text type="warning">

View File

@ -5,7 +5,6 @@ import { isCloudUser } from 'utils/app';
import { import {
alertChannels, alertChannels,
generalSettings, generalSettings,
generalSettingsCloud,
ingestionSettings, ingestionSettings,
organizationSettings, organizationSettings,
} from './config'; } from './config';
@ -23,11 +22,12 @@ export const getRoutes = (
if (isCloudUser()) { if (isCloudUser()) {
settings.push(...ingestionSettings(t)); settings.push(...ingestionSettings(t));
settings.push(...alertChannels(t)); settings.push(...alertChannels(t));
settings.push(...generalSettingsCloud(t));
} else { } else {
settings.push(...alertChannels(t)); settings.push(...alertChannels(t));
settings.push(...generalSettings(t)); settings.push(...generalSettings(t));
} }
settings.push(...generalSettings(t));
return settings; return settings;
}; };

View File

@ -218,3 +218,9 @@ body {
gap: 8px; gap: 8px;
} }
} }
.members-count {
display: inline-block;
margin-left: 8px;
margin-right: 8px;
}