Fix misc UI (#9912)

* fix(ui): show displayName instead name for entities

* fix ui improvements #9879

* fix alert details destination title rendering

* fixed failing cy test

Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
This commit is contained in:
Chirag Madlani 2023-01-27 10:13:18 +05:30 committed by GitHub
parent ed062c62d6
commit 788f5ff776
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 352 additions and 298 deletions

View File

@ -256,9 +256,10 @@ describe('Alerts page should work properly', () => {
.contains(TEST_CASE.testCaseAlert) .contains(TEST_CASE.testCaseAlert)
.click(); .click();
// Check data asset // Check data asset
cy.get( cy.get('[data-testid="display-name-entities"]').should(
'.ant-row-middle > :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(3)' 'contain',
).should('contain', TEST_CASE.dataAsset); TEST_CASE.dataAsset
);
cy.get('div.ant-typography').should('contain', TEST_CASE.filters); cy.get('div.ant-typography').should('contain', TEST_CASE.filters);
}); });

View File

@ -14,6 +14,10 @@
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import Icon from '@ant-design/icons/lib/components/Icon'; import Icon from '@ant-design/icons/lib/components/Icon';
import { Button, Card, Col, Divider, Row, Space, Tag, Typography } from 'antd'; import { Button, Card, Col, Divider, Row, Space, Tag, Typography } from 'antd';
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
import PageHeader from 'components/header/PageHeader.component';
import { HeaderProps } from 'components/header/PageHeader.interface';
import { isArray } from 'lodash'; import { isArray } from 'lodash';
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -38,15 +42,13 @@ import {
getFunctionDisplayName, getFunctionDisplayName,
} from '../../../utils/Alerts/AlertsUtil'; } from '../../../utils/Alerts/AlertsUtil';
import { getHostNameFromURL } from '../../../utils/CommonUtils'; import { getHostNameFromURL } from '../../../utils/CommonUtils';
import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer';
import TitleBreadcrumb from '../../common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from '../../common/title-breadcrumb/title-breadcrumb.interface';
interface AlertDetailsComponentProps { interface AlertDetailsComponentProps {
alerts: Alerts; alerts: Alerts;
alertActions: AlertAction[]; alertActions: AlertAction[];
onDelete: () => void; onDelete: () => void;
breadcrumb: TitleBreadcrumbProps['titleLinks']; pageHeaderData?: HeaderProps['data'];
breadcrumb?: TitleBreadcrumbProps['titleLinks'];
allowDelete?: boolean; allowDelete?: boolean;
allowEdit?: boolean; allowEdit?: boolean;
} }
@ -55,8 +57,9 @@ export const AlertDetailsComponent = ({
alerts, alerts,
alertActions, alertActions,
onDelete, onDelete,
breadcrumb, pageHeaderData,
allowDelete = true, allowDelete = true,
breadcrumb,
allowEdit = true, allowEdit = true,
}: AlertDetailsComponentProps) => { }: AlertDetailsComponentProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -65,7 +68,9 @@ export const AlertDetailsComponent = ({
<Row align="middle" gutter={[16, 16]}> <Row align="middle" gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
<div className="d-flex items-center justify-between"> <div className="d-flex items-center justify-between">
<TitleBreadcrumb titleLinks={breadcrumb} /> {breadcrumb ? <TitleBreadcrumb titleLinks={breadcrumb} /> : null}
{pageHeaderData ? <PageHeader data={pageHeaderData} /> : null}
<Space size={16}> <Space size={16}>
{allowEdit && ( {allowEdit && (
<Link to={`${EDIT_LINK_PATH}/${alerts?.id}`}> <Link to={`${EDIT_LINK_PATH}/${alerts?.id}`}>
@ -86,13 +91,6 @@ export const AlertDetailsComponent = ({
</Col> </Col>
<Col span={24}> <Col span={24}>
<Card> <Card>
{alerts.description && (
<>
<RichTextEditorPreviewer markdown={alerts?.description ?? ''} />
<Divider />
</>
)}
<Space direction="vertical" size={8}> <Space direction="vertical" size={8}>
<Typography.Title className="m-0" level={5}> <Typography.Title className="m-0" level={5}>
{t('label.trigger')} {t('label.trigger')}
@ -103,7 +101,7 @@ export const AlertDetailsComponent = ({
)} )}
: :
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text data-testid="display-name-entities">
{alerts?.triggerConfig.entities {alerts?.triggerConfig.entities
?.map(getDisplayNameForEntities) ?.map(getDisplayNameForEntities)
?.join(', ')} ?.join(', ')}
@ -149,7 +147,18 @@ export const AlertDetailsComponent = ({
)} )}
</Space> </Space>
) : ( ) : (
<Card className="h-full" title={<Space size={8} />}> <Card
className="h-full"
title={
<Space size={16}>
{getAlertsActionTypeIcon(action.alertActionType)}
{getAlertActionTypeDisplayName(
action.alertActionType ??
AlertActionType.GenericWebhook
)}
</Space>
}>
<Space direction="vertical" size={8}> <Space direction="vertical" size={8}>
{action.alertActionType === AlertActionType.Email && ( {action.alertActionType === AlertActionType.Email && (
<> <>

View File

@ -12,6 +12,7 @@
*/ */
import { Button, Typography } from 'antd'; import { Button, Typography } from 'antd';
import { toString } from 'lodash';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { ROUTES } from '../../../constants/constants'; import { ROUTES } from '../../../constants/constants';
@ -43,23 +44,24 @@ const TableDataCardTitle = ({
}: TableDataCardTitleProps) => { }: TableDataCardTitleProps) => {
const isTourRoute = location.pathname.includes(ROUTES.TOUR); const isTourRoute = location.pathname.includes(ROUTES.TOUR);
const testId = useMemo( const { testId, displayName } = useMemo(
() => () => ({
dataTestId testId: dataTestId
? dataTestId ? dataTestId
: `${getPartialNameFromTableFQN(source.fullyQualifiedName ?? '', [ : `${getPartialNameFromTableFQN(source.fullyQualifiedName ?? '', [
FqnPart.Service, FqnPart.Service,
])}-${getNameFromFQN(source.fullyQualifiedName ?? '')}`, ])}-${getNameFromFQN(source.fullyQualifiedName ?? '')}`,
displayName: toString(source.displayName),
}),
[dataTestId, source] [dataTestId, source]
); );
const title = ( const title = (
<Button <Button
data-testid={testId} data-testid={testId}
id={`${id ?? testId}-title`} id={`${id ?? testId}-title`}
type="link" type="link"
onClick={isTourRoute ? handleLinkClick : undefined}> onClick={isTourRoute ? handleLinkClick : undefined}>
{stringToHTML(source.name)} {stringToHTML(displayName)}
</Button> </Button>
); );
@ -72,7 +74,7 @@ const TableDataCardTitle = ({
ellipsis ellipsis
className="m-b-0 text-base" className="m-b-0 text-base"
level={5} level={5}
title={source.name}> title={displayName}>
<Link <Link
className="table-data-card-title-container w-fit-content w-max-90" className="table-data-card-title-container w-fit-content w-max-90"
to={getEntityLink(searchIndex, source.fullyQualifiedName ?? '')}> to={getEntityLink(searchIndex, source.fullyQualifiedName ?? '')}>

View File

@ -13,15 +13,9 @@
import { Typography } from 'antd'; import { Typography } from 'antd';
import React from 'react'; import React from 'react';
import { HeaderProps } from './PageHeader.interface';
import './PageHeader.style.less'; import './PageHeader.style.less';
interface HeaderProps {
data: {
header: string;
subHeader: string;
};
}
const PageHeader = ({ data: { header, subHeader } }: HeaderProps) => { const PageHeader = ({ data: { header, subHeader } }: HeaderProps) => {
return ( return (
<div className="page-header-container"> <div className="page-header-container">

View File

@ -0,0 +1,19 @@
/*
* 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 interface HeaderProps {
data: {
header: string;
subHeader: string;
};
}

View File

@ -31,6 +31,7 @@ type Fields =
| 'fullyQualifiedName' | 'fullyQualifiedName'
| 'description' | 'description'
| 'serviceType' | 'serviceType'
| 'displayName'
| 'deleted'; | 'deleted';
export type SourceType = ( export type SourceType = (

View File

@ -12,7 +12,7 @@
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { isUndefined } from 'lodash'; import { isUndefined, toString } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { PAGE_SIZE } from '../../constants/constants'; import { PAGE_SIZE } from '../../constants/constants';
@ -64,7 +64,7 @@ const SearchedData: React.FC<SearchedDataProps> = ({
}); });
} }
let name = table.name; let name = toString(table.displayName);
if (!isUndefined(highlight)) { if (!isUndefined(highlight)) {
name = highlight?.name?.join(' ') || name; name = highlight?.name?.join(' ') || name;
} }

View File

@ -188,6 +188,7 @@
"edit-workflow-ingestion": "Edit {{workflow}} Ingestion", "edit-workflow-ingestion": "Edit {{workflow}} Ingestion",
"edited": "Edited", "edited": "Edited",
"effect": "Effect", "effect": "Effect",
"elastic-search": "Elasticsearch",
"elastic-search-re-index": "ElasticsearchReindex", "elastic-search-re-index": "ElasticsearchReindex",
"email": "Email", "email": "Email",
"email-plural": "Emails", "email-plural": "Emails",
@ -658,6 +659,7 @@
"delete-team-message": "Any teams under \"{{teamName}}\" will be {{deleteType}} deleted as well.", "delete-team-message": "Any teams under \"{{teamName}}\" will be {{deleteType}} deleted as well.",
"delete-webhook-permanently": "You want to delete webhook {{webhookName}} permanently? This action cannot be reverted.", "delete-webhook-permanently": "You want to delete webhook {{webhookName}} permanently? This action cannot be reverted.",
"discover-your-data-and-unlock-the-value-of-data-assets": "Discover your data and unlock the value of data assets.", "discover-your-data-and-unlock-the-value-of-data-assets": "Discover your data and unlock the value of data assets.",
"elastic-search-message": "Manage Elastisearch related works here. You can trigger re-create indexes or check the status of re-creating indexes",
"email-is-invalid": "Invalid Email.", "email-is-invalid": "Invalid Email.",
"enable-column-profile": "Enable column profile", "enable-column-profile": "Enable column profile",
"enable-debug-logging": "Enable debug logging", "enable-debug-logging": "Enable debug logging",

View File

@ -67,13 +67,11 @@ const AlertsActivityFeedPage = () => {
fetchActivityFeedAlert(); fetchActivityFeedAlert();
}, []); }, []);
const breadcrumb = useMemo( const pageHeaderData = useMemo(
() => [ () => ({
{ header: getEntityName(alert),
name: getEntityName(alert), subHeader: alert?.description || '',
url: '', }),
},
],
[alert] [alert]
); );
@ -86,7 +84,7 @@ const AlertsActivityFeedPage = () => {
alertActions={alertActions} alertActions={alertActions}
alerts={alert} alerts={alert}
allowDelete={false} allowDelete={false}
breadcrumb={breadcrumb} pageHeaderData={pageHeaderData}
onDelete={noop} onDelete={noop}
/> />
) : ( ) : (

View File

@ -10,9 +10,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { Button, Col, Row, Table, Tooltip, Typography } from 'antd'; import { Button, Col, Row, Table, Tooltip } from 'antd';
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';
import PageHeader from 'components/header/PageHeader.component';
import Loader from 'components/Loader/Loader'; import Loader from 'components/Loader/Loader';
import { isNil } from 'lodash'; import { isNil } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
@ -131,19 +132,20 @@ const AlertsPage = () => {
[handleAlertDelete] [handleAlertDelete]
); );
const pageHeaderData = useMemo(
() => ({
header: t('label.alert-plural'),
subHeader: t('message.alerts-description'),
}),
[]
);
return ( return (
<> <>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
<div className="d-flex justify-between"> <div className="d-flex justify-between">
<div> <PageHeader data={pageHeaderData} />
<Typography.Title level={5}>
{t('label.alert-plural')}
</Typography.Title>
<Typography.Text>
{t('message.alerts-description')}
</Typography.Text>
</div>
<Link <Link
to={getSettingPath( to={getSettingPath(
GlobalSettingsMenuCategory.NOTIFICATIONS, GlobalSettingsMenuCategory.NOTIFICATIONS,

View File

@ -15,9 +15,11 @@ import { ReloadOutlined } from '@ant-design/icons';
import { Badge, Button, Card, Col, Divider, Row, Space } from 'antd'; import { Badge, Button, Card, Col, Divider, Row, Space } from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
import PageHeader from 'components/header/PageHeader.component';
import { useWebSocketConnector } from 'components/web-scoket/web-scoket.provider'; import { useWebSocketConnector } from 'components/web-scoket/web-scoket.provider';
import { isEmpty, startCase } from 'lodash'; import { isEmpty, startCase } from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
getAllReIndexStatus, getAllReIndexStatus,
reIndexByPublisher, reIndexByPublisher,
@ -41,6 +43,7 @@ import './ElasticSearchReIndex.style.less';
import ReIndexAllModal from './ElasticSearchReIndexModal.component'; import ReIndexAllModal from './ElasticSearchReIndexModal.component';
const ElasticSearchIndexPage = () => { const ElasticSearchIndexPage = () => {
const { t } = useTranslation();
const [batchJobData, setBatchJobData] = useState<EventPublisherJob>(); const [batchJobData, setBatchJobData] = useState<EventPublisherJob>();
const [streamJobData, setStreamJobData] = useState<EventPublisherJob>(); const [streamJobData, setStreamJobData] = useState<EventPublisherJob>();
@ -128,6 +131,16 @@ const ElasticSearchIndexPage = () => {
}, []); }, []);
return ( return (
<Row align="middle" gutter={[16, 16]}>
<Col span={24}>
<PageHeader
data={{
header: t('label.elastic-search'),
subHeader: t('message.elastic-search-message'),
}}
/>
</Col>
<Col span={24}>
<div> <div>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@ -179,8 +192,9 @@ const ElasticSearchIndexPage = () => {
/> />
)} )}
<span> <span>
{getEventPublisherStatusText(batchJobData?.status) || {getEventPublisherStatusText(
'--'} batchJobData?.status
) || '--'}
</span> </span>
</Space> </Space>
</span> </span>
@ -261,7 +275,9 @@ const ElasticSearchIndexPage = () => {
{batchJobData?.failureDetails?.lastFailedReason ? ( {batchJobData?.failureDetails?.lastFailedReason ? (
<RichTextEditorPreviewer <RichTextEditorPreviewer
enableSeeMoreVariant={Boolean(batchJobData)} enableSeeMoreVariant={Boolean(batchJobData)}
markdown={batchJobData?.failureDetails?.lastFailedReason} markdown={
batchJobData?.failureDetails?.lastFailedReason
}
/> />
) : ( ) : (
'--' '--'
@ -309,8 +325,9 @@ const ElasticSearchIndexPage = () => {
/> />
)} )}
<span> <span>
{getEventPublisherStatusText(streamJobData?.status) || {getEventPublisherStatusText(
'--'} streamJobData?.status
) || '--'}
</span> </span>
</Space> </Space>
</span> </span>
@ -357,7 +374,9 @@ const ElasticSearchIndexPage = () => {
{streamJobData?.failureDetails?.lastFailedReason ? ( {streamJobData?.failureDetails?.lastFailedReason ? (
<RichTextEditorPreviewer <RichTextEditorPreviewer
enableSeeMoreVariant={Boolean(streamJobData)} enableSeeMoreVariant={Boolean(streamJobData)}
markdown={streamJobData?.failureDetails?.lastFailedReason} markdown={
streamJobData?.failureDetails?.lastFailedReason
}
/> />
) : ( ) : (
'--' '--'
@ -375,6 +394,8 @@ const ElasticSearchIndexPage = () => {
onSave={performReIndexAll} onSave={performReIndexAll}
/> />
</div> </div>
</Col>
</Row>
); );
}; };

View File

@ -87,11 +87,11 @@ const GlossaryLeftPanel = ({ glossaries }: GlossaryLeftPanelProps) => {
return ( return (
<LeftPanelCard id="glossary"> <LeftPanelCard id="glossary">
<GlossaryV1Skeleton loading={glossaries.length === 0}> <GlossaryV1Skeleton loading={glossaries.length === 0}>
<Row className="m-t-sm" gutter={[0, 16]}> <Row className="p-y-xs" gutter={[0, 16]}>
<Col className="p-x-sm" span={24}> <Col className="p-x-sm" span={24}>
<Typography.Paragraph className="m-b-0"> <Typography.Text strong className="m-b-0">
{t('label.glossary')} {t('label.glossary')}
</Typography.Paragraph> </Typography.Text>
</Col> </Col>
<Col className="p-x-sm" span={24}> <Col className="p-x-sm" span={24}>
<Searchbar <Searchbar
@ -113,12 +113,10 @@ const GlossaryLeftPanel = ({ glossaries }: GlossaryLeftPanelProps) => {
: t('message.no-permission-for-action') : t('message.no-permission-for-action')
}> }>
<Button <Button
ghost className="w-full flex-center gap-2 text-primary"
className="w-full flex-center gap-2"
data-testid="add-glossary" data-testid="add-glossary"
disabled={!createGlossaryPermission} disabled={!createGlossaryPermission}
icon={<PlusIcon />} icon={<PlusIcon />}
type="primary"
onClick={handleAddGlossaryClick}> onClick={handleAddGlossaryClick}>
{t('label.add-glossary')} {t('label.add-glossary')}
</Button> </Button>

View File

@ -804,12 +804,11 @@ const TagsPage = () => {
</Row> </Row>
) : ( ) : (
<Space> <Space>
<Typography.Title <Typography.Text
className="m-b-0" className="m-b-0 font-bold text-lg"
data-testid="classification-name" data-testid="classification-name">
level={5}>
{getEntityName(currentClassification)} {getEntityName(currentClassification)}
</Typography.Title> </Typography.Text>
{currentClassification.provider === ProviderType.User && ( {currentClassification.provider === ProviderType.User && (
<Tooltip <Tooltip
title={ title={
@ -886,7 +885,7 @@ const TagsPage = () => {
</div> </div>
</Space> </Space>
)} )}
<div className="m-b-sm" data-testid="description-container"> <div className="m-b-sm m-t-xs" data-testid="description-container">
<Description <Description
description={currentClassification?.description || ''} description={currentClassification?.description || ''}
entityName={ entityName={

View File

@ -14,3 +14,6 @@
.overflow-y-auto { .overflow-y-auto {
overflow-y: auto; overflow-y: auto;
} }
.min-h-24 {
min-height: 6rem;
}

View File

@ -71,7 +71,7 @@ export const StyledCard = ({
subHeading: string; subHeading: string;
}) => { }) => {
return ( return (
<div className="bg-grey p-sm rounded-4"> <div className="bg-grey p-sm rounded-4 min-h-24">
<Typography.Text>{heading}</Typography.Text> <Typography.Text>{heading}</Typography.Text>
<br /> <br />
<Typography.Text className="text-xs text-grey-muted"> <Typography.Text className="text-xs text-grey-muted">

View File

@ -278,7 +278,12 @@ export const getGlobalSettingMenuItem = (
icon, icon,
children: subItems, children: subItems,
label: isBeta ? ( label: isBeta ? (
<Badge color="#7147e8" count="beta" offset={[30, 8]} size="small"> <Badge
className="text-xs text-grey-muted"
color="#7147e8"
count="beta"
offset={[30, 8]}
size="small">
{label} {label}
</Badge> </Badge>
) : ( ) : (