mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-12 19:48:26 +00:00
UI : Data Insight KPI improvements (#9188)
* UI : Data Insight KPI improvements * Remove commented code * Remove time from start and end date * refactor : add logic to generate kpi name * Make insight to insights * Show chart at the top * Fix: breadcrumb spacing * refactor : start date and end date of kpi * test: add unit test for kpi list * test : add unit tests for add and edit kpi forms * chore : minor change * Address review comments * Address review comments
This commit is contained in:
parent
6f12c971e1
commit
fce151c0d4
@ -181,7 +181,7 @@ export const SUPPORTED_CHARTS_FOR_KPI = [
|
|||||||
DataInsightChartType.PercentageOfEntitiesWithOwnerByType,
|
DataInsightChartType.PercentageOfEntitiesWithOwnerByType,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const KPI_DATE_PICKER_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
export const KPI_DATE_PICKER_FORMAT = 'YYYY-MM-DD';
|
||||||
|
|
||||||
export const KPI_DATES = {
|
export const KPI_DATES = {
|
||||||
startDate: '',
|
startDate: '',
|
||||||
|
@ -383,6 +383,7 @@
|
|||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"restore-team": "Restore Team",
|
"restore-team": "Restore Team",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
|
"data-insight-plural": "Data Insights",
|
||||||
"configure-entity": "Configure {{entity}}",
|
"configure-entity": "Configure {{entity}}",
|
||||||
"name-lowercase": "name",
|
"name-lowercase": "name",
|
||||||
"field-invalid": "{{field}} is invalid",
|
"field-invalid": "{{field}} is invalid",
|
||||||
|
@ -178,7 +178,9 @@ const DataInsightPage = () => {
|
|||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space className="w-full justify-between">
|
<Space className="w-full justify-between">
|
||||||
<div data-testid="data-insight-header">
|
<div data-testid="data-insight-header">
|
||||||
<Typography.Title level={5}>Data Insight</Typography.Title>
|
<Typography.Title level={5}>
|
||||||
|
{t('label.data-insight-plural')}
|
||||||
|
</Typography.Title>
|
||||||
<Typography.Text className="data-insight-label-text">
|
<Typography.Text className="data-insight-label-text">
|
||||||
{t('label.data-insight-subtitle')}
|
{t('label.data-insight-subtitle')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { act } from 'react-test-renderer';
|
||||||
|
import KPIList from './KPIList';
|
||||||
|
import { KPI_DATA } from './mocks/KPIList';
|
||||||
|
|
||||||
|
const mockPush = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
useHistory: jest.fn().mockImplementation(() => ({
|
||||||
|
push: mockPush,
|
||||||
|
})),
|
||||||
|
Link: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(({ children }: { children: React.ReactNode }) => (
|
||||||
|
<span>{children}</span>
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../components/common/DeleteWidget/DeleteWidgetModal', () =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="delete-modal">Delete Modal</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../components/common/next-previous/NextPrevious', () =>
|
||||||
|
jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue(<div data-testid="next-previous">Next Previous</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'../../components/common/rich-text-editor/RichTextEditorPreviewer',
|
||||||
|
() => jest.fn().mockReturnValue(<div data-testid="editor">Editor</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../components/Loader/Loader', () =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="loader">Loader</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../hooks/authHooks', () => ({
|
||||||
|
useAuth: jest.fn().mockReturnValue({ isAdminUser: true }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../utils/TimeUtils', () => ({
|
||||||
|
formatDateTime: jest.fn().mockReturnValue('7 Dec 2022, 00:00'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../axiosAPIs/KpiAPI', () => ({
|
||||||
|
getListKPIs: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve({ data: KPI_DATA })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('KPI list component', () => {
|
||||||
|
it('Should render the kpi list', async () => {
|
||||||
|
render(<KPIList />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const container = await screen.findByTestId('kpi-table');
|
||||||
|
const descriptionKPI = await screen.findByText('Description KPI');
|
||||||
|
const ownerKPI = await screen.findByText('Owner KPI');
|
||||||
|
|
||||||
|
expect(container).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(descriptionKPI).toBeInTheDocument();
|
||||||
|
expect(ownerKPI).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Action button should work', async () => {
|
||||||
|
const KPI = KPI_DATA[0];
|
||||||
|
|
||||||
|
render(<KPIList />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const editButton = await screen.findByTestId(
|
||||||
|
`edit-action-${KPI.displayName}`
|
||||||
|
);
|
||||||
|
const deleteButton = await screen.findByTestId(
|
||||||
|
`delete-action-${KPI.displayName}`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(editButton).toBeInTheDocument();
|
||||||
|
expect(deleteButton).toBeInTheDocument();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(editButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockPush).toBeCalled();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(deleteButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await screen.findByTestId('delete-modal')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -11,12 +11,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Col, Table, Tooltip, Typography } from 'antd';
|
import { Button, Col, Space, Table, Tooltip, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import { getListKPIs } from '../../axiosAPIs/KpiAPI';
|
import { getListKPIs } from '../../axiosAPIs/KpiAPI';
|
||||||
import DeleteWidgetModal from '../../components/common/DeleteWidget/DeleteWidgetModal';
|
import DeleteWidgetModal from '../../components/common/DeleteWidget/DeleteWidgetModal';
|
||||||
import NextPrevious from '../../components/common/next-previous/NextPrevious';
|
import NextPrevious from '../../components/common/next-previous/NextPrevious';
|
||||||
@ -28,7 +28,6 @@ import {
|
|||||||
PAGE_SIZE_MEDIUM,
|
PAGE_SIZE_MEDIUM,
|
||||||
pagingObject,
|
pagingObject,
|
||||||
} from '../../constants/constants';
|
} from '../../constants/constants';
|
||||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
|
||||||
import { EntityType } from '../../enums/entity.enum';
|
import { EntityType } from '../../enums/entity.enum';
|
||||||
import { Kpi, KpiTargetType } from '../../generated/dataInsight/kpi/kpi';
|
import { Kpi, KpiTargetType } from '../../generated/dataInsight/kpi/kpi';
|
||||||
|
|
||||||
@ -39,6 +38,7 @@ import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
|||||||
import { formatDateTime } from '../../utils/TimeUtils';
|
import { formatDateTime } from '../../utils/TimeUtils';
|
||||||
|
|
||||||
const KPIList = () => {
|
const KPIList = () => {
|
||||||
|
const history = useHistory();
|
||||||
const { isAdminUser } = useAuth();
|
const { isAdminUser } = useAuth();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
||||||
@ -122,21 +122,46 @@ const KPIList = () => {
|
|||||||
key: 'actions',
|
key: 'actions',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Space>
|
||||||
placement="left"
|
<Tooltip
|
||||||
title={
|
placement="left"
|
||||||
isAdminUser ? t('label.delete') : NO_PERMISSION_FOR_ACTION
|
title={
|
||||||
}>
|
isAdminUser
|
||||||
<Button
|
? t('label.edit')
|
||||||
data-testid={`delete-action-${getEntityName(record)}`}
|
: t('message.no-permission-for-action')
|
||||||
disabled={!isAdminUser}
|
}>
|
||||||
icon={
|
<Button
|
||||||
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
data-testid={`edit-action-${getEntityName(record)}`}
|
||||||
}
|
disabled={!isAdminUser}
|
||||||
type="text"
|
icon={
|
||||||
onClick={() => setSelectedKpi(record)}
|
<SVGIcons
|
||||||
/>
|
alt={t('label.edit')}
|
||||||
</Tooltip>
|
icon={Icons.EDIT}
|
||||||
|
width="18px"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
type="text"
|
||||||
|
onClick={() => history.push(getKpiPath(record.name))}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
title={
|
||||||
|
isAdminUser
|
||||||
|
? t('label.delete')
|
||||||
|
: t('message.no-permission-for-action')
|
||||||
|
}>
|
||||||
|
<Button
|
||||||
|
data-testid={`delete-action-${getEntityName(record)}`}
|
||||||
|
disabled={!isAdminUser}
|
||||||
|
icon={
|
||||||
|
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
||||||
|
}
|
||||||
|
type="text"
|
||||||
|
onClick={() => setSelectedKpi(record)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -165,6 +190,7 @@ const KPIList = () => {
|
|||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
data-testid="kpi-table"
|
||||||
dataSource={kpiList}
|
dataSource={kpiList}
|
||||||
loading={{ spinning: isLoading, indicator: <Loader /> }}
|
loading={{ spinning: isLoading, indicator: <Loader /> }}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const KPI_DATA = [
|
||||||
|
{
|
||||||
|
id: 'dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
name: 'description-kpi',
|
||||||
|
displayName: 'Description KPI',
|
||||||
|
fullyQualifiedName: 'description-kpi',
|
||||||
|
description: '',
|
||||||
|
metricType: 'PERCENTAGE',
|
||||||
|
targetDefinition: [
|
||||||
|
{
|
||||||
|
name: 'completedDescriptionFraction',
|
||||||
|
value: '0.65',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
startDate: 1670351400000,
|
||||||
|
endDate: 1672165800000,
|
||||||
|
version: 0.2,
|
||||||
|
updatedAt: 1670414685805,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/kpi/dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
changeDescription: {
|
||||||
|
fieldsAdded: [],
|
||||||
|
fieldsUpdated: [
|
||||||
|
{
|
||||||
|
name: 'startDate',
|
||||||
|
oldValue: 1670395661000,
|
||||||
|
newValue: 1670351400000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'endDate',
|
||||||
|
oldValue: 1672210072000,
|
||||||
|
newValue: 1672165800000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.1,
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bb71e000-f837-4e3c-8e03-0a0f1fd35667',
|
||||||
|
name: 'owner-kpi-has-owner-fraction',
|
||||||
|
displayName: 'Owner KPI',
|
||||||
|
fullyQualifiedName: 'owner-kpi-has-owner-fraction',
|
||||||
|
description: '',
|
||||||
|
metricType: 'PERCENTAGE',
|
||||||
|
targetDefinition: [
|
||||||
|
{
|
||||||
|
name: 'hasOwnerFraction',
|
||||||
|
value: '0.64',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
startDate: 1670351400000,
|
||||||
|
endDate: 1672511340000,
|
||||||
|
version: 0.2,
|
||||||
|
updatedAt: 1670415195507,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/kpi/bb71e000-f837-4e3c-8e03-0a0f1fd35667',
|
||||||
|
changeDescription: {
|
||||||
|
fieldsAdded: [],
|
||||||
|
fieldsUpdated: [
|
||||||
|
{
|
||||||
|
name: 'endDate',
|
||||||
|
oldValue: 1672252140000,
|
||||||
|
newValue: 1672511340000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.1,
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
act,
|
||||||
|
findByRole,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitForElement,
|
||||||
|
} from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import AddKPIPage from './AddKPIPage';
|
||||||
|
|
||||||
|
import { KPI_CHARTS, KPI_DATA, KPI_LIST } from './KPIMock.mock';
|
||||||
|
|
||||||
|
const mockPush = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
useHistory: jest.fn().mockReturnValue({
|
||||||
|
push: mockPush,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../axiosAPIs/DataInsightAPI', () => ({
|
||||||
|
getListDataInsightCharts: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve({ data: KPI_CHARTS })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../components/common/rich-text-editor/RichTextEditor', () =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="editor">Editor</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'../../components/common/title-breadcrumb/title-breadcrumb.component',
|
||||||
|
() =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="breadcrumb">BreadCrumb</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../axiosAPIs/KpiAPI', () => ({
|
||||||
|
getListKPIs: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve({ data: KPI_LIST })),
|
||||||
|
postKPI: jest.fn().mockImplementation(() => Promise.resolve(KPI_DATA)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../utils/CommonUtils', () => ({
|
||||||
|
isUrlFriendlyName: jest.fn().mockReturnValue(true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../utils/DataInsightUtils', () => ({
|
||||||
|
getKpiTargetValueByMetricType: jest.fn().mockReturnValue(10),
|
||||||
|
getKPIFormattedDates: jest.fn().mockReturnValue({
|
||||||
|
startDate: `2022-12-08 00:00`,
|
||||||
|
endDate: `2022-12-28 23:59`,
|
||||||
|
}),
|
||||||
|
getDisabledDates: jest.fn().mockReturnValue(true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Add KPI page', () => {
|
||||||
|
it('Should render all the components', async () => {
|
||||||
|
render(<AddKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const container = await screen.findByTestId('add-kpi-container');
|
||||||
|
const breadCrumb = await screen.findByTestId('breadcrumb');
|
||||||
|
const formTitle = await screen.findByTestId('form-title');
|
||||||
|
const rightPanel = await screen.findByTestId('right-panel');
|
||||||
|
|
||||||
|
expect(container).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(breadCrumb).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(formTitle).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(formTitle.textContent).toContain('label.add-new-kpi');
|
||||||
|
|
||||||
|
const formContainer = await screen.findByTestId('kpi-form');
|
||||||
|
|
||||||
|
expect(formContainer).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(rightPanel).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should render all the form fields', async () => {
|
||||||
|
render(<AddKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const formContainer = await screen.findByTestId('kpi-form');
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
const displayName = await screen.findByTestId('displayName');
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
const startDate = await screen.findByTestId('start-date');
|
||||||
|
const endDate = await screen.findByTestId('end-date');
|
||||||
|
const editor = await screen.findByTestId('editor');
|
||||||
|
const cancelButton = await screen.findByTestId('cancel-btn');
|
||||||
|
const submitButton = await screen.findByTestId('submit-btn');
|
||||||
|
|
||||||
|
expect(formContainer).toBeInTheDocument();
|
||||||
|
expect(chart).toBeInTheDocument();
|
||||||
|
expect(displayName).toBeInTheDocument();
|
||||||
|
expect(metricType).toBeInTheDocument();
|
||||||
|
expect(startDate).toBeInTheDocument();
|
||||||
|
expect(endDate).toBeInTheDocument();
|
||||||
|
expect(editor).toBeInTheDocument();
|
||||||
|
expect(cancelButton).toBeInTheDocument();
|
||||||
|
expect(submitButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Metric type input should be disable if chart is not selected', async () => {
|
||||||
|
render(<AddKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
|
||||||
|
expect(chart).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(metricType).toHaveClass('ant-select-disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Metric type input should not be disable if chart is selected', async () => {
|
||||||
|
render(<AddKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
|
||||||
|
const chartInput = await findByRole(chart, 'combobox');
|
||||||
|
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
userEvent.click(chartInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForElement(() =>
|
||||||
|
screen.getByText('Percentage of Entities With Owner')
|
||||||
|
);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(screen.getByText('Percentage of Entities With Owner'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(chart).toBeInTheDocument();
|
||||||
|
expect(metricType).not.toHaveClass('ant-select-disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should render the proper metric input based on metric type', async () => {
|
||||||
|
render(<AddKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
|
||||||
|
const chartInput = await findByRole(chart, 'combobox');
|
||||||
|
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
|
||||||
|
const metricInput = await findByRole(metricType, 'combobox');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
userEvent.click(chartInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForElement(() =>
|
||||||
|
screen.getByText('Percentage of Entities With Owner')
|
||||||
|
);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(screen.getByText('Percentage of Entities With Owner'));
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
userEvent.click(metricInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
// check for percentage type
|
||||||
|
await waitForElement(() =>
|
||||||
|
screen.getByText('hasOwnerFraction (PERCENTAGE)')
|
||||||
|
);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(screen.getByText('hasOwnerFraction (PERCENTAGE)'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('metric-percentage-input')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
// check for number type
|
||||||
|
await waitForElement(() => screen.getByText('hasOwner (NUMBER)'));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(screen.getByText('hasOwner (NUMBER)'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('metric-number-input')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -27,7 +27,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined, kebabCase } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
@ -46,7 +46,6 @@ import {
|
|||||||
VALIDATE_MESSAGES,
|
VALIDATE_MESSAGES,
|
||||||
} from '../../constants/DataInsight.constants';
|
} from '../../constants/DataInsight.constants';
|
||||||
import { ADD_KPI_TEXT } from '../../constants/HelperTextUtil';
|
import { ADD_KPI_TEXT } from '../../constants/HelperTextUtil';
|
||||||
import { nameWithSpace } from '../../constants/regex.constants';
|
|
||||||
import { EntityType } from '../../enums/entity.enum';
|
import { EntityType } from '../../enums/entity.enum';
|
||||||
import {
|
import {
|
||||||
CreateKpiRequest,
|
CreateKpiRequest,
|
||||||
@ -60,9 +59,9 @@ import {
|
|||||||
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
import { DataInsightChartType } from '../../generated/dataInsight/dataInsightChartResult';
|
||||||
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
|
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
|
||||||
import { KpiDate, KpiDates } from '../../interface/data-insight.interface';
|
import { KpiDate, KpiDates } from '../../interface/data-insight.interface';
|
||||||
import { isUrlFriendlyName } from '../../utils/CommonUtils';
|
|
||||||
import {
|
import {
|
||||||
getDisabledDates,
|
getDisabledDates,
|
||||||
|
getKPIFormattedDates,
|
||||||
getKpiTargetValueByMetricType,
|
getKpiTargetValueByMetricType,
|
||||||
} from '../../utils/DataInsightUtils';
|
} from '../../utils/DataInsightUtils';
|
||||||
import { getTimeStampByDateTime } from '../../utils/TimeUtils';
|
import { getTimeStampByDateTime } from '../../utils/TimeUtils';
|
||||||
@ -164,8 +163,10 @@ const AddKPIPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit: FormProps['onFinish'] = async (values) => {
|
const handleSubmit: FormProps['onFinish'] = async (values) => {
|
||||||
const startDate = getTimeStampByDateTime(kpiDates.startDate);
|
const formattedDates = getKPIFormattedDates(kpiDates);
|
||||||
const endDate = getTimeStampByDateTime(kpiDates.endDate);
|
|
||||||
|
const startDate = getTimeStampByDateTime(formattedDates.startDate);
|
||||||
|
const endDate = getTimeStampByDateTime(formattedDates.endDate);
|
||||||
const metricType =
|
const metricType =
|
||||||
selectedMetric?.chartDataType as unknown as KpiTargetType;
|
selectedMetric?.chartDataType as unknown as KpiTargetType;
|
||||||
|
|
||||||
@ -177,7 +178,7 @@ const AddKPIPage = () => {
|
|||||||
type: EntityType.DATA_INSIGHT_CHART,
|
type: EntityType.DATA_INSIGHT_CHART,
|
||||||
},
|
},
|
||||||
description,
|
description,
|
||||||
name: values.name,
|
name: kebabCase(`${values.displayName} ${selectedMetric?.name}`),
|
||||||
displayName: values.displayName,
|
displayName: values.displayName,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
@ -211,8 +212,8 @@ const AddKPIPage = () => {
|
|||||||
data-testid="add-kpi-container"
|
data-testid="add-kpi-container"
|
||||||
gutter={[16, 16]}>
|
gutter={[16, 16]}>
|
||||||
<Col offset={4} span={12}>
|
<Col offset={4} span={12}>
|
||||||
<TitleBreadcrumb titleLinks={breadcrumb} />
|
<TitleBreadcrumb className="my-4" titleLinks={breadcrumb} />
|
||||||
<Card className="mt-4">
|
<Card>
|
||||||
<Typography.Paragraph className="text-base" data-testid="form-title">
|
<Typography.Paragraph className="text-base" data-testid="form-title">
|
||||||
{t('label.add-new-kpi')}
|
{t('label.add-new-kpi')}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
@ -222,43 +223,6 @@ const AddKPIPage = () => {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
validateMessages={VALIDATE_MESSAGES}
|
validateMessages={VALIDATE_MESSAGES}
|
||||||
onFinish={handleSubmit}>
|
onFinish={handleSubmit}>
|
||||||
<Form.Item
|
|
||||||
label={t('label.name')}
|
|
||||||
name="name"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
max: 128,
|
|
||||||
min: 1,
|
|
||||||
validator: (_, value) => {
|
|
||||||
if (
|
|
||||||
!isUrlFriendlyName(value) ||
|
|
||||||
nameWithSpace.test(value)
|
|
||||||
) {
|
|
||||||
return Promise.reject(
|
|
||||||
t('label.special-character-not-allowed')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Input
|
|
||||||
data-testid="name"
|
|
||||||
placeholder={t('label.kpi-name')}
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label={t('label.display-name')} name="displayName">
|
|
||||||
<Input
|
|
||||||
data-testid="displayName"
|
|
||||||
placeholder={t('label.kpi-display-name')}
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('label.select-a-chart')}
|
label={t('label.select-a-chart')}
|
||||||
name="dataInsightChart"
|
name="dataInsightChart"
|
||||||
@ -282,6 +246,14 @@ const AddKPIPage = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label={t('label.display-name')} name="displayName">
|
||||||
|
<Input
|
||||||
|
data-testid="displayName"
|
||||||
|
placeholder={t('label.kpi-display-name')}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('label.select-a-metric-type')}
|
label={t('label.select-a-metric-type')}
|
||||||
name="metricType"
|
name="metricType"
|
||||||
@ -324,7 +296,7 @@ const AddKPIPage = () => {
|
|||||||
<>
|
<>
|
||||||
{selectedMetric.chartDataType ===
|
{selectedMetric.chartDataType ===
|
||||||
ChartDataType.Percentage && (
|
ChartDataType.Percentage && (
|
||||||
<Row gutter={20}>
|
<Row data-testid="metric-percentage-input" gutter={20}>
|
||||||
<Col span={20}>
|
<Col span={20}>
|
||||||
<Slider
|
<Slider
|
||||||
className="kpi-slider"
|
className="kpi-slider"
|
||||||
@ -359,6 +331,7 @@ const AddKPIPage = () => {
|
|||||||
{selectedMetric.chartDataType === ChartDataType.Number && (
|
{selectedMetric.chartDataType === ChartDataType.Number && (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="metric-number-input"
|
||||||
min={0}
|
min={0}
|
||||||
value={metricValue}
|
value={metricValue}
|
||||||
onChange={(value) => setMetricValue(Number(value))}
|
onChange={(value) => setMetricValue(Number(value))}
|
||||||
@ -377,11 +350,14 @@ const AddKPIPage = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
message: t('label.field-required', {
|
||||||
|
field: t('label.start-date'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
showTime
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="start-date"
|
||||||
disabledDate={getDisabledDates}
|
disabledDate={getDisabledDates}
|
||||||
format={KPI_DATE_PICKER_FORMAT}
|
format={KPI_DATE_PICKER_FORMAT}
|
||||||
onChange={(_, dateString) =>
|
onChange={(_, dateString) =>
|
||||||
@ -398,11 +374,14 @@ const AddKPIPage = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
message: t('label.field-required', {
|
||||||
|
field: t('label.end-date'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
showTime
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="end-date"
|
||||||
disabledDate={getDisabledDates}
|
disabledDate={getDisabledDates}
|
||||||
format={KPI_DATE_PICKER_FORMAT}
|
format={KPI_DATE_PICKER_FORMAT}
|
||||||
onChange={(_, dateString) =>
|
onChange={(_, dateString) =>
|
||||||
@ -442,7 +421,7 @@ const AddKPIPage = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col className="m-t-md" span={4}>
|
<Col className="m-t-md" data-testid="right-panel" span={4}>
|
||||||
<Typography.Paragraph className="text-base font-medium">
|
<Typography.Paragraph className="text-base font-medium">
|
||||||
{t('label.add-kpi')}
|
{t('label.add-kpi')}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import EditKPIPage from './EditKPIPage';
|
||||||
|
|
||||||
|
import { DESCRIPTION_CHART, KPI_DATA } from './KPIMock.mock';
|
||||||
|
|
||||||
|
const mockPush = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
useHistory: jest.fn().mockReturnValue({
|
||||||
|
push: mockPush,
|
||||||
|
}),
|
||||||
|
useParams: jest.fn().mockReturnValue({ useParams: 'description-kpi' }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../axiosAPIs/DataInsightAPI', () => ({
|
||||||
|
getChartById: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve(DESCRIPTION_CHART)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../components/common/rich-text-editor/RichTextEditor', () =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="editor">Editor</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'../../components/common/title-breadcrumb/title-breadcrumb.component',
|
||||||
|
() =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="breadcrumb">BreadCrumb</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../components/Loader/Loader', () =>
|
||||||
|
jest.fn().mockReturnValue(<div data-testid="loader">Loader</div>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../axiosAPIs/KpiAPI', () => ({
|
||||||
|
getKPIByName: jest.fn().mockImplementation(() => Promise.resolve(KPI_DATA)),
|
||||||
|
patchKPI: jest.fn().mockImplementation(() => Promise.resolve(KPI_DATA)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../hooks/authHooks', () => ({
|
||||||
|
useAuth: jest.fn().mockReturnValue({ isAdminUser: true }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../utils/DataInsightUtils', () => ({
|
||||||
|
getKpiTargetValueByMetricType: jest.fn().mockReturnValue(10),
|
||||||
|
getKPIFormattedDates: jest.fn().mockReturnValue({
|
||||||
|
startDate: `2022-12-08 00:00`,
|
||||||
|
endDate: `2022-12-28 23:59`,
|
||||||
|
}),
|
||||||
|
getDisabledDates: jest.fn().mockReturnValue(true),
|
||||||
|
getKpiDateFormatByTimeStamp: jest.fn().mockReturnValue('2022-12-08'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Edit KPI page', () => {
|
||||||
|
it('Should render all the components', async () => {
|
||||||
|
render(<EditKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const container = await screen.findByTestId('edit-kpi-container');
|
||||||
|
const breadCrumb = await screen.findByTestId('breadcrumb');
|
||||||
|
const formTitle = await screen.findByTestId('form-title');
|
||||||
|
const rightPanel = await screen.findByTestId('right-panel');
|
||||||
|
|
||||||
|
expect(container).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(breadCrumb).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(formTitle).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(formTitle.textContent).toContain('label.edit-entity');
|
||||||
|
|
||||||
|
const formContainer = await screen.findByTestId('kpi-form');
|
||||||
|
|
||||||
|
expect(formContainer).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(rightPanel).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should render all the form fields', async () => {
|
||||||
|
render(<EditKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const formContainer = await screen.findByTestId('kpi-form');
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
const displayName = await screen.findByTestId('displayName');
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
const startDate = await screen.findByTestId('start-date');
|
||||||
|
const endDate = await screen.findByTestId('end-date');
|
||||||
|
const editor = await screen.findByTestId('editor');
|
||||||
|
const cancelButton = await screen.findByTestId('cancel-btn');
|
||||||
|
const submitButton = await screen.findByTestId('submit-btn');
|
||||||
|
|
||||||
|
expect(formContainer).toBeInTheDocument();
|
||||||
|
expect(chart).toBeInTheDocument();
|
||||||
|
expect(displayName).toBeInTheDocument();
|
||||||
|
expect(metricType).toBeInTheDocument();
|
||||||
|
expect(startDate).toBeInTheDocument();
|
||||||
|
expect(endDate).toBeInTheDocument();
|
||||||
|
expect(editor).toBeInTheDocument();
|
||||||
|
expect(cancelButton).toBeInTheDocument();
|
||||||
|
expect(submitButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Chart input and Metric type input should be disable for edit form', async () => {
|
||||||
|
render(<EditKPIPage />, { wrapper: MemoryRouter });
|
||||||
|
|
||||||
|
const chart = await screen.findByTestId('dataInsightChart');
|
||||||
|
|
||||||
|
const metricType = await screen.findByTestId('metricType');
|
||||||
|
|
||||||
|
expect(chart).toHaveClass('ant-input-disabled');
|
||||||
|
|
||||||
|
expect(metricType).toHaveClass('ant-input-disabled');
|
||||||
|
});
|
||||||
|
});
|
@ -47,10 +47,7 @@ import {
|
|||||||
KPI_DATE_PICKER_FORMAT,
|
KPI_DATE_PICKER_FORMAT,
|
||||||
VALIDATE_MESSAGES,
|
VALIDATE_MESSAGES,
|
||||||
} from '../../constants/DataInsight.constants';
|
} from '../../constants/DataInsight.constants';
|
||||||
import {
|
import { ADD_KPI_TEXT } from '../../constants/HelperTextUtil';
|
||||||
ADD_KPI_TEXT,
|
|
||||||
NO_PERMISSION_FOR_ACTION,
|
|
||||||
} from '../../constants/HelperTextUtil';
|
|
||||||
import { DataInsightChart } from '../../generated/dataInsight/dataInsightChart';
|
import { DataInsightChart } from '../../generated/dataInsight/dataInsightChart';
|
||||||
import { Kpi, KpiTargetType } from '../../generated/dataInsight/kpi/kpi';
|
import { Kpi, KpiTargetType } from '../../generated/dataInsight/kpi/kpi';
|
||||||
import { useAuth } from '../../hooks/authHooks';
|
import { useAuth } from '../../hooks/authHooks';
|
||||||
@ -58,12 +55,13 @@ import { KpiDate, KpiDates } from '../../interface/data-insight.interface';
|
|||||||
import {
|
import {
|
||||||
getDisabledDates,
|
getDisabledDates,
|
||||||
getKpiDateFormatByTimeStamp,
|
getKpiDateFormatByTimeStamp,
|
||||||
|
getKPIFormattedDates,
|
||||||
getKpiTargetValueByMetricType,
|
getKpiTargetValueByMetricType,
|
||||||
} from '../../utils/DataInsightUtils';
|
} from '../../utils/DataInsightUtils';
|
||||||
import { getTimeStampByDateTime } from '../../utils/TimeUtils';
|
import { getTimeStampByDateTime } from '../../utils/TimeUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
|
||||||
const AddKPIPage = () => {
|
const EditKPIPage = () => {
|
||||||
const { isAdminUser } = useAuth();
|
const { isAdminUser } = useAuth();
|
||||||
const { kpiName } = useParams<{ kpiName: string }>();
|
const { kpiName } = useParams<{ kpiName: string }>();
|
||||||
|
|
||||||
@ -164,8 +162,10 @@ const AddKPIPage = () => {
|
|||||||
|
|
||||||
const handleSubmit: FormProps['onFinish'] = async (values) => {
|
const handleSubmit: FormProps['onFinish'] = async (values) => {
|
||||||
if (kpiData && metricData) {
|
if (kpiData && metricData) {
|
||||||
const startDate = getTimeStampByDateTime(kpiDates.startDate);
|
const formattedDates = getKPIFormattedDates(kpiDates);
|
||||||
const endDate = getTimeStampByDateTime(kpiDates.endDate);
|
|
||||||
|
const startDate = getTimeStampByDateTime(formattedDates.startDate);
|
||||||
|
const endDate = getTimeStampByDateTime(formattedDates.endDate);
|
||||||
|
|
||||||
const targetValue = getKpiTargetValueByMetricType(
|
const targetValue = getKpiTargetValueByMetricType(
|
||||||
kpiData.metricType,
|
kpiData.metricType,
|
||||||
@ -234,15 +234,15 @@ const AddKPIPage = () => {
|
|||||||
{kpiData ? (
|
{kpiData ? (
|
||||||
<Row
|
<Row
|
||||||
className="bg-body-main h-full"
|
className="bg-body-main h-full"
|
||||||
data-testid="add-kpi-container"
|
data-testid="edit-kpi-container"
|
||||||
gutter={[16, 16]}>
|
gutter={[16, 16]}>
|
||||||
<Col offset={4} span={12}>
|
<Col offset={4} span={12}>
|
||||||
<TitleBreadcrumb titleLinks={breadcrumb} />
|
<TitleBreadcrumb className="my-4" titleLinks={breadcrumb} />
|
||||||
<Card className="mt-4">
|
<Card>
|
||||||
<Typography.Paragraph
|
<Typography.Paragraph
|
||||||
className="text-base"
|
className="text-base"
|
||||||
data-testid="form-title">
|
data-testid="form-title">
|
||||||
{t('label.add-new-kpi')}
|
{t('label.edit-entity', { entity: t('label.kpi-uppercase') })}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
<Form
|
<Form
|
||||||
data-testid="kpi-form"
|
data-testid="kpi-form"
|
||||||
@ -251,12 +251,13 @@ const AddKPIPage = () => {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
validateMessages={VALIDATE_MESSAGES}
|
validateMessages={VALIDATE_MESSAGES}
|
||||||
onFinish={handleSubmit}>
|
onFinish={handleSubmit}>
|
||||||
<Form.Item label={t('label.name')} name="name">
|
<Form.Item
|
||||||
|
label={t('label.data-insight-chart')}
|
||||||
|
name="dataInsightChart">
|
||||||
<Input
|
<Input
|
||||||
disabled
|
disabled
|
||||||
data-testid="name"
|
data-testid="dataInsightChart"
|
||||||
placeholder={t('label.kpi-name')}
|
value={selectedChart?.displayName || selectedChart?.name}
|
||||||
type="text"
|
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@ -268,19 +269,14 @@ const AddKPIPage = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item label={t('label.metric-type')} name="metricType">
|
||||||
label={t('label.data-insight-chart')}
|
|
||||||
name="dataInsightChart">
|
|
||||||
<Input
|
<Input
|
||||||
disabled
|
disabled
|
||||||
value={selectedChart?.displayName || selectedChart?.name}
|
data-testid="metricType"
|
||||||
|
value={metricData?.name}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('label.metric-type')} name="metricType">
|
|
||||||
<Input disabled value={metricData?.name} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
{!isUndefined(metricData) && (
|
{!isUndefined(metricData) && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('label.metric-value')}
|
label={t('label.metric-value')}
|
||||||
@ -301,7 +297,7 @@ const AddKPIPage = () => {
|
|||||||
]}>
|
]}>
|
||||||
<>
|
<>
|
||||||
{kpiData?.metricType === KpiTargetType.Percentage && (
|
{kpiData?.metricType === KpiTargetType.Percentage && (
|
||||||
<Row gutter={20}>
|
<Row data-testid="metric-percentage-input" gutter={20}>
|
||||||
<Col span={20}>
|
<Col span={20}>
|
||||||
<Slider
|
<Slider
|
||||||
className="kpi-slider"
|
className="kpi-slider"
|
||||||
@ -336,6 +332,7 @@ const AddKPIPage = () => {
|
|||||||
{kpiData?.metricType === KpiTargetType.Number && (
|
{kpiData?.metricType === KpiTargetType.Number && (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="metric-number-input"
|
||||||
min={0}
|
min={0}
|
||||||
value={metricValue}
|
value={metricValue}
|
||||||
onChange={(value) => setMetricValue(Number(value))}
|
onChange={(value) => setMetricValue(Number(value))}
|
||||||
@ -354,11 +351,14 @@ const AddKPIPage = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
message: t('label.field-required', {
|
||||||
|
field: t('label.start-date'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
showTime
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="start-date"
|
||||||
disabledDate={getDisabledDates}
|
disabledDate={getDisabledDates}
|
||||||
format={KPI_DATE_PICKER_FORMAT}
|
format={KPI_DATE_PICKER_FORMAT}
|
||||||
onChange={(_, dateString) =>
|
onChange={(_, dateString) =>
|
||||||
@ -375,11 +375,14 @@ const AddKPIPage = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
message: t('label.field-required', {
|
||||||
|
field: t('label.end-date'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
showTime
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
data-testid="end-date"
|
||||||
disabledDate={getDisabledDates}
|
disabledDate={getDisabledDates}
|
||||||
format={KPI_DATE_PICKER_FORMAT}
|
format={KPI_DATE_PICKER_FORMAT}
|
||||||
onChange={(_, dateString) =>
|
onChange={(_, dateString) =>
|
||||||
@ -409,7 +412,9 @@ const AddKPIPage = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
isAdminUser ? t('label.save') : NO_PERMISSION_FOR_ACTION
|
isAdminUser
|
||||||
|
? t('label.save')
|
||||||
|
: t('message.no-permission-for-action')
|
||||||
}>
|
}>
|
||||||
<Button
|
<Button
|
||||||
data-testid="submit-btn"
|
data-testid="submit-btn"
|
||||||
@ -425,7 +430,7 @@ const AddKPIPage = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col className="m-t-md" span={4}>
|
<Col className="m-t-md" data-testid="right-panel" span={4}>
|
||||||
<Typography.Paragraph className="text-base font-medium">
|
<Typography.Paragraph className="text-base font-medium">
|
||||||
{t('label.edit-entity', { entity: t('label.kpi-uppercase') })}
|
{t('label.edit-entity', { entity: t('label.kpi-uppercase') })}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
@ -441,4 +446,4 @@ const AddKPIPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddKPIPage;
|
export default EditKPIPage;
|
||||||
|
@ -0,0 +1,444 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Collate
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const KPI_CHARTS = [
|
||||||
|
{
|
||||||
|
id: 'd2f093d4-0ca8-42b8-8721-1c2a59951b59',
|
||||||
|
name: 'dailyActiveUsers',
|
||||||
|
displayName: 'Daily active users on the platform',
|
||||||
|
fullyQualifiedName: 'dailyActiveUsers',
|
||||||
|
description: 'Display the number of users active.',
|
||||||
|
dataIndexType: 'web_analytic_user_activity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'activeUsers',
|
||||||
|
displayName: 'Number of active users',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952802,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/d2f093d4-0ca8-42b8-8721-1c2a59951b59',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fbad142d-16d5-479d-bed3-67bb5fd4104d',
|
||||||
|
name: 'mostActiveUsers',
|
||||||
|
displayName: 'Most Active Users',
|
||||||
|
fullyQualifiedName: 'mostActiveUsers',
|
||||||
|
description:
|
||||||
|
'Displays the most active users on the platform based on page views.',
|
||||||
|
dataIndexType: 'web_analytic_user_activity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'userName',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'team',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'lastSession',
|
||||||
|
displayName: 'Last time the user visited the platform',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sessions',
|
||||||
|
displayName: 'Total number of sessions',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'avgSessionDuration',
|
||||||
|
displayName: 'The average duration time of a session',
|
||||||
|
chartDataType: 'FLOAT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pageViews',
|
||||||
|
displayName: 'Total number of page view',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952821,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/fbad142d-16d5-479d-bed3-67bb5fd4104d',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9217560a-3fed-4c0d-85fa-d7c699feefac',
|
||||||
|
name: 'mostViewedEntities',
|
||||||
|
displayName: 'Most Viewed entites',
|
||||||
|
fullyQualifiedName: 'mostViewedEntities',
|
||||||
|
description: 'Displays the most viewed entities.',
|
||||||
|
dataIndexType: 'web_analytic_entity_view_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'entityFqn',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'pageViews',
|
||||||
|
displayName: 'Total number of page view',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952805,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/9217560a-3fed-4c0d-85fa-d7c699feefac',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '73d9b934-7664-4e84-8a7e-7fa00fe03f5f',
|
||||||
|
name: 'pageViewsByEntities',
|
||||||
|
displayName: 'Page views by entities',
|
||||||
|
fullyQualifiedName: 'pageViewsByEntities',
|
||||||
|
description: 'Displays the number of time an entity type was viewed.',
|
||||||
|
dataIndexType: 'web_analytic_entity_view_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityType',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'pageViews',
|
||||||
|
displayName: 'Total number of page view',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952798,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/73d9b934-7664-4e84-8a7e-7fa00fe03f5f',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
name: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
displayName: 'Percentage of Entities With Description',
|
||||||
|
fullyQualifiedName: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
description: 'Display the percentage of entities with description by type.',
|
||||||
|
dataIndexType: 'entity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityType',
|
||||||
|
displayName: 'Entity Type',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'completedDescriptionFraction',
|
||||||
|
displayName: 'Percentage of Completed Description',
|
||||||
|
chartDataType: 'PERCENTAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'completedDescription',
|
||||||
|
displayName: 'Entities with Completed Description',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityCount',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952816,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'd712533a-ea8c-409f-a9e3-3f68d06d7864',
|
||||||
|
name: 'PercentageOfEntitiesWithOwnerByType',
|
||||||
|
displayName: 'Percentage of Entities With Owner',
|
||||||
|
fullyQualifiedName: 'PercentageOfEntitiesWithOwnerByType',
|
||||||
|
description: 'Display the percentage of entities with owner by type.',
|
||||||
|
dataIndexType: 'entity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityType',
|
||||||
|
displayName: 'Entity Type',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'hasOwnerFraction',
|
||||||
|
displayName: 'Percentage of Completed Owner',
|
||||||
|
chartDataType: 'PERCENTAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hasOwner',
|
||||||
|
displayName: 'Entities with Owner',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityCount',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952825,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/d712533a-ea8c-409f-a9e3-3f68d06d7864',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '71d7f330-40d1-4c9e-b843-b5b62ff2efcd',
|
||||||
|
name: 'TotalEntitiesByTier',
|
||||||
|
displayName: 'Percentage of Entities With Tier',
|
||||||
|
fullyQualifiedName: 'TotalEntitiesByTier',
|
||||||
|
description: 'Display the percentage of entities with tier by type.',
|
||||||
|
dataIndexType: 'entity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityTier',
|
||||||
|
displayName: 'Entity Tier',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'entityCountFraction',
|
||||||
|
displayName: 'Total Count of Entity',
|
||||||
|
chartDataType: 'PERCENTAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityCount',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952786,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/71d7f330-40d1-4c9e-b843-b5b62ff2efcd',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'd3eff37a-1196-4e4a-bc6a-5a81bb6504b5',
|
||||||
|
name: 'TotalEntitiesByType',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
fullyQualifiedName: 'TotalEntitiesByType',
|
||||||
|
description: 'Display the total of entities by type.',
|
||||||
|
dataIndexType: 'entity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityType',
|
||||||
|
displayName: 'Entity Tier',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'entityCountFraction',
|
||||||
|
displayName: 'Total Count of Entity',
|
||||||
|
chartDataType: 'PERCENTAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityCount',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952810,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/d3eff37a-1196-4e4a-bc6a-5a81bb6504b5',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const KPI_LIST = [
|
||||||
|
{
|
||||||
|
id: 'dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
name: 'description-kpi',
|
||||||
|
displayName: 'Description KPI',
|
||||||
|
fullyQualifiedName: 'description-kpi',
|
||||||
|
description: '',
|
||||||
|
metricType: 'PERCENTAGE',
|
||||||
|
dataInsightChart: {
|
||||||
|
id: '7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
type: 'dataInsightChart',
|
||||||
|
name: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
fullyQualifiedName: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
description:
|
||||||
|
'Display the percentage of entities with description by type.',
|
||||||
|
displayName: 'Percentage of Entities With Description',
|
||||||
|
deleted: false,
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
},
|
||||||
|
targetDefinition: [
|
||||||
|
{
|
||||||
|
name: 'completedDescriptionFraction',
|
||||||
|
value: '0.65',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
startDate: 1670351400000,
|
||||||
|
endDate: 1672165800000,
|
||||||
|
version: 0.2,
|
||||||
|
updatedAt: 1670414685805,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/kpi/dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
changeDescription: {
|
||||||
|
fieldsAdded: [],
|
||||||
|
fieldsUpdated: [
|
||||||
|
{
|
||||||
|
name: 'startDate',
|
||||||
|
oldValue: 1670395661000,
|
||||||
|
newValue: 1670351400000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'endDate',
|
||||||
|
oldValue: 1672210072000,
|
||||||
|
newValue: 1672165800000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.1,
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const KPI_DATA = {
|
||||||
|
id: 'dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
name: 'description-kpi',
|
||||||
|
displayName: 'Description KPI',
|
||||||
|
fullyQualifiedName: 'description-kpi',
|
||||||
|
description: '',
|
||||||
|
metricType: 'PERCENTAGE',
|
||||||
|
dataInsightChart: {
|
||||||
|
id: '7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
type: 'dataInsightChart',
|
||||||
|
name: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
fullyQualifiedName: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
description: 'Display the percentage of entities with description by type.',
|
||||||
|
displayName: 'Percentage of Entities With Description',
|
||||||
|
deleted: false,
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
},
|
||||||
|
targetDefinition: [
|
||||||
|
{
|
||||||
|
name: 'completedDescriptionFraction',
|
||||||
|
value: '0.65',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
startDate: 1670351400000,
|
||||||
|
endDate: 1672165800000,
|
||||||
|
version: 0.2,
|
||||||
|
updatedAt: 1670414685805,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/kpi/dabd01bb-095d-448e-af21-0427859b99b5',
|
||||||
|
changeDescription: {
|
||||||
|
fieldsAdded: [],
|
||||||
|
fieldsUpdated: [
|
||||||
|
{
|
||||||
|
name: 'startDate',
|
||||||
|
oldValue: 1670395661000,
|
||||||
|
newValue: 1670351400000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'endDate',
|
||||||
|
oldValue: 1672210072000,
|
||||||
|
newValue: 1672165800000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.1,
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DESCRIPTION_CHART = {
|
||||||
|
id: '7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
name: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
displayName: 'Percentage of Entities With Description',
|
||||||
|
fullyQualifiedName: 'PercentageOfEntitiesWithDescriptionByType',
|
||||||
|
description: 'Display the percentage of entities with description by type.',
|
||||||
|
dataIndexType: 'entity_report_data_index',
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'timestamp',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityType',
|
||||||
|
displayName: 'Entity Type',
|
||||||
|
chartDataType: 'STRING',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
name: 'completedDescriptionFraction',
|
||||||
|
displayName: 'Percentage of Completed Description',
|
||||||
|
chartDataType: 'PERCENTAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'completedDescription',
|
||||||
|
displayName: 'Entities with Completed Description',
|
||||||
|
chartDataType: 'NUMBER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'entityCount',
|
||||||
|
displayName: 'Total Entities',
|
||||||
|
chartDataType: 'INT',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1670231952816,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/dataInsight/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
|
deleted: false,
|
||||||
|
};
|
@ -45,6 +45,7 @@ import { TotalEntitiesByTier } from '../generated/dataInsight/type/totalEntities
|
|||||||
import {
|
import {
|
||||||
ChartValue,
|
ChartValue,
|
||||||
DataInsightChartTooltipProps,
|
DataInsightChartTooltipProps,
|
||||||
|
KpiDates,
|
||||||
} from '../interface/data-insight.interface';
|
} from '../interface/data-insight.interface';
|
||||||
import { pluralize } from './CommonUtils';
|
import { pluralize } from './CommonUtils';
|
||||||
import { getFormattedDateFromMilliSeconds } from './TimeUtils';
|
import { getFormattedDateFromMilliSeconds } from './TimeUtils';
|
||||||
@ -523,3 +524,10 @@ export const getKpiResultFeedback = (day: number, isTargetMet: boolean) => {
|
|||||||
|
|
||||||
export const getDataInsightPathWithFqn = (fqn: string) =>
|
export const getDataInsightPathWithFqn = (fqn: string) =>
|
||||||
ROUTES.DATA_INSIGHT_WITH_TAB.replace(PLACEHOLDER_ROUTE_TAB, fqn);
|
ROUTES.DATA_INSIGHT_WITH_TAB.replace(PLACEHOLDER_ROUTE_TAB, fqn);
|
||||||
|
|
||||||
|
export const getKPIFormattedDates = (kpiDates: KpiDates): KpiDates => {
|
||||||
|
return {
|
||||||
|
startDate: `${kpiDates.startDate} 00:00`,
|
||||||
|
endDate: `${kpiDates.endDate} 23:59`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user