Revert "Add alert bar component (#17230)" (#18812)

This reverts commit e32f9cf8623cbe96cce8d0cc6c063b489c3f9590.
This commit is contained in:
Chirag Madlani 2024-11-27 16:43:33 +05:30 committed by GitHub
parent e1f717ad08
commit 3ff11d8518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 574 additions and 899 deletions

View File

@ -120,6 +120,7 @@
"react-quill-new": "^3.3.2",
"react-reflex": "^4.0.12",
"react-router-dom": "^5.2.0",
"react-toastify": "^8.2.0",
"reactflow": "^11.10.2",
"reactjs-localstorage": "^1.0.1",
"recharts": "2.10.3",

View File

@ -363,15 +363,13 @@ test.describe('Activity feed', () => {
await page.getByText('OK').click();
await resolveTask;
await toastNotification(page, /Task resolved successfully/, 'success');
await toastNotification(page, /Task resolved successfully/);
// Task 1 - Resolved the task
const resolveTask2 = page.waitForResponse('/api/v1/feed/tasks/*/resolve');
await page.getByText('Accept Suggestion').click();
await resolveTask2;
await toastNotification(page, /Task resolved successfully/, 'success');
await toastNotification(page, /Task resolved successfully/);
await checkTaskCount(page, 0, 2);
});
@ -414,11 +412,7 @@ test.describe('Activity feed', () => {
await page.getByRole('menuitem', { name: 'close' }).click();
await toastNotification(
page,
'Task cannot be closed without a comment.',
'error'
);
await toastNotification(page, 'Task cannot be closed without a comment.');
// Close the task from the Button.Group, with comment is added.
await page.fill(
@ -677,8 +671,7 @@ base.describe('Activity feed with Data Consumer User', () => {
// await toastNotification(page1, 'Task closed successfully.');
await toastNotification(
page1,
'An exception with message [Cannot invoke "java.util.List.stream()" because "owners" is null] was thrown while processing request.',
'error'
'An exception with message [Cannot invoke "java.util.List.stream()" because "owners" is null] was thrown while processing request.'
);
// TODO: Ashish - Enable them once issue is resolved from Backend https://github.com/open-metadata/OpenMetadata/issues/17059
@ -991,8 +984,7 @@ base.describe('Activity feed with Data Consumer User', () => {
await toastNotification(
page2,
// eslint-disable-next-line max-len
`Principal: CatalogPrincipal{name='${viewAllUser.responseData.name}'} operation EditDescription denied by role ${viewAllRoles.responseData.name}, policy ${viewAllPolicy.responseData.name}, rule editNotAllowed`,
'error'
`Principal: CatalogPrincipal{name='${viewAllUser.responseData.name}'} operation EditDescription denied by role ${viewAllRoles.responseData.name}, policy ${viewAllPolicy.responseData.name}, rule editNotAllowed`
);
await afterActionUser2();

View File

@ -118,8 +118,7 @@ test.describe('Teams drag and drop should work properly', () => {
await dragAndDropElement(page, team, teamNameGroup);
await toastNotification(
page,
`You cannot move to this team as Team Type ${TEAM_TYPE_BY_NAME[team]} can't be Group children`,
'error'
`You cannot move to this team as Team Type ${TEAM_TYPE_BY_NAME[team]} can't be Group children`
);
}
});
@ -133,8 +132,7 @@ test.describe('Teams drag and drop should work properly', () => {
await dragAndDropElement(page, team, teamNameDepartment);
await toastNotification(
page,
`You cannot move to this team as Team Type ${TEAM_TYPE_BY_NAME[team]} can't be Department children`,
'error'
`You cannot move to this team as Team Type ${TEAM_TYPE_BY_NAME[team]} can't be Department children`
);
}
});
@ -145,8 +143,7 @@ test.describe('Teams drag and drop should work properly', () => {
await dragAndDropElement(page, teamNameBusiness, teamNameDivision);
await toastNotification(
page,
"You cannot move to this team as Team Type BusinessUnit can't be Division children",
'error'
"You cannot move to this team as Team Type BusinessUnit can't be Division children"
);
});

View File

@ -12,12 +12,7 @@
*/
import { expect, test } from '@playwright/test';
import { GlobalSettingOptions } from '../../constant/settings';
import {
descriptionBox,
redirectToHomePage,
toastNotification,
uuid,
} from '../../utils/common';
import { descriptionBox, redirectToHomePage, uuid } from '../../utils/common';
import { settingClick } from '../../utils/sidebar';
const apiServiceConfig = {
@ -94,6 +89,10 @@ test.describe('API service', () => {
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
});
});

View File

@ -84,11 +84,7 @@ test.describe('Domains', () => {
await deleteRes;
await expect(
page.getByText(
`"${
domain.data.displayName ?? domain.data.name
}" deleted successfully!`
)
page.getByText(`"${domain.data.displayName}" deleted`)
).toBeVisible();
});

View File

@ -378,8 +378,7 @@ test.describe('Glossary tests', () => {
await toastNotification(
page,
/mutually exclusive and can't be assigned together/,
'error'
/mutually exclusive and can't be assigned together/
);
// Add non mutually exclusive tags

View File

@ -25,11 +25,7 @@ import {
UPDATED_RULE_NAME,
} from '../../constant/permission';
import { GlobalSettingOptions } from '../../constant/settings';
import {
descriptionBox,
redirectToHomePage,
toastNotification,
} from '../../utils/common';
import { descriptionBox, redirectToHomePage } from '../../utils/common';
import { validateFormNameFieldInput } from '../../utils/form';
import { settingClick } from '../../utils/sidebar';
@ -262,10 +258,8 @@ test.describe('Policy page should work properly', () => {
await page.locator('[data-testid="delete-rule"]').click();
// Validate the error message
await toastNotification(
page,
ERROR_MESSAGE_VALIDATION.lastRuleCannotBeRemoved,
'error'
await expect(page.locator('.Toastify__toast-body')).toContainText(
ERROR_MESSAGE_VALIDATION.lastRuleCannotBeRemoved
);
});

View File

@ -12,12 +12,7 @@
*/
import { expect, test } from '@playwright/test';
import { GlobalSettingOptions } from '../../constant/settings';
import {
descriptionBox,
redirectToHomePage,
toastNotification,
uuid,
} from '../../utils/common';
import { descriptionBox, redirectToHomePage, uuid } from '../../utils/common';
import { removePolicyFromRole } from '../../utils/roles';
import { settingClick } from '../../utils/sidebar';
@ -206,12 +201,9 @@ test('Roles page should work properly', async ({ page }) => {
// Removing the last policy and validating the error message
await removePolicyFromRole(page, policies.dataConsumerPolicy, roleName);
await toastNotification(
page,
errorMessageValidation.lastPolicyCannotBeRemoved,
'error'
await expect(page.locator('.Toastify__toast-body')).toContainText(
errorMessageValidation.lastPolicyCannotBeRemoved
);
await expect(page.locator('.ant-table-row')).toContainText(
policies.dataConsumerPolicy
);

View File

@ -16,7 +16,6 @@ import {
descriptionBox,
getApiContext,
redirectToHomePage,
toastNotification,
} from '../../utils/common';
import { deleteTestCase, visitDataQualityTab } from '../../utils/testCases';
@ -134,7 +133,11 @@ test('Table difference test case', async ({ page }) => {
await page.getByTitle('name', { exact: true }).click();
await page.getByRole('button', { name: 'Submit' }).click();
await toastNotification(page, 'Test case updated successfully.');
await expect(page.getByRole('alert')).toContainText(
'Test case updated successfully.'
);
await page.getByLabel('close', { exact: true }).click();
});
await test.step('Delete', async () => {
@ -230,7 +233,11 @@ test('Custom SQL Query', async ({ page }) => {
await page.getByPlaceholder('Enter a Threshold').fill('244');
await page.getByRole('button', { name: 'Submit' }).click();
await toastNotification(page, 'Test case updated successfully.');
await expect(page.getByRole('alert')).toContainText(
'Test case updated successfully.'
);
await page.getByLabel('close', { exact: true }).click();
});
await test.step('Delete', async () => {
@ -327,7 +334,11 @@ test('Column Values To Be Not Null', async ({ page }) => {
await page.keyboard.type(' update');
await page.getByRole('button', { name: 'Submit' }).click();
await toastNotification(page, 'Test case updated successfully.');
await expect(page.getByRole('alert')).toContainText(
'Test case updated successfully.'
);
await page.getByLabel('close', { exact: true }).click();
});
await test.step('Delete', async () => {

View File

@ -22,11 +22,7 @@ import { SearchIndexClass } from '../../support/entity/SearchIndexClass';
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
import { TableClass } from '../../support/entity/TableClass';
import { TopicClass } from '../../support/entity/TopicClass';
import {
createNewPage,
redirectToHomePage,
toastNotification,
} from '../../utils/common';
import { createNewPage, redirectToHomePage } from '../../utils/common';
import { addMultiOwner, assignTier } from '../../utils/entity';
const entities = [
@ -237,7 +233,11 @@ entities.forEach((EntityClass) => {
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
await page.reload();

View File

@ -23,11 +23,7 @@ import { MlmodelServiceClass } from '../../support/entity/service/MlmodelService
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
import {
createNewPage,
redirectToHomePage,
toastNotification,
} from '../../utils/common';
import { createNewPage, redirectToHomePage } from '../../utils/common';
import { addMultiOwner, assignTier } from '../../utils/entity';
const entities = [
@ -202,7 +198,11 @@ entities.forEach((EntityClass) => {
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
await page.reload();

View File

@ -113,14 +113,15 @@ export const getEntityTypeSearchIndexMapping = (entityType: string) => {
export const toastNotification = async (
page: Page,
message: string | RegExp,
type: 'info' | 'success' | 'warning' | 'error' = 'success'
message: string | RegExp
) => {
await expect(page.locator(`.alert-container.${type}`)).toHaveText(message);
await expect(page.getByRole('alert').first()).toHaveText(message);
await expect(page.locator('.ant-alert-icon')).toBeVisible();
await expect(page.locator('.alert-container button')).toBeVisible();
await page
.locator('.Toastify__toast')
.getByLabel('close', { exact: true })
.first()
.click();
};
export const clickOutside = async (page: Page) => {

View File

@ -16,7 +16,6 @@ import {
NAME_MAX_LENGTH_VALIDATION_ERROR,
NAME_VALIDATION_ERROR,
} from '../constant/common';
import { toastNotification } from './common';
type CustomMetricDetails = {
page: Page;
@ -119,11 +118,12 @@ export const createCustomMetric = async ({
await page.click('[data-testid="submit-button"]');
await createMetricResponse;
await toastNotification(
page,
await expect(page.locator('.Toastify__toast-body')).toHaveText(
new RegExp(`${metric.name} created successfully.`)
);
await page.locator('.Toastify__close-button').click();
// verify the created custom metric
await expect(page).toHaveURL(/profiler/);
await expect(

View File

@ -24,7 +24,7 @@ import {
} from '../constant/delete';
import { ES_RESERVED_CHARACTERS } from '../constant/entity';
import { EntityTypeEndpoint } from '../support/entity/Entity.interface';
import { clickOutside, redirectToHomePage, toastNotification } from './common';
import { clickOutside, redirectToHomePage } from './common';
export const visitEntityPage = async (data: {
page: Page;
@ -789,6 +789,7 @@ const announcementForm = async (
);
await page.click('#announcement-submit');
await announcementSubmit;
await page.click('.Toastify__close-button');
};
export const createAnnouncement = async (
@ -1178,7 +1179,11 @@ export const restoreEntity = async (page: Page) => {
await page.click('[data-testid="restore-button"]');
await page.click('button:has-text("Restore")');
await toastNotification(page, /restored successfully/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/restored successfully/
);
await page.click('.Toastify__close-button');
const exists = await page
.locator('[data-testid="deleted-badge"]')
@ -1217,7 +1222,11 @@ export const softDeleteEntity = async (
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
await page.reload();
@ -1282,7 +1291,11 @@ export const hardDeleteEntity = async (
await page.click('[data-testid="confirm-button"]');
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
};
export const checkDataAssetWidget = async (

View File

@ -63,9 +63,9 @@ export const LINEAGE_CSV_HEADERS = [
export const verifyColumnLayerInactive = async (page: Page) => {
await page.click('[data-testid="lineage-layer-btn"]'); // Open Layer popover
await expect(page.getByTestId('lineage-layer-column-btn')).not.toBeFocused;
await page.waitForSelector(
'[data-testid="lineage-layer-column-btn"]:not(.active)'
);
await page.click('[data-testid="lineage-layer-btn"]'); // Close Layer popover
};

View File

@ -138,7 +138,11 @@ export const softDeleteUserProfilePage = async (
await deleteResponse;
await toastNotification(page, /deleted successfully!/);
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
await deletedUserChecks(page);
};
@ -715,8 +719,7 @@ const resetPasswordModal = async (
page,
isOldPasswordCorrect
? 'Password updated successfully.'
: 'Old Password is not correct',
isOldPasswordCorrect ? 'success' : 'error'
: 'Old Password is not correct'
);
};

View File

@ -16,12 +16,15 @@ import React, { FC, useEffect } from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { I18nextProvider } from 'react-i18next';
import { Router } from 'react-router-dom';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.min.css';
import AppRouter from './components/AppRouter/AppRouter';
import { AuthProvider } from './components/Auth/AuthProviders/AuthProvider';
import ErrorBoundary from './components/common/ErrorBoundary/ErrorBoundary';
import { EntityExportModalProvider } from './components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import ApplicationsProvider from './components/Settings/Applications/ApplicationsProvider/ApplicationsProvider';
import WebAnalyticsProvider from './components/WebAnalytics/WebAnalyticsProvider';
import { TOAST_OPTIONS } from './constants/Toasts.constants';
import AntDConfigProvider from './context/AntDConfigProvider/AntDConfigProvider';
import PermissionProvider from './context/PermissionProvider/PermissionProvider';
import TourProvider from './context/TourProvider/TourProvider';
@ -95,6 +98,7 @@ const App: FC = () => {
</ErrorBoundary>
</I18nextProvider>
</Router>
<ToastContainer {...TOAST_OPTIONS} newestOnTop />
</div>
</div>
);

View File

@ -1,3 +0,0 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 1.91L12.59 0.5L7 6.09L1.41 0.5L0 1.91L5.59 7.5L0 13.09L1.41 14.5L7 8.91L12.59 14.5L14 13.09L8.41 7.5L14 1.91Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 249 B

View File

@ -1,12 +0,0 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6713_202785)">
<path d="M6.28843 0L0 6.28843V18.7116L6.28843 25H18.7116L25 18.7116V6.28843L18.7116 0L6.28843 0ZM23.5352 18.1048L18.1048 23.5352H6.89517L1.46484 18.1048V6.89517L6.89517 1.46484H18.1048L23.5352 6.89517V18.1048Z" fill="currentColor"/>
<path d="M12.5 14.428C11.9606 14.428 11.5234 13.9908 11.5234 13.4514V7.59204C11.5234 7.05269 11.9606 6.61548 12.5 6.61548C13.0394 6.61548 13.4766 7.05269 13.4766 7.59204V13.4514C13.4766 13.9908 13.0394 14.428 12.5 14.428Z" fill="currentColor"/>
<path d="M12.5 18.3342C13.0393 18.3342 13.4766 17.897 13.4766 17.3577C13.4766 16.8183 13.0393 16.3811 12.5 16.3811C11.9607 16.3811 11.5234 16.8183 11.5234 17.3577C11.5234 17.897 11.9607 18.3342 12.5 18.3342Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0_6713_202785">
<rect width="25" height="25" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 962 B

View File

@ -1,12 +0,0 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6713_202773)">
<path d="M12.5 0C5.59126 0 0 5.59063 0 12.5C0 19.4086 5.59063 25 12.5 25C19.4087 25 25 19.4094 25 12.5C25 5.59136 19.4094 0 12.5 0ZM12.5 23.2558C6.56924 23.2558 1.74419 18.4308 1.74419 12.5C1.74419 6.56919 6.56924 1.74419 12.5 1.74419C18.4308 1.74419 23.2558 6.56919 23.2558 12.5C23.2558 18.4308 18.4308 23.2558 12.5 23.2558Z" fill="currentColor"/>
<path d="M12.5003 10.4197C11.7599 10.4197 11.2334 10.7324 11.2334 11.1931V17.4619C11.2334 17.8568 11.7599 18.2517 12.5003 18.2517C13.2079 18.2517 13.7837 17.8568 13.7837 17.4619V11.193C13.7837 10.7323 13.2079 10.4197 12.5003 10.4197Z" fill="currentColor"/>
<path d="M12.5005 6.55322C11.7437 6.55322 11.1514 7.09619 11.1514 7.72144C11.1514 8.34673 11.7437 8.90615 12.5005 8.90615C13.241 8.90615 13.8333 8.34673 13.8333 7.72144C13.8333 7.09619 13.2409 6.55322 12.5005 6.55322Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0_6713_202773">
<rect width="25" height="25" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6713_202783)">
<path d="M18.0256 8.53367C18.4071 8.91514 18.4071 9.5335 18.0256 9.91478L11.4742 16.4663C11.0928 16.8476 10.4746 16.8476 10.0931 16.4663L6.97441 13.3474C6.59294 12.9662 6.59294 12.3478 6.97441 11.9665C7.35569 11.585 7.97405 11.585 8.35533 11.9665L10.7836 14.3948L16.6445 8.53367C17.0259 8.15239 17.6443 8.15239 18.0256 8.53367ZM25 12.5C25 19.4094 19.4084 25 12.5 25C5.59063 25 0 19.4084 0 12.5C0 5.59063 5.59158 0 12.5 0C19.4094 0 25 5.59158 25 12.5ZM23.0469 12.5C23.0469 6.67019 18.329 1.95312 12.5 1.95312C6.67019 1.95312 1.95312 6.67095 1.95312 12.5C1.95312 18.3298 6.67095 23.0469 12.5 23.0469C18.3298 23.0469 23.0469 18.329 23.0469 12.5Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0_6713_202783">
<rect width="25" height="25" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 919 B

View File

@ -1,12 +0,0 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6713_202641)">
<path d="M21.0938 24.2171H3.90625C1.75234 24.2171 0 22.4648 0 20.3109C0 19.6424 0.171973 18.9826 0.497461 18.4023L9.08911 2.78097C9.09062 2.77823 9.09214 2.77555 9.09365 2.77281C10.5859 0.119837 14.4135 0.11886 15.9064 2.77281C15.9079 2.7755 15.9094 2.77823 15.9109 2.78097L24.5026 18.4022C24.828 18.9825 25 19.6424 25 20.3109C25 22.4648 23.2477 24.2171 21.0938 24.2171ZM10.798 3.72667L2.20674 19.3472C2.20522 19.35 2.20371 19.3527 2.2022 19.3554C2.03926 19.645 1.95312 19.9755 1.95312 20.3109C1.95312 21.3878 2.8293 22.264 3.90625 22.264H21.0938C22.1707 22.264 23.0469 21.3878 23.0469 20.3109C23.0469 19.9754 22.9607 19.645 22.7978 19.3554C22.7963 19.3527 22.7948 19.35 22.7933 19.3472L14.2021 3.72692C13.4545 2.40426 11.5465 2.40275 10.798 3.72667Z" fill="currentColor"/>
<path d="M12.5 15.428C11.9606 15.428 11.5234 14.9908 11.5234 14.4514V8.59204C11.5234 8.05269 11.9606 7.61548 12.5 7.61548C13.0394 7.61548 13.4766 8.05269 13.4766 8.59204V14.4514C13.4766 14.9908 13.0394 15.428 12.5 15.428Z" fill="currentColor"/>
<path d="M12.5 19.3342C13.0393 19.3342 13.4766 18.897 13.4766 18.3577C13.4766 17.8183 13.0393 17.3811 12.5 17.3811C11.9607 17.3811 11.5234 17.8183 11.5234 18.3577C11.5234 18.897 11.9607 19.3342 12.5 19.3342Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0_6713_202641">
<rect width="25" height="25" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,43 +0,0 @@
/*
* Copyright 2024 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 { Alert } from 'antd';
import classNames from 'classnames';
import React, { useMemo } from 'react';
import { ReactComponent as CrossIcon } from '../../assets/svg/ic-cross.svg';
import { useAlertStore } from '../../hooks/useAlertStore';
import { getIconAndClassName } from '../../utils/ToastUtils';
import './alert-bar.style.less';
import { AlertBarProps } from './AlertBar.interface';
const AlertBar = ({ type, message }: AlertBarProps): JSX.Element => {
const { resetAlert, animationClass } = useAlertStore();
const { icon: AlertIcon, className } = useMemo(() => {
return getIconAndClassName(type);
}, [type]);
return (
<Alert
closable
showIcon
afterClose={resetAlert}
className={classNames(className, 'alert-container', animationClass)}
closeIcon={<CrossIcon color="currentColor" />}
description={message}
icon={AlertIcon && <AlertIcon />}
type={type}
/>
);
};
export default AlertBar;

View File

@ -1,85 +0,0 @@
/*
* Copyright 2024 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 (reference) url('../../styles/variables.less');
@keyframes resize-show-animation {
from {
height: 0;
padding: 0 20px;
}
to {
height: 64px;
padding: 20px;
}
}
@keyframes resize-hide-animation {
from {
height: 64px;
padding: 20px;
}
to {
height: 0;
padding: 0 20px;
}
}
.alert-container {
overflow-y: scroll;
backdrop-filter: blur(500px);
&.show-alert {
animation: resize-show-animation 1.2s ease-in-out forwards;
position: fixed;
top: 64px;
z-index: 20;
width: 96%;
}
&.hide-alert {
animation: resize-hide-animation 1.2s ease-in-out forwards;
}
&.info {
background-color: @info-bg-color;
color: @info-color;
border: none;
border-radius: 0px;
}
&.success {
background-color: @success-bg-color;
color: @success-color;
border: none;
border-radius: 0px;
}
&.warning {
background-color: @warning-bg-color;
color: @warning-color;
border: none;
border-radius: 0px;
}
&.error {
background-color: @error-bg-color;
color: @error-color;
border: none;
border-radius: 0px;
}
.ant-alert-description {
font-size: 16px;
}
}

View File

@ -24,13 +24,13 @@ import React, {
useEffect,
useImperativeHandle,
} from 'react';
import { toast } from 'react-toastify';
import {
msalLoginRequest,
parseMSALResponse,
} from '../../../utils/AuthProvider.util';
import { getPopupSettingLink } from '../../../utils/BrowserUtils';
import { Transi18next } from '../../../utils/CommonUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import Loader from '../../common/Loader/Loader';
import {
AuthenticatorRef,
@ -96,7 +96,7 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>(
// eslint-disable-next-line no-console
console.error(e);
if (e?.message?.includes('popup_window_error')) {
showErrorToast(
toast.error(
<Transi18next
i18nKey="message.popup-block-message"
renderElement={

View File

@ -37,7 +37,6 @@ import { getDomainPath } from '../../utils/RouterUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import Loader from '../common/Loader/Loader';
import ResizableLeftPanels from '../common/ResizablePanels/ResizableLeftPanels';
import PageLayoutV1 from '../PageLayoutV1/PageLayoutV1';
import './domain.less';
import DomainDetailsPage from './DomainDetailsPage/DomainDetailsPage.component';
import DomainsLeftPanel from './DomainLeftPanel/DomainLeftPanel.component';
@ -177,6 +176,10 @@ const DomainPage = () => {
}
}, [rootDomains, domainFqn]);
if (domainLoading) {
return <Loader />;
}
if (!(viewBasicDomainPermission || viewAllDomainPermission)) {
return (
<ErrorPlaceHolder
@ -186,23 +189,27 @@ const DomainPage = () => {
);
}
const pageContent = isEmpty(rootDomains) ? (
<ErrorPlaceHolder
buttonId="add-domain"
className="mt-44"
heading={t('label.domain')}
permission={createDomainPermission}
type={
createDomainPermission
? ERROR_PLACEHOLDER_TYPE.CREATE
: ERROR_PLACEHOLDER_TYPE.CUSTOM
}
onClick={handleAddDomainClick}>
{t('message.domains-not-configured')}
</ErrorPlaceHolder>
) : (
if (isEmpty(rootDomains)) {
return (
<ErrorPlaceHolder
buttonId="add-domain"
className="mt-0-important"
heading={t('label.domain')}
permission={createDomainPermission}
type={
createDomainPermission
? ERROR_PLACEHOLDER_TYPE.CREATE
: ERROR_PLACEHOLDER_TYPE.CUSTOM
}
onClick={handleAddDomainClick}>
{t('message.domains-not-configured')}
</ErrorPlaceHolder>
);
}
return (
<ResizableLeftPanels
className="content-height-with-resizable-panel domain-page"
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
minWidth: 280,
@ -218,12 +225,6 @@ const DomainPage = () => {
}}
/>
);
return (
<PageLayoutV1 pageTitle={t('label.domain-plural')}>
{domainLoading ? <Loader /> : pageContent}
</PageLayoutV1>
);
};
export default DomainPage;

View File

@ -15,9 +15,6 @@
height: @domain-page-height;
overflow-y: auto;
}
.domain-page {
margin-top: -12px;
}
.domain-details-page-tabs {
.ant-tabs-nav {

View File

@ -21,8 +21,6 @@ import React, {
ReactNode,
useMemo,
} from 'react';
import { useAlertStore } from '../../hooks/useAlertStore';
import AlertBar from '../AlertBar/AlertBar';
import DocumentTitle from '../common/DocumentTitle/DocumentTitle';
import './../../styles/layout/page-layout.less';
@ -62,8 +60,6 @@ const PageLayoutV1: FC<PageLayoutProp> = ({
mainContainerClassName = '',
pageContainerStyle = {},
}: PageLayoutProp) => {
const { alert } = useAlertStore();
const contentWidth = useMemo(() => {
if (leftPanel && rightPanel) {
return `calc(100% - ${leftPanelWidth + rightPanelWidth}px)`;
@ -103,35 +99,27 @@ const PageLayoutV1: FC<PageLayoutProp> = ({
{leftPanel}
</Col>
)}
<Col span={24}>
<Col
className={classNames(
'page-layout-v1-center p-t-sm page-layout-v1-vertical-scroll',
{
'flex justify-center': center,
},
mainContainerClassName
)}
flex={contentWidth}
offset={center ? 3 : 0}
span={center ? 18 : 24}>
<div className="alert-page-container">
{alert && <AlertBar message={alert.message} type={alert.type} />}
<div
className={`page-content ${alert && 'page-content-shifted'}`}>
{children}
</div>
</div>
</Col>
{rightPanel && (
<Col
className="page-layout-rightpanel page-layout-v1-vertical-scroll"
flex={rightPanelWidth + 'px'}
id="right-panelV1">
{rightPanel}
</Col>
<Col
className={classNames(
'page-layout-v1-center p-t-sm page-layout-v1-vertical-scroll',
{
'flex justify-center': center,
},
mainContainerClassName
)}
flex={contentWidth}
offset={center ? 3 : 0}
span={center ? 18 : 24}>
{children}
</Col>
{rightPanel && (
<Col
className="page-layout-rightpanel page-layout-v1-vertical-scroll"
flex={rightPanelWidth + 'px'}
id="right-panelV1">
{rightPanel}
</Col>
)}
</Row>
</Fragment>
);

View File

@ -1,7 +1,5 @@
import { AlertProps } from 'antd';
/*
* Copyright 2024 Collate.
* 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
@ -12,7 +10,12 @@ import { AlertProps } from 'antd';
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface AlertBarProps {
type: AlertProps['type'];
message: string | JSX.Element;
}
import { ToastOptions } from 'react-toastify';
export const TOAST_OPTIONS: ToastOptions = {
autoClose: false,
hideProgressBar: true,
draggable: false,
closeOnClick: false,
};

View File

@ -1,45 +0,0 @@
/*
* Copyright 2024 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 { AlertProps } from 'antd';
import { create } from 'zustand';
type AlertType = {
type: AlertProps['type'];
message: string | JSX.Element;
};
interface AlertStore {
alert: AlertType | undefined;
animationClass: string;
addAlert: (alert: AlertType, timer?: number) => void;
resetAlert: () => void;
}
export const useAlertStore = create<AlertStore>()((set) => ({
alert: undefined,
animationClass: '',
addAlert: (alert: AlertType, timer?: number) => {
set({ alert, animationClass: 'show-alert' });
const autoCloseTimer = timer ?? (alert.type === 'error' ? Infinity : 5000);
if (autoCloseTimer !== Infinity) {
setTimeout(() => {
set({ animationClass: 'hide-alert', alert: undefined });
}, autoCloseTimer);
}
},
resetAlert: () => {
set({ alert: undefined });
},
}));

View File

@ -56,7 +56,6 @@
"aggregate": "Aggregate",
"airflow-config-plural": "Airflow-Konfigurationen",
"alert": "Warnung",
"alert-details": "Alert Details",
"alert-lowercase": "warnung",
"alert-lowercase-plural": "warnungen",
"alert-plural": "Warnungen",

View File

@ -56,7 +56,6 @@
"aggregate": "Aggregate",
"airflow-config-plural": "airflow configs",
"alert": "Alert",
"alert-details": "Alert Details",
"alert-lowercase": "alert",
"alert-lowercase-plural": "alerts",
"alert-plural": "Alerts",

View File

@ -56,7 +56,6 @@
"aggregate": "Agregar",
"airflow-config-plural": "Configuraciones de airflow",
"alert": "Alerta",
"alert-details": "Alert Details",
"alert-lowercase": "alerta",
"alert-lowercase-plural": "alertas",
"alert-plural": "Alertas",

View File

@ -56,7 +56,6 @@
"aggregate": "Aggregate",
"airflow-config-plural": "Configurations Airflow",
"alert": "Alerte",
"alert-details": "Alert Details",
"alert-lowercase": "alerte",
"alert-lowercase-plural": "alertes",
"alert-plural": "Alertes",

View File

@ -56,7 +56,6 @@
"aggregate": "Agrupar",
"airflow-config-plural": "configuracións de Airflow",
"alert": "Alerta",
"alert-details": "Alert Details",
"alert-lowercase": "alerta",
"alert-lowercase-plural": "alertas",
"alert-plural": "Alertas",

View File

@ -56,7 +56,6 @@
"aggregate": "כלול",
"airflow-config-plural": "תצורות airflow",
"alert": "התראה",
"alert-details": "Alert Details",
"alert-lowercase": "התראה",
"alert-lowercase-plural": "התראות",
"alert-plural": "התראות",

View File

@ -56,7 +56,6 @@
"aggregate": "Aggregate",
"airflow-config-plural": "Airflowの設定",
"alert": "アラート",
"alert-details": "Alert Details",
"alert-lowercase": "alert",
"alert-lowercase-plural": "alerts",
"alert-plural": "アラート",

View File

@ -56,7 +56,6 @@
"aggregate": "Agregaat",
"airflow-config-plural": "Airflowconfiguraties",
"alert": "Alert",
"alert-details": "Alert Details",
"alert-lowercase": "alert",
"alert-lowercase-plural": "alerts",
"alert-plural": "Alerts",

View File

@ -56,7 +56,6 @@
"aggregate": "تجمیع",
"airflow-config-plural": "پیکربندی‌های ایر‌فلو",
"alert": "هشدار",
"alert-details": "Alert Details",
"alert-lowercase": "هشدار",
"alert-lowercase-plural": "هشدارها",
"alert-plural": "هشدارها",

View File

@ -56,7 +56,6 @@
"aggregate": "Agregado",
"airflow-config-plural": "configs do airflow",
"alert": "Alerta",
"alert-details": "Alert Details",
"alert-lowercase": "alerta",
"alert-lowercase-plural": "alertas",
"alert-plural": "Alertas",

View File

@ -56,7 +56,6 @@
"aggregate": "Agregado",
"airflow-config-plural": "configs do airflow",
"alert": "Alerta",
"alert-details": "Alert Details",
"alert-lowercase": "alerta",
"alert-lowercase-plural": "alertas",
"alert-plural": "Alertas",

View File

@ -56,7 +56,6 @@
"aggregate": "Aggregate",
"airflow-config-plural": "конфиги airflow",
"alert": "Предупреждение",
"alert-details": "Alert Details",
"alert-lowercase": "предупреждение",
"alert-lowercase-plural": "предупреждения",
"alert-plural": "Предупреждения",

View File

@ -56,7 +56,6 @@
"aggregate": "รวม",
"airflow-config-plural": "การกำหนดค่าของ airflow",
"alert": "การแจ้งเตือน",
"alert-details": "Alert Details",
"alert-lowercase": "การแจ้งเตือน",
"alert-lowercase-plural": "การแจ้งเตือนหลายอย่าง",
"alert-plural": "การแจ้งเตือนหลายอย่าง",

View File

@ -56,7 +56,6 @@
"aggregate": "聚合",
"airflow-config-plural": "Airflow 配置",
"alert": "提醒",
"alert-details": "Alert Details",
"alert-lowercase": "提醒",
"alert-lowercase-plural": "提醒",
"alert-plural": "提醒",

View File

@ -43,15 +43,6 @@ jest.mock('react-router-dom', () => ({
}),
}));
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => {
return jest.fn(({ children, pageTitle }) => (
<div data-testid="page-layout-v1">
<h1>{pageTitle}</h1>
<div>{children}</div>
</div>
));
});
jest.mock('../../rest/observabilityAPI', () => ({
getObservabilityAlertByFQN: jest
.fn()

View File

@ -30,7 +30,6 @@ import { OwnerLabel } from '../../components/common/OwnerLabel/OwnerLabel.compon
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import EntityHeaderTitle from '../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import { ROUTES } from '../../constants/constants';
import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants';
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
@ -328,140 +327,136 @@ function AlertDetailsPage({
}
return (
<PageLayoutV1 pageTitle={t('label.alert-details')}>
<ResizablePanels
hideSecondPanel
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
children: loadingCount ? (
<Loader />
) : (
<div
className="steps-form-container"
data-testid="alert-details-container">
<Row
className="add-notification-container p-x-lg p-t-md"
gutter={[0, 16]}>
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumb} />
</Col>
<ResizablePanels
hideSecondPanel
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
children: loadingCount ? (
<Loader />
) : (
<div
className="steps-form-container"
data-testid="alert-details-container">
<Row
className="add-notification-container p-x-lg p-t-md"
gutter={[0, 16]}>
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumb} />
</Col>
<Col span={24}>
<Row justify="space-between">
<Col span={21}>
<Row gutter={[16, 16]}>
<Col span={24}>
<EntityHeaderTitle
displayName={alertDetails?.displayName}
icon={alertIcon}
name={alertDetails?.name ?? ''}
serviceName=""
/>
</Col>
<Col span={24}>
<div className="d-flex items-center flex-wrap gap-2">
{ownerLoading ? (
<Skeleton.Button
active
className="extra-info-skeleton"
/>
) : (
<OwnerLabel
hasPermission={editOwnersPermission}
owners={alertDetails?.owners}
onUpdate={onOwnerUpdate}
/>
)}
{extraInfo}
</div>
</Col>
</Row>
</Col>
<Col>
<Space align="center" size={8}>
{editPermission &&
alertDetails?.provider !== ProviderType.System && (
<Tooltip
title={t('label.edit-entity', {
entity: t('label.alert'),
})}>
<Button
className="flex flex-center"
data-testid="edit-button"
icon={<EditIcon height={16} width={16} />}
onClick={handleAlertEdit}
/>
</Tooltip>
<Col span={24}>
<Row justify="space-between">
<Col span={21}>
<Row gutter={[16, 16]}>
<Col span={24}>
<EntityHeaderTitle
displayName={alertDetails?.displayName}
icon={alertIcon}
name={alertDetails?.name ?? ''}
serviceName=""
/>
</Col>
<Col span={24}>
<div className="d-flex items-center flex-wrap gap-2">
{ownerLoading ? (
<Skeleton.Button
active
className="extra-info-skeleton"
/>
) : (
<OwnerLabel
hasPermission={editOwnersPermission}
owners={alertDetails?.owners}
onUpdate={onOwnerUpdate}
/>
)}
{deletePermission &&
alertDetails?.provider !== ProviderType.System && (
<Tooltip
title={t('label.delete-entity', {
entity: t('label.alert'),
})}>
<Button
className="flex flex-center"
data-testid="delete-button"
icon={<DeleteIcon height={16} width={16} />}
onClick={() => setShowDeleteModal(true)}
/>
</Tooltip>
)}
</Space>
</Col>
</Row>
</Col>
{extraInfo}
</div>
</Col>
</Row>
</Col>
<Col>
<Space align="center" size={8}>
{editPermission &&
alertDetails?.provider !== ProviderType.System && (
<Tooltip
title={t('label.edit-entity', {
entity: t('label.alert'),
})}>
<Button
className="flex flex-center"
data-testid="edit-button"
icon={<EditIcon height={16} width={16} />}
onClick={handleAlertEdit}
/>
</Tooltip>
)}
{deletePermission &&
alertDetails?.provider !== ProviderType.System && (
<Tooltip
title={t('label.delete-entity', {
entity: t('label.alert'),
})}>
<Button
className="flex flex-center"
data-testid="delete-button"
icon={<DeleteIcon height={16} width={16} />}
onClick={() => setShowDeleteModal(true)}
/>
</Tooltip>
)}
</Space>
</Col>
</Row>
</Col>
<Col
className="alert-description"
data-testid="alert-description"
span={24}>
<DescriptionV1
description={alertDetails?.description}
entityType={EntityType.EVENT_SUBSCRIPTION}
hasEditAccess={editDescriptionPermission}
isEdit={showDescriptionModal}
showCommentsIcon={false}
onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit}
onDescriptionUpdate={onDescriptionUpdate}
/>
</Col>
<Col
className="alert-description"
data-testid="alert-description"
span={24}>
<DescriptionV1
description={alertDetails?.description}
entityType={EntityType.EVENT_SUBSCRIPTION}
hasEditAccess={editDescriptionPermission}
isEdit={showDescriptionModal}
showCommentsIcon={false}
onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit}
onDescriptionUpdate={onDescriptionUpdate}
/>
</Col>
<Col span={24}>
<Tabs
activeKey={tab}
className="m-b-lg"
items={tabItems}
onTabClick={handleTabChange}
/>
</Col>
</Row>
<DeleteWidgetModal
afterDeleteAction={handleAlertDelete}
allowSoftDelete={false}
entityId={alertDetails?.id ?? ''}
entityName={getEntityName(alertDetails)}
entityType={EntityType.SUBSCRIPTION}
visible={showDeleteModal}
onCancel={hideDeleteModal}
/>
</div>
),
minWidth: 700,
flex: 0.7,
}}
pageTitle={t('label.entity-detail-plural', {
entity: t('label.alert'),
})}
secondPanel={{
children: <></>,
minWidth: 0,
className: 'content-resizable-panel-container',
}}
/>
</PageLayoutV1>
<Col span={24}>
<Tabs
activeKey={tab}
className="m-b-lg"
items={tabItems}
onTabClick={handleTabChange}
/>
</Col>
</Row>
<DeleteWidgetModal
afterDeleteAction={handleAlertDelete}
allowSoftDelete={false}
entityId={alertDetails?.id ?? ''}
entityName={getEntityName(alertDetails)}
entityType={EntityType.SUBSCRIPTION}
visible={showDeleteModal}
onCancel={hideDeleteModal}
/>
</div>
),
minWidth: 700,
flex: 0.7,
}}
pageTitle={t('label.entity-detail-plural', { entity: t('label.alert') })}
secondPanel={{
children: <></>,
minWidth: 0,
className: 'content-resizable-panel-container',
}}
/>
);
}

View File

@ -63,10 +63,6 @@ jest.mock('../../components/common/ResizablePanels/ResizableLeftPanels', () => {
));
});
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => {
return jest.fn().mockImplementation(({ children }) => <div>{children}</div>);
});
jest.mock('react-router-dom', () => {
return {
...jest.requireActual('react-router-dom'),

View File

@ -24,7 +24,6 @@ import {
import LeftPanelCard from '../../components/common/LeftPanelCard/LeftPanelCard';
import ResizableLeftPanels from '../../components/common/ResizablePanels/ResizableLeftPanels';
import TabsLabel from '../../components/common/TabsLabel/TabsLabel.component';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import { ROUTES } from '../../constants/constants';
import { getDataQualityPagePath } from '../../utils/RouterUtils';
import './data-quality-page.less';
@ -69,78 +68,76 @@ const DataQualityPage = () => {
};
return (
<PageLayoutV1 pageTitle={t('label.data-quality')}>
<ResizableLeftPanels
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
minWidth: 280,
flex: 0.13,
children: (
<LeftPanelCard id="data-quality">
<Menu
className="custom-menu custom-menu-with-description data-quality-page-left-panel-menu"
data-testid="tabs"
items={menuItems}
mode="inline"
selectedKeys={[
activeTab ?? DataQualityClassBase.getDefaultActiveTab(),
]}
onClick={handleTabChange}
/>
</LeftPanelCard>
),
}}
pageTitle="Quality"
secondPanel={{
children: (
<DataQualityProvider>
<Row
className="page-container"
data-testid="data-insight-container"
gutter={[16, 16]}>
<Col span={24}>
<Typography.Title
className="m-b-md p-x-md"
data-testid="page-title"
level={5}>
{t('label.data-quality')}
</Typography.Title>
<Typography.Paragraph
className="text-grey-muted p-x-md"
data-testid="page-sub-title">
{t('message.page-sub-header-for-data-quality')}
</Typography.Paragraph>
</Col>
<Col span={24}>
<Switch>
{tabDetailsComponent.map((tab) => (
<Route
exact
component={tab.component}
key={tab.key}
path={tab.path}
/>
))}
<ResizableLeftPanels
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
minWidth: 280,
flex: 0.13,
children: (
<LeftPanelCard id="data-quality">
<Menu
className="custom-menu custom-menu-with-description data-quality-page-left-panel-menu"
data-testid="tabs"
items={menuItems}
mode="inline"
selectedKeys={[
activeTab ?? DataQualityClassBase.getDefaultActiveTab(),
]}
onClick={handleTabChange}
/>
</LeftPanelCard>
),
}}
pageTitle="Quality"
secondPanel={{
children: (
<DataQualityProvider>
<Row
className="page-container"
data-testid="data-insight-container"
gutter={[16, 16]}>
<Col span={24}>
<Typography.Title
className="m-b-md p-x-md"
data-testid="page-title"
level={5}>
{t('label.data-quality')}
</Typography.Title>
<Typography.Paragraph
className="text-grey-muted p-x-md"
data-testid="page-sub-title">
{t('message.page-sub-header-for-data-quality')}
</Typography.Paragraph>
</Col>
<Col span={24}>
<Switch>
{tabDetailsComponent.map((tab) => (
<Route
exact
component={tab.component}
key={tab.key}
path={tab.path}
/>
))}
<Route exact path={ROUTES.DATA_QUALITY}>
<Redirect
to={getDataQualityPagePath(
DataQualityClassBase.getDefaultActiveTab()
)}
/>
</Route>
</Switch>
</Col>
</Row>
</DataQualityProvider>
),
className: 'content-resizable-panel-container p-t-sm',
minWidth: 800,
flex: 0.87,
}}
/>
</PageLayoutV1>
<Route exact path={ROUTES.DATA_QUALITY}>
<Redirect
to={getDataQualityPagePath(
DataQualityClassBase.getDefaultActiveTab()
)}
/>
</Route>
</Switch>
</Col>
</Row>
</DataQualityProvider>
),
className: 'content-resizable-panel-container p-t-sm',
minWidth: 800,
flex: 0.87,
}}
/>
);
};

View File

@ -29,7 +29,6 @@ import {
ModifiedGlossary,
useGlossaryStore,
} from '../../../components/Glossary/useGlossary.store';
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
import { PAGE_SIZE_LARGE, ROUTES } from '../../../constants/constants';
import { GLOSSARIES_DOCS } from '../../../constants/docs.constants';
@ -349,6 +348,24 @@ const GlossaryPage = () => {
return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />;
}
if (glossaries.length === 0 && !isLoading) {
return (
<ErrorPlaceHolder
buttonId="add-glossary"
className="mt-0-important"
doc={GLOSSARIES_DOCS}
heading={t('label.glossary')}
permission={createGlossaryPermission}
type={
createGlossaryPermission
? ERROR_PLACEHOLDER_TYPE.CREATE
: ERROR_PLACEHOLDER_TYPE.NO_DATA
}
onClick={handleAddGlossaryClick}
/>
);
}
const glossaryElement = (
<div className="p-t-sm">
{isRightPanelLoading ? (
@ -416,27 +433,7 @@ const GlossaryPage = () => {
/>
);
return (
<PageLayoutV1 pageTitle={t('label.glossary-plural')}>
{glossaries.length === 0 && !isLoading ? (
<ErrorPlaceHolder
buttonId="add-glossary"
className="mt-44"
doc={GLOSSARIES_DOCS}
heading={t('label.glossary')}
permission={createGlossaryPermission}
type={
createGlossaryPermission
? ERROR_PLACEHOLDER_TYPE.CREATE
: ERROR_PLACEHOLDER_TYPE.NO_DATA
}
onClick={handleAddGlossaryClick}
/>
) : (
resizableLayout
)}
</PageLayoutV1>
);
return <>{resizableLayout}</>;
};
export default GlossaryPage;

View File

@ -46,15 +46,6 @@ jest.mock('../../../context/PermissionProvider/PermissionProvider', () => {
};
});
jest.mock('../../../components/PageLayoutV1/PageLayoutV1', () => {
return jest.fn(({ children, pageTitle }) => (
<div data-testid="page-layout-v1">
<h1>{pageTitle}</h1>
<div>{children}</div>
</div>
));
});
jest.mock('../../../components/Glossary/GlossaryV1.component', () => {
return jest.fn().mockImplementation((props) => (
<div>

View File

@ -26,7 +26,6 @@ import ResizablePanels from '../../components/common/ResizablePanels/ResizablePa
import ServiceDocPanel from '../../components/common/ServiceDocPanel/ServiceDocPanel';
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants';
import { OPEN_METADATA } from '../../constants/service-guide.constant';
import {
@ -117,130 +116,128 @@ const LineageConfigPage = () => {
}
return (
<PageLayoutV1 pageTitle={t('label.lineage-config')}>
<ResizablePanels
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
children: (
<div
className="max-width-md w-9/10 service-form-container"
data-testid="add-metric-container">
<Row gutter={[16, 16]}>
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumbs} />
</Col>
<ResizablePanels
className="content-height-with-resizable-panel"
firstPanel={{
className: 'content-resizable-panel-container',
children: (
<div
className="max-width-md w-9/10 service-form-container"
data-testid="add-metric-container">
<Row gutter={[16, 16]}>
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumbs} />
</Col>
<Col span={24}>
<Typography.Title
className="m-b-0"
data-testid="heading"
level={5}>
{t('label.lineage')}
</Typography.Title>
</Col>
<Col span={24}>
<Form
form={form}
id="lineage-config"
initialValues={lineageConfig}
layout="vertical"
onFinish={handleSave}
onFocus={handleFieldFocus}>
<Form.Item
id="root/upstreamDepth"
label={t('label.upstream-depth')}
name="upstreamDepth"
rules={[
{
required: true,
message: t('message.upstream-depth-message'),
},
]}>
<Input
data-testid="field-upstream"
max={5}
min={1}
type="number"
/>
</Form.Item>
<Col span={24}>
<Typography.Title
className="m-b-0"
data-testid="heading"
level={5}>
{t('label.lineage')}
</Typography.Title>
</Col>
<Col span={24}>
<Form
form={form}
id="lineage-config"
initialValues={lineageConfig}
layout="vertical"
onFinish={handleSave}
onFocus={handleFieldFocus}>
<Form.Item
id="root/upstreamDepth"
label={t('label.upstream-depth')}
name="upstreamDepth"
rules={[
{
required: true,
message: t('message.upstream-depth-message'),
},
]}>
<Input
data-testid="field-upstream"
max={5}
min={1}
type="number"
/>
</Form.Item>
<Form.Item
className="m-t-sm"
id="root/downstreamDepth"
label={t('label.downstream-depth')}
name="downstreamDepth"
rules={[
{
required: true,
message: t('message.downstream-depth-message'),
},
]}>
<Input
data-testid="field-downstream"
max={5}
min={1}
type="number"
/>
</Form.Item>
<Form.Item
className="m-t-sm"
id="root/downstreamDepth"
label={t('label.downstream-depth')}
name="downstreamDepth"
rules={[
{
required: true,
message: t('message.downstream-depth-message'),
},
]}>
<Input
data-testid="field-downstream"
max={5}
min={1}
type="number"
/>
</Form.Item>
<Form.Item
className="m-t-sm"
id="root/lineageLayer"
label={t('label.lineage-layer')}
name="lineageLayer">
<Select data-testid="field-lineage-layer">
<Select.Option value={LineageLayer.EntityLineage}>
{t('label.entity-lineage')}
</Select.Option>
<Select.Option value={LineageLayer.ColumnLevelLineage}>
{t('label.column-level-lineage')}
</Select.Option>
<Select.Option value={LineageLayer.DataObservability}>
{t('label.data-observability')}
</Select.Option>
</Select>
</Form.Item>
</Form>
<Row className="m-b-xl" justify="end">
<Col className="d-flex justify-end gap-2" span={24}>
<Button
data-testid="cancel-button"
onClick={() => history.goBack()}>
{t('label.cancel')}
</Button>
<Button
data-testid="save-button"
form="lineage-config"
htmlType="submit"
loading={isUpdating}
type="primary">
{t('label.save')}
</Button>
</Col>
</Row>
</Col>
</Row>
</div>
),
minWidth: 700,
flex: 0.7,
}}
pageTitle={t('label.lineage-config')}
secondPanel={{
className: 'service-doc-panel content-resizable-panel-container',
minWidth: 400,
flex: 0.3,
children: (
<ServiceDocPanel
activeField={activeField}
serviceName="LineageConfiguration"
serviceType={OPEN_METADATA}
/>
),
}}
/>
</PageLayoutV1>
<Form.Item
className="m-t-sm"
id="root/lineageLayer"
label={t('label.lineage-layer')}
name="lineageLayer">
<Select data-testid="field-lineage-layer">
<Select.Option value={LineageLayer.EntityLineage}>
{t('label.entity-lineage')}
</Select.Option>
<Select.Option value={LineageLayer.ColumnLevelLineage}>
{t('label.column-level-lineage')}
</Select.Option>
<Select.Option value={LineageLayer.DataObservability}>
{t('label.data-observability')}
</Select.Option>
</Select>
</Form.Item>
</Form>
<Row className="m-b-xl" justify="end">
<Col className="d-flex justify-end gap-2" span={24}>
<Button
data-testid="cancel-button"
onClick={() => history.goBack()}>
{t('label.cancel')}
</Button>
<Button
data-testid="save-button"
form="lineage-config"
htmlType="submit"
loading={isUpdating}
type="primary">
{t('label.save')}
</Button>
</Col>
</Row>
</Col>
</Row>
</div>
),
minWidth: 700,
flex: 0.7,
}}
pageTitle={t('label.lineage-config')}
secondPanel={{
className: 'service-doc-panel content-resizable-panel-container',
minWidth: 400,
flex: 0.3,
children: (
<ServiceDocPanel
activeField={activeField}
serviceName="LineageConfiguration"
serviceType={OPEN_METADATA}
/>
),
}}
/>
);
};

View File

@ -120,73 +120,75 @@ export const PersonaPage = () => {
}
};
if (isEmpty(persona) && !isLoading) {
return (
<>
{errorPlaceHolder}
{Boolean(addEditPersona) && (
<AddEditPersonaForm
persona={addEditPersona}
onCancel={handlePersonalAddEditCancel}
onSave={handlePersonaAddEditSave}
/>
)}
</>
);
}
return (
<PageLayoutV1 pageTitle={t('label.persona-plural')}>
{isEmpty(persona) && !isLoading ? (
<div className="mt-44">
{errorPlaceHolder}
{Boolean(addEditPersona) && (
<AddEditPersonaForm
persona={addEditPersona}
onCancel={handlePersonalAddEditCancel}
onSave={handlePersonaAddEditSave}
/>
)}
</div>
) : (
<Row className="user-listing page-container p-b-md" gutter={[16, 16]}>
<Row className="user-listing page-container p-b-md" gutter={[16, 16]}>
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumbs} />
</Col>
<Col span={18}>
<PageHeader data={PAGE_HEADERS.PERSONAS} />
</Col>
<Col span={6}>
<Space align="center" className="w-full justify-end" size={16}>
<Button
data-testid="add-persona-button"
type="primary"
onClick={handleAddNewPersona}>
{t('label.add-entity', { entity: t('label.persona') })}
</Button>
</Space>
</Col>
{isLoading
? [1, 2, 3].map((key) => (
<Col key={key} span={8}>
<Card>
<Skeleton active paragraph title />
</Card>
</Col>
))
: persona?.map((persona) => (
<Col key={persona.id} span={8}>
<PersonaDetailsCard persona={persona} />
</Col>
))}
{showPagination && (
<Col span={24}>
<TitleBreadcrumb titleLinks={breadcrumbs} />
</Col>
<Col span={18}>
<PageHeader data={PAGE_HEADERS.PERSONAS} />
</Col>
<Col span={6}>
<Space align="center" className="w-full justify-end" size={16}>
<Button
data-testid="add-persona-button"
type="primary"
onClick={handleAddNewPersona}>
{t('label.add-entity', { entity: t('label.persona') })}
</Button>
</Space>
</Col>
{isLoading
? [1, 2, 3].map((key) => (
<Col key={key} span={8}>
<Card>
<Skeleton active paragraph title />
</Card>
</Col>
))
: persona?.map((persona) => (
<Col key={persona.id} span={8}>
<PersonaDetailsCard persona={persona} />
</Col>
))}
{showPagination && (
<Col span={24}>
<NextPrevious
currentPage={currentPage}
isLoading={isLoading}
pageSize={pageSize}
paging={paging}
pagingHandler={handlePersonaPageChange}
onShowSizeChange={handlePageSizeChange}
/>
</Col>
)}
{Boolean(addEditPersona) && (
<AddEditPersonaForm
persona={addEditPersona}
onCancel={handlePersonalAddEditCancel}
onSave={handlePersonaAddEditSave}
<NextPrevious
currentPage={currentPage}
isLoading={isLoading}
pageSize={pageSize}
paging={paging}
pagingHandler={handlePersonaPageChange}
onShowSizeChange={handlePageSizeChange}
/>
)}
</Row>
)}
</Col>
)}
{Boolean(addEditPersona) && (
<AddEditPersonaForm
persona={addEditPersona}
onCancel={handlePersonalAddEditCancel}
onSave={handlePersonaAddEditSave}
/>
)}
</Row>
</PageLayoutV1>
);
};

View File

@ -20,20 +20,6 @@
overflow-x: hidden;
}
.alert-page-container {
position: relative;
}
.page-content {
position: relative;
top: 0;
transition: top 1s ease-in-out;
}
.page-content-shifted {
top: 64px;
}
.page-layout-v1-left-panel {
border: @global-border;
border-radius: @border-radius-base;

View File

@ -110,10 +110,6 @@
@team-avatar-bg: #0950c51a;
@om-navbar-height: ~'var(--ant-navbar-height)';
@sidebar-width: 60px;
@error-bg-color: rgb(from @error-color r g b / 0.1);
@success-bg-color: rgb(from @success-color r g b / 0.1);
@warning-bg-color: rgb(from @warning-color r g b / 0.1);
@info-bg-color: rgb(from @info-color r g b / 0.1);
// Sizing
@page-height: calc(100vh - @om-navbar-height);

View File

@ -11,53 +11,14 @@
* limitations under the License.
*/
import { AlertProps } from 'antd';
import { AxiosError } from 'axios';
import { isEmpty, isString } from 'lodash';
import React from 'react';
import { ReactComponent as ErrorIcon } from '../assets/svg/ic-error.svg';
import { ReactComponent as InfoIcon } from '../assets/svg/ic-info-tag.svg';
import { ReactComponent as SuccessIcon } from '../assets/svg/ic-success.svg';
import { ReactComponent as WarningIcon } from '../assets/svg/ic-warning-tag.svg';
import { toast } from 'react-toastify';
import { ClientErrors } from '../enums/Axios.enum';
import { useAlertStore } from '../hooks/useAlertStore';
import i18n from './i18next/LocalUtil';
import { getErrorText } from './StringsUtils';
export const getIconAndClassName = (type: AlertProps['type']) => {
switch (type) {
case 'info':
return {
icon: InfoIcon,
className: 'info',
};
case 'success':
return {
icon: SuccessIcon,
className: 'success',
};
case 'warning':
return {
icon: WarningIcon,
className: 'warning',
};
case 'error':
return {
icon: ErrorIcon,
className: 'error',
};
default:
return {
icon: null,
className: '',
};
}
};
export const hashCode = (str: string) => {
let hash = 0,
i,
@ -81,18 +42,15 @@ export const hashCode = (str: string) => {
* @param autoCloseTimer Set the delay in ms to close the toast automatically.
*/
export const showErrorToast = (
error: AxiosError | string | JSX.Element,
error: AxiosError | string,
fallbackText?: string,
autoCloseTimer?: number,
callback?: (value: React.SetStateAction<string | JSX.Element>) => void
callback?: (value: React.SetStateAction<string>) => void
) => {
let errorMessage: string | JSX.Element;
if (React.isValidElement(error)) {
errorMessage = error;
} else if (isString(error)) {
let errorMessage;
if (isString(error)) {
errorMessage = error.toString();
} else if ('config' in error && 'response' in error) {
} else {
const method = error.config?.method?.toUpperCase();
const fallback =
fallbackText && fallbackText.length > 0
@ -111,15 +69,12 @@ export const showErrorToast = (
) {
return;
}
} else {
errorMessage = fallbackText ?? i18n.t('server.unexpected-error');
}
callback && callback(errorMessage);
useAlertStore
.getState()
.addAlert({ type: 'error', message: errorMessage }, autoCloseTimer);
toast.error(errorMessage, {
toastId: hashCode(errorMessage),
autoClose: autoCloseTimer,
});
};
/**
@ -128,9 +83,9 @@ export const showErrorToast = (
* @param autoCloseTimer Set the delay in ms to close the toast automatically. `Default: 5000`
*/
export const showSuccessToast = (message: string, autoCloseTimer = 5000) => {
useAlertStore
.getState()
.addAlert({ type: 'success', message }, autoCloseTimer);
toast.success(message, {
autoClose: autoCloseTimer,
});
};
/**
@ -139,5 +94,15 @@ export const showSuccessToast = (message: string, autoCloseTimer = 5000) => {
* @param autoCloseTimer Set the delay in ms to close the toast automatically. `Default: 5000`
*/
export const showInfoToast = (message: string, autoCloseTimer = 5000) => {
useAlertStore.getState().addAlert({ type: 'info', message }, autoCloseTimer);
toast.info(message, {
autoClose: autoCloseTimer,
});
};
/**
* Clear all the toast messages.
*/
export const clearAllToasts = () => {
toast.clearWaitingQueue();
toast.dismiss();
};

View File

@ -12481,6 +12481,13 @@ react-test-renderer@^16.14.0:
react-is "^16.8.6"
scheduler "^0.19.1"
react-toastify@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-8.2.0.tgz#ef7d56bdfdc6272ca6b228368ab564721c3a3244"
integrity sha512-Pg2Ju7NngAamarFvLwqrFomJ57u/Ay6i6zfLurt/qPynWkAkOthu6vxfqYpJCyNhHRhR4hu7+bySSeWWJu6PAg==
dependencies:
clsx "^1.1.1"
react-transition-group@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"