mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-30 03:46:10 +00:00
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:
parent
a8c6f8bce0
commit
ccf585efba
@ -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',
|
||||
})}`;
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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(() => {
|
||||
|
@ -169,7 +169,7 @@ const Services = ({
|
||||
<div>
|
||||
<Link
|
||||
to={getServiceDetailsPath(
|
||||
service.name,
|
||||
service.fullyQualifiedName ?? service.name,
|
||||
serviceName
|
||||
)}>
|
||||
<button>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -35,6 +35,7 @@ const mockProps: TopicSchemaFieldsProps = {
|
||||
isReadOnly: false,
|
||||
onUpdate: mockOnUpdate,
|
||||
hasTagEditAccess: true,
|
||||
entityFqn: 'topic.fqn',
|
||||
};
|
||||
|
||||
jest.mock('utils/TagsUtils', () => ({
|
||||
|
@ -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={{
|
||||
|
@ -127,6 +127,7 @@ const TopicVersion: FC<TopicVersionProp> = ({
|
||||
<TopicSchemaFields
|
||||
defaultExpandAllRows
|
||||
isReadOnly
|
||||
entityFqn={currentVersionData?.fullyQualifiedName ?? ''}
|
||||
hasDescriptionEditAccess={false}
|
||||
hasTagEditAccess={false}
|
||||
messageSchema={messageSchemaDiff}
|
||||
|
@ -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', {
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -508,6 +508,7 @@
|
||||
"loading": "加载中",
|
||||
"local-config-source": "本地配置源",
|
||||
"log-plural": "日志",
|
||||
"log-viewer": "Log Viewer",
|
||||
"logged-in-user-lowercase": "已登录用户",
|
||||
"login": "登录",
|
||||
"logo-url": "Logo URL",
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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',
|
||||
})}`;
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user