added pie chart as per new mock

This commit is contained in:
Shailesh Parmar 2025-06-16 19:22:54 +05:30
parent b424f0f594
commit 2375c40e37
4 changed files with 475 additions and 232 deletions

View File

@ -0,0 +1,208 @@
import { Card, Col, Row } from 'antd';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Cell, Pie, PieChart } from 'recharts';
import { SummaryPanelProps } from './SummaryPanel.interface';
const renderCenterText = (value: number) => (
<text
dominantBaseline="middle"
fill="#222"
fontSize="28"
fontWeight="bold"
textAnchor="middle"
x="50%"
y="50%">
{value.toLocaleString()}
</text>
);
const PieChartSummaryPanel = ({
testSummary,
isLoading = false,
showAdditionalSummary = false,
}: SummaryPanelProps) => {
const { t } = useTranslation();
console.log('testSummary', testSummary);
// {
// "success": 4,
// "failed": 3,
// "aborted": 1,
// "total": 8,
// "unhealthy": 1,
// "healthy": 1,
// "totalDQEntities": 2,
// "totalEntityCount": 36
// }
const success = useMemo(
() => [
{
name: 'success',
value: testSummary?.success,
color: '#22C55E',
},
{
name: 'total',
value: testSummary?.total,
color: '#E5E7EB',
},
],
[testSummary]
);
return (
<Card loading={isLoading}>
<div style={{ marginBottom: 16 }}>
<div style={{ fontWeight: 600, fontSize: 20 }}>
{t('test-cases-status', 'Test Cases Status')}
</div>
<div style={{ color: '#6B7280' }}>
{t(
'test-cases-status-desc',
'Understand the metadata available in your service and keep track of the main KPIs coverage'
)}
</div>
</div>
<Row gutter={[32, 16]} justify="center">
{/* Total Tests */}
<Col md={8} xs={24}>
<Card style={{ textAlign: 'center', minHeight: 320 }}>
<div style={{ fontWeight: 600, fontSize: 20, marginBottom: 8 }}>
{t('total-tests', 'Total Tests')}
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
height: 180,
}}>
<PieChart height={200} width={200}>
<Pie
cx="50%"
cy="50%"
data={success}
dataKey="value"
innerRadius={85}
outerRadius={100}>
{success?.map((entry) => (
<Cell fill={entry.color} key={`cell-${entry.name}`} />
))}
</Pie>
{/* {renderCenterText(testSummary?.totalTests || 0)} */}
</PieChart>
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
gap: 16,
marginTop: 8,
}}>
{testSummary?.totalTests?.map((item: any) => (
<div
key={item.name}
style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<span
style={{
width: 10,
height: 10,
borderRadius: '50%',
background: item.color,
display: 'inline-block',
}}
/>
<span style={{ color: '#222', fontWeight: 500 }}>
{t(item.name)}
</span>
<span style={{ color: '#6B7280', fontWeight: 500 }}>
{item.value}
</span>
</div>
))}
</div>
</Card>
</Col>
{/* Healthy Data Assets */}
<Col md={8} xs={24}>
<Card style={{ textAlign: 'center', minHeight: 320 }}>
<div style={{ fontWeight: 600, fontSize: 20, marginBottom: 8 }}>
{t('healthy-data-asset-plural')}
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
height: 180,
}}>
<PieChart height={180} width={180}>
<Pie
cx="50%"
cy="50%"
data={testSummary?.healthyDataAssets}
dataKey="value"
endAngle={-30}
innerRadius={60}
nameKey="name"
outerRadius={80}
startAngle={210}
stroke="none">
{testSummary?.healthyDataAssets?.map(
(entry: any, idx: number) => (
<Cell fill={entry.color} key={`cell-healthy-${idx}`} />
)
)}
</Pie>
{renderCenterText(1000)}
</PieChart>
</div>
</Card>
</Col>
{/* Data Assets Coverage */}
<Col md={8} xs={24}>
<Card style={{ textAlign: 'center', minHeight: 320 }}>
<div style={{ fontWeight: 600, fontSize: 20, marginBottom: 8 }}>
{t('data-asset-plural-coverage')}
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
height: 180,
}}>
<PieChart height={180} width={180}>
<Pie
cx="50%"
cy="50%"
data={testSummary?.dataAssetsCoverage}
dataKey="value"
endAngle={-30}
innerRadius={60}
nameKey="name"
outerRadius={80}
startAngle={210}
stroke="none">
{testSummary?.dataAssetsCoverage?.map(
(entry: any, idx: number) => (
<Cell fill={entry.color} key={`cell-coverage-${idx}`} />
)
)}
</Pie>
{renderCenterText(1000)}
</PieChart>
</div>
</Card>
</Col>
</Row>
</Card>
);
};
export default PieChartSummaryPanel;

