mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-22 16:08:13 +00:00
Work on manage tab to improve UI (#4366)
This commit is contained in:
parent
1c4bea4613
commit
8c60b22180
@ -1,52 +0,0 @@
|
||||
import React from 'react';
|
||||
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
|
||||
type DeleteWidgetBodyProps = {
|
||||
header: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
isOwner?: boolean;
|
||||
hasPermission: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
const DeleteWidgetBody = ({
|
||||
header,
|
||||
description,
|
||||
buttonText,
|
||||
isOwner,
|
||||
hasPermission,
|
||||
onClick,
|
||||
}: DeleteWidgetBodyProps) => {
|
||||
return (
|
||||
<div className="tw-flex tw-justify-between tw-px-4 tw-py-2">
|
||||
<div data-testid="danger-zone-text">
|
||||
<h4 className="tw-text-base" data-testid="danger-zone-text-title">
|
||||
{header}
|
||||
</h4>
|
||||
<p data-testid="danger-zone-text-para">{description}</p>
|
||||
</div>
|
||||
<NonAdminAction
|
||||
className="tw-self-center"
|
||||
html={<p>{TITLE_FOR_NON_ADMIN_ACTION}</p>}
|
||||
isOwner={isOwner}
|
||||
position="left">
|
||||
<Button
|
||||
className="tw-px-2 tw-py-1 tw-rounded tw-h-auto tw-self-center tw-shadow"
|
||||
data-testid="delete-button"
|
||||
disabled={!hasPermission}
|
||||
size="custom"
|
||||
theme="primary"
|
||||
type="button"
|
||||
variant="outlined"
|
||||
onClick={onClick}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteWidgetBody;
|
@ -11,36 +11,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { AxiosError, AxiosResponse } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import { TableDetail } from 'Models';
|
||||
import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import appState from '../../AppState';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { deleteEntity } from '../../axiosAPIs/miscAPI';
|
||||
import { getCategory } from '../../axiosAPIs/tagAPI';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||
import { ENTITY_DELETE_STATE } from '../../constants/entity.constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { getOwnerList } from '../../utils/ManageUtils';
|
||||
import SVGIcons from '../../utils/SvgUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import CardListItem from '../card-list/CardListItem/CardWithListItems';
|
||||
import { CardWithListItems } from '../card-list/CardListItem/CardWithListItems.interface';
|
||||
import DeleteWidget from '../common/DeleteWidget/DeleteWidget';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import ToggleSwitchV1 from '../common/toggle-switch/ToggleSwitchV1';
|
||||
import DropDownList from '../dropdown/DropDownList';
|
||||
import OwnerWidget from '../common/OwnerWidget/OwnerWidget';
|
||||
import Loader from '../Loader/Loader';
|
||||
import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal';
|
||||
import DeleteWidgetBody from './DeleteWidgetBody';
|
||||
import { ManageProps, Status } from './ManageTab.interface';
|
||||
|
||||
const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
@ -58,9 +49,9 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
allowSoftDelete,
|
||||
isRecursiveDelete,
|
||||
deletEntityMessage,
|
||||
manageSectionType,
|
||||
handleIsJoinable,
|
||||
}: ManageProps) => {
|
||||
const history = useHistory();
|
||||
const { userPermissions, isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
|
||||
@ -74,17 +65,11 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
const [listOwners, setListOwners] = useState(getOwnerList());
|
||||
const [owner, setOwner] = useState(currentUser);
|
||||
const [isLoadingTierData, setIsLoadingTierData] = useState<boolean>(false);
|
||||
const [entityDeleteState, setEntityDeleteState] =
|
||||
useState<typeof ENTITY_DELETE_STATE>(ENTITY_DELETE_STATE);
|
||||
|
||||
const getOwnerById = (): string => {
|
||||
return listOwners.find((item) => item.value === owner)?.name || '';
|
||||
};
|
||||
|
||||
const getOwnerGroup = () => {
|
||||
return allowTeamOwner ? ['Teams', 'Users'] : ['Users'];
|
||||
};
|
||||
|
||||
const setInitialOwnerLoadingState = () => {
|
||||
setStatusOwner('initial');
|
||||
};
|
||||
@ -164,115 +149,20 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
setActiveTier(cardId);
|
||||
};
|
||||
|
||||
const handleOnEntityDelete = (softDelete = false) => {
|
||||
setEntityDeleteState((prev) => ({ ...prev, state: true, softDelete }));
|
||||
};
|
||||
|
||||
const handleOnEntityDeleteCancel = () => {
|
||||
setEntityDeleteState(ENTITY_DELETE_STATE);
|
||||
};
|
||||
|
||||
const prepareEntityType = () => {
|
||||
const services = [
|
||||
EntityType.DASHBOARD_SERVICE,
|
||||
EntityType.DATABASE_SERVICE,
|
||||
EntityType.MESSAGING_SERVICE,
|
||||
EntityType.PIPELINE_SERVICE,
|
||||
];
|
||||
|
||||
if (services.includes((entityType || '') as EntityType)) {
|
||||
return `services/${entityType}s`;
|
||||
} else {
|
||||
return `${entityType}s`;
|
||||
}
|
||||
};
|
||||
|
||||
const prepareDeleteMessage = () => {
|
||||
return `Once you delete this ${entityType}, it will be removed permanently`;
|
||||
};
|
||||
|
||||
const handleOnEntityDeleteConfirm = () => {
|
||||
setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
|
||||
deleteEntity(
|
||||
prepareEntityType(),
|
||||
entityId,
|
||||
isRecursiveDelete,
|
||||
allowSoftDelete
|
||||
)
|
||||
.then((res: AxiosResponse) => {
|
||||
if (res.status === 200) {
|
||||
setTimeout(() => {
|
||||
handleOnEntityDeleteCancel();
|
||||
showSuccessToast(
|
||||
jsonData['api-success-messages']['delete-entity-success']
|
||||
);
|
||||
setTimeout(() => {
|
||||
history.push('/');
|
||||
}, 500);
|
||||
}, 1000);
|
||||
} else {
|
||||
showErrorToast(
|
||||
jsonData['api-error-messages']['unexpected-server-response']
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
showErrorToast(
|
||||
error,
|
||||
jsonData['api-error-messages']['delete-entity-error']
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
handleOnEntityDeleteCancel();
|
||||
});
|
||||
};
|
||||
|
||||
const getDeleteModal = () => {
|
||||
if (allowDelete && entityDeleteState.state) {
|
||||
return (
|
||||
<EntityDeleteModal
|
||||
bodyText={deletEntityMessage || prepareDeleteMessage()}
|
||||
entityName={entityName as string}
|
||||
entityType={entityType as string}
|
||||
loadingState={entityDeleteState.loading}
|
||||
softDelete={entityDeleteState.softDelete}
|
||||
onCancel={handleOnEntityDeleteCancel}
|
||||
onConfirm={handleOnEntityDeleteConfirm}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getDeleteEntityWidget = () => {
|
||||
return allowDelete && entityId && entityName && entityType ? (
|
||||
<div className="tw-mt-1" data-testid="danger-zone">
|
||||
<hr className="tw-border-main tw-mb-4" />
|
||||
<div className="tw-border tw-border-error tw-rounded tw-mt-3 tw-shadow">
|
||||
{allowSoftDelete && (
|
||||
<div className="tw-border-b tw-border-error">
|
||||
<DeleteWidgetBody
|
||||
buttonText={`Soft delete this ${entityType}`}
|
||||
description={prepareDeleteMessage()}
|
||||
<DeleteWidget
|
||||
allowSoftDelete={allowSoftDelete}
|
||||
deletEntityMessage={deletEntityMessage}
|
||||
entityId={entityId}
|
||||
entityName={entityName}
|
||||
entityType={entityType}
|
||||
hasPermission={isAdminUser || isAuthDisabled}
|
||||
header={`Soft delete ${entityType} ${entityName}`}
|
||||
isOwner={isAdminUser}
|
||||
onClick={() => handleOnEntityDelete(true)}
|
||||
isAdminUser={isAdminUser}
|
||||
isRecursiveDelete={isRecursiveDelete}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DeleteWidgetBody
|
||||
buttonText={`Delete this ${entityType}`}
|
||||
description={prepareDeleteMessage()}
|
||||
hasPermission={isAdminUser || isAuthDisabled}
|
||||
header={`Delete ${entityType} ${entityName}`}
|
||||
isOwner={isAdminUser}
|
||||
onClick={handleOnEntityDelete}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@ -311,29 +201,13 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const getJoinableWidget = () => {
|
||||
const isActionAllowed =
|
||||
const isJoinableActionAllowed = () => {
|
||||
return (
|
||||
isAdminUser ||
|
||||
isAuthDisabled ||
|
||||
userPermissions[Operation.UpdateTeam] ||
|
||||
!hasEditAccess;
|
||||
|
||||
const joinableSwitch =
|
||||
isActionAllowed && !isUndefined(teamJoinable) ? (
|
||||
<div className="tw-flex">
|
||||
<label htmlFor="join-team">Open to join</label>
|
||||
<ToggleSwitchV1
|
||||
checked={teamJoinable}
|
||||
handleCheck={() => {
|
||||
handleIsJoinable?.(!teamJoinable);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return !isUndefined(isJoinable) ? (
|
||||
<div className="tw-mt-3 tw-mb-1">{joinableSwitch}</div>
|
||||
) : null;
|
||||
!hasEditAccess
|
||||
);
|
||||
};
|
||||
|
||||
const ownerName = getOwnerById();
|
||||
@ -372,23 +246,6 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const getOwnerUpdateLoader = () => {
|
||||
return (
|
||||
<span className="tw-ml-4">
|
||||
{statusOwner === 'waiting' ? (
|
||||
<Loader
|
||||
className="tw-inline-block"
|
||||
size="small"
|
||||
style={{ marginBottom: '-4px' }}
|
||||
type="default"
|
||||
/>
|
||||
) : statusOwner === 'success' ? (
|
||||
<FontAwesomeIcon icon="check" />
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!hideTier) {
|
||||
getTierData();
|
||||
@ -434,77 +291,33 @@ const ManageTab: FunctionComponent<ManageProps> = ({
|
||||
className="tw-max-w-3xl tw-mx-auto"
|
||||
data-testid="manage-tab"
|
||||
id="manageTabDetails">
|
||||
<p className="tw-text-base tw-font-medium">
|
||||
Manage {manageSectionType ? manageSectionType : 'Section'}
|
||||
</p>
|
||||
<div
|
||||
className={classNames('tw-mt-2 tw-pb-4', {
|
||||
'tw-border-b tw-border-separator tw-mb-4': !hideTier,
|
||||
})}>
|
||||
<div>
|
||||
<span className="tw-mr-2">Owner:</span>
|
||||
<span className="tw-relative">
|
||||
<NonAdminAction
|
||||
html={
|
||||
<Fragment>
|
||||
<p>You do not have permissions to update the owner.</p>
|
||||
</Fragment>
|
||||
<OwnerWidget
|
||||
allowTeamOwner={allowTeamOwner}
|
||||
handleIsJoinable={handleIsJoinable}
|
||||
handleOwnerSelection={handleOwnerSelection}
|
||||
handleSelectOwnerDropdown={() =>
|
||||
setListVisible((visible) => !visible)
|
||||
}
|
||||
isOwner={hasEditAccess}
|
||||
permission={Operation.UpdateOwner}
|
||||
position="left">
|
||||
<Button
|
||||
className={classNames('tw-underline', {
|
||||
'tw-opacity-40':
|
||||
!userPermissions[Operation.UpdateOwner] &&
|
||||
!isAuthDisabled &&
|
||||
!hasEditAccess,
|
||||
})}
|
||||
data-testid="owner-dropdown"
|
||||
disabled={
|
||||
!userPermissions[Operation.UpdateOwner] &&
|
||||
!isAuthDisabled &&
|
||||
!hasEditAccess
|
||||
}
|
||||
size="custom"
|
||||
theme="primary"
|
||||
variant="link"
|
||||
onClick={() => setListVisible((visible) => !visible)}>
|
||||
{ownerName ? (
|
||||
<span
|
||||
className={classNames('tw-truncate', {
|
||||
'tw-w-52': ownerName.length > 32,
|
||||
})}
|
||||
title={ownerName}>
|
||||
{ownerName}
|
||||
</span>
|
||||
) : (
|
||||
'Select Owner'
|
||||
)}
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
className="tw-ml-1"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="12px"
|
||||
hasEditAccess={hasEditAccess}
|
||||
isAuthDisabled={isAuthDisabled}
|
||||
isJoinableActionAllowed={isJoinableActionAllowed()}
|
||||
listOwners={listOwners}
|
||||
listVisible={listVisible}
|
||||
owner={owner}
|
||||
ownerName={ownerName}
|
||||
statusOwner={statusOwner}
|
||||
teamJoinable={teamJoinable}
|
||||
/>
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
{listVisible && (
|
||||
<DropDownList
|
||||
showSearchBar
|
||||
dropDownList={listOwners}
|
||||
groupType="tab"
|
||||
listGroups={getOwnerGroup()}
|
||||
value={owner}
|
||||
onSelect={handleOwnerSelection}
|
||||
/>
|
||||
)}
|
||||
{getOwnerUpdateLoader()}
|
||||
</span>
|
||||
</div>
|
||||
{getJoinableWidget()}
|
||||
</div>
|
||||
{getTierCards()}
|
||||
{getDeleteEntityWidget()}
|
||||
{getDeleteModal()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ import { TableDetail } from 'Models';
|
||||
export interface ManageProps {
|
||||
currentTier?: string;
|
||||
currentUser?: string;
|
||||
manageSectionType?: string;
|
||||
hideTier?: boolean;
|
||||
isJoinable?: boolean;
|
||||
allowSoftDelete?: boolean;
|
||||
|
@ -45,6 +45,10 @@ jest.mock('../common/toggle-switch/ToggleSwitchV1', () => {
|
||||
return jest.fn().mockImplementation(() => <p>ToggleSwitchV1.Component</p>);
|
||||
});
|
||||
|
||||
jest.mock('../common/DeleteWidget/DeleteWidget', () => {
|
||||
return jest.fn().mockImplementation(() => <p>DeleteWidget.Component</p>);
|
||||
});
|
||||
|
||||
const mockTierData = {
|
||||
children: [
|
||||
{
|
||||
@ -121,7 +125,9 @@ describe('Test Manage tab Component', () => {
|
||||
);
|
||||
|
||||
const dangerZone = await findByTestId(container, 'danger-zone');
|
||||
const DeleteWidget = await findByText(container, 'DeleteWidget.Component');
|
||||
|
||||
expect(dangerZone).toBeInTheDocument();
|
||||
expect(DeleteWidget).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ const EntityDeleteModal: FC<Prop> = ({
|
||||
data-testid="confirmation-text-input"
|
||||
disabled={loadingState === 'waiting'}
|
||||
name="entityName"
|
||||
placeholder={`${entityType}/${entityName}`}
|
||||
placeholder="DELETE"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={handleOnChange}
|
||||
|
@ -493,7 +493,7 @@ const TeamDetails = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="tw-h-full tw-flex tw-flex-col tw-flex-grow">
|
||||
{teams.length && currentTeam ? (
|
||||
<Fragment>
|
||||
<div
|
||||
@ -568,11 +568,12 @@ const TeamDetails = ({
|
||||
<div className="tw-flex tw-flex-col tw-flex-grow">
|
||||
<TabsPane
|
||||
activeTab={currentTab}
|
||||
className="tw-px-6"
|
||||
setActiveTab={(tab) => setCurrentTab(tab)}
|
||||
tabs={tabs}
|
||||
/>
|
||||
|
||||
<div className="tw-flex-grow tw-pt-4">
|
||||
<div className="tw-flex-grow tw-flex tw-flex-col tw-pt-4">
|
||||
{currentTab === 1 && getUserCards()}
|
||||
|
||||
{currentTab === 2 && getDatasetCards()}
|
||||
@ -580,7 +581,7 @@ const TeamDetails = ({
|
||||
{currentTab === 3 && getDefaultRoles()}
|
||||
|
||||
{currentTab === 4 && (
|
||||
<div>
|
||||
<div className="tw-bg-white tw-shadow-md tw-py-4 tw-flex-grow">
|
||||
<ManageTab
|
||||
allowDelete
|
||||
allowSoftDelete
|
||||
@ -593,6 +594,7 @@ const TeamDetails = ({
|
||||
entityType="team"
|
||||
handleIsJoinable={handleOpenToJoinToggle}
|
||||
isJoinable={currentTeam.isJoinable}
|
||||
manageSectionType="Team"
|
||||
onSave={handleManageSave}
|
||||
/>
|
||||
</div>
|
||||
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2021 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 { AxiosError, AxiosResponse } from 'axios';
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { deleteEntity } from '../../../axiosAPIs/miscAPI';
|
||||
import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import jsonData from '../../../jsons/en';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import EntityDeleteModal from '../../Modals/EntityDeleteModal/EntityDeleteModal';
|
||||
import DeleteWidgetBody from './DeleteWidgetBody';
|
||||
|
||||
interface DeleteSectionProps {
|
||||
allowSoftDelete?: boolean;
|
||||
entityName: string;
|
||||
entityType: string;
|
||||
deletEntityMessage?: string;
|
||||
hasPermission: boolean;
|
||||
isAdminUser?: boolean;
|
||||
entityId: string;
|
||||
isRecursiveDelete?: boolean;
|
||||
}
|
||||
|
||||
const DeleteWidget = ({
|
||||
allowSoftDelete,
|
||||
entityName,
|
||||
entityType,
|
||||
hasPermission,
|
||||
isAdminUser,
|
||||
deletEntityMessage,
|
||||
entityId,
|
||||
isRecursiveDelete,
|
||||
}: DeleteSectionProps) => {
|
||||
const history = useHistory();
|
||||
const [entityDeleteState, setEntityDeleteState] =
|
||||
useState<typeof ENTITY_DELETE_STATE>(ENTITY_DELETE_STATE);
|
||||
|
||||
const prepareDeleteMessage = (softDelete = false) => {
|
||||
const softDeleteText = `Soft deleting will deactivate the ${entityName}. This will disable any discovery, read or write operations on ${entityName}`;
|
||||
const hardDeleteText = `Once you delete this ${entityType}, it will be removed permanently`;
|
||||
|
||||
return softDelete ? softDeleteText : hardDeleteText;
|
||||
};
|
||||
|
||||
const handleOnEntityDelete = (softDelete = false) => {
|
||||
setEntityDeleteState((prev) => ({ ...prev, state: true, softDelete }));
|
||||
};
|
||||
|
||||
const handleOnEntityDeleteCancel = () => {
|
||||
setEntityDeleteState(ENTITY_DELETE_STATE);
|
||||
};
|
||||
|
||||
const prepareEntityType = () => {
|
||||
const services = [
|
||||
EntityType.DASHBOARD_SERVICE,
|
||||
EntityType.DATABASE_SERVICE,
|
||||
EntityType.MESSAGING_SERVICE,
|
||||
EntityType.PIPELINE_SERVICE,
|
||||
];
|
||||
|
||||
if (services.includes((entityType || '') as EntityType)) {
|
||||
return `services/${entityType}s`;
|
||||
} else {
|
||||
return `${entityType}s`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnEntityDeleteConfirm = () => {
|
||||
setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
|
||||
deleteEntity(
|
||||
prepareEntityType(),
|
||||
entityId,
|
||||
isRecursiveDelete,
|
||||
entityDeleteState.softDelete
|
||||
)
|
||||
.then((res: AxiosResponse) => {
|
||||
if (res.status === 200) {
|
||||
setTimeout(() => {
|
||||
handleOnEntityDeleteCancel();
|
||||
showSuccessToast(
|
||||
jsonData['api-success-messages']['delete-entity-success']
|
||||
);
|
||||
setTimeout(() => {
|
||||
history.push('/');
|
||||
}, 500);
|
||||
}, 1000);
|
||||
} else {
|
||||
showErrorToast(
|
||||
jsonData['api-error-messages']['unexpected-server-response']
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
showErrorToast(
|
||||
error,
|
||||
jsonData['api-error-messages']['delete-entity-error']
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
handleOnEntityDeleteCancel();
|
||||
});
|
||||
};
|
||||
|
||||
const getDeleteModal = () => {
|
||||
if (entityDeleteState.state) {
|
||||
return (
|
||||
<EntityDeleteModal
|
||||
bodyText={
|
||||
deletEntityMessage ||
|
||||
prepareDeleteMessage(entityDeleteState.softDelete)
|
||||
}
|
||||
entityName={entityName}
|
||||
entityType={entityType}
|
||||
loadingState={entityDeleteState.loading}
|
||||
softDelete={entityDeleteState.softDelete}
|
||||
onCancel={handleOnEntityDeleteCancel}
|
||||
onConfirm={handleOnEntityDeleteConfirm}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<p className="tw-text-base tw-font-medium">Delete section</p>
|
||||
<div className="tw-mt-1 tw-bg-white" data-testid="danger-zone">
|
||||
<div className="tw-border tw-border-error tw-rounded tw-mt-3 tw-shadow">
|
||||
{allowSoftDelete && (
|
||||
<div className="tw-border-b">
|
||||
<DeleteWidgetBody
|
||||
buttonText="Soft delete"
|
||||
description={prepareDeleteMessage(true)}
|
||||
hasPermission={hasPermission}
|
||||
header={`Soft delete ${entityType} ${entityName}`}
|
||||
isOwner={isAdminUser}
|
||||
onClick={() => handleOnEntityDelete(true)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DeleteWidgetBody
|
||||
buttonText="Delete"
|
||||
description={prepareDeleteMessage()}
|
||||
hasPermission={hasPermission}
|
||||
header={`Delete ${entityType} ${entityName}`}
|
||||
isOwner={isAdminUser}
|
||||
onClick={() => handleOnEntityDelete(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{getDeleteModal()}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteWidget;
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021 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 React from 'react';
|
||||
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../../constants/constants';
|
||||
import NonAdminAction from '../non-admin-action/NonAdminAction';
|
||||
|
||||
type DeleteWidgetBodyProps = {
|
||||
header: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
isOwner?: boolean;
|
||||
hasPermission: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
const DeleteWidgetBody = ({
|
||||
header,
|
||||
description,
|
||||
buttonText,
|
||||
isOwner,
|
||||
hasPermission,
|
||||
onClick,
|
||||
}: DeleteWidgetBodyProps) => {
|
||||
return (
|
||||
<div className="tw-flex tw-justify-between tw-px-5 tw-py-3">
|
||||
<div className="tw-w-10/12" data-testid="danger-zone-text">
|
||||
<p
|
||||
className="tw-text-sm tw-mb-1 tw-font-medium"
|
||||
data-testid="danger-zone-text-title">
|
||||
{header}
|
||||
</p>
|
||||
<p
|
||||
className="tw-text-grey-muted tw-text-xs"
|
||||
data-testid="danger-zone-text-para">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<NonAdminAction
|
||||
className="tw-self-center"
|
||||
html={<p>{TITLE_FOR_NON_ADMIN_ACTION}</p>}
|
||||
isOwner={isOwner}
|
||||
position="left">
|
||||
<button
|
||||
className="tw-px-3 tw-py-1 tw-rounded tw-h-auto tw-self-center tw-font-medium tw-delete-outline-button "
|
||||
data-testid="delete-button"
|
||||
disabled={!hasPermission}
|
||||
onClick={onClick}>
|
||||
{buttonText}
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteWidgetBody;
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2021 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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import classNames from 'classnames';
|
||||
import { isUndefined } from 'lodash';
|
||||
import React, { Fragment } from 'react';
|
||||
import { Operation } from '../../../generated/entity/policies/policy';
|
||||
import { useAuth } from '../../../hooks/authHooks';
|
||||
import { Button } from '../../buttons/Button/Button';
|
||||
import DropDownList from '../../dropdown/DropDownList';
|
||||
import Loader from '../../Loader/Loader';
|
||||
import { Status } from '../../ManageTab/ManageTab.interface';
|
||||
import NonAdminAction from '../non-admin-action/NonAdminAction';
|
||||
import ToggleSwitchV1 from '../toggle-switch/ToggleSwitchV1';
|
||||
|
||||
interface OwnerWidgetProps {
|
||||
isJoinableActionAllowed: boolean;
|
||||
hasEditAccess: boolean;
|
||||
isAuthDisabled: boolean;
|
||||
listVisible: boolean;
|
||||
teamJoinable?: boolean;
|
||||
allowTeamOwner?: boolean;
|
||||
ownerName: string;
|
||||
statusOwner: Status;
|
||||
owner: string;
|
||||
listOwners: {
|
||||
name: string;
|
||||
value: string;
|
||||
group: string;
|
||||
type: string;
|
||||
}[];
|
||||
handleIsJoinable?: (bool: boolean) => void;
|
||||
handleSelectOwnerDropdown: () => void;
|
||||
handleOwnerSelection: (
|
||||
_e: React.MouseEvent<HTMLElement, MouseEvent>,
|
||||
value?: string | undefined
|
||||
) => void;
|
||||
}
|
||||
|
||||
const OwnerWidget = ({
|
||||
isJoinableActionAllowed,
|
||||
teamJoinable,
|
||||
isAuthDisabled,
|
||||
hasEditAccess,
|
||||
ownerName,
|
||||
listVisible,
|
||||
owner,
|
||||
allowTeamOwner,
|
||||
statusOwner,
|
||||
listOwners,
|
||||
handleIsJoinable,
|
||||
handleSelectOwnerDropdown,
|
||||
handleOwnerSelection,
|
||||
}: OwnerWidgetProps) => {
|
||||
const { userPermissions } = useAuth();
|
||||
|
||||
const getOwnerGroup = () => {
|
||||
return allowTeamOwner ? ['Teams', 'Users'] : ['Users'];
|
||||
};
|
||||
|
||||
const getOwnerUpdateLoader = () => {
|
||||
switch (statusOwner) {
|
||||
case 'waiting':
|
||||
return (
|
||||
<Loader
|
||||
className="tw-inline-block tw-ml-2"
|
||||
size="small"
|
||||
style={{ marginBottom: '-4px' }}
|
||||
type="default"
|
||||
/>
|
||||
);
|
||||
|
||||
case 'success':
|
||||
return <FontAwesomeIcon className="tw-ml-2" icon="check" />;
|
||||
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="tw-mt-1 tw-bg-white">
|
||||
<div className="tw-border tw-border-main tw-rounded tw-mt-3 tw-shadow">
|
||||
<div className="tw-flex tw-justify-between tw-items-center tw-px-5 tw-py-3">
|
||||
<div className="tw-w-10/12">
|
||||
<p className="tw-text-sm tw-mb-1 tw-font-medium">Owner</p>
|
||||
<p className="tw-text-grey-muted tw-text-xs">
|
||||
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
|
||||
Necessitatibus, sint.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span className="tw-relative">
|
||||
<NonAdminAction
|
||||
html={
|
||||
<Fragment>
|
||||
<p>You do not have permissions to update the owner.</p>
|
||||
</Fragment>
|
||||
}
|
||||
isOwner={hasEditAccess}
|
||||
permission={Operation.UpdateOwner}
|
||||
position="left">
|
||||
<Button
|
||||
className={classNames('tw-underline', {
|
||||
'tw-opacity-40':
|
||||
!userPermissions[Operation.UpdateOwner] &&
|
||||
!isAuthDisabled &&
|
||||
!hasEditAccess,
|
||||
})}
|
||||
data-testid="owner-dropdown"
|
||||
disabled={
|
||||
!userPermissions[Operation.UpdateOwner] &&
|
||||
!isAuthDisabled &&
|
||||
!hasEditAccess
|
||||
}
|
||||
size="custom"
|
||||
theme="primary"
|
||||
variant="link"
|
||||
onClick={handleSelectOwnerDropdown}>
|
||||
{ownerName ? (
|
||||
<span
|
||||
className={classNames('tw-truncate', {
|
||||
'tw-w-52': ownerName.length > 32,
|
||||
})}
|
||||
title={ownerName}>
|
||||
{ownerName}
|
||||
</span>
|
||||
) : (
|
||||
'Add Owner'
|
||||
)}
|
||||
{getOwnerUpdateLoader()}
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
{listVisible && (
|
||||
<DropDownList
|
||||
horzPosRight
|
||||
showSearchBar
|
||||
dropDownList={listOwners}
|
||||
groupType="tab"
|
||||
listGroups={getOwnerGroup()}
|
||||
value={owner}
|
||||
onSelect={handleOwnerSelection}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{isJoinableActionAllowed && !isUndefined(teamJoinable) && (
|
||||
<div className="tw-flex tw-justify-between tw-px-5 tw-py-3 tw-border-t">
|
||||
<div className="tw-w-10/12">
|
||||
<p className="tw-text-sm tw-mb-1 tw-font-medium">
|
||||
Open to join
|
||||
</p>
|
||||
<p className="tw-text-grey-muted tw-text-xs">
|
||||
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
|
||||
Necessitatibus, sint.
|
||||
</p>
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center">
|
||||
<ToggleSwitchV1
|
||||
checked={teamJoinable}
|
||||
handleCheck={() => {
|
||||
handleIsJoinable?.(!teamJoinable);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default OwnerWidget;
|
@ -457,4 +457,11 @@
|
||||
.Toastify__toast-body {
|
||||
@apply tw-items-start;
|
||||
}
|
||||
|
||||
/* delete button style */
|
||||
|
||||
.tw-delete-outline-button {
|
||||
@apply tw-border-gray-300 tw-border tw-text-error focus:tw-outline-none focus:tw-bg-transparent hover:tw-border-error focus:tw-border-error
|
||||
hover:tw-text-error focus:tw-text-error focus:tw-ring focus:tw-ring-error;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user