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