View File

@ -13,6 +13,7 @@
import { RightOutlined } from '@ant-design/icons'; import { RightOutlined } from '@ant-design/icons';
import { import {
Button, Button,
Card,
Col, Col,
Dropdown, Dropdown,
Form, Form,
@ -81,6 +82,7 @@ import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.inte
import Searchbar from '../../common/SearchBarComponent/SearchBar.component'; import Searchbar from '../../common/SearchBarComponent/SearchBar.component';
import DataQualityTab from '../../Database/Profiler/DataQualityTab/DataQualityTab'; import DataQualityTab from '../../Database/Profiler/DataQualityTab/DataQualityTab';
import { TestCaseSearchParams } from '../DataQuality.interface'; import { TestCaseSearchParams } from '../DataQuality.interface';
import PieChartSummaryPanel from '../SummaryPannel/PieChartSummaryPanel.component';
import { SummaryPanel } from '../SummaryPannel/SummaryPanel.component'; import { SummaryPanel } from '../SummaryPannel/SummaryPanel.component';
export const TestCases = () => { export const TestCases = () => {
@ -482,196 +484,204 @@ export const TestCases = () => {
} }
return ( return (
<Row data-testid="test-case-container" gutter={[16, 16]}> <Card>
<Col span={24}> <Row data-testid="test-case-container" gutter={[16, 16]}>
<Form<TestCaseSearchParams> <Col span={24}>
form={form} <Form<TestCaseSearchParams>
layout="horizontal" form={form}
onValuesChange={handleFilterChange}> layout="horizontal"
<Space wrap align="center" className="w-full" size={16}> onValuesChange={handleFilterChange}>
<Form.Item className="m-0 w-80"> <Space wrap align="center" className="w-full" size={16}>
<Searchbar <Form.Item className="m-0 w-80">
removeMargin <Searchbar
placeholder={t('label.search-entity', { removeMargin
entity: t('label.test-case-lowercase'), placeholder={t('label.search-entity', {
})} entity: t('label.test-case-lowercase'),
searchValue={searchValue} })}
onSearch={(value) => handleSearchParam('searchValue', value)} searchValue={searchValue}
/> onSearch={(value) => handleSearchParam('searchValue', value)}
</Form.Item>
<Form.Item noStyle name="selectedFilters">
<Dropdown
menu={{
items: filterMenu,
selectedKeys: selectedFilter,
onClick: handleMenuClick,
}}
trigger={['click']}>
<Button
ghost
className="expand-btn"
data-testid="advanced-filter"
type="primary">
{t('label.advanced')}
<RightOutlined />
</Button>
</Dropdown>
</Form.Item>
{selectedFilter.includes(TEST_CASE_FILTERS.table) && (
<Form.Item
className="m-0 w-80"
label={t('label.table')}
name="tableFqn">
<Select
allowClear
showSearch
data-testid="table-select-filter"
loading={isOptionsLoading}
options={tableOptions}
placeholder={t('label.table')}
onSearch={debounceFetchTableData}
/> />
</Form.Item> </Form.Item>
)} <Form.Item noStyle name="selectedFilters">
{selectedFilter.includes(TEST_CASE_FILTERS.platform) && ( <Dropdown
<Form.Item menu={{
className="m-0 w-min-20" items: filterMenu,
label={t('label.platform')} selectedKeys: selectedFilter,
name="testPlatforms"> onClick: handleMenuClick,
<Select }}
allowClear trigger={['click']}>
data-testid="platform-select-filter" <Button
mode="multiple" ghost
options={TEST_CASE_PLATFORM_OPTION} className="expand-btn"
placeholder={t('label.platform')} data-testid="advanced-filter"
/> type="primary">
{t('label.advanced')}
<RightOutlined />
</Button>
</Dropdown>
</Form.Item> </Form.Item>
)} {selectedFilter.includes(TEST_CASE_FILTERS.table) && (
{selectedFilter.includes(TEST_CASE_FILTERS.type) && ( <Form.Item
<Form.Item className="m-0 w-80"
className="m-0 w-40" label={t('label.table')}
label={t('label.type')} name="tableFqn">
name="testCaseType"> <Select
<Select allowClear
allowClear showSearch
data-testid="test-case-type-select-filter" data-testid="table-select-filter"
options={TEST_CASE_TYPE_OPTION} loading={isOptionsLoading}
placeholder={t('label.type')} options={tableOptions}
/> placeholder={t('label.table')}
</Form.Item> onSearch={debounceFetchTableData}
)} />
{selectedFilter.includes(TEST_CASE_FILTERS.status) && ( </Form.Item>
<Form.Item )}
className="m-0 w-40" {selectedFilter.includes(TEST_CASE_FILTERS.platform) && (
label={t('label.status')} <Form.Item
name="testCaseStatus"> className="m-0 w-min-20"
<Select label={t('label.platform')}
allowClear name="testPlatforms">
data-testid="status-select-filter" <Select
options={TEST_CASE_STATUS_OPTION} allowClear
placeholder={t('label.status')} data-testid="platform-select-filter"
/> mode="multiple"
</Form.Item> options={TEST_CASE_PLATFORM_OPTION}
)} placeholder={t('label.platform')}
{selectedFilter.includes(TEST_CASE_FILTERS.lastRun) && ( />
<Form.Item </Form.Item>
className="m-0" )}
label={t('label.last-run')} {selectedFilter.includes(TEST_CASE_FILTERS.type) && (
name="lastRunRange" <Form.Item
trigger="handleDateRangeChange" className="m-0 w-40"
valuePropName="defaultDateRange"> label={t('label.type')}
<DatePickerMenu showSelectedCustomRange /> name="testCaseType">
</Form.Item> <Select
)} allowClear
{selectedFilter.includes(TEST_CASE_FILTERS.tags) && ( data-testid="test-case-type-select-filter"
<Form.Item options={TEST_CASE_TYPE_OPTION}
className="m-0 w-80" placeholder={t('label.type')}
label={t('label.tag-plural')} />
name="tags"> </Form.Item>
<Select )}
allowClear {selectedFilter.includes(TEST_CASE_FILTERS.status) && (
showSearch <Form.Item
data-testid="tags-select-filter" className="m-0 w-40"
loading={isOptionsLoading} label={t('label.status')}
mode="multiple" name="testCaseStatus">
options={tagOptions} <Select
placeholder={t('label.tag-plural')} allowClear
onSearch={debounceFetchTagOptions} data-testid="status-select-filter"
/> options={TEST_CASE_STATUS_OPTION}
</Form.Item> placeholder={t('label.status')}
)} />
{selectedFilter.includes(TEST_CASE_FILTERS.tier) && ( </Form.Item>
<Form.Item )}
className="m-0 w-40" {selectedFilter.includes(TEST_CASE_FILTERS.lastRun) && (
label={t('label.tier')} <Form.Item
name="tier"> className="m-0"
<Select label={t('label.last-run')}
allowClear name="lastRunRange"
showSearch trigger="handleDateRangeChange"
data-testid="tier-select-filter" valuePropName="defaultDateRange">
options={tierOptions} <DatePickerMenu showSelectedCustomRange />
placeholder={t('label.tier')} </Form.Item>
/> )}
</Form.Item> {selectedFilter.includes(TEST_CASE_FILTERS.tags) && (
)} <Form.Item
{selectedFilter.includes(TEST_CASE_FILTERS.service) && ( className="m-0 w-80"
<Form.Item label={t('label.tag-plural')}
className="m-0 w-80" name="tags">
label={t('label.service')} <Select
name="serviceName"> allowClear
<Select showSearch
allowClear data-testid="tags-select-filter"
showSearch loading={isOptionsLoading}
data-testid="service-select-filter" mode="multiple"
loading={isOptionsLoading} options={tagOptions}
options={serviceOptions} placeholder={t('label.tag-plural')}
placeholder={t('label.service')} onSearch={debounceFetchTagOptions}
onSearch={debounceFetchServiceOptions} />
/> </Form.Item>
</Form.Item> )}
)} {selectedFilter.includes(TEST_CASE_FILTERS.tier) && (
{selectedFilter.includes(TEST_CASE_FILTERS.dimension) && ( <Form.Item
<Form.Item className="m-0 w-40"
className="m-0 w-80" label={t('label.tier')}
label={t('label.dimension')} name="tier">
name="dataQualityDimension"> <Select
<Select allowClear
allowClear showSearch
showSearch data-testid="tier-select-filter"
data-testid="dimension-select-filter" options={tierOptions}
options={TEST_CASE_DIMENSIONS_OPTION} placeholder={t('label.tier')}
placeholder={t('label.dimension')} />
/> </Form.Item>
</Form.Item> )}
)} {selectedFilter.includes(TEST_CASE_FILTERS.service) && (
</Space> <Form.Item
</Form> className="m-0 w-80"
</Col> label={t('label.service')}
<Col span={24}> name="serviceName">
<SummaryPanel <Select
showAdditionalSummary allowClear
isLoading={isTestCaseSummaryLoading} showSearch
testSummary={testCaseSummary} data-testid="service-select-filter"
/> loading={isOptionsLoading}
</Col> options={serviceOptions}
<Col span={24}> placeholder={t('label.service')}
<DataQualityTab onSearch={debounceFetchServiceOptions}
afterDeleteAction={fetchTestCases} />
breadcrumbData={[ </Form.Item>
{ )}
name: t('label.data-quality'), {selectedFilter.includes(TEST_CASE_FILTERS.dimension) && (
url: getDataQualityPagePath(DataQualityPageTabs.TEST_CASES), <Form.Item
}, className="m-0 w-80"
]} label={t('label.dimension')}
fetchTestCases={sortTestCase} name="dataQualityDimension">
isLoading={isLoading} <Select
pagingData={pagingData} allowClear
showPagination={showPagination} showSearch
testCases={testCase} data-testid="dimension-select-filter"
onTestCaseResultUpdate={handleStatusSubmit} options={TEST_CASE_DIMENSIONS_OPTION}
onTestUpdate={handleTestCaseUpdate} placeholder={t('label.dimension')}
/> />
</Col> </Form.Item>
</Row> )}
</Space>
</Form>
</Col>
<Col span={24}>
<SummaryPanel
showAdditionalSummary
isLoading={isTestCaseSummaryLoading}
testSummary={testCaseSummary}
/>
</Col>
<Col span={24}>
<PieChartSummaryPanel
isLoading={isTestCaseSummaryLoading}
testSummary={testCaseSummary}
/>
</Col>
<Col span={24}>
<DataQualityTab
afterDeleteAction={fetchTestCases}
breadcrumbData={[
{
name: t('label.data-quality'),
url: getDataQualityPagePath(DataQualityPageTabs.TEST_CASES),
},
]}
fetchTestCases={sortTestCase}
isLoading={isLoading}
pagingData={pagingData}
showPagination={showPagination}
testCases={testCase}
onTestCaseResultUpdate={handleStatusSubmit}
onTestUpdate={handleTestCaseUpdate}
/>
</Col>
</Row>
</Card>
); );
}; };

View File

@ -11,13 +11,15 @@
* limitations under the License. * limitations under the License.
*/ */
import { Card, Col, Row, Tabs, Typography } from 'antd'; import { Button, Card, Col, Row, Tabs, Typography } from 'antd';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
import ManageButton from '../../components/common/EntityPageInfos/ManageButton/ManageButton'; import ManageButton from '../../components/common/EntityPageInfos/ManageButton/ManageButton';
import TabsLabel from '../../components/common/TabsLabel/TabsLabel.component'; import TabsLabel from '../../components/common/TabsLabel/TabsLabel.component';
import { ROUTES } from '../../constants/constants';
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { withPageLayout } from '../../hoc/withPageLayout'; import { withPageLayout } from '../../hoc/withPageLayout';
import i18n from '../../utils/i18next/LocalUtil'; import i18n from '../../utils/i18next/LocalUtil';
@ -32,6 +34,8 @@ const DataQualityPage = () => {
useParams<{ tab: DataQualityPageTabs }>(); useParams<{ tab: DataQualityPageTabs }>();
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
const { permissions } = usePermissionProvider();
const { testSuite: testSuitePermission } = permissions;
const menuItems = useMemo(() => { const menuItems = useMemo(() => {
const data = DataQualityClassBase.getDataQualityTab(); const data = DataQualityClassBase.getDataQualityTab();
@ -58,45 +62,67 @@ const DataQualityPage = () => {
}; };
return ( return (
<div> <DataQualityProvider>
<Card className="h-full overflow-y-auto"> <Row data-testid="data-insight-container" gutter={[0, 16]}>
<DataQualityProvider> <Col span={24}>
<Row data-testid="data-insight-container" gutter={[0, 16]}> <Card>
<Col span={isEmpty(extraDropdownContent) ? 24 : 23}> <Row>
<Typography.Title <Col span={isEmpty(extraDropdownContent) ? 16 : 23}>
className="m-b-md" <Typography.Title
data-testid="page-title" className="m-b-md"
level={5}> data-testid="page-title"
{t('label.data-quality')} level={5}>
</Typography.Title> {t('label.data-quality')}
<Typography.Paragraph </Typography.Title>
className="text-grey-muted" <Typography.Paragraph
data-testid="page-sub-title"> className="text-grey-muted m-b-0"
{t('message.page-sub-header-for-data-quality')} data-testid="page-sub-title">
</Typography.Paragraph> {t('message.page-sub-header-for-data-quality')}
</Col> </Typography.Paragraph>
{isEmpty(extraDropdownContent) ? null : (
<Col className="d-flex justify-end" span={1}>
<ManageButton
entityName={EntityType.TEST_CASE}
entityType={EntityType.TEST_CASE}
extraDropdownContent={extraDropdownContent}
/>
</Col> </Col>
)} <Col className="d-flex justify-end" span={8}>
<Col span={24}> {activeTab === DataQualityPageTabs.TEST_SUITES &&
<Tabs testSuitePermission?.Create && (
activeKey={activeTab} <Link
className="tabs-new" data-testid="add-test-suite-btn"
data-testid="tabs" to={ROUTES.ADD_TEST_SUITES}>
items={menuItems} <Button type="primary">
onChange={handleTabChange} {t('label.add-entity', {
/> entity: t('label.bundle-suite-plural'),
</Col> })}
</Row> </Button>
</DataQualityProvider> </Link>
</Card> )}
</div> {activeTab === DataQualityPageTabs.TEST_CASES &&
testSuitePermission?.Create && (
<Button data-testid="add-test-case-btn" type="primary">
{t('label.add-entity', {
entity: t('label.test-case'),
})}
</Button>
)}
{isEmpty(extraDropdownContent) ? null : (
<ManageButton
entityName={EntityType.TEST_CASE}
entityType={EntityType.TEST_CASE}
extraDropdownContent={extraDropdownContent}
/>
)}
</Col>
</Row>
</Card>
</Col>
<Col span={24}>
<Tabs
activeKey={activeTab}
className="tabs-new data-quality-page-tabs"
data-testid="tabs"
items={menuItems}
onChange={handleTabChange}
/>
</Col>
</Row>
</DataQualityProvider>
); );
}; };

View File

@ -13,9 +13,8 @@
@import (reference) '../../styles/variables.less'; @import (reference) '../../styles/variables.less';
.data-quality-page-left-panel-menu { .data-quality-page-tabs {
&.ant-menu-root.ant-menu-inline { .ant-tabs-tabpane {
padding-top: @size-md; margin-top: @size-md;
border-radius: @border-radius-sm;
} }
} }