mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-28 19:05:53 +00:00
UI : Add localization (#9309)
* UI : Add localization * address comments
This commit is contained in:
parent
d2deb38766
commit
c562be6f78
@ -301,10 +301,6 @@ jest.mock(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.mock('../../utils/EntityVersionUtils', () => ({
|
|
||||||
getFeedSummary: jest.fn().mockImplementation(() => <p>EntityVersionUtils</p>),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../../utils/ServiceUtils', () => ({
|
jest.mock('../../utils/ServiceUtils', () => ({
|
||||||
getAllServices: jest
|
getAllServices: jest
|
||||||
.fn()
|
.fn()
|
||||||
|
@ -286,7 +286,7 @@ const TeamDetailsV1 = ({
|
|||||||
disabled={!entityPermissions.EditAll}
|
disabled={!entityPermissions.EditAll}
|
||||||
icon={
|
icon={
|
||||||
<SVGIcons
|
<SVGIcons
|
||||||
alt="Remove"
|
alt={t('label.remove')}
|
||||||
className="tw-w-4 tw-mb-2.5"
|
className="tw-w-4 tw-mb-2.5"
|
||||||
icon={Icons.ICON_REMOVE}
|
icon={Icons.ICON_REMOVE}
|
||||||
/>
|
/>
|
||||||
@ -554,7 +554,7 @@ const TeamDetailsV1 = ({
|
|||||||
showErrorToast(
|
showErrorToast(
|
||||||
error as AxiosError,
|
error as AxiosError,
|
||||||
t('server.entity-fetch-error', {
|
t('server.entity-fetch-error', {
|
||||||
entity: 'User Permissions',
|
entity: t('label.user-permissions'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
@ -639,7 +639,9 @@ const TeamDetailsV1 = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const restoreIcon = useMemo(
|
const restoreIcon = useMemo(
|
||||||
() => <SVGIcons alt="Restore" icon={Icons.RESTORE} width="16px" />,
|
() => (
|
||||||
|
<SVGIcons alt={t('label.restore')} icon={Icons.RESTORE} width="16px" />
|
||||||
|
),
|
||||||
[currentTeam.isJoinable]
|
[currentTeam.isJoinable]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1366,7 +1368,7 @@ const TeamDetailsV1 = ({
|
|||||||
okText={t('label.confirm')}
|
okText={t('label.confirm')}
|
||||||
title={`${t('label.remove-entity', {
|
title={`${t('label.remove-entity', {
|
||||||
entity: getEntityName(selectedEntity?.record),
|
entity: getEntityName(selectedEntity?.record),
|
||||||
})} ${t('label.from')} ${getEntityName(currentTeam)}`}
|
})} ${t('label.from-lowercase')} ${getEntityName(currentTeam)}`}
|
||||||
visible={!isUndefined(selectedEntity.record)}
|
visible={!isUndefined(selectedEntity.record)}
|
||||||
onCancel={() => setEntity(undefined)}
|
onCancel={() => setEntity(undefined)}
|
||||||
onOk={async () => {
|
onOk={async () => {
|
||||||
@ -1380,7 +1382,7 @@ const TeamDetailsV1 = ({
|
|||||||
{t('label.sure-to-remove')}{' '}
|
{t('label.sure-to-remove')}{' '}
|
||||||
{`${getEntityName(
|
{`${getEntityName(
|
||||||
selectedEntity.record
|
selectedEntity.record
|
||||||
)} t('label.from') ${getEntityName(currentTeam)}?`}
|
)} t('label.from-lowercase') ${getEntityName(currentTeam)}?`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
"sure-to-remove": "Are you sure you want to remove the",
|
"sure-to-remove": "Are you sure you want to remove the",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"remove-entity": "Remove {{entity}}",
|
"remove-entity": "Remove {{entity}}",
|
||||||
"from": "from",
|
"from-lowercase": "from",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"add-policy": "Add Policy",
|
"add-policy": "Add Policy",
|
||||||
"go-back": "Go Back",
|
"go-back": "Go Back",
|
||||||
@ -473,6 +473,25 @@
|
|||||||
"retention-size": "retention-size",
|
"retention-size": "retention-size",
|
||||||
"clean-up-policies": "clean-up policies",
|
"clean-up-policies": "clean-up policies",
|
||||||
"maximum-size": "maximum size",
|
"maximum-size": "maximum size",
|
||||||
|
"add-term": "Add Term",
|
||||||
|
"accept-suggestion": "Accept Suggestion",
|
||||||
|
"edit-amp-accept-suggestion": "Edit & Accept Suggestion",
|
||||||
|
"to-lowercase": "to",
|
||||||
|
"of-lowercase": "of",
|
||||||
|
"description-lowercase": "description",
|
||||||
|
"started-following": "Started following",
|
||||||
|
"unfollowed": "Unfollowed",
|
||||||
|
"data-asset-has-been-action-type": "Data asset has been {{actionType}}",
|
||||||
|
"deleted-lowercase": "deleted",
|
||||||
|
"restored-lowercase": "restored",
|
||||||
|
"edited": "Edited",
|
||||||
|
"added-lowercase": "added",
|
||||||
|
"updated-lowercase": "updated",
|
||||||
|
"has-been-action-type-lowercase": "has been {{actionType}}",
|
||||||
|
"rules": "Rules",
|
||||||
|
"manage-rule": "Manage Rule",
|
||||||
|
"users": "Users",
|
||||||
|
"user-permissions": "User Permissions",
|
||||||
"openmetadata": "OpenMetadata",
|
"openmetadata": "OpenMetadata",
|
||||||
"activity-feeds": "Activity Feeds",
|
"activity-feeds": "Activity Feeds",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
@ -504,13 +523,20 @@
|
|||||||
"search-entity": "Search {{entity}}",
|
"search-entity": "Search {{entity}}",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
|
"reply-in-conversation": "Reply in conversation",
|
||||||
|
"older-reply-lowercase": "older reply",
|
||||||
|
"older-replies-lowercase": "older replies",
|
||||||
|
"created-a-task-lowercase": "created a task",
|
||||||
|
"posted-on-lowercase": "posted on",
|
||||||
|
"announcement": "Announcement",
|
||||||
|
"conversation": "Conversation",
|
||||||
|
"url-lowercase": "url",
|
||||||
|
"schemas": "Schemas",
|
||||||
"deleted": "Deleted {{entity}}",
|
"deleted": "Deleted {{entity}}",
|
||||||
"test-plural": "Tests",
|
"test-plural": "Tests",
|
||||||
"test": "Test",
|
"test": "Test",
|
||||||
"profiler": "Profiler",
|
|
||||||
"here-lowercase": "here",
|
"here-lowercase": "here",
|
||||||
"insert": "Insert",
|
"insert": "Insert",
|
||||||
"update": "Update",
|
|
||||||
"table-profile": "Table Profile",
|
"table-profile": "Table Profile",
|
||||||
"column-profile": "Column Profile",
|
"column-profile": "Column Profile",
|
||||||
"applied-advanced-search": "Applied advanced search"
|
"applied-advanced-search": "Applied advanced search"
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
"sure-to-remove": "Etes-vous sûr de vouloir supprimer le",
|
"sure-to-remove": "Etes-vous sûr de vouloir supprimer le",
|
||||||
"confirm": "Confirmer",
|
"confirm": "Confirmer",
|
||||||
"remove-entity": "Supprimer {{entity}}",
|
"remove-entity": "Supprimer {{entity}}",
|
||||||
"from": "de",
|
"from-lowercase": "de",
|
||||||
"add": "Ajouter",
|
"add": "Ajouter",
|
||||||
"add-policy": "Ajouter une Police",
|
"add-policy": "Ajouter une Police",
|
||||||
"go-back": "Retour en arrière",
|
"go-back": "Retour en arrière",
|
||||||
|
@ -108,6 +108,12 @@ jest.mock('../../../utils/ToastUtils', () => ({
|
|||||||
showErrorToast: jest.fn().mockReturnValue(''),
|
showErrorToast: jest.fn().mockReturnValue(''),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: jest.fn().mockReturnValue({
|
||||||
|
t: (label: string) => label,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Test Policy details page', () => {
|
describe('Test Policy details page', () => {
|
||||||
it('Should render the policy details page component', async () => {
|
it('Should render the policy details page component', async () => {
|
||||||
render(<PoliciesDetailPage />);
|
render(<PoliciesDetailPage />);
|
||||||
@ -118,11 +124,11 @@ describe('Test Policy details page', () => {
|
|||||||
|
|
||||||
const description = await screen.findByTestId('description-data');
|
const description = await screen.findByTestId('description-data');
|
||||||
|
|
||||||
const rulesTab = await screen.findByText('Rules');
|
const rulesTab = await screen.findByText('label.rules');
|
||||||
|
|
||||||
const rolesTab = await screen.findByText('Roles');
|
const rolesTab = await screen.findByText('label.roles');
|
||||||
|
|
||||||
const teamsTab = await screen.findByText('Teams');
|
const teamsTab = await screen.findByText('label.teams');
|
||||||
|
|
||||||
expect(container).toBeInTheDocument();
|
expect(container).toBeInTheDocument();
|
||||||
|
|
||||||
|
@ -21,18 +21,16 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
Row,
|
Row,
|
||||||
Space,
|
Space,
|
||||||
Table,
|
|
||||||
Tabs,
|
Tabs,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { isEmpty, isUndefined, startCase } from 'lodash';
|
import { isEmpty, isUndefined, startCase } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
getPolicyByName,
|
getPolicyByName,
|
||||||
getRoleByName,
|
getRoleByName,
|
||||||
@ -54,10 +52,6 @@ import {
|
|||||||
GlobalSettingOptions,
|
GlobalSettingOptions,
|
||||||
GlobalSettingsMenuCategory,
|
GlobalSettingsMenuCategory,
|
||||||
} from '../../../constants/GlobalSettings.constants';
|
} from '../../../constants/GlobalSettings.constants';
|
||||||
import {
|
|
||||||
NO_PERMISSION_FOR_ACTION,
|
|
||||||
NO_PERMISSION_TO_VIEW,
|
|
||||||
} from '../../../constants/HelperTextUtil';
|
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
import { Rule } from '../../../generated/api/policies/createPolicy';
|
import { Rule } from '../../../generated/api/policies/createPolicy';
|
||||||
import { Policy } from '../../../generated/entity/policies/policy';
|
import { Policy } from '../../../generated/entity/policies/policy';
|
||||||
@ -67,105 +61,17 @@ import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
|
|||||||
import {
|
import {
|
||||||
getAddPolicyRulePath,
|
getAddPolicyRulePath,
|
||||||
getEditPolicyRulePath,
|
getEditPolicyRulePath,
|
||||||
getRoleWithFqnPath,
|
|
||||||
getSettingPath,
|
getSettingPath,
|
||||||
getTeamsWithFqnPath,
|
|
||||||
} from '../../../utils/RouterUtils';
|
} from '../../../utils/RouterUtils';
|
||||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||||
import './PoliciesDetail.less';
|
import './PoliciesDetail.less';
|
||||||
|
import PoliciesDetailsList from './PoliciesDetailsList.component';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
type Attribute = 'roles' | 'teams';
|
type Attribute = 'roles' | 'teams';
|
||||||
|
|
||||||
const List = ({
|
|
||||||
list,
|
|
||||||
type,
|
|
||||||
onDelete,
|
|
||||||
hasAccess,
|
|
||||||
}: {
|
|
||||||
list: EntityReference[];
|
|
||||||
type: 'role' | 'team';
|
|
||||||
onDelete: (record: EntityReference) => void;
|
|
||||||
hasAccess: boolean;
|
|
||||||
}) => {
|
|
||||||
const columns: ColumnsType<EntityReference> = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
width: '200px',
|
|
||||||
key: 'name',
|
|
||||||
render: (_, record) => {
|
|
||||||
let link = '';
|
|
||||||
switch (type) {
|
|
||||||
case 'role':
|
|
||||||
link = getRoleWithFqnPath(record.fullyQualifiedName || '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'team':
|
|
||||||
link = getTeamsWithFqnPath(record.fullyQualifiedName || '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link className="hover:tw-underline tw-cursor-pointer" to={link}>
|
|
||||||
{getEntityName(record)}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Description',
|
|
||||||
dataIndex: 'description',
|
|
||||||
key: 'description',
|
|
||||||
render: (_, record) => (
|
|
||||||
<RichTextEditorPreviewer markdown={record?.description || ''} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'actions',
|
|
||||||
width: '80px',
|
|
||||||
key: 'actions',
|
|
||||||
render: (_, record) => {
|
|
||||||
return (
|
|
||||||
<Tooltip title={hasAccess ? 'Remove' : NO_PERMISSION_FOR_ACTION}>
|
|
||||||
<Button
|
|
||||||
data-testid={`remove-action-${getEntityName(record)}`}
|
|
||||||
disabled={!hasAccess}
|
|
||||||
type="text"
|
|
||||||
onClick={() => onDelete(record)}>
|
|
||||||
<SVGIcons
|
|
||||||
alt="remove"
|
|
||||||
icon={Icons.ICON_REMOVE}
|
|
||||||
title="Remove"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
bordered
|
|
||||||
className="list-table"
|
|
||||||
columns={columns}
|
|
||||||
dataSource={list}
|
|
||||||
pagination={false}
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PoliciesDetailPage = () => {
|
const PoliciesDetailPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@ -191,7 +97,7 @@ const PoliciesDetailPage = () => {
|
|||||||
const breadcrumb = useMemo(
|
const breadcrumb = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
name: 'Policies',
|
name: t('label.policies'),
|
||||||
url: policiesPath,
|
url: policiesPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -402,8 +308,8 @@ const PoliciesDetailPage = () => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
policyPermission.EditAll
|
policyPermission.EditAll
|
||||||
? 'Manage Rule'
|
? t('label.manage-rule')
|
||||||
: NO_PERMISSION_FOR_ACTION
|
: t('message.no-permission-for-action')
|
||||||
}>
|
}>
|
||||||
<Button
|
<Button
|
||||||
data-testid={`manage-button-${rule.name}`}
|
data-testid={`manage-button-${rule.name}`}
|
||||||
@ -473,7 +379,7 @@ const PoliciesDetailPage = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tabs defaultActiveKey="rules">
|
<Tabs defaultActiveKey="rules">
|
||||||
<TabPane key="rules" tab="Rules">
|
<TabPane key="rules" tab={t('label.rules')}>
|
||||||
{isEmpty(policy.rules) ? (
|
{isEmpty(policy.rules) ? (
|
||||||
<ErrorPlaceHolder>
|
<ErrorPlaceHolder>
|
||||||
<p>{t('label.no-rule-found')}</p>
|
<p>{t('label.no-rule-found')}</p>
|
||||||
@ -486,7 +392,7 @@ const PoliciesDetailPage = () => {
|
|||||||
title={
|
title={
|
||||||
policyPermission.EditAll
|
policyPermission.EditAll
|
||||||
? t('label.add-rule')
|
? t('label.add-rule')
|
||||||
: NO_PERMISSION_FOR_ACTION
|
: t('message.no-permission-for-action')
|
||||||
}>
|
}>
|
||||||
<Button
|
<Button
|
||||||
data-testid="add-rule"
|
data-testid="add-rule"
|
||||||
@ -596,8 +502,8 @@ const PoliciesDetailPage = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane key="roles" tab="Roles">
|
<TabPane key="roles" tab={t('label.roles')}>
|
||||||
<List
|
<PoliciesDetailsList
|
||||||
hasAccess={policyPermission.EditAll}
|
hasAccess={policyPermission.EditAll}
|
||||||
list={policy.roles ?? []}
|
list={policy.roles ?? []}
|
||||||
type="role"
|
type="role"
|
||||||
@ -606,8 +512,8 @@ const PoliciesDetailPage = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane key="teams" tab="Teams">
|
<TabPane key="teams" tab={t('label.teams')}>
|
||||||
<List
|
<PoliciesDetailsList
|
||||||
hasAccess={policyPermission.EditAll}
|
hasAccess={policyPermission.EditAll}
|
||||||
list={policy.teams ?? []}
|
list={policy.teams ?? []}
|
||||||
type="team"
|
type="team"
|
||||||
@ -621,7 +527,9 @@ const PoliciesDetailPage = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ErrorPlaceHolder>{NO_PERMISSION_TO_VIEW}</ErrorPlaceHolder>
|
<ErrorPlaceHolder>
|
||||||
|
{t('message.no-permission-to-view')}
|
||||||
|
</ErrorPlaceHolder>
|
||||||
)}
|
)}
|
||||||
{selectedEntity && (
|
{selectedEntity && (
|
||||||
<Modal
|
<Modal
|
||||||
@ -631,7 +539,7 @@ const PoliciesDetailPage = () => {
|
|||||||
okText={t('label.confirm')}
|
okText={t('label.confirm')}
|
||||||
title={`${t('label.remove-entity', {
|
title={`${t('label.remove-entity', {
|
||||||
entity: getEntityName(selectedEntity.record),
|
entity: getEntityName(selectedEntity.record),
|
||||||
})} ${t('label.from')} ${getEntityName(policy)}`}
|
})} ${t('label.from-lowercase')} ${getEntityName(policy)}`}
|
||||||
visible={!isUndefined(selectedEntity.record)}
|
visible={!isUndefined(selectedEntity.record)}
|
||||||
onCancel={() => setEntity(undefined)}
|
onCancel={() => setEntity(undefined)}
|
||||||
onOk={async () => {
|
onOk={async () => {
|
||||||
@ -641,7 +549,7 @@ const PoliciesDetailPage = () => {
|
|||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
{` ${t('label.sure-to-remove')} ${getEntityName(
|
{` ${t('label.sure-to-remove')} ${getEntityName(
|
||||||
selectedEntity.record
|
selectedEntity.record
|
||||||
)} ${t('label.from')} ${getEntityName(policy)}?`}
|
)} ${t('label.from-lowercase')} ${getEntityName(policy)}?`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import Table, { ColumnsType } from 'antd/lib/table';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||||
|
import { EntityReference } from '../../../generated/type/entityReference';
|
||||||
|
import { getEntityName } from '../../../utils/CommonUtils';
|
||||||
|
import {
|
||||||
|
getRoleWithFqnPath,
|
||||||
|
getTeamsWithFqnPath,
|
||||||
|
} from '../../../utils/RouterUtils';
|
||||||
|
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||||
|
|
||||||
|
const PoliciesDetailsList = ({
|
||||||
|
list,
|
||||||
|
type,
|
||||||
|
onDelete,
|
||||||
|
hasAccess,
|
||||||
|
}: {
|
||||||
|
list: EntityReference[];
|
||||||
|
type: 'role' | 'team';
|
||||||
|
onDelete: (record: EntityReference) => void;
|
||||||
|
hasAccess: boolean;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const columns: ColumnsType<EntityReference> = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: t('label.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: '200px',
|
||||||
|
key: 'name',
|
||||||
|
render: (_, record) => {
|
||||||
|
let link = '';
|
||||||
|
switch (type) {
|
||||||
|
case 'role':
|
||||||
|
link = getRoleWithFqnPath(record.fullyQualifiedName || '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
link = getTeamsWithFqnPath(record.fullyQualifiedName || '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link className="hover:tw-underline tw-cursor-pointer" to={link}>
|
||||||
|
{getEntityName(record)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('label.description'),
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
render: (_, record) => (
|
||||||
|
<RichTextEditorPreviewer markdown={record?.description || ''} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('label.actions'),
|
||||||
|
dataIndex: 'actions',
|
||||||
|
width: '80px',
|
||||||
|
key: 'actions',
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
hasAccess
|
||||||
|
? t('label.remove')
|
||||||
|
: t('message.no-permission-for-action')
|
||||||
|
}>
|
||||||
|
<Button
|
||||||
|
data-testid={`remove-action-${getEntityName(record)}`}
|
||||||
|
disabled={!hasAccess}
|
||||||
|
type="text"
|
||||||
|
onClick={() => onDelete(record)}>
|
||||||
|
<SVGIcons
|
||||||
|
alt="remove"
|
||||||
|
icon={Icons.ICON_REMOVE}
|
||||||
|
title={t('label.remove')}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
bordered
|
||||||
|
className="list-table"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={list}
|
||||||
|
pagination={false}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PoliciesDetailsList;
|
@ -88,9 +88,9 @@ describe('Test Roles Details Page', () => {
|
|||||||
|
|
||||||
const tabs = await screen.findByTestId('tabs');
|
const tabs = await screen.findByTestId('tabs');
|
||||||
|
|
||||||
const policiesTab = await screen.findByText('Policies');
|
const policiesTab = await screen.findByText('label.policies');
|
||||||
const teamsTab = await screen.findByText('Teams');
|
const teamsTab = await screen.findByText('label.teams');
|
||||||
const usersTab = await screen.findByText('Users');
|
const usersTab = await screen.findByText('label.users');
|
||||||
|
|
||||||
expect(container).toBeInTheDocument();
|
expect(container).toBeInTheDocument();
|
||||||
|
|
||||||
|
@ -11,20 +11,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Modal, Space, Table, Tabs, Tooltip, Typography } from 'antd';
|
import { Button, Modal, Space, Tabs, Tooltip, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { isEmpty, isUndefined } from 'lodash';
|
import { isEmpty, isUndefined } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { getRoleByName, patchRole } from '../../../axiosAPIs/rolesAPIV1';
|
import { getRoleByName, patchRole } from '../../../axiosAPIs/rolesAPIV1';
|
||||||
import { getTeamByName, patchTeamDetail } from '../../../axiosAPIs/teamsAPI';
|
import { getTeamByName, patchTeamDetail } from '../../../axiosAPIs/teamsAPI';
|
||||||
import { getUserByName, updateUserDetail } from '../../../axiosAPIs/userAPI';
|
import { getUserByName, updateUserDetail } from '../../../axiosAPIs/userAPI';
|
||||||
import Description from '../../../components/common/description/Description';
|
import Description from '../../../components/common/description/Description';
|
||||||
import ErrorPlaceHolder from '../../../components/common/error-with-placeholder/ErrorPlaceHolder';
|
import ErrorPlaceHolder from '../../../components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||||
import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer';
|
|
||||||
import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component';
|
import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component';
|
||||||
import Loader from '../../../components/Loader/Loader';
|
import Loader from '../../../components/Loader/Loader';
|
||||||
import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider';
|
||||||
@ -32,29 +30,20 @@ import {
|
|||||||
OperationPermission,
|
OperationPermission,
|
||||||
ResourceEntity,
|
ResourceEntity,
|
||||||
} from '../../../components/PermissionProvider/PermissionProvider.interface';
|
} from '../../../components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { getUserPath } from '../../../constants/constants';
|
|
||||||
import {
|
import {
|
||||||
GlobalSettingOptions,
|
GlobalSettingOptions,
|
||||||
GlobalSettingsMenuCategory,
|
GlobalSettingsMenuCategory,
|
||||||
} from '../../../constants/GlobalSettings.constants';
|
} from '../../../constants/GlobalSettings.constants';
|
||||||
import {
|
|
||||||
NO_PERMISSION_FOR_ACTION,
|
|
||||||
NO_PERMISSION_TO_VIEW,
|
|
||||||
} from '../../../constants/HelperTextUtil';
|
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
import { Role } from '../../../generated/entity/teams/role';
|
import { Role } from '../../../generated/entity/teams/role';
|
||||||
import { EntityReference } from '../../../generated/type/entityReference';
|
import { EntityReference } from '../../../generated/type/entityReference';
|
||||||
import { getEntityName } from '../../../utils/CommonUtils';
|
import { getEntityName } from '../../../utils/CommonUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
|
||||||
import {
|
import { getSettingPath } from '../../../utils/RouterUtils';
|
||||||
getPolicyWithFqnPath,
|
|
||||||
getSettingPath,
|
|
||||||
getTeamsWithFqnPath,
|
|
||||||
} from '../../../utils/RouterUtils';
|
|
||||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
|
||||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||||
import AddAttributeModal from '../AddAttributeModal/AddAttributeModal';
|
import AddAttributeModal from '../AddAttributeModal/AddAttributeModal';
|
||||||
import './RolesDetail.less';
|
import './RolesDetail.less';
|
||||||
|
import RolesDetailPageList from './RolesDetailPageList.component';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
@ -65,101 +54,6 @@ interface AddAttribute {
|
|||||||
selectedData: EntityReference[];
|
selectedData: EntityReference[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const List = ({
|
|
||||||
list,
|
|
||||||
type,
|
|
||||||
onDelete,
|
|
||||||
hasAccess,
|
|
||||||
}: {
|
|
||||||
list: EntityReference[];
|
|
||||||
type: 'policy' | 'team' | 'user';
|
|
||||||
onDelete: (record: EntityReference) => void;
|
|
||||||
hasAccess: boolean;
|
|
||||||
}) => {
|
|
||||||
const columns: ColumnsType<EntityReference> = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
width: '200px',
|
|
||||||
key: 'name',
|
|
||||||
render: (_, record) => {
|
|
||||||
let link = '';
|
|
||||||
switch (type) {
|
|
||||||
case 'policy':
|
|
||||||
link = getPolicyWithFqnPath(record.fullyQualifiedName || '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'team':
|
|
||||||
link = getTeamsWithFqnPath(record.fullyQualifiedName || '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'user':
|
|
||||||
link = getUserPath(record.fullyQualifiedName || '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
className="hover:tw-underline tw-cursor-pointer"
|
|
||||||
data-testid="entity-name"
|
|
||||||
to={link}>
|
|
||||||
{getEntityName(record)}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Description',
|
|
||||||
dataIndex: 'description',
|
|
||||||
key: 'description',
|
|
||||||
render: (_, record) => (
|
|
||||||
<RichTextEditorPreviewer markdown={record?.description || ''} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'actions',
|
|
||||||
width: '80px',
|
|
||||||
key: 'actions',
|
|
||||||
render: (_, record) => {
|
|
||||||
return (
|
|
||||||
<Tooltip title={hasAccess ? 'Remove' : NO_PERMISSION_FOR_ACTION}>
|
|
||||||
<Button
|
|
||||||
data-testid={`remove-action-${getEntityName(record)}`}
|
|
||||||
disabled={!hasAccess}
|
|
||||||
type="text"
|
|
||||||
onClick={() => onDelete(record)}>
|
|
||||||
<SVGIcons
|
|
||||||
alt="remove"
|
|
||||||
icon={Icons.ICON_REMOVE}
|
|
||||||
title="Remove"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
bordered
|
|
||||||
className="list-table"
|
|
||||||
columns={columns}
|
|
||||||
dataSource={list}
|
|
||||||
pagination={false}
|
|
||||||
rowKey="id"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RolesDetailPage = () => {
|
const RolesDetailPage = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -187,7 +81,7 @@ const RolesDetailPage = () => {
|
|||||||
const breadcrumb = useMemo(
|
const breadcrumb = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
name: 'Roles',
|
name: t('label.roles'),
|
||||||
url: rolesPath,
|
url: rolesPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -393,13 +287,13 @@ const RolesDetailPage = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tabs data-testid="tabs" defaultActiveKey="policies">
|
<Tabs data-testid="tabs" defaultActiveKey="policies">
|
||||||
<TabPane key="policies" tab="Policies">
|
<TabPane key="policies" tab={t('label.policies')}>
|
||||||
<Space className="tw-w-full" direction="vertical">
|
<Space className="tw-w-full" direction="vertical">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
rolePermission.EditAll
|
rolePermission.EditAll
|
||||||
? t('label.add-policy')
|
? t('label.add-policy')
|
||||||
: NO_PERMISSION_FOR_ACTION
|
: t('message.no-permission-for-action')
|
||||||
}>
|
}>
|
||||||
<Button
|
<Button
|
||||||
data-testid="add-policy"
|
data-testid="add-policy"
|
||||||
@ -414,7 +308,7 @@ const RolesDetailPage = () => {
|
|||||||
{t('label.add-policy')}
|
{t('label.add-policy')}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<List
|
<RolesDetailPageList
|
||||||
hasAccess={rolePermission.EditAll}
|
hasAccess={rolePermission.EditAll}
|
||||||
list={role.policies ?? []}
|
list={role.policies ?? []}
|
||||||
type="policy"
|
type="policy"
|
||||||
@ -424,8 +318,8 @@ const RolesDetailPage = () => {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane key="teams" tab="Teams">
|
<TabPane key="teams" tab={t('label.teams')}>
|
||||||
<List
|
<RolesDetailPageList
|
||||||
hasAccess={rolePermission.EditAll}
|
hasAccess={rolePermission.EditAll}
|
||||||
list={role.teams ?? []}
|
list={role.teams ?? []}
|
||||||
type="team"
|
type="team"
|
||||||
@ -434,8 +328,8 @@ const RolesDetailPage = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane key="users" tab="Users">
|
<TabPane key="users" tab={t('label.users')}>
|
||||||
<List
|
<RolesDetailPageList
|
||||||
hasAccess={rolePermission.EditAll}
|
hasAccess={rolePermission.EditAll}
|
||||||
list={role.users ?? []}
|
list={role.users ?? []}
|
||||||
type="user"
|
type="user"
|
||||||
@ -449,7 +343,9 @@ const RolesDetailPage = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ErrorPlaceHolder>{NO_PERMISSION_TO_VIEW}</ErrorPlaceHolder>
|
<ErrorPlaceHolder>
|
||||||
|
{t('message.no-permission-to-view')}
|
||||||
|
</ErrorPlaceHolder>
|
||||||
)}
|
)}
|
||||||
{selectedEntity && (
|
{selectedEntity && (
|
||||||
<Modal
|
<Modal
|
||||||
@ -459,7 +355,7 @@ const RolesDetailPage = () => {
|
|||||||
okText={t('label.confirm')}
|
okText={t('label.confirm')}
|
||||||
title={`${t('label.remove-entity', {
|
title={`${t('label.remove-entity', {
|
||||||
entity: getEntityName(selectedEntity.record),
|
entity: getEntityName(selectedEntity.record),
|
||||||
})} ${t('label.from')} ${getEntityName(role)}`}
|
})} ${t('label.from-lowercase')} ${getEntityName(role)}`}
|
||||||
visible={!isUndefined(selectedEntity.record)}
|
visible={!isUndefined(selectedEntity.record)}
|
||||||
onCancel={() => setEntity(undefined)}
|
onCancel={() => setEntity(undefined)}
|
||||||
onOk={async () => {
|
onOk={async () => {
|
||||||
@ -469,7 +365,7 @@ const RolesDetailPage = () => {
|
|||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
{t('label.sure-to-remove')}{' '}
|
{t('label.sure-to-remove')}{' '}
|
||||||
{`${getEntityName(selectedEntity.record)} ${t(
|
{`${getEntityName(selectedEntity.record)} ${t(
|
||||||
'label.from'
|
'label.from-lowercase'
|
||||||
)} ${getEntityName(role)}?`}
|
)} ${getEntityName(role)}?`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import { Button, Table, Tooltip } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||||
|
import { getUserPath } from '../../../constants/constants';
|
||||||
|
import { EntityReference } from '../../../generated/type/entityReference';
|
||||||
|
import { getEntityName } from '../../../utils/CommonUtils';
|
||||||
|
import {
|
||||||
|
getPolicyWithFqnPath,
|
||||||
|
getTeamsWithFqnPath,
|
||||||
|
} from '../../../utils/RouterUtils';
|
||||||
|
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||||
|
|
||||||
|
const RolesDetailPageList = ({
|
||||||
|
list,
|
||||||
|
type,
|
||||||
|
onDelete,
|
||||||
|
hasAccess,
|
||||||
|
}: {
|
||||||
|
list: EntityReference[];
|
||||||
|
type: 'policy' | 'team' | 'user';
|
||||||
|
onDelete: (record: EntityReference) => void;
|
||||||
|
hasAccess: boolean;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const columns: ColumnsType<EntityReference> = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: t('label.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: '200px',
|
||||||
|
key: 'name',
|
||||||
|
render: (_, record) => {
|
||||||
|
let link = '';
|
||||||
|
switch (type) {
|
||||||
|
case 'policy':
|
||||||
|
link = getPolicyWithFqnPath(record.fullyQualifiedName || '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
link = getTeamsWithFqnPath(record.fullyQualifiedName || '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
link = getUserPath(record.fullyQualifiedName || '');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className="hover:tw-underline tw-cursor-pointer"
|
||||||
|
data-testid="entity-name"
|
||||||
|
to={link}>
|
||||||
|
{getEntityName(record)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('label.description'),
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
render: (_, record) => (
|
||||||
|
<RichTextEditorPreviewer markdown={record?.description || ''} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('label.actions'),
|
||||||
|
dataIndex: 'actions',
|
||||||
|
width: '80px',
|
||||||
|
key: 'actions',
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
hasAccess
|
||||||
|
? t('label.remove')
|
||||||
|
: t('message.no-permission-for-action')
|
||||||
|
}>
|
||||||
|
<Button
|
||||||
|
data-testid={`remove-action-${getEntityName(record)}`}
|
||||||
|
disabled={!hasAccess}
|
||||||
|
type="text"
|
||||||
|
onClick={() => onDelete(record)}>
|
||||||
|
<SVGIcons
|
||||||
|
alt="remove"
|
||||||
|
icon={Icons.ICON_REMOVE}
|
||||||
|
title={t('label.remove')}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
bordered
|
||||||
|
className="list-table"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={list}
|
||||||
|
pagination={false}
|
||||||
|
rowKey="id"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RolesDetailPageList;
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import i18next from 'i18next';
|
||||||
import { TabSpecificField } from '../enums/entity.enum';
|
import { TabSpecificField } from '../enums/entity.enum';
|
||||||
import { ChartType } from '../pages/DashboardDetailsPage/DashboardDetailsPage.component';
|
import { ChartType } from '../pages/DashboardDetailsPage/DashboardDetailsPage.component';
|
||||||
import { sortTagsCaseInsensitive } from './CommonUtils';
|
import { sortTagsCaseInsensitive } from './CommonUtils';
|
||||||
@ -20,21 +21,21 @@ ${TabSpecificField.USAGE_SUMMARY}, ${TabSpecificField.CHARTS},${TabSpecificField
|
|||||||
|
|
||||||
export const dashboardDetailsTabs = [
|
export const dashboardDetailsTabs = [
|
||||||
{
|
{
|
||||||
name: 'Details',
|
name: i18next.t('label.details'),
|
||||||
path: 'details',
|
path: 'details',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Activity Feeds & Tasks',
|
name: i18next.t('label.activity-feed-and-task-plural'),
|
||||||
path: 'activity_feed',
|
path: 'activity_feed',
|
||||||
field: TabSpecificField.ACTIVITY_FEED,
|
field: TabSpecificField.ACTIVITY_FEED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Lineage',
|
name: i18next.t('label.lineage'),
|
||||||
path: 'lineage',
|
path: 'lineage',
|
||||||
field: TabSpecificField.LINEAGE,
|
field: TabSpecificField.LINEAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Custom Properties',
|
name: i18next.t('label.custom-properties'),
|
||||||
path: 'custom_properties',
|
path: 'custom_properties',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
import i18next from 'i18next';
|
||||||
import { TabSpecificField } from '../enums/entity.enum';
|
import { TabSpecificField } from '../enums/entity.enum';
|
||||||
|
|
||||||
export const databaseDetailsTabs = [
|
export const databaseDetailsTabs = [
|
||||||
{
|
{
|
||||||
name: 'Schemas',
|
name: i18next.t('label.schemas'),
|
||||||
path: 'schemas',
|
path: 'schemas',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Activity Feeds',
|
name: i18next.t('label.activity-feeds'),
|
||||||
path: 'activity_feed',
|
path: 'activity_feed',
|
||||||
field: TabSpecificField.ACTIVITY_FEED,
|
field: TabSpecificField.ACTIVITY_FEED,
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import i18next from 'i18next';
|
||||||
import { TabSpecificField } from '../enums/entity.enum';
|
import { TabSpecificField } from '../enums/entity.enum';
|
||||||
|
|
||||||
export const defaultFields = `${TabSpecificField.COLUMNS}, ${TabSpecificField.USAGE_SUMMARY},
|
export const defaultFields = `${TabSpecificField.COLUMNS}, ${TabSpecificField.USAGE_SUMMARY},
|
||||||
@ -19,41 +20,41 @@ ${TabSpecificField.DATAMODEL},${TabSpecificField.TABLE_CONSTRAINTS},${TabSpecifi
|
|||||||
|
|
||||||
export const datasetTableTabs = [
|
export const datasetTableTabs = [
|
||||||
{
|
{
|
||||||
name: 'Schema',
|
name: i18next.t('label.schema'),
|
||||||
path: 'schema',
|
path: 'schema',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Activity Feeds & Tasks',
|
name: i18next.t('label.activity-feed-and-task-plural'),
|
||||||
path: 'activity_feed',
|
path: 'activity_feed',
|
||||||
field: TabSpecificField.ACTIVITY_FEED,
|
field: TabSpecificField.ACTIVITY_FEED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Sample Data',
|
name: i18next.t('label.sample-data'),
|
||||||
path: 'sample_data',
|
path: 'sample_data',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Queries',
|
name: i18next.t('label.query-plural'),
|
||||||
path: 'table_queries',
|
path: 'table_queries',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Profiler',
|
name: i18next.t('label.profiler'),
|
||||||
path: 'profiler',
|
path: 'profiler',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Data Quality',
|
name: i18next.t('label.data-quality'),
|
||||||
path: 'data-quality',
|
path: 'data-quality',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Lineage',
|
name: i18next.t('label.lineage'),
|
||||||
path: 'lineage',
|
path: 'lineage',
|
||||||
field: TabSpecificField.LINEAGE,
|
field: TabSpecificField.LINEAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'DBT',
|
name: i18next.t('label.dbt-uppercase'),
|
||||||
path: 'dbt',
|
path: 'dbt',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Custom Properties',
|
name: i18next.t('label.custom-properties'),
|
||||||
path: 'custom_properties',
|
path: 'custom_properties',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import i18next from 'i18next';
|
||||||
import { isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash';
|
import { isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash';
|
||||||
import { Bucket, LeafNodes, LineagePos } from 'Models';
|
import { Bucket, LeafNodes, LineagePos } from 'Models';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
@ -113,7 +114,7 @@ export const getEntityOverview = (
|
|||||||
|
|
||||||
const overview = [
|
const overview = [
|
||||||
{
|
{
|
||||||
name: 'Service',
|
name: i18next.t('label.service'),
|
||||||
value: service,
|
value: service,
|
||||||
url: getServiceDetailsPath(
|
url: getServiceDetailsPath(
|
||||||
service,
|
service,
|
||||||
@ -122,7 +123,7 @@ export const getEntityOverview = (
|
|||||||
isLink: true,
|
isLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Database',
|
name: i18next.t('label.database'),
|
||||||
value: database,
|
value: database,
|
||||||
url: getDatabaseDetailsPath(
|
url: getDatabaseDetailsPath(
|
||||||
getPartialNameFromTableFQN(
|
getPartialNameFromTableFQN(
|
||||||
@ -134,7 +135,7 @@ export const getEntityOverview = (
|
|||||||
isLink: true,
|
isLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Schema',
|
name: i18next.t('label.schema'),
|
||||||
value: schema,
|
value: schema,
|
||||||
url: getDatabaseSchemaDetailsPath(
|
url: getDatabaseSchemaDetailsPath(
|
||||||
getPartialNameFromTableFQN(
|
getPartialNameFromTableFQN(
|
||||||
@ -146,33 +147,33 @@ export const getEntityOverview = (
|
|||||||
isLink: true,
|
isLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Owner',
|
name: i18next.t('label.owner'),
|
||||||
value: getEntityName(owner) || '--',
|
value: getEntityName(owner) || '--',
|
||||||
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
||||||
isLink: owner ? owner.type === 'team' : false,
|
isLink: owner ? owner.type === 'team' : false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Tier',
|
name: i18next.t('label.tier'),
|
||||||
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Usage',
|
name: i18next.t('label.usage'),
|
||||||
value: usage,
|
value: usage,
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Queries',
|
name: i18next.t('label.query-plural'),
|
||||||
value: `${queries} past week`,
|
value: `${queries} past week`,
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Columns',
|
name: i18next.t('label.columns-plural'),
|
||||||
value: columns ? columns.length : '--',
|
value: columns ? columns.length : '--',
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Rows',
|
name: i18next.t('label.row-plural'),
|
||||||
value: profile && profile?.rowCount ? profile.rowCount : '--',
|
value: profile && profile?.rowCount ? profile.rowCount : '--',
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
@ -188,7 +189,7 @@ export const getEntityOverview = (
|
|||||||
|
|
||||||
const overview = [
|
const overview = [
|
||||||
{
|
{
|
||||||
name: 'Service',
|
name: i18next.t('label.service'),
|
||||||
value: service?.name as string,
|
value: service?.name as string,
|
||||||
url: getServiceDetailsPath(
|
url: getServiceDetailsPath(
|
||||||
service?.name as string,
|
service?.name as string,
|
||||||
@ -197,18 +198,18 @@ export const getEntityOverview = (
|
|||||||
isLink: true,
|
isLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Owner',
|
name: i18next.t('label.owner'),
|
||||||
value: getEntityName(owner) || '--',
|
value: getEntityName(owner) || '--',
|
||||||
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
||||||
isLink: owner ? owner.type === 'team' : false,
|
isLink: owner ? owner.type === 'team' : false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Tier',
|
name: i18next.t('label.tier'),
|
||||||
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `${serviceType} url`,
|
name: `${serviceType} ${i18next.t('label.url-lowercase')}`,
|
||||||
value: fullyQualifiedName?.split(FQN_SEPARATOR_CHAR)[1] as string,
|
value: fullyQualifiedName?.split(FQN_SEPARATOR_CHAR)[1] as string,
|
||||||
url: pipelineUrl as string,
|
url: pipelineUrl as string,
|
||||||
isLink: true,
|
isLink: true,
|
||||||
@ -231,7 +232,7 @@ export const getEntityOverview = (
|
|||||||
|
|
||||||
const overview = [
|
const overview = [
|
||||||
{
|
{
|
||||||
name: 'Service',
|
name: i18next.t('label.service'),
|
||||||
value: service?.name as string,
|
value: service?.name as string,
|
||||||
url: getServiceDetailsPath(
|
url: getServiceDetailsPath(
|
||||||
service?.name as string,
|
service?.name as string,
|
||||||
@ -240,18 +241,18 @@ export const getEntityOverview = (
|
|||||||
isLink: true,
|
isLink: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Owner',
|
name: i18next.t('label.owner'),
|
||||||
value: getEntityName(owner) || '--',
|
value: getEntityName(owner) || '--',
|
||||||
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
url: getTeamAndUserDetailsPath(owner?.name || ''),
|
||||||
isLink: owner ? owner.type === 'team' : false,
|
isLink: owner ? owner.type === 'team' : false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Tier',
|
name: i18next.t('label.tier'),
|
||||||
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : '--',
|
||||||
isLink: false,
|
isLink: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `${serviceType} url`,
|
name: `${serviceType} ${i18next.t('label.url-lowercase')}`,
|
||||||
value:
|
value:
|
||||||
displayName ||
|
displayName ||
|
||||||
(fullyQualifiedName?.split(FQN_SEPARATOR_CHAR)[1] as string),
|
(fullyQualifiedName?.split(FQN_SEPARATOR_CHAR)[1] as string),
|
||||||
|
@ -19,27 +19,19 @@ import {
|
|||||||
diffWords,
|
diffWords,
|
||||||
diffWordsWithSpace,
|
diffWordsWithSpace,
|
||||||
} from 'diff';
|
} from 'diff';
|
||||||
import { isEmpty, isUndefined, uniqueId } from 'lodash';
|
import { t } from 'i18next';
|
||||||
|
import { isUndefined, uniqueId } from 'lodash';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import ReactDOMServer from 'react-dom/server';
|
import ReactDOMServer from 'react-dom/server';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
|
||||||
import {
|
|
||||||
DESCRIPTIONLENGTH,
|
|
||||||
getTeamAndUserDetailsPath,
|
|
||||||
} from '../constants/constants';
|
|
||||||
import { EntityField } from '../constants/Feeds.constants';
|
import { EntityField } from '../constants/Feeds.constants';
|
||||||
import { ChangeType } from '../enums/entity.enum';
|
|
||||||
import { Column } from '../generated/entity/data/table';
|
import { Column } from '../generated/entity/data/table';
|
||||||
import {
|
import {
|
||||||
ChangeDescription,
|
ChangeDescription,
|
||||||
FieldChange,
|
FieldChange,
|
||||||
} from '../generated/entity/services/databaseService';
|
} from '../generated/entity/services/databaseService';
|
||||||
import { TagLabel } from '../generated/type/tagLabel';
|
import { TagLabel } from '../generated/type/tagLabel';
|
||||||
import { getEntityName } from './CommonUtils';
|
|
||||||
import { TagLabelWithStatus } from './EntityVersionUtils.interface';
|
import { TagLabelWithStatus } from './EntityVersionUtils.interface';
|
||||||
import { isValidJSONString } from './StringsUtils';
|
import { isValidJSONString } from './StringsUtils';
|
||||||
import { getEntityLink } from './TableUtils';
|
|
||||||
|
|
||||||
export const getDiffByFieldName = (
|
export const getDiffByFieldName = (
|
||||||
name: string,
|
name: string,
|
||||||
@ -143,328 +135,6 @@ export const getTagsDiff = (
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPreposition = (type: ChangeType) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'Added':
|
|
||||||
return 'to';
|
|
||||||
|
|
||||||
case 'Removed':
|
|
||||||
return 'from';
|
|
||||||
|
|
||||||
case 'Updated':
|
|
||||||
return 'of';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getColumnName = (column: string) => {
|
|
||||||
const name = column.split(FQN_SEPARATOR_CHAR);
|
|
||||||
const length = name.length;
|
|
||||||
|
|
||||||
return name
|
|
||||||
.slice(length > 1 ? 1 : 0, length > 1 ? length - 1 : length)
|
|
||||||
.join(FQN_SEPARATOR_CHAR);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLinkWithColumn = (column: string, eFqn: string, eType: string) => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
className="tw-pl-1"
|
|
||||||
to={`${getEntityLink(eType, eFqn)}.${getColumnName(column)}`}>
|
|
||||||
{getColumnName(column)}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDescriptionText = (value: string) => {
|
|
||||||
const length = value.length;
|
|
||||||
|
|
||||||
return `${value.slice(0, DESCRIPTIONLENGTH)}${
|
|
||||||
length > DESCRIPTIONLENGTH ? '...' : ''
|
|
||||||
}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDescriptionElement = (fieldChange: FieldChange) => {
|
|
||||||
return fieldChange?.newValue && fieldChange?.oldValue ? (
|
|
||||||
<Fragment>
|
|
||||||
|
|
||||||
<span className="tw-italic feed-change-description">{`${getDescriptionText(
|
|
||||||
fieldChange?.newValue
|
|
||||||
)}`}</span>
|
|
||||||
</Fragment>
|
|
||||||
) : fieldChange?.newValue ? (
|
|
||||||
<Fragment>
|
|
||||||
|
|
||||||
<span className="tw-italic feed-change-description">
|
|
||||||
{`${getDescriptionText(fieldChange?.newValue)}`}
|
|
||||||
</span>
|
|
||||||
</Fragment>
|
|
||||||
) : (
|
|
||||||
<Fragment>
|
|
||||||
|
|
||||||
<span className="tw-italic feed-change-description">
|
|
||||||
{`${getDescriptionText(fieldChange?.oldValue)}`}
|
|
||||||
</span>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const feedSummaryFromatter = (
|
|
||||||
fieldChange: FieldChange,
|
|
||||||
type: ChangeType,
|
|
||||||
_entityName: string,
|
|
||||||
entityType: string,
|
|
||||||
entityFQN: string
|
|
||||||
) => {
|
|
||||||
const value = JSON.parse(
|
|
||||||
isValidJSONString(fieldChange?.newValue)
|
|
||||||
? fieldChange?.newValue
|
|
||||||
: isValidJSONString(fieldChange?.oldValue)
|
|
||||||
? fieldChange?.oldValue
|
|
||||||
: '{}'
|
|
||||||
);
|
|
||||||
const oldValue = JSON.parse(
|
|
||||||
isValidJSONString(fieldChange?.oldValue) ? fieldChange?.oldValue : '{}'
|
|
||||||
);
|
|
||||||
const newValue = JSON.parse(
|
|
||||||
isValidJSONString(fieldChange?.newValue) ? fieldChange?.newValue : '{}'
|
|
||||||
);
|
|
||||||
let summary: JSX.Element;
|
|
||||||
switch (true) {
|
|
||||||
case fieldChange?.name?.startsWith('column'): {
|
|
||||||
if (fieldChange?.name?.endsWith('tags')) {
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>
|
|
||||||
{`${type} tags ${value
|
|
||||||
?.map((val: TagLabel) => val?.tagFQN)
|
|
||||||
?.join(', ')} ${getPreposition(type)} column`}
|
|
||||||
{getLinkWithColumn(
|
|
||||||
fieldChange?.name as string,
|
|
||||||
entityFQN,
|
|
||||||
entityType
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else if (fieldChange?.name?.endsWith(EntityField.DESCRIPTION)) {
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>
|
|
||||||
{`${
|
|
||||||
fieldChange?.newValue && fieldChange?.oldValue
|
|
||||||
? type
|
|
||||||
: fieldChange?.newValue
|
|
||||||
? 'Added'
|
|
||||||
: 'Removed'
|
|
||||||
} column description for`}
|
|
||||||
{getLinkWithColumn(
|
|
||||||
fieldChange?.name as string,
|
|
||||||
entityFQN,
|
|
||||||
entityType
|
|
||||||
)}
|
|
||||||
{isEmpty(value) ? getDescriptionElement(fieldChange) : ''}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else if (fieldChange?.name === EntityField.COLUMNS) {
|
|
||||||
const length = value?.length ?? 0;
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>
|
|
||||||
{`${type} ${fieldChange?.name}`}{' '}
|
|
||||||
{value?.map((column: Column, i: number) => (
|
|
||||||
<span key={uniqueId()}>
|
|
||||||
{getLinkWithColumn(column.name, entityFQN, entityType)}{' '}
|
|
||||||
{i !== length - 1 ? ', ' : ''}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>
|
|
||||||
{`${type}`}
|
|
||||||
{getLinkWithColumn(
|
|
||||||
fieldChange?.name as string,
|
|
||||||
entityFQN,
|
|
||||||
entityType
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case fieldChange?.name === 'tags': {
|
|
||||||
const tier = value?.find((t: TagLabel) => t?.tagFQN?.startsWith('Tier'));
|
|
||||||
const tags = value?.filter(
|
|
||||||
(t: TagLabel) => !t?.tagFQN?.startsWith('Tier')
|
|
||||||
);
|
|
||||||
summary = (
|
|
||||||
<div>
|
|
||||||
{tags?.length > 0 ? (
|
|
||||||
<p key={uniqueId()}>{`${type} tags ${tags
|
|
||||||
?.map((val: TagLabel) => val?.tagFQN)
|
|
||||||
?.join(', ')}`}</p>
|
|
||||||
) : null}
|
|
||||||
{tier ? (
|
|
||||||
<p key={uniqueId()}>{`${type} tier ${
|
|
||||||
tier?.tagFQN?.split(FQN_SEPARATOR_CHAR)[1]
|
|
||||||
}`}</p>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case fieldChange?.name === 'owner': {
|
|
||||||
const ownerName = getEntityName(newValue) || getEntityName(value);
|
|
||||||
const ownerText =
|
|
||||||
!isEmpty(oldValue) && !isEmpty(newValue) ? (
|
|
||||||
<Fragment>
|
|
||||||
{newValue?.type === 'team' ? (
|
|
||||||
<Link
|
|
||||||
className="tw-pl-1"
|
|
||||||
to={getTeamAndUserDetailsPath(newValue?.name || '')}>
|
|
||||||
<span title={ownerName}>{ownerName}</span>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span className="tw-pl-1" title={ownerName}>
|
|
||||||
{ownerName}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
) : (
|
|
||||||
<Fragment>
|
|
||||||
{value?.type === 'team' ? (
|
|
||||||
<Link
|
|
||||||
className="tw-pl-1"
|
|
||||||
to={getTeamAndUserDetailsPath(value?.name || '')}>
|
|
||||||
<span title={ownerName}>{ownerName}</span>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span className="tw-pl-1" title={ownerName}>
|
|
||||||
{ownerName}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
summary = (
|
|
||||||
<p
|
|
||||||
className={classNames('tw-truncate', {
|
|
||||||
'tw-w-52': ownerName.length > 32,
|
|
||||||
})}
|
|
||||||
key={uniqueId()}>
|
|
||||||
{`Assigned ownership to ${ownerText}`}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case fieldChange?.name === EntityField.DESCRIPTION: {
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>
|
|
||||||
{`${
|
|
||||||
fieldChange?.newValue && fieldChange?.oldValue
|
|
||||||
? type
|
|
||||||
: fieldChange?.newValue
|
|
||||||
? 'Added'
|
|
||||||
: 'Removed'
|
|
||||||
} description`}
|
|
||||||
{getDescriptionElement(fieldChange)}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case fieldChange?.name === 'followers': {
|
|
||||||
summary = (
|
|
||||||
<p key={uniqueId()}>{`${
|
|
||||||
fieldChange?.newValue ? 'Started following' : 'Unfollowed'
|
|
||||||
} ${_entityName}`}</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
summary = <p key={uniqueId()}>{`${type} ${fieldChange?.name}`}</p>;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return summary;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFeedSummary = (
|
|
||||||
changeDescription: ChangeDescription,
|
|
||||||
entityName: string,
|
|
||||||
entityType: string,
|
|
||||||
entityFQN: string
|
|
||||||
) => {
|
|
||||||
const fieldsAdded = [...(changeDescription?.fieldsAdded || [])];
|
|
||||||
const fieldsDeleted = [...(changeDescription?.fieldsDeleted || [])];
|
|
||||||
const fieldsUpdated = [...(changeDescription?.fieldsUpdated || [])];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
{fieldsDeleted?.length ? (
|
|
||||||
<div className="tw-mb-2">
|
|
||||||
{fieldsDeleted?.map((d) => (
|
|
||||||
<Fragment key={uniqueId()}>
|
|
||||||
{feedSummaryFromatter(
|
|
||||||
d,
|
|
||||||
ChangeType.REMOVED,
|
|
||||||
entityName,
|
|
||||||
entityType,
|
|
||||||
entityFQN
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{fieldsAdded?.length > 0 ? (
|
|
||||||
<div className="tw-mb-2">
|
|
||||||
{fieldsAdded?.map((a) => (
|
|
||||||
<Fragment key={uniqueId()}>
|
|
||||||
{feedSummaryFromatter(
|
|
||||||
a,
|
|
||||||
ChangeType.ADDED,
|
|
||||||
entityName,
|
|
||||||
entityType,
|
|
||||||
entityFQN
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{fieldsUpdated?.length ? (
|
|
||||||
<div className="tw-mb-2">
|
|
||||||
{fieldsUpdated?.map((u) => (
|
|
||||||
<Fragment key={uniqueId()}>
|
|
||||||
{feedSummaryFromatter(
|
|
||||||
u,
|
|
||||||
ChangeType.UPDATED,
|
|
||||||
entityName,
|
|
||||||
entityType,
|
|
||||||
entityFQN
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const summaryFormatter = (fieldChange: FieldChange) => {
|
export const summaryFormatter = (fieldChange: FieldChange) => {
|
||||||
const value = JSON.parse(
|
const value = JSON.parse(
|
||||||
isValidJSONString(fieldChange?.newValue)
|
isValidJSONString(fieldChange?.newValue)
|
||||||
@ -511,31 +181,53 @@ export const getSummary = (
|
|||||||
{isDeleteUpdated
|
{isDeleteUpdated
|
||||||
.map((field) => {
|
.map((field) => {
|
||||||
return field.newValue
|
return field.newValue
|
||||||
? 'Data asset has been deleted'
|
? t('label.data-asset-has-been-action-type', {
|
||||||
: 'Data asset has been restored';
|
actionType: t('label.deleted-lowercase'),
|
||||||
|
})
|
||||||
|
: t('label.data-asset-has-been-action-type', {
|
||||||
|
actionType: t('label.restored-lowercase'),
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.join(', ')}
|
.join(', ')}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{fieldsAdded?.length > 0 ? (
|
{fieldsAdded?.length > 0 ? (
|
||||||
<p className="tw-mb-2">
|
<p className="tw-mb-2">
|
||||||
{`${isPrefix ? '+ Added' : ''} ${fieldsAdded
|
{`${isPrefix ? `+ ${t('label.added')}` : ''} ${fieldsAdded
|
||||||
.map(summaryFormatter)
|
.map(summaryFormatter)
|
||||||
.join(', ')} ${!isPrefix ? `has been added` : ''}`}{' '}
|
.join(', ')} ${
|
||||||
|
!isPrefix
|
||||||
|
? t('label.has-been-action-type-lowercase', {
|
||||||
|
actionType: t('label.added-lowercase'),
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
}`}{' '}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{fieldsUpdated?.length ? (
|
{fieldsUpdated?.length ? (
|
||||||
<p className="tw-mb-2">
|
<p className="tw-mb-2">
|
||||||
{`${isPrefix ? 'Edited' : ''} ${fieldsUpdated
|
{`${isPrefix ? t('label.edited') : ''} ${fieldsUpdated
|
||||||
.map(summaryFormatter)
|
.map(summaryFormatter)
|
||||||
.join(', ')} ${!isPrefix ? `has been updated` : ''}`}{' '}
|
.join(', ')} ${
|
||||||
|
!isPrefix
|
||||||
|
? t('label.has-been-action-type-lowercase', {
|
||||||
|
actionType: t('label.updated-lowercase'),
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
}`}{' '}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{fieldsDeleted?.length ? (
|
{fieldsDeleted?.length ? (
|
||||||
<p className="tw-mb-2">
|
<p className="tw-mb-2">
|
||||||
{`${isPrefix ? '- Removed' : ''} ${fieldsDeleted
|
{`${isPrefix ? '- Removed' : ''} ${fieldsDeleted
|
||||||
.map(summaryFormatter)
|
.map(summaryFormatter)
|
||||||
.join(', ')} ${!isPrefix ? `has been Deleted` : ''}`}{' '}
|
.join(', ')} ${
|
||||||
|
!isPrefix
|
||||||
|
? t('label.has-been-action-type-lowercase', {
|
||||||
|
actionType: t('label.deleted-lowercase'),
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
}`}{' '}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -15,6 +15,7 @@ import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
|
import i18next from 'i18next';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import {
|
import {
|
||||||
EntityFieldThreadCount,
|
EntityFieldThreadCount,
|
||||||
@ -112,10 +113,15 @@ export const getReplyText = (
|
|||||||
singular?: string,
|
singular?: string,
|
||||||
plural?: string
|
plural?: string
|
||||||
) => {
|
) => {
|
||||||
if (count === 0) return 'Reply in conversation';
|
if (count === 0) return i18next.t('label.reply-in-conversation');
|
||||||
if (count === 1) return `${count} ${singular ? singular : 'older reply'}`;
|
if (count === 1)
|
||||||
|
return `${count} ${
|
||||||
|
singular ? singular : i18next.t('label.older-reply-lowercase')
|
||||||
|
}`;
|
||||||
|
|
||||||
return `${count} ${plural ? plural : 'older replies'}`;
|
return `${count} ${
|
||||||
|
plural ? plural : i18next.t('label.older-replies-lowercase')
|
||||||
|
}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEntityFieldThreadCounts = (
|
export const getEntityFieldThreadCounts = (
|
||||||
@ -481,10 +487,10 @@ export const updateThreadData = (
|
|||||||
|
|
||||||
export const getFeedAction = (type: ThreadType) => {
|
export const getFeedAction = (type: ThreadType) => {
|
||||||
if (type === ThreadType.Task) {
|
if (type === ThreadType.Task) {
|
||||||
return 'created a task';
|
return i18next.t('label.created-a-task-lowercase');
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'posted on';
|
return i18next.t('label.posted-on-lowercase');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prepareFeedLink = (entityType: string, entityFQN: string) => {
|
export const prepareFeedLink = (entityType: string, entityFQN: string) => {
|
||||||
@ -553,11 +559,11 @@ export const getFeedPanelHeaderText = (
|
|||||||
) => {
|
) => {
|
||||||
switch (threadType) {
|
switch (threadType) {
|
||||||
case ThreadType.Announcement:
|
case ThreadType.Announcement:
|
||||||
return 'Announcement';
|
return i18next.t('label.announcement');
|
||||||
case ThreadType.Task:
|
case ThreadType.Task:
|
||||||
return 'Task';
|
return i18next.t('label.task');
|
||||||
case ThreadType.Conversation:
|
case ThreadType.Conversation:
|
||||||
default:
|
default:
|
||||||
return 'Conversation';
|
return i18next.t('label.Conversation');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import { t } from 'i18next';
|
||||||
import { cloneDeep, isEmpty } from 'lodash';
|
import { cloneDeep, isEmpty } from 'lodash';
|
||||||
import { FormattedGlossarySuggestion } from 'Models';
|
import { FormattedGlossarySuggestion } from 'Models';
|
||||||
import { DataNode } from 'rc-tree/lib/interface';
|
import { DataNode } from 'rc-tree/lib/interface';
|
||||||
@ -230,7 +231,7 @@ export const updateGlossaryListBySearchedTerms = (
|
|||||||
export const getActionsList = () => {
|
export const getActionsList = () => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'Add Term',
|
name: t('label.add-term'),
|
||||||
value: 'add_term',
|
value: 'add_term',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { Change, diffWordsWithSpace } from 'diff';
|
import { Change, diffWordsWithSpace } from 'diff';
|
||||||
|
import { t } from 'i18next';
|
||||||
import { isEqual, isUndefined } from 'lodash';
|
import { isEqual, isUndefined } from 'lodash';
|
||||||
import { getDashboardByFqn } from '../axiosAPIs/dashboardAPI';
|
import { getDashboardByFqn } from '../axiosAPIs/dashboardAPI';
|
||||||
import { getUserSuggestions } from '../axiosAPIs/miscAPI';
|
import { getUserSuggestions } from '../axiosAPIs/miscAPI';
|
||||||
@ -311,11 +312,11 @@ export const fetchEntityDetail = (
|
|||||||
|
|
||||||
export const TASK_ACTION_LIST = [
|
export const TASK_ACTION_LIST = [
|
||||||
{
|
{
|
||||||
label: 'Accept Suggestion',
|
label: t('label.accept-suggestion'),
|
||||||
key: TaskActionMode.VIEW,
|
key: TaskActionMode.VIEW,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Edit & Accept Suggestion',
|
label: t('label.edit-amp-accept-suggestion'),
|
||||||
key: TaskActionMode.EDIT,
|
key: TaskActionMode.EDIT,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user