2021-09-23 15:43:43 +05:30
|
|
|
import { PlusOutlined } from '@ant-design/icons';
|
2023-11-24 00:03:47 +05:30
|
|
|
import { Card, Col, Dropdown, Input, Row, TableColumnProps } from 'antd';
|
2023-03-03 12:09:24 +01:00
|
|
|
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
2021-09-23 15:43:43 +05:30
|
|
|
import createDashboard from 'api/dashboard/create';
|
|
|
|
|
import { AxiosError } from 'axios';
|
2023-10-27 21:09:23 +05:30
|
|
|
import {
|
|
|
|
|
DynamicColumnsKey,
|
|
|
|
|
TableDataSource,
|
|
|
|
|
} from 'components/ResizeTable/contants';
|
|
|
|
|
import DynamicColumnTable from 'components/ResizeTable/DynamicColumnTable';
|
|
|
|
|
import LabelColumn from 'components/TableRenderer/LabelColumn';
|
2021-12-24 11:51:19 +05:30
|
|
|
import TextToolTip from 'components/TextToolTip';
|
2021-09-23 15:43:43 +05:30
|
|
|
import ROUTES from 'constants/routes';
|
2023-09-11 10:51:10 +05:30
|
|
|
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
2022-05-03 15:27:09 +05:30
|
|
|
import useComponentPermission from 'hooks/useComponentPermission';
|
2023-11-20 14:53:13 +05:30
|
|
|
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
2022-03-22 21:56:12 +05:30
|
|
|
import history from 'lib/history';
|
2023-10-08 23:21:17 +05:30
|
|
|
import { Key, useCallback, useEffect, useMemo, useState } from 'react';
|
2022-04-25 22:41:46 +05:30
|
|
|
import { useTranslation } from 'react-i18next';
|
2023-10-08 23:21:17 +05:30
|
|
|
import { useSelector } from 'react-redux';
|
2022-03-22 21:56:12 +05:30
|
|
|
import { generatePath } from 'react-router-dom';
|
2021-09-23 15:43:43 +05:30
|
|
|
import { AppState } from 'store/reducers';
|
2022-04-22 18:57:05 +05:30
|
|
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
2022-05-03 15:27:09 +05:30
|
|
|
import AppReducer from 'types/reducer/app';
|
2021-09-23 15:43:43 +05:30
|
|
|
|
2023-11-01 18:26:41 +05:30
|
|
|
import DateComponent from '../../components/ResizeTable/TableComponent/DateComponent';
|
2022-04-25 22:41:46 +05:30
|
|
|
import ImportJSON from './ImportJSON';
|
2022-03-14 20:12:42 +05:30
|
|
|
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
2023-10-08 23:21:17 +05:30
|
|
|
import DeleteButton from './TableComponents/DeleteButton';
|
2021-09-23 15:43:43 +05:30
|
|
|
import Name from './TableComponents/Name';
|
|
|
|
|
|
2022-03-22 12:10:31 +05:30
|
|
|
function ListOfAllDashboard(): JSX.Element {
|
2023-11-20 14:53:13 +05:30
|
|
|
const { Search } = Input;
|
|
|
|
|
|
2023-09-11 10:51:10 +05:30
|
|
|
const {
|
|
|
|
|
data: dashboardListResponse = [],
|
|
|
|
|
isLoading: isDashboardListLoading,
|
|
|
|
|
refetch: refetchDashboardList,
|
|
|
|
|
} = useGetAllDashboard();
|
|
|
|
|
|
2022-05-03 15:27:09 +05:30
|
|
|
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
|
|
|
|
|
2023-11-24 00:03:47 +05:30
|
|
|
const [action, createNewDashboard] = useComponentPermission(
|
|
|
|
|
['action', 'create_new_dashboards'],
|
2022-05-03 15:27:09 +05:30
|
|
|
role,
|
|
|
|
|
);
|
2021-09-23 15:43:43 +05:30
|
|
|
|
2022-04-25 22:41:46 +05:30
|
|
|
const { t } = useTranslation('dashboard');
|
2023-03-14 16:55:15 +05:30
|
|
|
|
2022-04-25 22:41:46 +05:30
|
|
|
const [
|
|
|
|
|
isImportJSONModalVisible,
|
|
|
|
|
setIsImportJSONModalVisible,
|
|
|
|
|
] = useState<boolean>(false);
|
2023-03-14 16:55:15 +05:30
|
|
|
|
2022-11-10 16:49:54 +05:30
|
|
|
const [uploadedGrafana, setUploadedGrafana] = useState<boolean>(false);
|
2023-11-20 14:53:13 +05:30
|
|
|
const [isFilteringDashboards, setIsFilteringDashboards] = useState(false);
|
|
|
|
|
|
|
|
|
|
const [dashboards, setDashboards] = useState<Dashboard[]>();
|
2022-04-25 22:41:46 +05:30
|
|
|
|
2023-11-20 14:53:13 +05:30
|
|
|
const sortDashboardsByCreatedAt = (dashboards: Dashboard[]): void => {
|
|
|
|
|
const sortedDashboards = dashboards.sort(
|
|
|
|
|
(a, b) =>
|
|
|
|
|
new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
|
|
|
|
);
|
|
|
|
|
setDashboards(sortedDashboards);
|
|
|
|
|
};
|
2022-04-22 18:57:05 +05:30
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-11-24 11:51:26 +05:30
|
|
|
sortDashboardsByCreatedAt(dashboardListResponse);
|
2023-09-11 10:51:10 +05:30
|
|
|
}, [dashboardListResponse]);
|
2023-03-14 16:55:15 +05:30
|
|
|
|
2021-09-23 15:43:43 +05:30
|
|
|
const [newDashboardState, setNewDashboardState] = useState({
|
|
|
|
|
loading: false,
|
|
|
|
|
error: false,
|
|
|
|
|
errorMessage: '',
|
|
|
|
|
});
|
|
|
|
|
|
2023-10-27 21:09:23 +05:30
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
2023-07-04 07:20:12 +03:00
|
|
|
const columns = useMemo(() => {
|
|
|
|
|
const tableColumns: TableColumnProps<Data>[] = [
|
2023-05-19 12:19:42 +05:30
|
|
|
{
|
|
|
|
|
title: 'Name',
|
|
|
|
|
dataIndex: 'name',
|
2023-10-27 21:09:23 +05:30
|
|
|
width: 40,
|
2023-05-19 12:19:42 +05:30
|
|
|
render: Name,
|
2021-09-28 18:32:02 +05:30
|
|
|
},
|
2023-05-19 12:19:42 +05:30
|
|
|
{
|
|
|
|
|
title: 'Description',
|
2023-10-27 21:09:23 +05:30
|
|
|
width: 50,
|
2023-05-19 12:19:42 +05:30
|
|
|
dataIndex: 'description',
|
2021-09-23 15:43:43 +05:30
|
|
|
},
|
2023-05-19 12:19:42 +05:30
|
|
|
{
|
2023-11-03 17:27:09 +05:30
|
|
|
title: 'Tags',
|
2023-05-19 12:19:42 +05:30
|
|
|
dataIndex: 'tags',
|
2023-10-27 21:09:23 +05:30
|
|
|
width: 50,
|
|
|
|
|
render: (value): JSX.Element => <LabelColumn labels={value} />,
|
2023-05-19 12:19:42 +05:30
|
|
|
},
|
2023-07-04 07:20:12 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (action) {
|
|
|
|
|
tableColumns.push({
|
|
|
|
|
title: 'Action',
|
|
|
|
|
dataIndex: '',
|
|
|
|
|
width: 40,
|
2023-10-08 23:21:17 +05:30
|
|
|
render: DeleteButton,
|
2023-07-04 07:20:12 +03:00
|
|
|
});
|
|
|
|
|
}
|
2022-05-03 15:27:09 +05:30
|
|
|
|
2023-07-04 07:20:12 +03:00
|
|
|
return tableColumns;
|
2023-10-08 23:21:17 +05:30
|
|
|
}, [action]);
|
2023-09-11 10:51:10 +05:30
|
|
|
|
|
|
|
|
const data: Data[] =
|
2023-11-20 14:53:13 +05:30
|
|
|
dashboards?.map((e) => ({
|
2023-10-27 21:09:23 +05:30
|
|
|
createdAt: e.created_at,
|
2023-09-11 10:51:10 +05:30
|
|
|
description: e.data.description || '',
|
|
|
|
|
id: e.uuid,
|
|
|
|
|
lastUpdatedTime: e.updated_at,
|
|
|
|
|
name: e.data.title,
|
|
|
|
|
tags: e.data.tags || [],
|
|
|
|
|
key: e.uuid,
|
2023-10-27 21:09:23 +05:30
|
|
|
createdBy: e.created_by,
|
2023-11-03 17:27:09 +05:30
|
|
|
isLocked: !!e.isLocked || false,
|
2023-10-27 21:09:23 +05:30
|
|
|
lastUpdatedBy: e.updated_by,
|
2023-09-11 10:51:10 +05:30
|
|
|
refetchDashboardList,
|
|
|
|
|
})) || [];
|
2021-09-23 15:43:43 +05:30
|
|
|
|
|
|
|
|
const onNewDashboardHandler = useCallback(async () => {
|
|
|
|
|
try {
|
|
|
|
|
setNewDashboardState({
|
|
|
|
|
...newDashboardState,
|
|
|
|
|
loading: true,
|
|
|
|
|
});
|
|
|
|
|
const response = await createDashboard({
|
2022-04-25 22:41:46 +05:30
|
|
|
title: t('new_dashboard_title', {
|
|
|
|
|
ns: 'dashboard',
|
|
|
|
|
}),
|
2022-11-10 16:49:54 +05:30
|
|
|
uploadedGrafana: false,
|
2021-09-23 15:43:43 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
2022-03-22 21:56:12 +05:30
|
|
|
history.push(
|
2021-10-22 17:07:22 +05:30
|
|
|
generatePath(ROUTES.DASHBOARD, {
|
2022-04-25 22:41:46 +05:30
|
|
|
dashboardId: response.payload.uuid,
|
2021-10-22 17:07:22 +05:30
|
|
|
}),
|
|
|
|
|
);
|
2021-09-23 15:43:43 +05:30
|
|
|
} else {
|
|
|
|
|
setNewDashboardState({
|
|
|
|
|
...newDashboardState,
|
|
|
|
|
loading: false,
|
|
|
|
|
error: true,
|
|
|
|
|
errorMessage: response.error || 'Something went wrong',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
setNewDashboardState({
|
|
|
|
|
...newDashboardState,
|
|
|
|
|
error: true,
|
|
|
|
|
errorMessage: (error as AxiosError).toString() || 'Something went Wrong',
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-10-08 23:21:17 +05:30
|
|
|
}, [newDashboardState, t]);
|
2021-09-23 15:43:43 +05:30
|
|
|
|
2022-04-25 22:41:46 +05:30
|
|
|
const getText = useCallback(() => {
|
2021-09-23 15:43:43 +05:30
|
|
|
if (!newDashboardState.error && !newDashboardState.loading) {
|
|
|
|
|
return 'New Dashboard';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newDashboardState.loading) {
|
|
|
|
|
return 'Loading';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newDashboardState.errorMessage;
|
2022-04-25 22:41:46 +05:30
|
|
|
}, [
|
|
|
|
|
newDashboardState.error,
|
|
|
|
|
newDashboardState.errorMessage,
|
|
|
|
|
newDashboardState.loading,
|
|
|
|
|
]);
|
|
|
|
|
|
2022-11-10 16:49:54 +05:30
|
|
|
const onModalHandler = (uploadedGrafana: boolean): void => {
|
2022-04-25 22:41:46 +05:30
|
|
|
setIsImportJSONModalVisible((state) => !state);
|
2022-11-10 16:49:54 +05:30
|
|
|
setUploadedGrafana(uploadedGrafana);
|
2021-09-23 15:43:43 +05:30
|
|
|
};
|
|
|
|
|
|
2023-05-19 12:19:42 +05:30
|
|
|
const getMenuItems = useMemo(() => {
|
2023-11-24 00:03:47 +05:30
|
|
|
const menuItems: ItemType[] = [
|
|
|
|
|
{
|
|
|
|
|
key: t('import_json').toString(),
|
|
|
|
|
label: t('import_json'),
|
|
|
|
|
onClick: (): void => onModalHandler(false),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: t('import_grafana_json').toString(),
|
|
|
|
|
label: t('import_grafana_json'),
|
|
|
|
|
onClick: (): void => onModalHandler(true),
|
|
|
|
|
disabled: true,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
2023-03-03 12:09:24 +01:00
|
|
|
if (createNewDashboard) {
|
2023-11-24 00:03:47 +05:30
|
|
|
menuItems.unshift({
|
2023-03-03 12:09:24 +01:00
|
|
|
key: t('create_dashboard').toString(),
|
|
|
|
|
label: t('create_dashboard'),
|
2023-09-11 10:51:10 +05:30
|
|
|
disabled: isDashboardListLoading,
|
2023-03-03 12:09:24 +01:00
|
|
|
onClick: onNewDashboardHandler,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menuItems;
|
2023-09-11 10:51:10 +05:30
|
|
|
}, [createNewDashboard, isDashboardListLoading, onNewDashboardHandler, t]);
|
2023-03-03 12:09:24 +01:00
|
|
|
|
2023-11-20 14:53:13 +05:30
|
|
|
const searchArrayOfObjects = (searchValue: string): any[] => {
|
|
|
|
|
// Convert the searchValue to lowercase for case-insensitive search
|
|
|
|
|
const searchValueLowerCase = searchValue.toLowerCase();
|
|
|
|
|
|
|
|
|
|
// Use the filter method to find matching objects
|
|
|
|
|
return dashboardListResponse.filter((item: any) => {
|
|
|
|
|
// Convert each property value to lowercase for case-insensitive search
|
|
|
|
|
const itemValues = Object.values(item?.data).map((value: any) =>
|
|
|
|
|
value.toString().toLowerCase(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Check if any property value contains the searchValue
|
|
|
|
|
return itemValues.some((value) => value.includes(searchValueLowerCase));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSearch = useDebouncedFn((event: unknown): void => {
|
|
|
|
|
setIsFilteringDashboards(true);
|
|
|
|
|
const searchText = (event as React.BaseSyntheticEvent)?.target?.value || '';
|
|
|
|
|
const filteredDashboards = searchArrayOfObjects(searchText);
|
|
|
|
|
setDashboards(filteredDashboards);
|
|
|
|
|
setIsFilteringDashboards(false);
|
|
|
|
|
}, 500);
|
|
|
|
|
|
2022-04-25 22:41:46 +05:30
|
|
|
const GetHeader = useMemo(
|
|
|
|
|
() => (
|
2023-11-20 14:53:13 +05:30
|
|
|
<Row gutter={16} align="middle">
|
|
|
|
|
<Col span={18}>
|
|
|
|
|
<Search
|
|
|
|
|
disabled={isDashboardListLoading}
|
|
|
|
|
placeholder="Search by Name, Description, Tags"
|
|
|
|
|
onChange={handleSearch}
|
|
|
|
|
loading={isFilteringDashboards}
|
|
|
|
|
style={{ marginBottom: 16, marginTop: 16 }}
|
2022-04-22 18:57:05 +05:30
|
|
|
/>
|
2023-11-20 14:53:13 +05:30
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
<Col
|
|
|
|
|
span={6}
|
|
|
|
|
style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'flex-end',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ButtonContainer>
|
|
|
|
|
<TextToolTip
|
|
|
|
|
{...{
|
|
|
|
|
text: `More details on how to create dashboards`,
|
|
|
|
|
url: 'https://signoz.io/docs/userguide/dashboards',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</ButtonContainer>
|
2023-11-24 00:03:47 +05:30
|
|
|
|
|
|
|
|
<Dropdown
|
|
|
|
|
menu={{ items: getMenuItems }}
|
|
|
|
|
disabled={isDashboardListLoading}
|
|
|
|
|
placement="bottomRight"
|
|
|
|
|
>
|
|
|
|
|
<NewDashboardButton
|
|
|
|
|
icon={<PlusOutlined />}
|
|
|
|
|
type="primary"
|
|
|
|
|
data-testid="create-new-dashboard"
|
|
|
|
|
loading={newDashboardState.loading}
|
|
|
|
|
danger={newDashboardState.error}
|
|
|
|
|
>
|
|
|
|
|
{getText()}
|
|
|
|
|
</NewDashboardButton>
|
|
|
|
|
</Dropdown>
|
2023-11-20 14:53:13 +05:30
|
|
|
</Col>
|
2022-04-22 18:57:05 +05:30
|
|
|
</Row>
|
2022-04-25 22:41:46 +05:30
|
|
|
),
|
2022-05-04 20:40:49 +05:30
|
|
|
[
|
2023-11-20 14:53:13 +05:30
|
|
|
Search,
|
2023-09-11 10:51:10 +05:30
|
|
|
isDashboardListLoading,
|
2023-11-20 14:53:13 +05:30
|
|
|
handleSearch,
|
|
|
|
|
isFilteringDashboards,
|
2023-11-24 00:03:47 +05:30
|
|
|
getMenuItems,
|
2023-05-19 12:19:42 +05:30
|
|
|
newDashboardState.loading,
|
|
|
|
|
newDashboardState.error,
|
|
|
|
|
getText,
|
2022-05-04 20:40:49 +05:30
|
|
|
],
|
2022-04-25 22:41:46 +05:30
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card>
|
|
|
|
|
{GetHeader}
|
|
|
|
|
|
2022-04-22 18:57:05 +05:30
|
|
|
<TableContainer>
|
2022-04-25 22:41:46 +05:30
|
|
|
<ImportJSON
|
|
|
|
|
isImportJSONModalVisible={isImportJSONModalVisible}
|
2022-11-10 16:49:54 +05:30
|
|
|
uploadedGrafana={uploadedGrafana}
|
|
|
|
|
onModalHandler={(): void => onModalHandler(false)}
|
2022-04-25 22:41:46 +05:30
|
|
|
/>
|
2023-10-27 21:09:23 +05:30
|
|
|
<DynamicColumnTable
|
|
|
|
|
tablesource={TableDataSource.Dashboard}
|
|
|
|
|
dynamicColumns={dynamicColumns}
|
2023-02-03 18:06:26 +05:30
|
|
|
columns={columns}
|
|
|
|
|
pagination={{
|
2023-11-20 14:53:13 +05:30
|
|
|
pageSize: 10,
|
|
|
|
|
defaultPageSize: 10,
|
|
|
|
|
total: data?.length || 0,
|
2023-02-03 18:06:26 +05:30
|
|
|
}}
|
|
|
|
|
showHeader
|
|
|
|
|
bordered
|
|
|
|
|
sticky
|
2023-09-11 10:51:10 +05:30
|
|
|
loading={isDashboardListLoading}
|
2023-02-03 18:06:26 +05:30
|
|
|
dataSource={data}
|
|
|
|
|
showSorterTooltip
|
|
|
|
|
/>
|
2022-04-22 18:57:05 +05:30
|
|
|
</TableContainer>
|
|
|
|
|
</Card>
|
2021-09-23 15:43:43 +05:30
|
|
|
);
|
2022-03-22 12:10:31 +05:30
|
|
|
}
|
2021-09-23 15:43:43 +05:30
|
|
|
|
|
|
|
|
export interface Data {
|
2023-05-19 13:14:32 +05:30
|
|
|
key: Key;
|
2021-09-23 15:43:43 +05:30
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
tags: string[];
|
|
|
|
|
createdBy: string;
|
2023-10-27 21:09:23 +05:30
|
|
|
createdAt: string;
|
2021-09-23 15:43:43 +05:30
|
|
|
lastUpdatedTime: string;
|
2023-10-27 21:09:23 +05:30
|
|
|
lastUpdatedBy: string;
|
2023-11-03 17:27:09 +05:30
|
|
|
isLocked: boolean;
|
2021-09-23 15:43:43 +05:30
|
|
|
id: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ListOfAllDashboard;
|