ui: worked on ui feedback part 2 (#12228)

* updated request description in topic details

* miner fix

* worked on feedback

* fixed redirection link for activity feed

* fixed failing test

* miner fix

* fixed infinite loading issue when there is no permission
This commit is contained in:
Shailesh Parmar 2023-06-29 22:07:35 +05:30 committed by GitHub
parent a8c6f8bce0
commit ccf585efba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 340 additions and 126 deletions

View File

@ -140,9 +140,9 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
);
const name =
value.testName?.trim() ||
`${columnName ? columnName : table.name}_${snakeCase(
selectedTestType
)}_${cryptoRandomString({
`${replaceAllSpacialCharWith_(
columnName ? columnName : table.name
)}_${snakeCase(selectedTestType)}_${cryptoRandomString({
length: 4,
type: 'alphanumeric',
})}`;

View File

@ -319,9 +319,9 @@ const Appbar: React.FC = (): JSX.Element => {
{remainingTeamsCount} {t('label.more')}
</Link>
) : null}
<hr className="m-t-sm" />
</div>
) : null}
<hr className="m-t-sm" />
</div>
);
};

View File

@ -13,6 +13,8 @@
import { Badge, Button, List, Tabs, Typography } from 'antd';
import { AxiosError } from 'axios';
import { ActivityFeedTabs } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
import { EntityTabs } from 'enums/entity.enum';
import { UserProfileTab } from 'enums/user.enum';
import { isEmpty } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
@ -52,9 +54,11 @@ const NotificationBox = ({
const [isLoading, setIsLoading] = useState<boolean>(false);
const [viewAllPath, setViewAllPath] = useState<string>(
`${getUserPath(currentUser?.name as string)}/tasks?feedFilter=${
FeedFilter.ASSIGNED_TO
}`
getUserPath(
currentUser?.name as string,
EntityTabs.ACTIVITY_FEED,
ActivityFeedTabs.TASKS
)
);
const notificationDropDownList = useMemo(() => {

View File

@ -169,7 +169,7 @@ const Services = ({
<div>
<Link
to={getServiceDetailsPath(
service.name,
service.fullyQualifiedName ?? service.name,
serviceName
)}>
<button>

View File

@ -158,11 +158,11 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
excludeColumns,
} = tableProfilerConfig;
handleStateChange({
sqlQuery: profileQuery || '',
sqlQuery: profileQuery ?? '',
profileSample: profileSample,
excludeCol: excludeColumns || [],
excludeCol: excludeColumns ?? [],
selectedProfileSampleType:
profileSampleType || ProfileSampleType.Percentage,
profileSampleType ?? ProfileSampleType.Percentage,
});
const profileSampleTypeCheck =
@ -429,6 +429,7 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
})}
name="profileSampleType">
<Select
autoFocus
className="w-full"
data-testid="profile-sample"
options={PROFILE_SAMPLE_OPTIONS}
@ -477,13 +478,13 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
</p>
<SchemaEditor
className="custom-query-editor query-editor-h-200"
className="sql-editor-container custom-query-editor query-editor-h-200 custom-code-mirror-theme"
data-testid="profiler-setting-sql-editor"
mode={{ name: CSMode.SQL }}
options={{
readOnly: false,
}}
value={state?.sqlQuery || ''}
value={state?.sqlQuery ?? ''}
onChange={handleCodeMirrorChange}
/>
</Col>

View File

@ -53,6 +53,13 @@
overflow: scroll;
}
}
.sql-editor-container {
.CodeMirror {
.CodeMirror-foldgutter {
width: 2em;
}
}
}
.include-columns-add-button.ant-btn-icon-only.ant-btn-sm {
width: 18px;
height: 18px;

View File

@ -22,7 +22,9 @@ import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlac
import QueryViewer from 'components/common/QueryViewer/QueryViewer.component';
import PageLayoutV1 from 'components/containers/PageLayoutV1';
import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
import SampleDataTopic from 'components/SampleDataTopic/SampleDataTopic';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import { getTopicDetailsPath } from 'constants/constants';
@ -51,8 +53,6 @@ import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import EntityLineageComponent from '../EntityLineage/EntityLineage.component';
import SampleDataTopic from '../SampleDataTopic/SampleDataTopic';
import { TopicDetailsProps } from './TopicDetails.interface';
import TopicSchemaFields from './TopicSchema/TopicSchema';
@ -294,6 +294,15 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
onThreadLinkSelect={onThreadLinkSelect}
/>
<TopicSchemaFields
entityFieldTasks={getEntityFieldThreadCounts(
EntityField.COLUMNS,
entityFieldTaskCount
)}
entityFieldThreads={getEntityFieldThreadCounts(
EntityField.COLUMNS,
entityFieldThreadCount
)}
entityFqn={topicDetails.fullyQualifiedName ?? ''}
hasDescriptionEditAccess={
topicPermissions.EditAll || topicPermissions.EditDescription
}
@ -302,6 +311,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
}
isReadOnly={Boolean(topicDetails.deleted)}
messageSchema={topicDetails.messageSchema}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={handleSchemaFieldsUpdate}
/>
</div>
@ -464,6 +474,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
</Col>
<Col span={24}>
<Tabs
destroyInactiveTabPane
activeKey={activeTab ?? EntityTabs.SCHEMA}
className="entity-details-page-tabs"
data-testid="tabs"

View File

@ -12,6 +12,8 @@
*/
import { TableProps } from 'antd';
import { ThreadType } from 'generated/api/feed/createThread';
import { EntityFieldThreads } from 'interface/feed.interface';
import { HTMLAttributes, ReactNode } from 'react';
import { Field, Topic } from '../../../generated/entity/data/topic';
@ -27,9 +29,13 @@ export interface TopicSchemaFieldsProps
hasDescriptionEditAccess: boolean;
hasTagEditAccess: boolean;
isReadOnly: boolean;
entityFqn: string;
defaultExpandAllRows?: boolean;
showSchemaDisplayTypeSwitch?: boolean;
onUpdate?: (updatedMessageSchema: Topic['messageSchema']) => Promise<void>;
entityFieldThreads?: EntityFieldThreads[];
entityFieldTasks?: EntityFieldThreads[];
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
}
export enum SchemaViewType {

View File

@ -35,6 +35,7 @@ const mockProps: TopicSchemaFieldsProps = {
isReadOnly: false,
onUpdate: mockOnUpdate,
hasTagEditAccess: true,
entityFqn: 'topic.fqn',
};
jest.mock('utils/TagsUtils', () => ({

View File

@ -28,14 +28,28 @@ import classNames from 'classnames';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import SchemaEditor from 'components/schema-editor/SchemaEditor';
import TableTags from 'components/TableTags/TableTags.component';
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
import { DE_ACTIVE_COLOR } from 'constants/constants';
import { EntityField } from 'constants/Feeds.constants';
import { TABLE_SCROLL_VALUE } from 'constants/Table.constants';
import { CSMode } from 'enums/codemirror.enum';
import { EntityType } from 'enums/entity.enum';
import { ThreadType } from 'generated/api/feed/createThread';
import { TagLabel, TagSource } from 'generated/type/tagLabel';
import { EntityFieldThreads } from 'interface/feed.interface';
import { cloneDeep, isEmpty, isUndefined, map } from 'lodash';
import { EntityTags, TagOption } from 'Models';
import React, { FC, useMemo, useState } from 'react';
import React, { FC, Fragment, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getEntityName } from 'utils/EntityUtils';
import { useHistory } from 'react-router-dom';
import { getPartialNameFromTopicFQN } from 'utils/CommonUtils';
import { ENTITY_LINK_SEPARATOR, getEntityName } from 'utils/EntityUtils';
import { getFieldThreadElement } from 'utils/FeedElementUtils';
import {
getRequestDescriptionPath,
getUpdateDescriptionPath,
} from 'utils/TasksUtils';
import { ReactComponent as IconRequest } from '../../../assets/svg/request-icon.svg';
import { DataTypeTopic, Field } from '../../../generated/entity/data/topic';
import { getTableExpandableConfig } from '../../../utils/TableUtils';
import {
@ -59,13 +73,28 @@ const TopicSchemaFields: FC<TopicSchemaFieldsProps> = ({
hasTagEditAccess,
defaultExpandAllRows = false,
showSchemaDisplayTypeSwitch = true,
entityFqn,
entityFieldThreads,
onThreadLinkSelect,
entityFieldTasks,
}) => {
const history = useHistory();
const { t } = useTranslation();
const [editFieldDescription, setEditFieldDescription] = useState<Field>();
const [viewType, setViewType] = useState<SchemaViewType>(
SchemaViewType.FIELDS
);
const getColumnName = (cell: Field) => {
const fqn = cell?.fullyQualifiedName || '';
const columnName = getPartialNameFromTopicFQN(fqn);
// wrap it in quotes if dot is present
return columnName.includes(FQN_SEPARATOR_CHAR)
? `"${columnName}"`
: columnName;
};
const handleFieldTagsChange = async (
selectedTags: EntityTags[],
editColumnTag: Field
@ -101,6 +130,66 @@ const TopicSchemaFields: FC<TopicSchemaFieldsProps> = ({
}
};
const onUpdateDescriptionHandler = (cell: Field) => {
const field = EntityField.COLUMNS;
const value = getColumnName(cell);
history.push(
getUpdateDescriptionPath(
EntityType.TOPIC,
entityFqn as string,
field,
value
)
);
};
const onRequestDescriptionHandler = (cell: Field) => {
const field = EntityField.COLUMNS;
const value = getColumnName(cell);
history.push(
getRequestDescriptionPath(
EntityType.TOPIC,
entityFqn as string,
field,
value
)
);
};
const getRequestDescriptionElement = (cell: Field) => {
const hasDescription = Boolean(cell?.description ?? '');
return (
<Button
className="p-0 w-7 h-7 flex-none flex-center link-text focus:tw-outline-none hover-cell-icon m-r-xss"
data-testid="request-description"
type="text"
onClick={() =>
hasDescription
? onUpdateDescriptionHandler(cell)
: onRequestDescriptionHandler(cell)
}>
<Popover
destroyTooltipOnHide
content={
hasDescription
? t('message.request-update-description')
: t('message.request-description')
}
overlayClassName="ant-popover-request-description"
trigger="hover"
zIndex={9999}>
<IconRequest
height={14}
name={t('message.request-description')}
style={{ color: DE_ACTIVE_COLOR }}
width={14}
/>
</Popover>
</Button>
);
};
const renderFieldDescription: CellRendered<Field, 'description'> = (
description,
record,
@ -110,28 +199,68 @@ const TopicSchemaFields: FC<TopicSchemaFieldsProps> = ({
<Space
className="custom-group w-full"
data-testid="description"
direction={isEmpty(description) ? 'horizontal' : 'vertical'}
id={`field-description-${index}`}
size={4}>
<>
<div>
{description ? (
<RichTextEditorPreviewer markdown={description} />
) : (
<Typography.Text className="text-grey-muted">
<span className="text-grey-muted">
{t('label.no-entity', {
entity: t('label.description'),
})}
</Typography.Text>
</span>
)}
</>
{isReadOnly && !hasDescriptionEditAccess ? null : (
<Button
className="p-0 opacity-0 group-hover-opacity-100"
data-testid="edit-button"
icon={<EditIcon width={16} />}
type="text"
onClick={() => setEditFieldDescription(record)}
/>
)}
</div>
<div className="d-flex tw--mt-1.5">
{!isReadOnly ? (
<Fragment>
{hasDescriptionEditAccess && (
<>
<Button
className="p-0 tw-self-start flex-center w-7 h-7 d-flex-none hover-cell-icon"
data-testid="edit-button"
type="text"
onClick={() => setEditFieldDescription(record)}>
<EditIcon
height={14}
name={t('label.edit')}
style={{ color: DE_ACTIVE_COLOR }}
width={14}
/>
</Button>
</>
)}
{getRequestDescriptionElement(record)}
{getFieldThreadElement(
getColumnName(record),
EntityField.DESCRIPTION,
entityFieldThreads as EntityFieldThreads[],
onThreadLinkSelect,
EntityType.TOPIC,
entityFqn,
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
record
)}${ENTITY_LINK_SEPARATOR}description`,
Boolean(record)
)}
{getFieldThreadElement(
getColumnName(record),
EntityField.DESCRIPTION,
entityFieldTasks as EntityFieldThreads[],
onThreadLinkSelect,
EntityType.TOPIC,
entityFqn,
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
record
)}${ENTITY_LINK_SEPARATOR}description`,
Boolean(record),
ThreadType.Task
)}
</Fragment>
) : null}
</div>
</Space>
);
};
@ -264,6 +393,7 @@ const TopicSchemaFields: FC<TopicSchemaFieldsProps> = ({
isEmpty(messageSchema?.schemaFields) ? (
messageSchema?.schemaText && (
<SchemaEditor
className="custom-code-mirror-theme custom-query-editor"
editorClass={classNames('table-query-editor')}
mode={{ name: CSMode.JAVASCRIPT }}
options={{

View File

@ -127,6 +127,7 @@ const TopicVersion: FC<TopicVersionProp> = ({
<TopicSchemaFields
defaultExpandAllRows
isReadOnly
entityFqn={currentVersionData?.fullyQualifiedName ?? ''}
hasDescriptionEditAccess={false}
hasTagEditAccess={false}
messageSchema={messageSchemaDiff}

View File

@ -273,17 +273,15 @@ const Users = ({
);
} else {
return (
<div className="p-x-sm">
<p className="m-t-xs">
{userData.description || (
<span className="text-grey-muted">
{t('label.no-entity', {
entity: t('label.description'),
})}
</span>
)}
</p>
</div>
<Typography.Paragraph className="m-b-0">
{userData.description || (
<span className="text-grey-muted">
{t('label.no-entity', {
entity: t('label.description'),
})}
</span>
)}
</Typography.Paragraph>
);
}
};
@ -584,7 +582,11 @@ const Users = ({
)}
<Space className="p-sm w-full" direction="vertical" size={8}>
{getDisplayNameComponent()}
<p>{userData.email}</p>
<Typography.Paragraph
className="m-b-0"
ellipsis={{ tooltip: true }}>
{userData.email}
</Typography.Paragraph>
{getDescriptionComponent()}
{isAuthProviderBasic &&
(isAdminUser || isLoggedinUser) &&
@ -719,7 +721,7 @@ const Users = ({
}
/>
) : (
<ErrorPlaceHolder>
<ErrorPlaceHolder className="m-0">
<Typography.Paragraph>
{tab === UserPageTabs.MY_DATA
? t('server.you-have-not-action-anything-yet', {

View File

@ -31,6 +31,7 @@ import { getMlModelByFQN } from 'rest/mlModelAPI';
import { getPipelineByFqn } from 'rest/pipelineAPI';
import { getTableDetailsByFQN } from 'rest/tableAPI';
import { getTopicByFqn } from 'rest/topicsAPI';
import { getTableFQNFromColumnFQN } from 'utils/CommonUtils';
import { getEntityName } from 'utils/EntityUtils';
import AppState from '../../../AppState';
import { EntityType } from '../../../enums/entity.enum';
@ -60,6 +61,13 @@ const PopoverContent: React.FC<{
case EntityType.TABLE:
promise = getTableDetailsByFQN(entityFQN, fields);
break;
case EntityType.TEST_CASE:
promise = getTableDetailsByFQN(
getTableFQNFromColumnFQN(entityFQN),
fields
);
break;
case EntityType.TOPIC:
promise = getTopicByFqn(entityFQN, fields);

View File

@ -508,6 +508,7 @@
"loading": "Loading",
"local-config-source": "Local Config Source",
"log-plural": "Logs",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "logged-in user",
"login": "Login",
"logo-url": "Logo URL",

View File

@ -508,6 +508,7 @@
"loading": "Cargando",
"local-config-source": "Origen de configuración local",
"log-plural": "Registros",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "usuario conectado",
"login": "Iniciar sesión",
"logo-url": "Logo URL",

View File

@ -508,6 +508,7 @@
"loading": "Chargement",
"local-config-source": "Source de Configuration Locale",
"log-plural": "Journal",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "Utilisateur Connecté",
"login": "Se Connecter",
"logo-url": "Logo URL",

View File

@ -508,6 +508,7 @@
"loading": "読み込み中",
"local-config-source": "Local Config Source",
"log-plural": "ログ",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "logged-in user",
"login": "ログイン",
"logo-url": "Logo URL",

View File

@ -508,6 +508,7 @@
"loading": "Carregando",
"local-config-source": "Origem da configuração local",
"log-plural": "Logs",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "conectar com usuário",
"login": "Entrar",
"logo-url": "Logo URL",

View File

@ -508,6 +508,7 @@
"loading": "加载中",
"local-config-source": "本地配置源",
"log-plural": "日志",
"log-viewer": "Log Viewer",
"logged-in-user-lowercase": "已登录用户",
"login": "登录",
"logo-url": "Logo URL",

View File

@ -267,11 +267,11 @@ const AddQueryPage = () => {
<Tooltip
placement="top"
title={
!permissions.query.Create && NO_PERMISSION_FOR_ACTION
!permissions.query?.Create && NO_PERMISSION_FOR_ACTION
}>
<Button
data-testid="save-btn"
disabled={!permissions.query.Create}
disabled={!permissions.query?.Create}
htmlType="submit"
loading={isSaving}
type="primary">

View File

@ -22,11 +22,19 @@ jest.mock('react-router-dom', () => ({
ingestionName: 'ingestion_123456',
}),
}));
jest.mock('../../utils/LogsViewer.utils', () => ({
getLogBreadCrumbs: jest
.fn()
.mockReturnValue({ name: 'getLogBreadCrumbs', url: '' }),
}));
jest.mock(
'components/common/title-breadcrumb/title-breadcrumb.component',
() => () => <>TitleBreadcrumb.component</>
);
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)
);
jest.mock('react-lazylog', () => ({
LazyLog: jest

View File

@ -11,10 +11,11 @@
* limitations under the License.
*/
import { Button, Card, Col, Row, Space, Typography } from 'antd';
import { Button, Col, Row, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
import { CopyToClipboardButton } from 'components/buttons/CopyToClipboardButton/CopyToClipboardButton';
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
import PageLayoutV1 from 'components/containers/PageLayoutV1';
import { IngestionRecentRuns } from 'components/Ingestion/IngestionRecentRun/IngestionRecentRuns.component';
import Loader from 'components/Loader/Loader';
import { isEmpty, isNil, isUndefined, toNumber } from 'lodash';
@ -220,10 +221,12 @@ const LogsViewer = () => {
};
}, [ingestionDetails]);
return isLoading ? (
<Loader />
) : (
<div className="m-xs ">
if (isLoading) {
return <Loader />;
}
return (
<PageLayoutV1 pageTitle={t('label.log-viewer')}>
<Space align="start" className="w-full m-md m-t-xs" direction="vertical">
<Space align="center">
<TitleBreadcrumb
@ -241,77 +244,76 @@ const LogsViewer = () => {
</Space>
</Space>
<Card className="h-full p-0 log-card">
{!isEmpty(logs) ? (
<Row>
<Col className="p-md" span={18}>
<Row className="relative" gutter={[16, 16]}>
<Col span={24}>
<Row justify="end">
<Col>
<Button
ghost
data-testid="jump-to-end-button"
type="primary"
onClick={handleJumpToEnd}>
{t('label.jump-to-end')}
</Button>
</Col>
<Col>
<CopyToClipboardButton copyText={logs} />
</Col>
</Row>
</Col>
<Col
className="h-min-80 lazy-log-container"
data-testid="lazy-log"
span={24}>
<LazyLog
caseInsensitive
enableSearch
selectableLines
extraLines={1} // 1 is to be add so that linux users can see last line of the log
text={logs}
/>
</Col>
</Row>
</Col>
<Col span={6}>
<Card className="h-full" data-testid="summary-card">
<Space className="p-md w-full" direction="vertical">
<Typography.Title level={5}>
{t('label.summary')}
</Typography.Title>
{!isEmpty(logs) ? (
<Row className="border-top">
<Col className="p-md border-right" span={18}>
<Row className="relative" gutter={[16, 16]}>
<Col span={24}>
<Row justify="end">
<Col>
<Button
ghost
data-testid="jump-to-end-button"
type="primary"
onClick={handleJumpToEnd}>
{t('label.jump-to-end')}
</Button>
</Col>
<Col>
<CopyToClipboardButton copyText={logs} />
</Col>
</Row>
</Col>
<Col
className="h-min-80 lazy-log-container"
data-testid="lazy-log"
span={24}>
<LazyLog
caseInsensitive
enableSearch
selectableLines
extraLines={1} // 1 is to be add so that linux users can see last line of the log
text={logs}
/>
</Col>
</Row>
</Col>
<Col span={6}>
<Space
className="p-md w-full"
data-testid="summary-card"
direction="vertical">
<Typography.Title level={5}>
{t('label.summary')}
</Typography.Title>
<div>
<Typography.Text type="secondary">
{t('label.basic-configuration')}
</Typography.Text>
<div>
<Typography.Text type="secondary">
{t('label.basic-configuration')}
</Typography.Text>
<Row className="m-t-xs" gutter={[8, 8]}>
{Object.entries(logSummaries).map(([key, value]) => {
return (
<Fragment key={key}>
<Col className="summary-key" span={12}>
{key}
</Col>
<Col className="flex" span={12}>
{value}
</Col>
</Fragment>
);
})}
</Row>
</div>
</Space>
</Card>
</Col>
</Row>
) : (
<LogViewerSkeleton />
)}
</Card>
</div>
<Row className="m-t-xs" gutter={[8, 8]}>
{Object.entries(logSummaries).map(([key, value]) => {
return (
<Fragment key={key}>
<Col className="summary-key" span={12}>
{key}
</Col>
<Col className="flex" span={12}>
{value}
</Col>
</Fragment>
);
})}
</Row>
</div>
</Space>
</Col>
</Row>
) : (
<LogViewerSkeleton />
)}
</PageLayoutV1>
);
};

View File

@ -254,6 +254,8 @@ const TableDetailsPageV1 = () => {
entity: t('label.resource-permission-lowercase'),
})
);
} finally {
setLoading(false);
}
},
[getEntityPermissionByFqn, setTablePermissions]
@ -862,7 +864,12 @@ const TableDetailsPageV1 = () => {
}
if (!(tablePermissions.ViewAll || tablePermissions.ViewBasic)) {
return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />;
return (
<ErrorPlaceHolder
className="m-0"
type={ERROR_PLACEHOLDER_TYPE.PERMISSION}
/>
);
}
if (!tableDetails) {

View File

@ -977,3 +977,7 @@ export const getEntityDetailLink = (
return path;
};
export const getPartialNameFromTopicFQN = (fqn: string): string => {
return Fqn.split(fqn).slice(2).join(FQN_SEPARATOR_CHAR);
};

View File

@ -14,6 +14,8 @@
import { OPEN_METADATA } from 'constants/service-guide.constant';
import { isUndefined, startCase } from 'lodash';
import { IngestionPipeline } from '../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { getNameFromFQN } from './CommonUtils';
import Fqn from './Fqn';
import { getSettingsPathFromPipelineType } from './IngestionUtils';
import { getLogEntityPath } from './RouterUtils';
@ -32,7 +34,8 @@ export const getLogBreadCrumbs = (
ingestionName: string,
ingestionDetails: IngestionPipeline | undefined
) => {
if (ingestionName.split('.')[0] === OPEN_METADATA && ingestionDetails) {
const updateIngestionName = Fqn.split(ingestionName);
if (updateIngestionName.includes(OPEN_METADATA) && ingestionDetails) {
return [
{
name: startCase(ingestionDetails.pipelineType),
@ -40,7 +43,7 @@ export const getLogBreadCrumbs = (
activeTitle: true,
},
{
name: startCase(ingestionName.split('.')[1]),
name: getNameFromFQN(ingestionName),
url: '',
activeTitle: true,
},
@ -50,7 +53,7 @@ export const getLogBreadCrumbs = (
return [];
}
const urlPath = [serviceType, ...ingestionName.split('.')];
const urlPath = [serviceType, ...updateIngestionName];
return urlPath.map((path, index) => {
return {

View File

@ -124,7 +124,11 @@ import {
PipelineServiceType,
} from '../generated/entity/services/pipelineService';
import { ServicesType } from '../interface/service.interface';
import { getEntityDeleteMessage, pluralize } from './CommonUtils';
import {
getEntityDeleteMessage,
pluralize,
replaceAllSpacialCharWith_,
} from './CommonUtils';
import { getDashboardURL } from './DashboardServiceUtils';
import { getBrokers } from './MessagingServiceUtils';
import { showErrorToast } from './ToastUtils';
@ -604,7 +608,9 @@ export const getIngestionName = (
IngestionPipelineType.Dbt,
].includes(type)
) {
return `${serviceName}_${type}_${cryptoRandomString({
return `${replaceAllSpacialCharWith_(
serviceName
)}_${type}_${cryptoRandomString({
length: 8,
type: 'alphanumeric',
})}`;

View File

@ -51,12 +51,13 @@ import {
getPipelineDetailsPath,
getServiceDetailsPath,
getTableDetailsPath,
getTableTabPath,
getTagsDetailsPath,
getTopicDetailsPath,
TEXT_BODY_COLOR,
} from '../constants/constants';
import { GlobalSettingsMenuCategory } from '../constants/GlobalSettings.constants';
import { EntityType, FqnPart } from '../enums/entity.enum';
import { EntityTabs, EntityType, FqnPart } from '../enums/entity.enum';
import { SearchIndex } from '../enums/search.enum';
import { ConstraintTypes, PrimaryTableDataTypes } from '../enums/table.enum';
import {
@ -272,6 +273,12 @@ export const getEntityLink = (
case EntityType.DASHBOARD_DATA_MODEL:
return getDataModelDetailsPath(fullyQualifiedName);
case EntityType.TEST_CASE:
return `${getTableTabPath(
getTableFQNFromColumnFQN(fullyQualifiedName),
EntityTabs.PROFILER
)}?activeTab=Data Quality`;
case SearchIndex.TABLE:
case EntityType.TABLE:
default: