mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-25 21:04:24 +08:00
[Feat]: Dynamic columns in tables (#3809)
* feat: added dropdown in alert list table * refactor: done with combining actions * feat: done with label and dynamic table * feat: dynamic column in table * chore: show all label on hover * refactor: create to created timestamp - highlighted action * refactor: storing the column data in localstorage
This commit is contained in:
parent
b34eafcab1
commit
fc49833c9f
7
frontend/src/components/DropDown/DropDown.styles.scss
Normal file
7
frontend/src/components/DropDown/DropDown.styles.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.Dropdown-button {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Dropdown-icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
29
frontend/src/components/DropDown/DropDown.tsx
Normal file
29
frontend/src/components/DropDown/DropDown.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import './DropDown.styles.scss';
|
||||||
|
|
||||||
|
import { EllipsisOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, MenuProps, Space } from 'antd';
|
||||||
|
|
||||||
|
function DropDown({ element }: { element: JSX.Element[] }): JSX.Element {
|
||||||
|
const items: MenuProps['items'] = element.map(
|
||||||
|
(e: JSX.Element, index: number) => ({
|
||||||
|
label: e,
|
||||||
|
key: index,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown menu={{ items }} className="Dropdown-container">
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
className="Dropdown-button"
|
||||||
|
onClick={(e): void => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<EllipsisOutlined className="Dropdown-icon" />
|
||||||
|
</Space>
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DropDown;
|
@ -0,0 +1,17 @@
|
|||||||
|
.DynamicColumnTable {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.dynamicColumnTable-button {
|
||||||
|
align-self: flex-end;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamicColumnsTable-items {
|
||||||
|
display: flex;
|
||||||
|
width: 10.625rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
116
frontend/src/components/ResizeTable/DynamicColumnTable.tsx
Normal file
116
frontend/src/components/ResizeTable/DynamicColumnTable.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/* eslint-disable react/jsx-props-no-spreading */
|
||||||
|
import './DynamicColumnTable.syles.scss';
|
||||||
|
|
||||||
|
import { SettingOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Dropdown, MenuProps, Switch } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import ResizeTable from './ResizeTable';
|
||||||
|
import { DynamicColumnTableProps } from './types';
|
||||||
|
import { getVisibleColumns, setVisibleColumns } from './unit';
|
||||||
|
|
||||||
|
function DynamicColumnTable({
|
||||||
|
tablesource,
|
||||||
|
columns,
|
||||||
|
dynamicColumns,
|
||||||
|
onDragColumn,
|
||||||
|
...restProps
|
||||||
|
}: DynamicColumnTableProps): JSX.Element {
|
||||||
|
const [columnsData, setColumnsData] = useState<ColumnsType | undefined>(
|
||||||
|
columns,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const visibleColumns = getVisibleColumns({
|
||||||
|
tablesource,
|
||||||
|
columnsData: columns,
|
||||||
|
dynamicColumns,
|
||||||
|
});
|
||||||
|
setColumnsData((prevColumns) =>
|
||||||
|
prevColumns
|
||||||
|
? [
|
||||||
|
...prevColumns.slice(0, prevColumns.length - 1),
|
||||||
|
...visibleColumns,
|
||||||
|
prevColumns[prevColumns.length - 1],
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onToggleHandler = (index: number) => (
|
||||||
|
checked: boolean,
|
||||||
|
event: React.MouseEvent<HTMLButtonElement>,
|
||||||
|
): void => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setVisibleColumns({
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
index,
|
||||||
|
checked,
|
||||||
|
});
|
||||||
|
setColumnsData((prevColumns) => {
|
||||||
|
if (checked && dynamicColumns) {
|
||||||
|
return prevColumns
|
||||||
|
? [
|
||||||
|
...prevColumns.slice(0, prevColumns.length - 1),
|
||||||
|
dynamicColumns[index],
|
||||||
|
prevColumns[prevColumns.length - 1],
|
||||||
|
]
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
return prevColumns && dynamicColumns
|
||||||
|
? prevColumns.filter(
|
||||||
|
(column) => dynamicColumns[index].title !== column.title,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const items: MenuProps['items'] =
|
||||||
|
dynamicColumns?.map((column, index) => ({
|
||||||
|
label: (
|
||||||
|
<div className="dynamicColumnsTable-items">
|
||||||
|
<div>{column.title?.toString()}</div>
|
||||||
|
<Switch
|
||||||
|
checked={columnsData?.findIndex((c) => c.key === column.key) !== -1}
|
||||||
|
onChange={onToggleHandler(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
key: index,
|
||||||
|
type: 'checkbox',
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="DynamicColumnTable">
|
||||||
|
{dynamicColumns && (
|
||||||
|
<Dropdown
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
|
menu={{ items }}
|
||||||
|
trigger={['click']}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="dynamicColumnTable-button"
|
||||||
|
size="middle"
|
||||||
|
icon={<SettingOutlined />}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ResizeTable
|
||||||
|
columns={columnsData}
|
||||||
|
onDragColumn={onDragColumn}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicColumnTable.defaultProps = {
|
||||||
|
onDragColumn: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DynamicColumnTable);
|
23
frontend/src/components/ResizeTable/TableComponent/Date.tsx
Normal file
23
frontend/src/components/ResizeTable/TableComponent/Date.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Typography } from 'antd';
|
||||||
|
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
|
||||||
|
import getFormattedDate from 'lib/getFormatedDate';
|
||||||
|
|
||||||
|
function DateComponent(
|
||||||
|
CreatedOrUpdateTime: string | number | Date,
|
||||||
|
): JSX.Element {
|
||||||
|
const time = new Date(CreatedOrUpdateTime);
|
||||||
|
|
||||||
|
const date = getFormattedDate(time);
|
||||||
|
|
||||||
|
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
|
||||||
|
|
||||||
|
if (CreatedOrUpdateTime === null) {
|
||||||
|
return <Typography> - </Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography className="DateComponent-container">{timeString}</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateComponent;
|
11
frontend/src/components/ResizeTable/contants.ts
Normal file
11
frontend/src/components/ResizeTable/contants.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const TableDataSource = {
|
||||||
|
Alert: 'alert',
|
||||||
|
Dashboard: 'dashboard',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const DynamicColumnsKey = {
|
||||||
|
CreatedAt: 'createdAt',
|
||||||
|
CreatedBy: 'createdBy',
|
||||||
|
UpdatedAt: 'updatedAt',
|
||||||
|
UpdatedBy: 'updatedBy',
|
||||||
|
};
|
@ -1,6 +1,32 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
|
||||||
|
|
||||||
|
import { TableDataSource } from './contants';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export interface ResizeTableProps extends TableProps<any> {
|
export interface ResizeTableProps extends TableProps<any> {
|
||||||
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
}
|
}
|
||||||
|
export interface DynamicColumnTableProps extends TableProps<any> {
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns: TableProps<any>['columns'];
|
||||||
|
onDragColumn?: (fromIndex: number, toIndex: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetVisibleColumnsFunction = (
|
||||||
|
props: GetVisibleColumnProps,
|
||||||
|
) => (ColumnGroupType<any> | ColumnType<any>)[];
|
||||||
|
|
||||||
|
export type GetVisibleColumnProps = {
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns?: ColumnsType<any>;
|
||||||
|
columnsData?: ColumnsType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SetVisibleColumnsProps = {
|
||||||
|
checked: boolean;
|
||||||
|
index: number;
|
||||||
|
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
|
||||||
|
dynamicColumns?: ColumnsType<any>;
|
||||||
|
};
|
||||||
|
57
frontend/src/components/ResizeTable/unit.ts
Normal file
57
frontend/src/components/ResizeTable/unit.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { DynamicColumnsKey } from './contants';
|
||||||
|
import {
|
||||||
|
GetVisibleColumnProps,
|
||||||
|
GetVisibleColumnsFunction,
|
||||||
|
SetVisibleColumnsProps,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const getVisibleColumns: GetVisibleColumnsFunction = ({
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
columnsData,
|
||||||
|
}: GetVisibleColumnProps) => {
|
||||||
|
let columnVisibilityData: { [key: string]: boolean };
|
||||||
|
try {
|
||||||
|
const storedData = localStorage.getItem(tablesource);
|
||||||
|
if (typeof storedData === 'string' && dynamicColumns) {
|
||||||
|
columnVisibilityData = JSON.parse(storedData);
|
||||||
|
return dynamicColumns.filter((column) => {
|
||||||
|
if (column.key && !columnsData?.find((c) => c.key === column.key)) {
|
||||||
|
return columnVisibilityData[column.key];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialColumnVisibility: Record<string, boolean> = {};
|
||||||
|
Object.values(DynamicColumnsKey).forEach((key) => {
|
||||||
|
initialColumnVisibility[key] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem(tablesource, JSON.stringify(initialColumnVisibility));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setVisibleColumns = ({
|
||||||
|
checked,
|
||||||
|
index,
|
||||||
|
tablesource,
|
||||||
|
dynamicColumns,
|
||||||
|
}: SetVisibleColumnsProps): void => {
|
||||||
|
try {
|
||||||
|
const storedData = localStorage.getItem(tablesource);
|
||||||
|
if (typeof storedData === 'string' && dynamicColumns) {
|
||||||
|
const columnVisibilityData = JSON.parse(storedData);
|
||||||
|
const { key } = dynamicColumns[index];
|
||||||
|
if (key) {
|
||||||
|
columnVisibilityData[key] = checked;
|
||||||
|
}
|
||||||
|
localStorage.setItem(tablesource, JSON.stringify(columnVisibilityData));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,9 @@
|
|||||||
|
.LabelColumn {
|
||||||
|
.LabelColumn-label-tag {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.labelColumn-popover {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
67
frontend/src/components/TableRenderer/LabelColumn.tsx
Normal file
67
frontend/src/components/TableRenderer/LabelColumn.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import './LabelColumn.styles.scss';
|
||||||
|
|
||||||
|
import { Popover, Tag, Tooltip } from 'antd';
|
||||||
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import { LabelColumnProps } from './TableRenderer.types';
|
||||||
|
import { getLabelRenderingValue } from './utils';
|
||||||
|
|
||||||
|
function LabelColumn({ labels, value, color }: LabelColumnProps): JSX.Element {
|
||||||
|
const newLabels = labels.length > 3 ? labels.slice(0, 3) : labels;
|
||||||
|
const remainingLabels = labels.length > 3 ? labels.slice(3) : [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="LabelColumn">
|
||||||
|
{newLabels.map(
|
||||||
|
(label: string): JSX.Element => {
|
||||||
|
const tooltipTitle =
|
||||||
|
value && value[label] ? `${label}: ${value[label]}` : label;
|
||||||
|
return (
|
||||||
|
<Tooltip title={tooltipTitle} key={label}>
|
||||||
|
<Tag className="LabelColumn-label-tag" color={color}>
|
||||||
|
{getLabelRenderingValue(label, value && value[label])}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
{remainingLabels.length > 0 && (
|
||||||
|
<Popover
|
||||||
|
getPopupContainer={popupContainer}
|
||||||
|
placement="bottomRight"
|
||||||
|
showArrow={false}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
{labels.map(
|
||||||
|
(label: string): JSX.Element => {
|
||||||
|
const tooltipTitle =
|
||||||
|
value && value[label] ? `${label}: ${value[label]}` : label;
|
||||||
|
return (
|
||||||
|
<div className="labelColumn-popover" key={label}>
|
||||||
|
<Tooltip title={tooltipTitle}>
|
||||||
|
<Tag className="LabelColumn-label-tag" color={color}>
|
||||||
|
{getLabelRenderingValue(label, value && value[label])}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
trigger="hover"
|
||||||
|
>
|
||||||
|
<Tag className="LabelColumn-label-tag" color={color}>
|
||||||
|
+{remainingLabels.length}
|
||||||
|
</Tag>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelColumn.defaultProps = {
|
||||||
|
value: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LabelColumn;
|
@ -0,0 +1,5 @@
|
|||||||
|
export type LabelColumnProps = {
|
||||||
|
labels: string[];
|
||||||
|
color?: string;
|
||||||
|
value?: { [key: string]: string };
|
||||||
|
};
|
@ -16,6 +16,28 @@ export const generatorResizeTableColumns = <T>({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getLabelRenderingValue = (
|
||||||
|
label: string,
|
||||||
|
value?: string,
|
||||||
|
): string => {
|
||||||
|
const maxLength = 20;
|
||||||
|
|
||||||
|
if (label.length > maxLength) {
|
||||||
|
return `${label.slice(0, maxLength)}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
const remainingSpace = maxLength - label.length;
|
||||||
|
let newValue = value;
|
||||||
|
if (value.length > remainingSpace) {
|
||||||
|
newValue = `${value.slice(0, remainingSpace)}...`;
|
||||||
|
}
|
||||||
|
return `${label}: ${newValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
interface GeneratorResizeTableColumnsProp<T> {
|
interface GeneratorResizeTableColumnsProp<T> {
|
||||||
baseColumnOptions: ColumnsType<T>;
|
baseColumnOptions: ColumnsType<T>;
|
||||||
dynamicColumnOption: { key: string; columnOption: ColumnType<T> }[];
|
dynamicColumnOption: { key: string; columnOption: ColumnType<T> }[];
|
||||||
|
@ -3,7 +3,14 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import DropDown from 'components/DropDown/DropDown';
|
||||||
|
import {
|
||||||
|
DynamicColumnsKey,
|
||||||
|
TableDataSource,
|
||||||
|
} from 'components/ResizeTable/contants';
|
||||||
|
import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
|
||||||
|
import DateComponent from 'components/ResizeTable/TableComponent/Date';
|
||||||
|
import LabelColumn from 'components/TableRenderer/LabelColumn';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -22,7 +29,7 @@ import { GettableAlert } from 'types/api/alerts/get';
|
|||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import DeleteAlert from './DeleteAlert';
|
import DeleteAlert from './DeleteAlert';
|
||||||
import { Button, ButtonContainer, ColumnButton, StyledTag } from './styles';
|
import { Button, ButtonContainer, ColumnButton } from './styles';
|
||||||
import Status from './TableComponents/Status';
|
import Status from './TableComponents/Status';
|
||||||
import ToggleAlertState from './ToggleAlertState';
|
import ToggleAlertState from './ToggleAlertState';
|
||||||
|
|
||||||
@ -121,6 +128,53 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dynamicColumns: ColumnsType<GettableAlert> = [
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createAt',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.CreatedAt,
|
||||||
|
align: 'center',
|
||||||
|
sorter: (a: GettableAlert, b: GettableAlert): number => {
|
||||||
|
const prev = new Date(a.createAt).getTime();
|
||||||
|
const next = new Date(b.createAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created By',
|
||||||
|
dataIndex: 'createBy',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.CreatedBy,
|
||||||
|
align: 'center',
|
||||||
|
render: (value): JSX.Element => <div>{value}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated At',
|
||||||
|
dataIndex: 'updateAt',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.UpdatedAt,
|
||||||
|
align: 'center',
|
||||||
|
sorter: (a: GettableAlert, b: GettableAlert): number => {
|
||||||
|
const prev = new Date(a.updateAt).getTime();
|
||||||
|
const next = new Date(b.updateAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated By',
|
||||||
|
dataIndex: 'updateBy',
|
||||||
|
width: 80,
|
||||||
|
key: DynamicColumnsKey.UpdatedBy,
|
||||||
|
align: 'center',
|
||||||
|
render: (value): JSX.Element => <div>{value}</div>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const columns: ColumnsType<GettableAlert> = [
|
const columns: ColumnsType<GettableAlert> = [
|
||||||
{
|
{
|
||||||
title: 'Status',
|
title: 'Status',
|
||||||
@ -178,13 +232,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<LabelColumn labels={withOutSeverityKeys} value={value} color="magenta" />
|
||||||
{withOutSeverityKeys.map((e) => (
|
|
||||||
<StyledTag key={e} color="magenta">
|
|
||||||
{e}: {value[e]}
|
|
||||||
</StyledTag>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -195,20 +243,30 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
title: 'Action',
|
title: 'Action',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 120,
|
width: 10,
|
||||||
render: (id: GettableAlert['id'], record): JSX.Element => (
|
render: (id: GettableAlert['id'], record): JSX.Element => (
|
||||||
<>
|
<DropDown
|
||||||
<ToggleAlertState disabled={record.disabled} setData={setData} id={id} />
|
element={[
|
||||||
|
<ToggleAlertState
|
||||||
<ColumnButton onClick={onEditHandler(record)} type="link">
|
key="1"
|
||||||
Edit
|
disabled={record.disabled}
|
||||||
</ColumnButton>
|
setData={setData}
|
||||||
<ColumnButton onClick={onCloneHandler(record)} type="link">
|
id={id}
|
||||||
Clone
|
/>,
|
||||||
</ColumnButton>
|
<ColumnButton key="2" onClick={onEditHandler(record)} type="link">
|
||||||
|
Edit
|
||||||
<DeleteAlert notifications={notificationsApi} setData={setData} id={id} />
|
</ColumnButton>,
|
||||||
</>
|
<ColumnButton key="3" onClick={onCloneHandler(record)} type="link">
|
||||||
|
Clone
|
||||||
|
</ColumnButton>,
|
||||||
|
<DeleteAlert
|
||||||
|
key="4"
|
||||||
|
notifications={notificationsApi}
|
||||||
|
setData={setData}
|
||||||
|
id={id}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -229,7 +287,13 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
<ResizeTable columns={columns} rowKey="id" dataSource={data} />
|
<DynamicColumnTable
|
||||||
|
tablesource={TableDataSource.Alert}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={data}
|
||||||
|
dynamicColumns={dynamicColumns}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button as ButtonComponent, Tag } from 'antd';
|
import { Button as ButtonComponent } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
export const ButtonContainer = styled.div`
|
||||||
@ -23,9 +23,3 @@ export const ColumnButton = styled(ButtonComponent)`
|
|||||||
margin-right: 1.5em;
|
margin-right: 1.5em;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledTag = styled(Tag)`
|
|
||||||
&&& {
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@ -10,6 +10,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'first dashboard',
|
title: 'first dashboard',
|
||||||
variables: {},
|
variables: {},
|
||||||
@ -20,6 +22,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'second dashboard',
|
title: 'second dashboard',
|
||||||
variables: {},
|
variables: {},
|
||||||
@ -30,6 +34,8 @@ describe('executeSearchQueries', () => {
|
|||||||
uuid: uuid(),
|
uuid: uuid(),
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: '',
|
updated_at: '',
|
||||||
|
created_by: '',
|
||||||
|
updated_by: '',
|
||||||
data: {
|
data: {
|
||||||
title: 'third dashboard (with special characters +?\\)',
|
title: 'third dashboard (with special characters +?\\)',
|
||||||
variables: {},
|
variables: {},
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Typography } from 'antd';
|
|
||||||
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
|
|
||||||
import getFormattedDate from 'lib/getFormatedDate';
|
|
||||||
|
|
||||||
import { Data } from '..';
|
|
||||||
|
|
||||||
function DateComponent(lastUpdatedTime: Data['lastUpdatedTime']): JSX.Element {
|
|
||||||
const time = new Date(lastUpdatedTime);
|
|
||||||
|
|
||||||
const date = getFormattedDate(time);
|
|
||||||
|
|
||||||
const timeString = `${date} ${convertDateToAmAndPm(time)}`;
|
|
||||||
|
|
||||||
return <Typography>{timeString}</Typography>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DateComponent;
|
|
@ -45,18 +45,30 @@ function DeleteButton({ id }: Data): JSX.Element {
|
|||||||
|
|
||||||
// This is to avoid the type collision
|
// This is to avoid the type collision
|
||||||
function Wrapper(props: Data): JSX.Element {
|
function Wrapper(props: Data): JSX.Element {
|
||||||
const { createdBy, description, id, key, lastUpdatedTime, name, tags } = props;
|
const {
|
||||||
|
createdAt,
|
||||||
|
description,
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
lastUpdatedTime,
|
||||||
|
name,
|
||||||
|
tags,
|
||||||
|
createdBy,
|
||||||
|
lastUpdatedBy,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
{...{
|
{...{
|
||||||
createdBy,
|
createdAt,
|
||||||
description,
|
description,
|
||||||
id,
|
id,
|
||||||
key,
|
key,
|
||||||
lastUpdatedTime,
|
lastUpdatedTime,
|
||||||
name,
|
name,
|
||||||
tags,
|
tags,
|
||||||
|
createdBy,
|
||||||
|
lastUpdatedBy,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,12 @@ import {
|
|||||||
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import {
|
||||||
|
DynamicColumnsKey,
|
||||||
|
TableDataSource,
|
||||||
|
} from 'components/ResizeTable/contants';
|
||||||
|
import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
|
||||||
|
import LabelColumn from 'components/TableRenderer/LabelColumn';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
||||||
@ -26,13 +31,11 @@ import { Dashboard } from 'types/api/dashboard/getAll';
|
|||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import { popupContainer } from 'utils/selectPopupContainer';
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
|
import DateComponent from '../../components/ResizeTable/TableComponent/Date';
|
||||||
import ImportJSON from './ImportJSON';
|
import ImportJSON from './ImportJSON';
|
||||||
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
||||||
import Createdby from './TableComponents/CreatedBy';
|
|
||||||
import DateComponent from './TableComponents/Date';
|
|
||||||
import DeleteButton from './TableComponents/DeleteButton';
|
import DeleteButton from './TableComponents/DeleteButton';
|
||||||
import Name from './TableComponents/Name';
|
import Name from './TableComponents/Name';
|
||||||
import Tags from './TableComponents/Tags';
|
|
||||||
|
|
||||||
function ListOfAllDashboard(): JSX.Element {
|
function ListOfAllDashboard(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
@ -71,48 +74,68 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dynamicColumns: TableColumnProps<Data>[] = [
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.CreatedAt,
|
||||||
|
sorter: (a: Data, b: Data): number => {
|
||||||
|
console.log({ a });
|
||||||
|
const prev = new Date(a.createdAt).getTime();
|
||||||
|
const next = new Date(b.createdAt).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created By',
|
||||||
|
dataIndex: 'createdBy',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.CreatedBy,
|
||||||
|
render: (value): JSX.Element => <div>{value}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Last Updated Time',
|
||||||
|
width: 30,
|
||||||
|
dataIndex: 'lastUpdatedTime',
|
||||||
|
key: DynamicColumnsKey.UpdatedAt,
|
||||||
|
sorter: (a: Data, b: Data): number => {
|
||||||
|
const prev = new Date(a.lastUpdatedTime).getTime();
|
||||||
|
const next = new Date(b.lastUpdatedTime).getTime();
|
||||||
|
|
||||||
|
return prev - next;
|
||||||
|
},
|
||||||
|
render: DateComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Last Updated By',
|
||||||
|
dataIndex: 'lastUpdatedBy',
|
||||||
|
width: 30,
|
||||||
|
key: DynamicColumnsKey.UpdatedBy,
|
||||||
|
render: (value): JSX.Element => <div>{value}</div>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const tableColumns: TableColumnProps<Data>[] = [
|
const tableColumns: TableColumnProps<Data>[] = [
|
||||||
{
|
{
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
width: 100,
|
width: 40,
|
||||||
render: Name,
|
render: Name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Description',
|
title: 'Description',
|
||||||
width: 100,
|
width: 50,
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Tags (can be multiple)',
|
title: 'Tags (can be multiple)',
|
||||||
dataIndex: 'tags',
|
dataIndex: 'tags',
|
||||||
width: 80,
|
width: 50,
|
||||||
render: Tags,
|
render: (value): JSX.Element => <LabelColumn labels={value} />,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Created At',
|
|
||||||
dataIndex: 'createdBy',
|
|
||||||
width: 80,
|
|
||||||
sorter: (a: Data, b: Data): number => {
|
|
||||||
const prev = new Date(a.createdBy).getTime();
|
|
||||||
const next = new Date(b.createdBy).getTime();
|
|
||||||
|
|
||||||
return prev - next;
|
|
||||||
},
|
|
||||||
render: Createdby,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Last Updated Time',
|
|
||||||
width: 90,
|
|
||||||
dataIndex: 'lastUpdatedTime',
|
|
||||||
sorter: (a: Data, b: Data): number => {
|
|
||||||
const prev = new Date(a.lastUpdatedTime).getTime();
|
|
||||||
const next = new Date(b.lastUpdatedTime).getTime();
|
|
||||||
|
|
||||||
return prev - next;
|
|
||||||
},
|
|
||||||
render: DateComponent,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -130,13 +153,15 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
|
|
||||||
const data: Data[] =
|
const data: Data[] =
|
||||||
filteredDashboards?.map((e) => ({
|
filteredDashboards?.map((e) => ({
|
||||||
createdBy: e.created_at,
|
createdAt: e.created_at,
|
||||||
description: e.data.description || '',
|
description: e.data.description || '',
|
||||||
id: e.uuid,
|
id: e.uuid,
|
||||||
lastUpdatedTime: e.updated_at,
|
lastUpdatedTime: e.updated_at,
|
||||||
name: e.data.title,
|
name: e.data.title,
|
||||||
tags: e.data.tags || [],
|
tags: e.data.tags || [],
|
||||||
key: e.uuid,
|
key: e.uuid,
|
||||||
|
createdBy: e.created_by,
|
||||||
|
lastUpdatedBy: e.updated_by,
|
||||||
refetchDashboardList,
|
refetchDashboardList,
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
@ -290,7 +315,9 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
uploadedGrafana={uploadedGrafana}
|
uploadedGrafana={uploadedGrafana}
|
||||||
onModalHandler={(): void => onModalHandler(false)}
|
onModalHandler={(): void => onModalHandler(false)}
|
||||||
/>
|
/>
|
||||||
<ResizeTable
|
<DynamicColumnTable
|
||||||
|
tablesource={TableDataSource.Dashboard}
|
||||||
|
dynamicColumns={dynamicColumns}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={{
|
pagination={{
|
||||||
pageSize: 9,
|
pageSize: 9,
|
||||||
@ -314,7 +341,9 @@ export interface Data {
|
|||||||
description: string;
|
description: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
|
createdAt: string;
|
||||||
lastUpdatedTime: string;
|
lastUpdatedTime: string;
|
||||||
|
lastUpdatedBy: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,10 @@ export interface GettableAlert extends AlertDef {
|
|||||||
alert: string;
|
alert: string;
|
||||||
state: string;
|
state: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
createAt: string;
|
||||||
|
createBy: string;
|
||||||
|
updateAt: string;
|
||||||
|
updateBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PayloadProps = {
|
export type PayloadProps = {
|
||||||
|
@ -42,6 +42,8 @@ export interface Dashboard {
|
|||||||
uuid: string;
|
uuid: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
created_by: string;
|
||||||
|
updated_by: string;
|
||||||
data: DashboardData;
|
data: DashboardData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user