mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-08 08:31:37 +00:00
fix(ui): revamp teams page added supported subscription webhooks (#13296)
* revamp teams page added supported subscription webhook * minor changes * minor changes * changes teams header page layout and subscription * minor changes * fix cypress and addressed comments * fix cypress for teams hierarchy (#13352) * fix sonar errors and users not showing in teams having space * code smell and bugs fixes * fix teams page cypress --------- Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
parent
e879d512d3
commit
16a4033645
@ -37,7 +37,7 @@ export const BASE_WAIT_TIME = 20000;
|
||||
const ADMIN = 'admin';
|
||||
const RETRIES_COUNT = 4;
|
||||
|
||||
const TEAM_TYPES = ['BusinessUnit', 'Department', 'Division', 'Group'];
|
||||
const TEAM_TYPES = ['Department', 'Division', 'Group'];
|
||||
|
||||
export const replaceAllSpacialCharWith_ = (text) => {
|
||||
return text.replaceAll(/[&/\\#, +()$~%.'":*?<>{}]/g, '_');
|
||||
@ -53,8 +53,31 @@ export const checkServiceFieldSectionHighlighting = (field) => {
|
||||
);
|
||||
};
|
||||
|
||||
const checkTeamTypeOptions = () => {
|
||||
for (const teamType of TEAM_TYPES) {
|
||||
const getTeamType = (currentTeam) => {
|
||||
switch (currentTeam) {
|
||||
case 'BusinessUnit':
|
||||
return {
|
||||
childTeamType: 'Division',
|
||||
teamTypeOptions: TEAM_TYPES,
|
||||
};
|
||||
|
||||
case 'Division':
|
||||
return {
|
||||
childTeamType: 'Department',
|
||||
teamTypeOptions: TEAM_TYPES,
|
||||
};
|
||||
|
||||
case 'Department':
|
||||
return {
|
||||
childTeamType: 'Group',
|
||||
teamTypeOptions: ['Department', 'Group'],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const checkTeamTypeOptions = (type) => {
|
||||
cy.log('check', type);
|
||||
for (const teamType of getTeamType(type).teamTypeOptions) {
|
||||
cy.get(`.ant-select-dropdown [title="${teamType}"]`)
|
||||
.should('exist')
|
||||
.should('be.visible');
|
||||
@ -961,12 +984,28 @@ export const addTeam = (TEAM_DETAILS, index) => {
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
checkTeamTypeOptions();
|
||||
if (index > 0) {
|
||||
cy.get('[data-testid="team-type"]')
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
cy.log(text);
|
||||
checkTeamTypeOptions(text);
|
||||
cy.log('check type', text);
|
||||
cy.get(
|
||||
`.ant-select-dropdown [title="${getTeamType(text).childTeamType}"]`
|
||||
)
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
});
|
||||
} else {
|
||||
checkTeamTypeOptions('BusinessUnit');
|
||||
|
||||
cy.get(`.ant-select-dropdown [title="${TEAM_DETAILS.teamType}"]`)
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.get(`.ant-select-dropdown [title='BusinessUnit']`)
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
}
|
||||
|
||||
cy.get(descriptionBox)
|
||||
.should('exist')
|
||||
|
@ -24,12 +24,12 @@ import {
|
||||
verifyResponseStatusCode,
|
||||
} from '../../common/common';
|
||||
|
||||
const updateddescription = 'This is updated description';
|
||||
const updatedDescription = 'This is updated description';
|
||||
|
||||
const teamName = 'team-group-test-430116' ?? `team-ct-test-${uuid()}`;
|
||||
const TEAM_DETAILS = {
|
||||
name: teamName,
|
||||
updatedname: `${teamName}-updated`,
|
||||
updatedName: `${teamName}-updated`,
|
||||
teamType: 'Group',
|
||||
description: `This is ${teamName} description`,
|
||||
username: 'Aaron Johnson',
|
||||
@ -125,10 +125,8 @@ describe('Teams flow should work properly', () => {
|
||||
.contains(TEAM_DETAILS.name)
|
||||
.click();
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
cy.get('[data-testid="add-new-user"]')
|
||||
.should('be.visible')
|
||||
.scrollIntoView();
|
||||
cy.get('[data-testid="add-new-user"]').click();
|
||||
cy.get('[data-testid="users"]').click();
|
||||
cy.get('[data-testid="add-new-user"]').scrollIntoView().click();
|
||||
verifyResponseStatusCode('@getUsers', 200);
|
||||
cy.get('[data-testid="selectable-list"] [data-testid="searchbar"]').type(
|
||||
TEAM_DETAILS.username
|
||||
@ -154,6 +152,7 @@ describe('Teams flow should work properly', () => {
|
||||
cy.get(`[data-row-key="${TEAM_DETAILS.name}"]`)
|
||||
.contains(TEAM_DETAILS.name)
|
||||
.click();
|
||||
cy.get('[data-testid="users"]').click();
|
||||
verifyResponseStatusCode('@getUserDetails', 200);
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
cy.get('[data-testid="add-new-user"]').should('be.visible').click();
|
||||
@ -176,10 +175,15 @@ describe('Teams flow should work properly', () => {
|
||||
.contains(TEAM_DETAILS.name)
|
||||
.click();
|
||||
|
||||
cy.get('[data-testid="users"]').click();
|
||||
|
||||
verifyResponseStatusCode('@getUsers', 200);
|
||||
|
||||
// Click on join teams button
|
||||
cy.get('[data-testid="join-teams"]').should('be.visible').click();
|
||||
cy.get('[data-testid="join-teams"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
// Verify toast notification
|
||||
toastNotification('Team joined successfully!');
|
||||
@ -201,14 +205,14 @@ describe('Teams flow should work properly', () => {
|
||||
|
||||
verifyResponseStatusCode('@getSelectedTeam', 200);
|
||||
// Click on edit display name
|
||||
cy.get('[data-testid="edit-synonyms"]').should('be.visible').click();
|
||||
cy.get('[data-testid="edit-team-name"]').should('be.visible').click();
|
||||
|
||||
// Enter the updated team name
|
||||
cy.get('[data-testid="synonyms"]')
|
||||
cy.get('[data-testid="team-name-input"]')
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.clear()
|
||||
.type(TEAM_DETAILS.updatedname);
|
||||
.type(TEAM_DETAILS.updatedName);
|
||||
|
||||
// Save the updated display name
|
||||
cy.get('[data-testid="saveAssociatedTag"]')
|
||||
@ -220,12 +224,12 @@ describe('Teams flow should work properly', () => {
|
||||
verifyResponseStatusCode('@getSelectedTeam', 200);
|
||||
// Validate the updated display name
|
||||
cy.get('[data-testid="team-heading"]').then(($el) => {
|
||||
cy.wrap($el).should('have.text', TEAM_DETAILS.updatedname);
|
||||
cy.wrap($el).should('have.text', TEAM_DETAILS.updatedName);
|
||||
});
|
||||
|
||||
cy.get('[data-testid="inactive-link"]')
|
||||
.should('be.visible')
|
||||
.should('contain', TEAM_DETAILS.updatedname);
|
||||
.scrollIntoView()
|
||||
.should('contain', TEAM_DETAILS.updatedName);
|
||||
});
|
||||
|
||||
it('Update description for created team', () => {
|
||||
@ -245,12 +249,12 @@ describe('Teams flow should work properly', () => {
|
||||
// Validate the updated display name
|
||||
cy.get('[data-testid="team-heading"]').should(
|
||||
'contain',
|
||||
`${TEAM_DETAILS.updatedname}`
|
||||
`${TEAM_DETAILS.updatedName}`
|
||||
);
|
||||
|
||||
cy.get('[data-testid="inactive-link"]')
|
||||
.should('be.visible')
|
||||
.should('contain', TEAM_DETAILS.updatedname);
|
||||
.should('contain', TEAM_DETAILS.updatedName);
|
||||
|
||||
// Click on edit description button
|
||||
cy.get('[data-testid="edit-description"]')
|
||||
@ -258,7 +262,7 @@ describe('Teams flow should work properly', () => {
|
||||
.click({ force: true });
|
||||
|
||||
// Entering updated description
|
||||
cy.get(descriptionBox).clear().type(updateddescription);
|
||||
cy.get(descriptionBox).clear().type(updatedDescription);
|
||||
|
||||
cy.get('[data-testid="save"]').should('be.visible').click();
|
||||
verifyResponseStatusCode('@patchDescription', 200);
|
||||
@ -266,7 +270,7 @@ describe('Teams flow should work properly', () => {
|
||||
// Validating the updated description
|
||||
cy.get('[data-testid="description"] p').should(
|
||||
'contain',
|
||||
updateddescription
|
||||
updatedDescription
|
||||
);
|
||||
});
|
||||
|
||||
@ -287,7 +291,7 @@ describe('Teams flow should work properly', () => {
|
||||
.should('be.visible')
|
||||
.contains(TEAM_DETAILS.name);
|
||||
// //Click on Leave team
|
||||
cy.get('[data-testid="leave-team-button"]').should('be.visible').click();
|
||||
cy.get('[data-testid="leave-team-button"]').click();
|
||||
|
||||
// //Click on confirm button
|
||||
cy.get('[data-testid="save-button"]').should('be.visible').click();
|
||||
@ -312,8 +316,8 @@ describe('Teams flow should work properly', () => {
|
||||
verifyResponseStatusCode('@getSelectedTeam', 200);
|
||||
cy.get('[data-testid="team-heading"]')
|
||||
.should('be.visible')
|
||||
.contains(TEAM_DETAILS.updatedname);
|
||||
cy.get('[data-testid="header"] [data-testid="manage-button"]')
|
||||
.contains(TEAM_DETAILS.updatedName);
|
||||
cy.get('[data-testid="manage-button"]')
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
@ -380,9 +384,9 @@ describe('Teams flow should work properly', () => {
|
||||
|
||||
cy.get('[data-testid="team-heading"]')
|
||||
.should('be.visible')
|
||||
.contains(TEAM_DETAILS.updatedname);
|
||||
.contains(TEAM_DETAILS.updatedName);
|
||||
|
||||
cy.get('[data-testid="header"] [data-testid="manage-button"]')
|
||||
cy.get('[data-testid="manage-button"]')
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
@ -436,7 +440,7 @@ describe('Teams flow should work properly', () => {
|
||||
.click();
|
||||
|
||||
verifyResponseStatusCode('@getSelectedTeam', 200);
|
||||
cy.get('[data-testid="header"] [data-testid="manage-button"]')
|
||||
cy.get('[data-testid="manage-button"]')
|
||||
.should('exist')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
@ -0,0 +1,12 @@
|
||||
<svg viewBox="0 0 25 27" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4154_24860)">
|
||||
<path d="M5.67376 14.5249V6.05078H1.97121C0.88131 6.05078 0 6.93731 0 8.02721V25.5439C0 26.4252 1.06383 26.8633 1.6844 26.2427L5.7572 22.1699H17.355C18.4449 22.1699 19.3262 21.2886 19.3262 20.1987V16.4961H7.65019C6.56028 16.4961 5.67376 15.6148 5.67376 14.5249Z" fill="#00AC47"/>
|
||||
<path d="M23.0288 0.460938H7.65017C6.56026 0.460938 5.67896 1.34225 5.67896 2.43215V6.05126H17.355C18.4449 6.05126 19.3262 6.93257 19.3262 8.02247V16.4914H23.0288C24.1187 16.4914 25 15.6101 25 14.5202V2.43215C25 1.34225 24.1187 0.460938 23.0288 0.460938Z" fill="#5BB974"/>
|
||||
<path d="M17.355 6.05078H5.67371V14.5197C5.67371 15.6096 6.55502 16.4909 7.64492 16.4909H19.321V8.02721C19.3262 6.93731 18.4449 6.05078 17.355 6.05078Z" fill="#00832D"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4154_24860">
|
||||
<rect width="25" height="26.0743" fill="white" transform="translate(0 0.460938)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 991 B |
@ -0,0 +1,5 @@
|
||||
|
||||
<svg viewBox="0 0 105 65" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M36.9019 2.53811C29.7245 1.986 15.3697 2.5371 15.3697 18.5493C15.3697 32.6833 21.995 36.9517 25.3076 37.3192V43.3924L10.4007 51.674C7.82417 53.1463 2.67114 57.416 2.67114 62.7162H25.3076L44.0794 52.7782V44.4966C36.9019 37.3192 35.2456 30.0108 35.2456 20.2038C35.2456 8.54045 41.3188 2.53811 52.9131 2.53811" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M68.3725 2.53811C75.5499 1.986 89.9047 2.5371 89.9047 18.5493C89.9047 32.6833 83.2794 36.9517 79.9668 37.3192V43.3924L94.8737 51.674C97.4502 53.1463 102.603 57.416 102.603 62.7162H79.9668L61.195 52.7782V44.4966C68.3725 37.3192 70.0288 30.0108 70.0288 20.2038C70.0288 8.54045 63.9556 2.53811 52.3613 2.53811" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 844 B |
File diff suppressed because it is too large
Load Diff
@ -138,7 +138,7 @@ const TeamHierarchy: FC<TeamHierarchyProps> = ({
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [data, onTeamExpand]);
|
||||
}, [data, isFetchingAllTeamAdvancedDetails, onTeamExpand]);
|
||||
|
||||
const handleMoveRow = useCallback(
|
||||
async (dragRecord: Team, dropRecord: Team) => {
|
||||
|
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright 2023 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 { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Divider, Form, Input, Space, Tooltip, Typography } from 'antd';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import Icon from '@ant-design/icons/lib/components/Icon';
|
||||
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
|
||||
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
|
||||
import { EMAIL_REG_EX } from 'constants/regex.constants';
|
||||
import { useAuth } from 'hooks/authHooks';
|
||||
import { isEmpty, last } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { hasEditAccess } from 'utils/CommonUtils';
|
||||
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import TeamTypeSelect from 'components/common/TeamTypeSelect/TeamTypeSelect.component';
|
||||
import { NO_DATA_PLACEHOLDER } from 'constants/constants';
|
||||
import { Team, TeamType } from 'generated/entity/teams/team';
|
||||
import { EntityReference } from 'generated/entity/type';
|
||||
import { TeamsInfoProps } from '../team.interface';
|
||||
|
||||
const TeamsInfo = ({
|
||||
parentTeams,
|
||||
isGroupType,
|
||||
childTeamsCount,
|
||||
entityPermissions,
|
||||
currentTeam,
|
||||
updateTeamHandler,
|
||||
}: TeamsInfoProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
|
||||
const [isHeadingEditing, setIsHeadingEditing] = useState(false);
|
||||
const [isEmailEdit, setIsEmailEdit] = useState<boolean>(false);
|
||||
const [showTypeSelector, setShowTypeSelector] = useState(false);
|
||||
const [heading, setHeading] = useState(
|
||||
currentTeam ? currentTeam.displayName : ''
|
||||
);
|
||||
|
||||
const { email, owner, teamType } = useMemo(() => currentTeam, [currentTeam]);
|
||||
|
||||
const { hasEditPermission, hasEditDisplayNamePermission, hasAccess } =
|
||||
useMemo(
|
||||
() => ({
|
||||
hasEditPermission: entityPermissions.EditAll,
|
||||
hasEditDisplayNamePermission:
|
||||
entityPermissions.EditDisplayName || entityPermissions.EditAll,
|
||||
hasAccess: isAuthDisabled || isAdminUser,
|
||||
}),
|
||||
|
||||
[entityPermissions]
|
||||
);
|
||||
|
||||
/**
|
||||
* Check if current team is the owner or not
|
||||
* @returns - True true or false based on hasEditAccess response
|
||||
*/
|
||||
const isCurrentTeamOwner = useMemo(
|
||||
() => hasEditAccess(owner?.type ?? '', owner?.id ?? ''),
|
||||
[owner]
|
||||
);
|
||||
|
||||
const onHeadingSave = async (): Promise<void> => {
|
||||
if (heading && currentTeam) {
|
||||
const updatedData: Team = {
|
||||
...currentTeam,
|
||||
displayName: heading,
|
||||
};
|
||||
|
||||
await updateTeamHandler(updatedData);
|
||||
}
|
||||
setIsHeadingEditing(false);
|
||||
};
|
||||
|
||||
const onEmailSave = async (data: { email: string }) => {
|
||||
if (currentTeam) {
|
||||
const updatedData: Team = {
|
||||
...currentTeam,
|
||||
email: isEmpty(data.email) ? undefined : data.email,
|
||||
};
|
||||
|
||||
await updateTeamHandler(updatedData);
|
||||
}
|
||||
setIsEmailEdit(false);
|
||||
};
|
||||
|
||||
const updateOwner = useCallback(
|
||||
async (owner?: EntityReference) => {
|
||||
if (currentTeam) {
|
||||
const updatedData: Team = {
|
||||
...currentTeam,
|
||||
owner,
|
||||
};
|
||||
|
||||
await updateTeamHandler(updatedData);
|
||||
}
|
||||
},
|
||||
[currentTeam]
|
||||
);
|
||||
|
||||
const updateTeamType = async (type: TeamType): Promise<void> => {
|
||||
if (currentTeam) {
|
||||
const updatedData: Team = {
|
||||
...currentTeam,
|
||||
teamType: type,
|
||||
};
|
||||
|
||||
await updateTeamHandler(updatedData);
|
||||
|
||||
setShowTypeSelector(false);
|
||||
}
|
||||
};
|
||||
|
||||
const teamHeadingRender = useMemo(
|
||||
() =>
|
||||
isHeadingEditing ? (
|
||||
<Space>
|
||||
<Input
|
||||
className="w-48"
|
||||
data-testid="team-name-input"
|
||||
placeholder={t('message.enter-comma-separated-field', {
|
||||
field: t('label.term-lowercase'),
|
||||
})}
|
||||
type="text"
|
||||
value={heading}
|
||||
onChange={(e) => setHeading(e.target.value)}
|
||||
/>
|
||||
<Space data-testid="buttons" size={4}>
|
||||
<Button
|
||||
className="rounded-4 text-sm p-xss"
|
||||
data-testid="cancelAssociatedTag"
|
||||
type="primary"
|
||||
onMouseDown={() => setIsHeadingEditing(false)}>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
className="rounded-4 text-sm p-xss"
|
||||
data-testid="saveAssociatedTag"
|
||||
type="primary"
|
||||
onMouseDown={onHeadingSave}>
|
||||
<CheckOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
) : (
|
||||
<Space align="baseline">
|
||||
<Typography.Title
|
||||
className="m-b-0 w-max-200"
|
||||
data-testid="team-heading"
|
||||
ellipsis={{ tooltip: true }}
|
||||
level={5}>
|
||||
{heading}
|
||||
</Typography.Title>
|
||||
{(hasAccess || isCurrentTeamOwner) && (
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={
|
||||
hasEditDisplayNamePermission
|
||||
? t('label.edit-entity', {
|
||||
entity: t('label.display-name'),
|
||||
})
|
||||
: t('message.no-permission-for-action')
|
||||
}>
|
||||
<Icon
|
||||
className="align-middle"
|
||||
component={EditIcon}
|
||||
data-testid="edit-team-name"
|
||||
disabled={!hasEditDisplayNamePermission}
|
||||
style={{ fontSize: '16px' }}
|
||||
onClick={() => setIsHeadingEditing(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
[heading, isHeadingEditing, hasEditDisplayNamePermission]
|
||||
);
|
||||
|
||||
const emailRender = useMemo(
|
||||
() => (
|
||||
<Space align="center" size={4}>
|
||||
<Typography.Text className="text-grey-muted">{`${t(
|
||||
'label.email'
|
||||
)} :`}</Typography.Text>
|
||||
{isEmailEdit ? (
|
||||
<Form initialValues={{ email }} onFinish={onEmailSave}>
|
||||
<Space align="baseline">
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
pattern: EMAIL_REG_EX,
|
||||
type: 'email',
|
||||
message: t('message.field-text-is-invalid', {
|
||||
fieldText: t('label.email'),
|
||||
}),
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
className="w-48"
|
||||
data-testid="email-input"
|
||||
placeholder={t('label.enter-entity', {
|
||||
entity: t('label.email-lowercase'),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Space size={4}>
|
||||
<Button
|
||||
className="h-8 p-x-xss"
|
||||
data-testid="cancel-edit-email"
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={() => setIsEmailEdit(false)}>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
className="h-8 p-x-xss"
|
||||
data-testid="save-edit-email"
|
||||
htmlType="submit"
|
||||
size="small"
|
||||
type="primary">
|
||||
<CheckOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</Form>
|
||||
) : (
|
||||
<Space align="center">
|
||||
<Typography.Text className="font-medium" data-testid="email-value">
|
||||
{email ?? NO_DATA_PLACEHOLDER}
|
||||
</Typography.Text>
|
||||
{hasEditPermission && (
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={
|
||||
hasEditPermission
|
||||
? t('label.edit-entity', {
|
||||
entity: t('label.email'),
|
||||
})
|
||||
: t('message.no-permission-for-action')
|
||||
}>
|
||||
<Icon
|
||||
className="toolbar-button align-middle"
|
||||
component={EditIcon}
|
||||
data-testid="edit-email"
|
||||
style={{ fontSize: '16px' }}
|
||||
onClick={() => setIsEmailEdit(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
[email, isEmailEdit, hasEditPermission]
|
||||
);
|
||||
|
||||
const teamTypeElement = useMemo(() => {
|
||||
if (teamType === TeamType.Organization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Space size={4}>
|
||||
<Divider type="vertical" />
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{`${t('label.type')} :`}
|
||||
</Typography.Text>
|
||||
{showTypeSelector ? (
|
||||
<TeamTypeSelect
|
||||
handleShowTypeSelector={setShowTypeSelector}
|
||||
parentTeamType={
|
||||
last(parentTeams)?.teamType ?? TeamType.Organization
|
||||
}
|
||||
showGroupOption={!childTeamsCount}
|
||||
teamType={teamType ?? TeamType.Department}
|
||||
updateTeamType={hasEditPermission ? updateTeamType : undefined}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Typography.Text className="font-medium" data-testid="team-type">
|
||||
{teamType}
|
||||
</Typography.Text>
|
||||
|
||||
{hasEditPermission && (
|
||||
<Icon
|
||||
className={classNames('vertical-middle m-l-xs', {
|
||||
'opacity-50': isGroupType,
|
||||
})}
|
||||
data-testid="edit-team-type-icon"
|
||||
title={
|
||||
isGroupType
|
||||
? t('message.group-team-type-change-message')
|
||||
: t('label.edit-entity', {
|
||||
entity: t('label.team-type'),
|
||||
})
|
||||
}
|
||||
onClick={
|
||||
isGroupType ? undefined : () => setShowTypeSelector(true)
|
||||
}>
|
||||
<EditIcon />
|
||||
</Icon>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
}, [
|
||||
teamType,
|
||||
parentTeams,
|
||||
isGroupType,
|
||||
childTeamsCount,
|
||||
showTypeSelector,
|
||||
hasEditPermission,
|
||||
updateTeamType,
|
||||
setShowTypeSelector,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTeam) {
|
||||
setHeading(currentTeam.displayName ?? currentTeam.name);
|
||||
}
|
||||
}, [currentTeam]);
|
||||
|
||||
return (
|
||||
<Space size={4}>
|
||||
{teamHeadingRender}
|
||||
<Divider type="vertical" />
|
||||
<OwnerLabel
|
||||
className="text-sm"
|
||||
hasPermission={hasAccess}
|
||||
owner={owner}
|
||||
onUpdate={updateOwner}
|
||||
/>
|
||||
<Divider type="vertical" />
|
||||
{emailRender}
|
||||
{teamTypeElement}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamsInfo;
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2023 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 { Form, Input, Modal, Select, Space, Typography } from 'antd';
|
||||
import { DE_ACTIVE_COLOR, ICON_DIMENSION } from 'constants/constants';
|
||||
import {
|
||||
SUBSCRIPTION_WEBHOOK,
|
||||
SUBSCRIPTION_WEBHOOK_OPTIONS,
|
||||
} from 'constants/Teams.constants';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
|
||||
|
||||
import { useForm } from 'antd/lib/form/Form';
|
||||
import TagsV1 from 'components/Tag/TagsV1/TagsV1.component';
|
||||
import { TAG_CONSTANT, TAG_START_WITH } from 'constants/Tag.constants';
|
||||
import { Webhook } from 'generated/type/profile';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { getWebhookIcon } from 'utils/TeamUtils';
|
||||
import { SubscriptionWebhook, TeamsSubscriptionProps } from '../team.interface';
|
||||
|
||||
const TeamsSubscription = ({
|
||||
subscription,
|
||||
hasEditPermission,
|
||||
updateTeamSubscription,
|
||||
}: TeamsSubscriptionProps) => {
|
||||
const [form] = useForm();
|
||||
const { t } = useTranslation();
|
||||
const [editSubscription, setEditSubscription] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
const getWebhookIconByKey = useCallback((item: SUBSCRIPTION_WEBHOOK) => {
|
||||
const Icon = getWebhookIcon(item);
|
||||
|
||||
return <Icon data-testid={`${item}-icon`} height={20} width={20} />;
|
||||
}, []);
|
||||
|
||||
// Watchers
|
||||
const webhooks: {
|
||||
webhook: string;
|
||||
endpoint: string;
|
||||
}[] = Form.useWatch(['subscriptions'], form);
|
||||
|
||||
// Run time values needed for conditional rendering
|
||||
const subscriptionOptions = useMemo(() => {
|
||||
const exitingWebhook = webhooks?.map((f) => f?.webhook) ?? [];
|
||||
|
||||
return SUBSCRIPTION_WEBHOOK_OPTIONS.map((func) => ({
|
||||
label: func.label,
|
||||
value: func.value,
|
||||
disabled: exitingWebhook.includes(func.value),
|
||||
}));
|
||||
}, [webhooks]);
|
||||
|
||||
const cellItem = useCallback(
|
||||
(key: string, value: Webhook) => (
|
||||
<Space align="start">
|
||||
{getWebhookIconByKey(key as SUBSCRIPTION_WEBHOOK)}
|
||||
<Typography.Text className="text-xs text-grey-muted">
|
||||
{value.endpoint}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const subscriptionRenderElement = useMemo(() => {
|
||||
const webhook = Object.entries(subscription ?? {})?.[0];
|
||||
|
||||
return isEmpty(subscription) && hasEditPermission ? (
|
||||
<div onClick={() => setEditSubscription(true)}>
|
||||
<TagsV1 startWith={TAG_START_WITH.PLUS} tag={TAG_CONSTANT} />
|
||||
</div>
|
||||
) : (
|
||||
cellItem(webhook[0], webhook[1])
|
||||
);
|
||||
}, [subscription]);
|
||||
|
||||
const handleSave = async (values: SubscriptionWebhook) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await updateTeamSubscription(values);
|
||||
} catch {
|
||||
// parent block will throw error
|
||||
} finally {
|
||||
setEditSubscription(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (subscription) {
|
||||
const data = Object.entries(subscription)[0];
|
||||
form.setFieldsValue({
|
||||
webhook: data[0],
|
||||
endpoint: data[1].endpoint,
|
||||
});
|
||||
}
|
||||
}, [subscription, editSubscription]);
|
||||
|
||||
return (
|
||||
<Space align="start" data-testid="teams-subscription">
|
||||
<Typography.Text className="right-panel-label font-normal">
|
||||
{`${t('label.subscription')} :`}
|
||||
</Typography.Text>
|
||||
{subscriptionRenderElement}
|
||||
|
||||
{!editSubscription && !isEmpty(subscription) && hasEditPermission && (
|
||||
<EditIcon
|
||||
className="cursor-pointer align-middle"
|
||||
color={DE_ACTIVE_COLOR}
|
||||
data-testid="edit-roles"
|
||||
{...ICON_DIMENSION}
|
||||
onClick={() => setEditSubscription(true)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{editSubscription && (
|
||||
<Modal
|
||||
centered
|
||||
open
|
||||
closable={false}
|
||||
confirmLoading={isLoading}
|
||||
maskClosable={false}
|
||||
okButtonProps={{
|
||||
form: 'subscription-form',
|
||||
type: 'primary',
|
||||
htmlType: 'submit',
|
||||
}}
|
||||
okText={t('label.confirm')}
|
||||
title={t('label.add-entity', {
|
||||
entity: t('label.subscription'),
|
||||
})}
|
||||
onCancel={() => setEditSubscription(false)}>
|
||||
<Form
|
||||
data-testid="subscription-modal"
|
||||
form={form}
|
||||
id="subscription-form"
|
||||
layout="vertical"
|
||||
onFinish={handleSave}>
|
||||
<Form.Item label={t('label.webhook')} name="webhook">
|
||||
<Select
|
||||
options={subscriptionOptions}
|
||||
placeholder={t('label.select-field', {
|
||||
field: t('label.condition'),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('label.endpoint')}
|
||||
name="endpoint"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('label.field-required-plural', {
|
||||
field: t('label.endpoint'),
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: t('message.endpoint-should-be-valid'),
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
placeholder={t('label.enter-entity-value', {
|
||||
entity: t('label.endpoint'),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamsSubscription;
|
@ -214,7 +214,7 @@ export const UserTab = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Row className="p-md" gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Row justify="space-between">
|
||||
<Col span={8}>
|
||||
|
@ -11,6 +11,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||
import { MessagingProvider } from 'generated/type/profile';
|
||||
import { Team } from '../../../generated/entity/teams/team';
|
||||
|
||||
export interface TeamHierarchyProps {
|
||||
@ -55,3 +57,23 @@ export enum TeamsPageTab {
|
||||
ROLES = 'roles',
|
||||
POLICIES = 'policies',
|
||||
}
|
||||
|
||||
export interface TeamsInfoProps {
|
||||
parentTeams: Team[];
|
||||
isGroupType: boolean;
|
||||
childTeamsCount: number;
|
||||
currentTeam: Team;
|
||||
entityPermissions: OperationPermission;
|
||||
updateTeamHandler: (data: Team) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface TeamsSubscriptionProps {
|
||||
hasEditPermission: boolean;
|
||||
subscription?: MessagingProvider;
|
||||
updateTeamSubscription: (value: SubscriptionWebhook) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SubscriptionWebhook {
|
||||
webhook: string;
|
||||
endpoint: string;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
@import url('../../../styles/variables.less');
|
||||
|
||||
.team-list-container {
|
||||
padding: 20px;
|
||||
.ant-btn {
|
||||
border-radius: 4px;
|
||||
}
|
||||
@ -76,9 +77,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
.teams-layout {
|
||||
margin: -16px -16px 0 -16px;
|
||||
|
||||
.ant-card-head-title {
|
||||
padding-top: 0;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.teams-profile {
|
||||
background-color: @team-avatar-bg;
|
||||
}
|
||||
|
||||
.teams-profile-container {
|
||||
background: @user-profile-background;
|
||||
|
||||
.ant-card {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.teams-tabs-content-container {
|
||||
width: 100%;
|
||||
.teams-scroll-component {
|
||||
width: 100%;
|
||||
height: calc(100vh - 120px);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.site-collapse-custom-collapse .site-collapse-custom-panel {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
background: @user-profile-background;
|
||||
border: 0px;
|
||||
|
||||
.ant-collapse-content-box {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.team-assets-right-panel {
|
||||
margin-top: -24px;
|
||||
margin-bottom: -24px;
|
||||
.summary-panel-container {
|
||||
height: 100%;
|
||||
border: 0;
|
||||
|
@ -14,6 +14,7 @@ import Icon from '@ant-design/icons';
|
||||
import { Typography } from 'antd';
|
||||
import { ReactComponent as IconTeamsGrey } from 'assets/svg/teams-grey.svg';
|
||||
import { ReactComponent as IconUser } from 'assets/svg/user.svg';
|
||||
import classNames from 'classnames';
|
||||
import { getTeamAndUserDetailsPath, getUserPath } from 'constants/constants';
|
||||
import { OwnerType } from 'enums/user.enum';
|
||||
import { EntityReference } from 'generated/entity/data/table';
|
||||
@ -27,11 +28,13 @@ import { UserTeamSelectableList } from '../UserTeamSelectableList/UserTeamSelect
|
||||
|
||||
export const OwnerLabel = ({
|
||||
owner,
|
||||
className,
|
||||
onUpdate,
|
||||
hasPermission,
|
||||
ownerDisplayName,
|
||||
}: {
|
||||
owner?: EntityReference;
|
||||
className?: string;
|
||||
onUpdate?: (owner?: EntityReference) => void;
|
||||
hasPermission?: boolean;
|
||||
ownerDisplayName?: ReactNode;
|
||||
@ -74,7 +77,10 @@ export const OwnerLabel = ({
|
||||
|
||||
{displayName ? (
|
||||
<Link
|
||||
className="text-primary font-medium text-xs no-underline"
|
||||
className={classNames(
|
||||
'text-primary font-medium text-xs no-underline',
|
||||
className
|
||||
)}
|
||||
data-testid="owner-link"
|
||||
to={
|
||||
owner?.type === 'team'
|
||||
@ -85,7 +91,7 @@ export const OwnerLabel = ({
|
||||
</Link>
|
||||
) : (
|
||||
<Typography.Text
|
||||
className="font-medium text-xs"
|
||||
className={classNames('font-medium text-xs', className)}
|
||||
data-testid="owner-link">
|
||||
{t('label.no-entity', { entity: t('label.owner') })}
|
||||
</Typography.Text>
|
||||
|
@ -17,7 +17,6 @@ import React, { useMemo, useState } from 'react';
|
||||
import { getTeamOptionsFromType } from 'utils/TeamUtils';
|
||||
import { TeamType } from '../../../generated/entity/teams/team';
|
||||
import { TeamTypeSelectProps } from './TeamTypeSelect.interface';
|
||||
import './TeamTypeSelect.style.less';
|
||||
|
||||
function TeamTypeSelect({
|
||||
handleShowTypeSelector,
|
||||
@ -63,17 +62,23 @@ function TeamTypeSelect({
|
||||
value={value}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
<Space className="edit-team-type-buttons" size={4}>
|
||||
<Space className="m-l-xs" size={4}>
|
||||
<Button
|
||||
className="h-8 p-x-xss"
|
||||
data-testid="cancel-btn"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={handleCancel}>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
className="h-8 p-x-xss"
|
||||
data-testid="save-btn"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={handleSubmit}>
|
||||
<CheckOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
);
|
||||
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 url('../../../styles/variables.less');
|
||||
|
||||
.team-type-select {
|
||||
.ant-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.edit-team-type-buttons {
|
||||
.ant-btn {
|
||||
background-color: @primary-color;
|
||||
color: white;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-team-icons {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import DraggableBodyRow from 'components/Team/TeamDetails/DraggableBodyRow';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export const DRAGGABLE_BODY_ROW = 'DraggableBodyRow';
|
||||
|
||||
@ -20,3 +21,25 @@ export const TABLE_CONSTANTS = {
|
||||
row: DraggableBodyRow,
|
||||
},
|
||||
};
|
||||
|
||||
export enum SUBSCRIPTION_WEBHOOK {
|
||||
MS_TEAMS = 'msTeams',
|
||||
SLACK = 'slack',
|
||||
G_CHAT = 'gChat',
|
||||
GENERIC = 'generic',
|
||||
}
|
||||
|
||||
export const SUBSCRIPTION_WEBHOOK_OPTIONS = [
|
||||
{
|
||||
label: t('label.ms-team-plural'),
|
||||
value: SUBSCRIPTION_WEBHOOK.MS_TEAMS,
|
||||
},
|
||||
{
|
||||
label: t('label.slack'),
|
||||
value: SUBSCRIPTION_WEBHOOK.SLACK,
|
||||
},
|
||||
{
|
||||
label: t('label.g-chat'),
|
||||
value: SUBSCRIPTION_WEBHOOK.G_CHAT,
|
||||
},
|
||||
];
|
||||
|
@ -102,7 +102,6 @@ export interface TeamDetailsProp {
|
||||
teamUsersSearchText: string;
|
||||
isDescriptionEditable: boolean;
|
||||
isTeamMemberLoading: number;
|
||||
hasAccess: boolean;
|
||||
isFetchingAdvancedDetails: boolean;
|
||||
isFetchingAllTeamAdvancedDetails: boolean;
|
||||
entityPermissions: OperationPermission;
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Eingeben",
|
||||
"enter-entity": "{{entity}} eingeben",
|
||||
"enter-entity-name": "Geben Sie einen Namen für {{entity}} ein",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Geben Sie eine Beschreibung für {{field}} ein",
|
||||
"enter-property-value": "Geben Sie einen Wert für die Eigenschaft ein",
|
||||
"enter-type-password": "{{type}}-Passwort eingeben",
|
||||
@ -382,6 +383,7 @@
|
||||
"feature-plural": "funktionen",
|
||||
"feature-plural-used": "verwendete funktionen",
|
||||
"february": "Februar",
|
||||
"feed-filter-plural": "Feed filters",
|
||||
"feed-lowercase": "feed",
|
||||
"feed-plural": "feeds",
|
||||
"field": "Feld",
|
||||
@ -903,6 +905,7 @@
|
||||
"sub-domain-plural": "Subdomains",
|
||||
"sub-team-plural": "Unter-Teams",
|
||||
"submit": "Einreichen",
|
||||
"subscription": "Subscription",
|
||||
"success": "Erfolg",
|
||||
"successfully-lowercase": "erfolgreich",
|
||||
"successfully-uploaded": "Erfolgreich hochgeladen",
|
||||
@ -1129,6 +1132,7 @@
|
||||
"connection-test-warning": "Der Test der Verbindung war teilweise erfolgreich: Einige Schritte hatten Fehler, es werden nur teilweise Metadaten übernommen.",
|
||||
"copied-to-clipboard": "In die Zwischenablage kopiert",
|
||||
"copy-to-clipboard": "Link in die Zwischenablage kopiert",
|
||||
"create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.",
|
||||
"create-new-glossary-guide": "Ein Glossar ist ein kontrolliertes Vokabular, das verwendet wird, um die Konzepte und Terminologie in einer Organisation zu definieren. Glossare können spezifisch für einen bestimmten Bereich sein (z. B. Business Glossar, Technisches Glossar). Im Glossar können die Standardbegriffe und Konzepte definiert werden, zusammen mit Synonymen und verwandten Begriffen. Es kann festgelegt werden, wie und von wem Begriffe im Glossar hinzugefügt werden können.",
|
||||
"create-or-update-email-account-for-bot": "Die Änderung der Kontaktemail aktualisiert oder erstellt einen neuen Bot-Benutzer.",
|
||||
"created-this-task-lowercase": "hat diese Aufgabe erstellt",
|
||||
@ -1215,6 +1219,9 @@
|
||||
"error-while-fetching-access-token": "Fehler beim Abrufen des Zugriffstokens.",
|
||||
"export-entity-help": "Laden Sie alle Ihre {{entity}} als CSV-Datei herunter und teilen Sie sie mit Ihrem Team.",
|
||||
"failed-status-for-entity-deploy": "<0>{{entity}}</0> wurde {{entityStatus}}, aber das Bereitstellen ist fehlgeschlagen.",
|
||||
"feed-filter-all": "Feeds for all the data assets that you own and follow",
|
||||
"feed-filter-following": "Feeds for all the data assets that you follow",
|
||||
"feed-filter-owner": "Feeds for all the data assets that you own",
|
||||
"fetch-dbt-files": "Hier sind die verfügbaren Quellen zum Abrufen von dbt-Katalog- und Manifestdateien.",
|
||||
"fetch-pipeline-status-error": "Fehler beim Abrufen des Pipeline-Status.",
|
||||
"field-ca-certs-description": "Der Zertifikatpfad muss in der Konfiguration hinzugefügt werden. Der Pfad sollte lokal im Ingestion-Container sein.",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Enter",
|
||||
"enter-entity": "Enter {{entity}}",
|
||||
"enter-entity-name": "Enter {{entity}} name",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Enter {{field}} description",
|
||||
"enter-property-value": "Enter Property Value",
|
||||
"enter-type-password": "Enter {{type}} Password",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sub Domains",
|
||||
"sub-team-plural": "Sub Teams",
|
||||
"submit": "Submit",
|
||||
"subscription": "Subscription",
|
||||
"success": "Success",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "Successfully Uploaded",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Entrar",
|
||||
"enter-entity": "Ingrese {{entity}}",
|
||||
"enter-entity-name": "Ingrese el nombre de {{entity}}",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Ingrese la descripción de {{field}}",
|
||||
"enter-property-value": "Ingrese el valor de la propiedad",
|
||||
"enter-type-password": "Ingrese la contraseña de {{type}}",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sub Domains",
|
||||
"sub-team-plural": "Sub Equipos",
|
||||
"submit": "Enviar",
|
||||
"subscription": "Subscription",
|
||||
"success": "Éxito",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "Cargado Exitosamente",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Entrer",
|
||||
"enter-entity": "Entrer {{entity}}",
|
||||
"enter-entity-name": "Entrer un nom pour {{entity}}",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Entrer une description pour {{field}}",
|
||||
"enter-property-value": "Entrer une valeur pour la propriété",
|
||||
"enter-type-password": "Entrer un mot de passe {{type}}",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sous-Domaines",
|
||||
"sub-team-plural": "Sous-Équipes",
|
||||
"submit": "Soumettre",
|
||||
"subscription": "Subscription",
|
||||
"success": "Succès",
|
||||
"successfully-lowercase": "avec succès",
|
||||
"successfully-uploaded": "Téléchargé avec succès",
|
||||
@ -1130,7 +1132,7 @@
|
||||
"connection-test-warning": "Le test de connexion a réussi partiellement : Certaines étapes ont échoué, nous n'ingérerons que les métadonnées partielles.",
|
||||
"copied-to-clipboard": "Copié dans le presse-papiers",
|
||||
"copy-to-clipboard": "Lien copié dans le presse-papiers",
|
||||
"create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.",
|
||||
"create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.",
|
||||
"create-new-glossary-guide": "Un Glossaire est un recueil de termes et vocabulaire utilisé pour définir des concepts et terminologies. Glossaires peuvent être spécifiques à certains domaines (e.g., Glossaire Business, Glossaire Technique, etc.). Dans le glossaire, les termes et concepts peuvent être définis tout en spécifiant des synonymes et des termes liés. Il est possible de contrôler qui peut ajouter des termes dans le dans le glossaire et comment ces termes peuvent être ajoutés.",
|
||||
"create-or-update-email-account-for-bot": "Changer l'email créera un nouveau ou mettra à jour l'agent numérique",
|
||||
"created-this-task-lowercase": "a créé cette tâche",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "入力",
|
||||
"enter-entity": "{{entity}}を入力",
|
||||
"enter-entity-name": "{{entity}}の名前を入力",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "{{field}}の説明を入力",
|
||||
"enter-property-value": "プロパティの値を入力",
|
||||
"enter-type-password": "{{type}} のパスワードを入力",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sub Domains",
|
||||
"sub-team-plural": "サブチーム",
|
||||
"submit": "Submit",
|
||||
"subscription": "Subscription",
|
||||
"success": "成功",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "アップロード成功",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Enter",
|
||||
"enter-entity": "Introduzir {{entity}}",
|
||||
"enter-entity-name": "Introduzir nome da {{entity}}",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Introduzir descrição do {{entity}}",
|
||||
"enter-property-value": "Introduzir valor da propriedade",
|
||||
"enter-type-password": "Introduzir {{type}} da senha",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sub Domains",
|
||||
"sub-team-plural": "Sub-equipes",
|
||||
"submit": "Enviar",
|
||||
"subscription": "Subscription",
|
||||
"success": "Sucesso",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "Enviado com sucesso",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "Введите",
|
||||
"enter-entity": "Введите {{entity}}",
|
||||
"enter-entity-name": "Введите имя {{entity}}",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "Введите описание {{field}}",
|
||||
"enter-property-value": "Введите значение свойства",
|
||||
"enter-type-password": "Введите {{type}} пароль",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "Sub Domains",
|
||||
"sub-team-plural": "Подгруппы",
|
||||
"submit": "Подтвердить",
|
||||
"subscription": "Subscription",
|
||||
"success": "Успешно",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "Успешно загружено",
|
||||
|
@ -345,6 +345,7 @@
|
||||
"enter": "输入",
|
||||
"enter-entity": "输入{{entity}}",
|
||||
"enter-entity-name": "输入{{entity}}的名称",
|
||||
"enter-entity-value": "Enter {{entity}} Value",
|
||||
"enter-field-description": "输入{{field}}的描述",
|
||||
"enter-property-value": "输入属性值",
|
||||
"enter-type-password": "输入{{type}}密码",
|
||||
@ -904,6 +905,7 @@
|
||||
"sub-domain-plural": "子域",
|
||||
"sub-team-plural": "子团队",
|
||||
"submit": "提交",
|
||||
"subscription": "Subscription",
|
||||
"success": "成功",
|
||||
"successfully-lowercase": "successfully",
|
||||
"successfully-uploaded": "上传成功",
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
|
||||
import { AxiosError } from 'axios';
|
||||
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
|
||||
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||
import { PagingHandlerParams } from 'components/common/next-previous/NextPrevious.interface';
|
||||
import Loader from 'components/Loader/Loader';
|
||||
@ -39,7 +38,7 @@ import {
|
||||
patchTeamDetail,
|
||||
} from 'rest/teamsAPI';
|
||||
import { getUsers, updateUserDetail } from 'rest/userAPI';
|
||||
import { getEncodedFqn } from 'utils/StringsUtils';
|
||||
import { getDecodedFqn, getEncodedFqn } from 'utils/StringsUtils';
|
||||
import AppState from '../../AppState';
|
||||
import {
|
||||
INITIAL_PAGING_VALUE,
|
||||
@ -52,7 +51,6 @@ import { EntityReference } from '../../generated/entity/data/table';
|
||||
import { Team } from '../../generated/entity/teams/team';
|
||||
import { User } from '../../generated/entity/teams/user';
|
||||
import { Paging } from '../../generated/type/paging';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { SearchResponse } from '../../interface/search.interface';
|
||||
import { formatUsersResponse } from '../../utils/APIUtils';
|
||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||
@ -64,8 +62,6 @@ const TeamsPage = () => {
|
||||
const history = useHistory();
|
||||
const { t } = useTranslation();
|
||||
const { getEntityPermissionByFqn } = usePermissionProvider();
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const { fqn } = useParams<{ fqn: string }>();
|
||||
const [currentFqn, setCurrentFqn] = useState<string>('');
|
||||
const [allTeam, setAllTeam] = useState<Team[]>([]);
|
||||
@ -223,7 +219,7 @@ const TeamsPage = () => {
|
||||
getUsers({
|
||||
fields: 'teams,roles',
|
||||
limit: PAGE_SIZE_BASE,
|
||||
team,
|
||||
team: getDecodedFqn(team),
|
||||
...paging,
|
||||
})
|
||||
.then((res) => {
|
||||
@ -285,7 +281,11 @@ const TeamsPage = () => {
|
||||
const fetchTeamBasicDetails = async (name: string, loadPage = false) => {
|
||||
setIsPageLoading(loadPage);
|
||||
try {
|
||||
const data = await getTeamByName(name, ['owner', 'parents'], 'all');
|
||||
const data = await getTeamByName(
|
||||
name,
|
||||
['owner', 'parents', 'profile'],
|
||||
'all'
|
||||
);
|
||||
|
||||
setSelectedTeam(data);
|
||||
if (!isEmpty(data.parents) && data.parents?.[0].name) {
|
||||
@ -621,7 +621,6 @@ const TeamsPage = () => {
|
||||
handleJoinTeamClick={handleJoinTeamClick}
|
||||
handleLeaveTeamClick={handleLeaveTeamClick}
|
||||
handleTeamUsersSearchAction={handleUsersSearchAction}
|
||||
hasAccess={isAuthDisabled || isAdminUser}
|
||||
isDescriptionEditable={isDescriptionEditable}
|
||||
isFetchingAdvancedDetails={isFetchingAdvancedDetails}
|
||||
isFetchingAllTeamAdvancedDetails={isFetchAllTeamAdvancedDetails}
|
||||
|
@ -175,6 +175,12 @@ a[href].link-text-grey,
|
||||
border-color: @warning-color;
|
||||
}
|
||||
|
||||
// Line height
|
||||
|
||||
.line-height-0 {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.line-height-16 {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ pre {
|
||||
}
|
||||
.text-sm {
|
||||
font-size: 14px;
|
||||
line-height: 1rem /* 16px */;
|
||||
}
|
||||
.text-md {
|
||||
font-size: 16px;
|
||||
|
@ -84,7 +84,7 @@
|
||||
@tag-background-color: rgba(0, 0, 0, 0.03);
|
||||
@trigger-btn-hover-bg: #efefef;
|
||||
@text-highlighter: #ffc34e40;
|
||||
|
||||
@team-avatar-bg: #0950c51a;
|
||||
@navbar-height: 64px;
|
||||
@sidebar-width: 60px;
|
||||
|
||||
|
@ -21,6 +21,11 @@ import {
|
||||
} from '../generated/entity/teams/team';
|
||||
import { getEntityIdArray } from './CommonUtils';
|
||||
|
||||
import { SUBSCRIPTION_WEBHOOK } from 'constants/Teams.constants';
|
||||
import { ReactComponent as GChatIcon } from '../assets/svg/gchat.svg';
|
||||
import { ReactComponent as MsTeamsIcon } from '../assets/svg/ms-teams.svg';
|
||||
import { ReactComponent as SlackIcon } from '../assets/svg/slack.svg';
|
||||
|
||||
/**
|
||||
* To get filtered list of non-deleted(active) users
|
||||
* @param users List of users
|
||||
@ -86,6 +91,23 @@ export const getMovedTeamData = (team: Team, parents: string[]): CreateTeam => {
|
||||
} as CreateTeam;
|
||||
};
|
||||
|
||||
/**
|
||||
* To get webhook svg icon
|
||||
* @param item webhook key
|
||||
* @returns SvgComponent
|
||||
*/
|
||||
export const getWebhookIcon = (item: SUBSCRIPTION_WEBHOOK): SvgComponent => {
|
||||
switch (item) {
|
||||
case SUBSCRIPTION_WEBHOOK.SLACK:
|
||||
return SlackIcon;
|
||||
|
||||
case SUBSCRIPTION_WEBHOOK.G_CHAT:
|
||||
return GChatIcon;
|
||||
|
||||
default:
|
||||
return MsTeamsIcon;
|
||||
}
|
||||
};
|
||||
export const getTeamOptionsFromType = (parentType: TeamType) => {
|
||||
switch (parentType) {
|
||||
case TeamType.Organization:
|
||||
|
Loading…
x
Reference in New Issue
Block a user