diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/DropDown.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/DropDown.svg index 7cab60ef71e..74ec7fe8a7f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/DropDown.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/DropDown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.component.tsx new file mode 100644 index 00000000000..b64d219d08b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.component.tsx @@ -0,0 +1,152 @@ +/* + * Copyright 2023 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 { CloseCircleOutlined } from '@ant-design/icons'; +import { Button, DatePicker, Dropdown, MenuProps, Space } from 'antd'; +import { RangePickerProps } from 'antd/lib/date-picker'; +import { DateRangeObject } from 'components/ProfilerDashboard/component/TestSummary'; +import { + DEFAULT_SELECTED_RANGE, + PROFILER_FILTER_RANGE, +} from 'constants/profiler.constant'; +import { MenuInfo } from 'rc-menu/lib/interface'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { getDaysCount, getTimestampLabel } from 'utils/DatePickerMenuUtils'; +import { + getCurrentDateTimeStamp, + getPastDatesTimeStampFromCurrentDate, +} from 'utils/TimeUtils'; +import { ReactComponent as DropdownIcon } from '../../assets/svg/DropDown.svg'; +import './DatePickerMenu.style.less'; + +interface DatePickerMenuProps { + showSelectedCustomRange?: boolean; + handleDateRangeChange: (value: DateRangeObject, days?: number) => void; +} + +function DatePickerMenu({ + showSelectedCustomRange, + handleDateRangeChange, +}: DatePickerMenuProps) { + const { t } = useTranslation(); + // State to display the label for selected range value + const [selectedTimeRange, setSelectedTimeRange] = useState( + DEFAULT_SELECTED_RANGE.title + ); + // state to determine the selected value to highlight in the dropdown + const [selectedTimeRangeKey, setSelectedTimeRangeKey] = useState< + keyof typeof PROFILER_FILTER_RANGE + >(DEFAULT_SELECTED_RANGE.key as keyof typeof PROFILER_FILTER_RANGE); + + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const handleCustomDateChange: RangePickerProps['onChange'] = ( + values, + dateStrings + ) => { + if (values) { + const startTs = Date.parse(dateStrings[0]) / 1000; + + const endTs = Date.parse(dateStrings[1]) / 1000; + + const daysCount = getDaysCount(dateStrings[0], dateStrings[1]); + + const selectedRangeLabel = getTimestampLabel( + dateStrings[0], + dateStrings[1], + showSelectedCustomRange + ); + + setSelectedTimeRange(selectedRangeLabel); + setSelectedTimeRangeKey( + 'customRange' as keyof typeof PROFILER_FILTER_RANGE + ); + setIsMenuOpen(false); + handleDateRangeChange({ startTs, endTs }, daysCount); + } + }; + + const handleOptionClick = ({ key }: MenuInfo) => { + const selectedNumberOfDays = + PROFILER_FILTER_RANGE[key as keyof typeof PROFILER_FILTER_RANGE].days; + const keyString = key as keyof typeof PROFILER_FILTER_RANGE; + const startTs = getPastDatesTimeStampFromCurrentDate(selectedNumberOfDays); + + const endTs = getCurrentDateTimeStamp(); + + setSelectedTimeRange(PROFILER_FILTER_RANGE[keyString].title); + setSelectedTimeRangeKey(keyString); + setIsMenuOpen(false); + + handleDateRangeChange({ startTs, endTs }, selectedNumberOfDays); + }; + + const getMenuItems = () => { + const items: MenuProps['items'] = Object.entries(PROFILER_FILTER_RANGE).map( + ([key, value]) => ({ + label: value.title, + key, + }) + ); + items.push({ + label: t('label.custom-range'), + key: 'customRange', + children: [ + { + label: ( + } + open={isMenuOpen} + placement="bottomRight" + suffixIcon={null} + onChange={handleCustomDateChange} + /> + ), + key: 'datePicker', + }, + ], + popupClassName: 'date-picker-sub-menu-popup', + }); + + return items; + }; + + const items: MenuProps['items'] = getMenuItems(); + + return ( + <> + setIsMenuOpen(value)}> + + + + ); +} + +export default DatePickerMenu; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.style.less b/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.style.less new file mode 100644 index 00000000000..f055396bad8 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatePickerMenu/DatePickerMenu.style.less @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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. + */ +.date-picker-sub-menu-popup { + visibility: hidden; + height: 0px; + ul { + height: 0px; + li { + height: 0px; + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.test.tsx index 1583a361930..be677917763 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.test.tsx @@ -102,6 +102,14 @@ jest.mock('../containers/PageLayoutV1', () => jest.fn().mockImplementation(({ children }) =>
{children}
) ); +jest.mock('components/DatePickerMenu/DatePickerMenu.component', () => + jest + .fn() + .mockImplementation(() => ( +
DatePickerMenu
+ )) +); + describe('Test ProfilerDashboardPage component', () => { beforeEach(() => cleanup()); @@ -115,15 +123,13 @@ describe('Test ProfilerDashboardPage component', () => { const profilerSwitch = await screen.findByTestId('profiler-switch'); const EntityPageInfo = await screen.findByText('EntityPageInfo component'); const ProfilerTab = await screen.findByText('ProfilerTab component'); - const selectedTimeFrame = await screen.findByText( - 'label.last-number-of-days' - ); + const DatePickerMenu = await screen.findByTestId('DatePickerMenu'); const DataQualityTab = screen.queryByText('DataQualityTab component'); expect(profilerSwitch).toBeInTheDocument(); expect(EntityPageInfo).toBeInTheDocument(); expect(ProfilerTab).toBeInTheDocument(); - expect(selectedTimeFrame).toBeInTheDocument(); + expect(DatePickerMenu).toBeInTheDocument(); expect(DataQualityTab).not.toBeInTheDocument(); }); @@ -167,15 +173,13 @@ describe('Test ProfilerDashboardPage component', () => { const profilerSwitch = await screen.findByTestId('profiler-switch'); const EntityPageInfo = await screen.findByText('EntityPageInfo component'); const ProfilerTab = await screen.findByText('ProfilerTab component'); - const selectedTimeFrame = await screen.findByText( - 'label.last-number-of-days' - ); + const DatePickerMenu = await screen.findByTestId('DatePickerMenu'); const DataQualityTab = screen.queryByText('DataQualityTab component'); expect(profilerSwitch).toBeInTheDocument(); expect(EntityPageInfo).toBeInTheDocument(); expect(ProfilerTab).toBeInTheDocument(); - expect(selectedTimeFrame).toBeInTheDocument(); + expect(DatePickerMenu).toBeInTheDocument(); expect(DataQualityTab).not.toBeInTheDocument(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.tsx index 82deed522bc..6782003c6d1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/ProfilerDashboard.tsx @@ -26,6 +26,8 @@ import { RadioChangeEvent } from 'antd/lib/radio'; import { SwitchChangeEventHandler } from 'antd/lib/switch'; import { AxiosError } from 'axios'; import PageLayoutV1 from 'components/containers/PageLayoutV1'; +import DatePickerMenu from 'components/DatePickerMenu/DatePickerMenu.component'; +import { isEqual } from 'lodash'; import { EntityTags, ExtraInfo } from 'Models'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -41,7 +43,7 @@ import { getTeamAndUserDetailsPath, } from '../../constants/constants'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; -import { PROFILER_FILTER_RANGE } from '../../constants/profiler.constant'; +import { DEFAULT_RANGE_DATA } from '../../constants/profiler.constant'; import { EntityType, FqnPart } from '../../enums/entity.enum'; import { ServiceCategory } from '../../enums/service.enum'; import { ProfilerDashboardType } from '../../enums/table.enum'; @@ -80,6 +82,7 @@ import { } from '../PermissionProvider/PermissionProvider.interface'; import DataQualityTab from './component/DataQualityTab'; import ProfilerTab from './component/ProfilerTab'; +import { DateRangeObject } from './component/TestSummary'; import { ProfilerDashboardProps, ProfilerDashboardTab, @@ -114,8 +117,8 @@ const ProfilerDashboard: React.FC = ({ ); const [selectedTestCaseStatus, setSelectedTestCaseStatus] = useState(''); - const [selectedTimeRange, setSelectedTimeRange] = - useState('last3days'); + const [dateRangeObject, setDateRangeObject] = + useState(DEFAULT_RANGE_DATA); const [activeColumnDetails, setActiveColumnDetails] = useState( {} as Column ); @@ -147,13 +150,6 @@ const ProfilerDashboard: React.FC = ({ }); }, [dashboardType]); - const timeRangeOption = useMemo(() => { - return Object.entries(PROFILER_FILTER_RANGE).map(([key, value]) => ({ - label: value.title, - value: key, - })); - }, []); - const testCaseStatusOption = useMemo(() => { const testCaseStatus: Record[] = Object.values( TestCaseStatus @@ -396,11 +392,11 @@ const ProfilerDashboard: React.FC = ({ ); }; - const handleTimeRangeChange = (value: keyof typeof PROFILER_FILTER_RANGE) => { - if (value !== selectedTimeRange) { - setSelectedTimeRange(value); + const handleDateRangeChange = (value: DateRangeObject) => { + if (!isEqual(value, dateRangeObject)) { + setDateRangeObject(value); if (activeTab === ProfilerDashboardTab.PROFILER) { - fetchProfilerData(entityTypeFQN, PROFILER_FILTER_RANGE[value].days); + fetchProfilerData(entityTypeFQN, dateRangeObject); } } }; @@ -523,11 +519,9 @@ const ProfilerDashboard: React.FC = ({ )} {activeTab === ProfilerDashboardTab.PROFILER && ( - - {isGraphLoading ? ( - - ) : results.length ? ( - - - - axisTickFormatter(value)} - /> - - - {data.parameterValues?.length === 2 && referenceArea()} - {chartData?.information?.map((info, i) => ( - - ))} - - - ) : ( - - - {t('message.try-different-time-period-filtering')} - - - )} + {getGraph()} )} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/profilerDashboard.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/profilerDashboard.interface.ts index c804eaa6c87..911d628821e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/profilerDashboard.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/ProfilerDashboard/profilerDashboard.interface.ts @@ -19,6 +19,7 @@ import { Table, } from '../../generated/entity/data/table'; import { TestCase } from '../../generated/tests/testCase'; +import { DateRangeObject } from './component/TestSummary'; export interface ProfilerDashboardProps { onTableChange: (table: Table) => void; @@ -26,7 +27,10 @@ export interface ProfilerDashboardProps { table: Table; testCases: TestCase[]; profilerData: ColumnProfile[]; - fetchProfilerData: (tableId: string, days?: number) => void; + fetchProfilerData: ( + tableId: string, + dateRangeObject?: DateRangeObject + ) => void; fetchTestCases: (fqn: string, params?: ListTestCaseParams) => void; onTestCaseUpdate: (deleted?: boolean) => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.test.tsx index ee73de2bab6..3f2ea806c6c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.test.tsx @@ -14,37 +14,20 @@ import { act, render, screen } from '@testing-library/react'; import React from 'react'; import { getSystemProfileList, getTableProfilesList } from 'rest/tableAPI'; -import { - getPastDatesTimeStampFromCurrentDate, - getPastDaysDateTimeMillis, -} from '../../../utils/TimeUtils'; import TableProfilerChart from './TableProfilerChart'; const mockFQN = 'testFQN'; const mockTimeValue = { endSec: 1670667984, startSec: 1670408784, - endMilli: 1670667985445, - startMilli: 1670408785445, + endMilli: 1670667984000, + startMilli: 1670408784000, }; +const mockDateRangeObject = { startTs: 1670408784, endTs: 1670667984 }; jest.mock('react-router-dom', () => ({ useParams: jest.fn().mockImplementation(() => ({ datasetFQN: mockFQN })), })); -jest.mock('../../../utils/TimeUtils', () => ({ - getCurrentDateTimeMillis: jest - .fn() - .mockImplementation(() => mockTimeValue.endMilli), - getCurrentDateTimeStamp: jest - .fn() - .mockImplementation(() => mockTimeValue.endSec), - getPastDatesTimeStampFromCurrentDate: jest - .fn() - .mockImplementation(() => mockTimeValue.startSec), - getPastDaysDateTimeMillis: jest - .fn() - .mockImplementation(() => mockTimeValue.startMilli), -})); jest.mock('rest/tableAPI'); jest.mock('../../ProfilerDashboard/component/ProfilerLatestValue', () => { return jest.fn().mockImplementation(() =>
ProfilerLatestValue
); @@ -64,7 +47,7 @@ describe('TableProfilerChart component test', () => { const mockGetSystemProfileList = getSystemProfileList as jest.Mock; const mockGetTableProfilesList = getTableProfilesList as jest.Mock; act(() => { - render(); + render(); }); expect( @@ -89,7 +72,7 @@ describe('TableProfilerChart component test', () => { const mockGetSystemProfileList = getSystemProfileList as jest.Mock; const mockGetTableProfilesList = getTableProfilesList as jest.Mock; await act(async () => { - render(); + render(); }); // API should be call once @@ -111,20 +94,14 @@ describe('TableProfilerChart component test', () => { it('If TimeRange change API should be call accordingly', async () => { const startTime = { - inMilli: 1670063664901, - inSec: 1670063664, + inMilli: 1670408784000, + inSec: 1670408784, }; const mockGetSystemProfileList = getSystemProfileList as jest.Mock; const mockGetTableProfilesList = getTableProfilesList as jest.Mock; - (getPastDatesTimeStampFromCurrentDate as jest.Mock).mockImplementationOnce( - () => startTime.inSec - ); - (getPastDaysDateTimeMillis as jest.Mock).mockImplementationOnce( - () => startTime.inMilli - ); await act(async () => { - render(); + render(); }); // API should be call with proper Param value diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.tsx index 64285cb4c80..f2456c643ae 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/Component/TableProfilerChart.tsx @@ -13,24 +13,18 @@ import { Card, Col, Row } from 'antd'; import { AxiosError } from 'axios'; +import { DateRangeObject } from 'components/ProfilerDashboard/component/TestSummary'; import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { getSystemProfileList, getTableProfilesList } from 'rest/tableAPI'; import { INITIAL_OPERATION_METRIC_VALUE, INITIAL_ROW_METRIC_VALUE, - PROFILER_FILTER_RANGE, } from '../../../constants/profiler.constant'; import { calculateRowCountMetrics, calculateSystemMetrics, } from '../../../utils/TableProfilerUtils'; -import { - getCurrentDateTimeMillis, - getCurrentDateTimeStamp, - getPastDatesTimeStampFromCurrentDate, - getPastDaysDateTimeMillis, -} from '../../../utils/TimeUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import CustomBarChart from '../../Chart/CustomBarChart'; import OperationDateBarChart from '../../Chart/OperationDateBarChart'; @@ -40,7 +34,7 @@ import ProfilerLatestValue from '../../ProfilerDashboard/component/ProfilerLates import { MetricChartType } from '../../ProfilerDashboard/profilerDashboard.interface'; import { TableProfilerChartProps } from '../TableProfiler.interface'; -const TableProfilerChart = ({ selectedTimeRange }: TableProfilerChartProps) => { +const TableProfilerChart = ({ dateRangeObject }: TableProfilerChartProps) => { const { datasetFQN } = useParams<{ datasetFQN: string }>(); const [rowCountMetrics, setRowCountMetrics] = useState( @@ -53,28 +47,24 @@ const TableProfilerChart = ({ selectedTimeRange }: TableProfilerChartProps) => { useState(INITIAL_OPERATION_METRIC_VALUE); const [isLoading, setIsLoading] = useState(true); - const fetchTableProfiler = async (fqn: string, days = 3) => { + const fetchTableProfiler = async ( + fqn: string, + dateRangeObj: DateRangeObject + ) => { try { - const startTs = getPastDatesTimeStampFromCurrentDate(days); - const endTs = getCurrentDateTimeStamp(); - const { data } = await getTableProfilesList(fqn, { - startTs, - endTs, - }); + const { data } = await getTableProfilesList(fqn, dateRangeObj); const rowMetricsData = calculateRowCountMetrics(data, rowCountMetrics); setRowCountMetrics(rowMetricsData); } catch (error) { showErrorToast(error as AxiosError); } }; - const fetchSystemProfiler = async (fqn: string, days = 3) => { + const fetchSystemProfiler = async ( + fqn: string, + dateRangeObj: DateRangeObject + ) => { try { - const startTs = getPastDaysDateTimeMillis(days); - const endTs = getCurrentDateTimeMillis(); - const { data } = await getSystemProfileList(fqn, { - startTs, - endTs, - }); + const { data } = await getSystemProfileList(fqn, dateRangeObj); const { operationMetrics: metricsData, operationDateMetrics } = calculateSystemMetrics(data, operationMetrics); @@ -85,23 +75,26 @@ const TableProfilerChart = ({ selectedTimeRange }: TableProfilerChartProps) => { } }; - const fetchProfilerData = async (fqn: string, days = 3) => { + const fetchProfilerData = async ( + fqn: string, + dateRangeObj: DateRangeObject + ) => { setIsLoading(true); - await fetchTableProfiler(fqn, days); - await fetchSystemProfiler(fqn, days); + await fetchTableProfiler(fqn, dateRangeObj); + await fetchSystemProfiler(fqn, { + startTs: dateRangeObj.startTs * 1000, + endTs: dateRangeObj.endTs * 1000, + }); setIsLoading(false); }; useEffect(() => { if (datasetFQN) { - fetchProfilerData( - datasetFQN, - PROFILER_FILTER_RANGE[selectedTimeRange].days - ); + fetchProfilerData(datasetFQN, dateRangeObject); } else { setIsLoading(false); } - }, [datasetFQN, selectedTimeRange]); + }, [datasetFQN, dateRangeObject]); if (isLoading) { return ; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfiler.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfiler.interface.ts index 6dfd0064f64..3286bbeb21d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfiler.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfiler.interface.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import { PROFILER_FILTER_RANGE } from '../../constants/profiler.constant'; +import { DateRangeObject } from 'components/ProfilerDashboard/component/TestSummary'; import { SystemProfile } from '../../generated/api/data/createTableProfile'; import { Column, @@ -82,7 +82,7 @@ export type TableProfilerData = { }; export type TableProfilerChartProps = { - selectedTimeRange: keyof typeof PROFILER_FILTER_RANGE; + dateRangeObject: DateRangeObject; }; export interface ProfilerSettingModalState { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfilerV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfilerV1.tsx index 5dd834c5f9e..6df8f687d56 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfilerV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableProfiler/TableProfilerV1.tsx @@ -28,7 +28,9 @@ import { DefaultOptionType } from 'antd/lib/select'; import { SwitchChangeEventHandler } from 'antd/lib/switch'; import { AxiosError } from 'axios'; import classNames from 'classnames'; -import { isUndefined, map } from 'lodash'; +import DatePickerMenu from 'components/DatePickerMenu/DatePickerMenu.component'; +import { DateRangeObject } from 'components/ProfilerDashboard/component/TestSummary'; +import { isEqual, isUndefined, map } from 'lodash'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useParams } from 'react-router-dom'; @@ -42,8 +44,8 @@ import { ReactComponent as TableProfileIcon } from '../../assets/svg/table-profi import { API_RES_MAX_SIZE } from '../../constants/constants'; import { PAGE_HEADERS } from '../../constants/PageHeaders.constant'; import { + DEFAULT_RANGE_DATA, INITIAL_TEST_RESULT_SUMMARY, - PROFILER_FILTER_RANGE, } from '../../constants/profiler.constant'; import { ProfilerDashboardType } from '../../enums/table.enum'; import { ProfileSampleType, Table } from '../../generated/entity/data/table'; @@ -95,8 +97,8 @@ const TableProfilerV1: FC = ({ const [selectedTestType, setSelectedTestType] = useState(''); const [deleted, setDeleted] = useState(false); const [isTestCaseLoading, setIsTestCaseLoading] = useState(false); - const [selectedTimeRange, setSelectedTimeRange] = - useState('last3days'); + const [dateRangeObject, setDateRangeObject] = + useState(DEFAULT_RANGE_DATA); const isSummary = activeTab === ProfilerDashboardTab.SUMMARY; const isDataQuality = activeTab === ProfilerDashboardTab.DATA_QUALITY; const isProfiler = activeTab === ProfilerDashboardTab.PROFILER; @@ -229,20 +231,13 @@ const TableProfilerV1: FC = ({ }, ]; - const timeRangeOption = useMemo(() => { - return Object.entries(PROFILER_FILTER_RANGE).map(([key, value]) => ({ - label: value.title, - value: key, - })); - }, []); - const handleTabChange: MenuProps['onClick'] = (value) => { setActiveTab(value.key as ProfilerDashboardTab); }; - const handleTimeRangeChange = (value: keyof typeof PROFILER_FILTER_RANGE) => { - if (value !== selectedTimeRange) { - setSelectedTimeRange(value); + const handleDateRangeChange = (value: DateRangeObject) => { + if (!isEqual(value, dateRangeObject)) { + setDateRangeObject(value); } }; @@ -400,11 +395,9 @@ const TableProfilerV1: FC = ({ )} {isProfiler && ( - diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ProfilerDashboardPage/ProfilerDashboardPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ProfilerDashboardPage/ProfilerDashboardPage.tsx index 871357e0d33..cc2015c2790 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ProfilerDashboardPage/ProfilerDashboardPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ProfilerDashboardPage/ProfilerDashboardPage.tsx @@ -20,8 +20,10 @@ import { OperationPermission, ResourceEntity, } from 'components/PermissionProvider/PermissionProvider.interface'; +import { DateRangeObject } from 'components/ProfilerDashboard/component/TestSummary'; import ProfilerDashboard from 'components/ProfilerDashboard/ProfilerDashboard'; import { ProfilerDashboardTab } from 'components/ProfilerDashboard/profilerDashboard.interface'; +import { DEFAULT_RANGE_DATA } from 'constants/profiler.constant'; import { compare } from 'fast-json-patch'; import { isEmpty } from 'lodash'; import React, { useEffect, useState } from 'react'; @@ -46,10 +48,6 @@ import { import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { getDecodedFqn } from '../../utils/StringsUtils'; import { generateEntityLink } from '../../utils/TableUtils'; -import { - getCurrentDateTimeStamp, - getPastDatesTimeStampFromCurrentDate, -} from '../../utils/TimeUtils'; import { showErrorToast } from '../../utils/ToastUtils'; const ProfilerDashboardPage = () => { @@ -89,16 +87,15 @@ const ProfilerDashboardPage = () => { } }; - const fetchProfilerData = async (fqn: string, days = 3) => { + const fetchProfilerData = async ( + fqn: string, + dateRangeObject?: DateRangeObject + ) => { try { - const startTs = getPastDatesTimeStampFromCurrentDate(days); - - const endTs = getCurrentDateTimeStamp(); - - const { data } = await getColumnProfilerList(fqn, { - startTs, - endTs, - }); + const { data } = await getColumnProfilerList( + fqn, + dateRangeObject ?? DEFAULT_RANGE_DATA + ); setProfilerData(data); } catch (error) { showErrorToast(error as AxiosError); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatePickerMenuUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DatePickerMenuUtils.tsx new file mode 100644 index 00000000000..d1858291b2d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatePickerMenuUtils.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2023 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 { t } from 'i18next'; + +export const getTimestampLabel = ( + startDate: string, + endDate: string, + showSelectedCustomRange?: boolean +) => { + let label = t('label.custom-range'); + if (showSelectedCustomRange) { + label += `: ${startDate} -> ${endDate}`; + } + + return label; +}; + +export const getDaysCount = (startDate: string, endDate: string) => { + const startDateObj = new Date(startDate); + const endDateObj = new Date(endDate); + const timeDifference = endDateObj.getTime() - startDateObj.getTime(); + + // Dividing time difference with number of milliseconds in a day to get number of days + const numOfDays = timeDifference / (1000 * 60 * 60 * 24); + + return numOfDays; +};