Repository: open-metadata/OpenMetadata
Pull request #20382: Feat : Notification Update for glossary term approval 
Author: login: shrushti2000
Labels: name: UI
description: UI specific issues
name: safe to test
description: Add this label to run secure Github workflows on PRs
Created: 2025-03-21T17:16:33Z | Merged: 2025-03-23T16:56:28Z

Fixes [#19849](https://github.com/open-metadata/OpenMetadata/issues/19849)

Updated notification for Glossary Term Approval so that the reviewers can see which term they are approving and it belongs to which glossary from the Notifications Card as reviewers of the glossary term were not able to see for which term their approvals are required from the notifications.
Also added links to the glossary term, glossary and task


https://github.com/user-attachments/assets/7fadc7e6-1bb6-4717-8200-fdc54ce5c1df



Existing Notification

<img width="432" alt="Screenshot 2025-03-21 at 10 42 45 PM" src="https://github.com/user-attachments/assets/4179c2fc-cac6-42f4-96da-86afb7a04ce4" />

Updated Notification
<img width="425" alt="Screenshot 2025-03-21 at 10 30 05 PM" src="https://github.com/user-attachments/assets/9096c5cd-0e4c-4fc7-bd39-06986cf5f130" />






#
### Type of change:
<!-- You should choose 1 option and delete options that aren't relevant -->
- [ ] Bug fix
- [ ] Improvement
- [ ] New feature
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation

#
### Checklist:
<!-- add an x in [] if done, don't mark items that you didn't do !-->
- [x] I have read the [**CONTRIBUTING**](https://docs.open-metadata.org/developers/contribute) document.
- [ ] My PR title is `Fixes <issue-number>: <short explanation>`
- [ ] I have commented on my code, particularly in hard-to-understand areas. 
- [ ] For JSON Schema changes: I updated the migration scripts or explained why it is not needed.

<!-- Based on the type(s) of your change, uncomment the required checklist 👇 -->

<!-- Bug fix
- [ ] I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.
-->

<!-- Improvement
- [ ] I have added tests around the new logic.
- [ ] For connector/ingestion changes: I updated the documentation.
-->

<!-- New feature
- [ ] The issue properly describes why the new feature is needed, what's the goal, and how we are building it. Any discussion
    or decision-making process is reflected in the issue.
- [ ] I have updated the documentation.
- [ ] I have added tests around the new logic.
-->

<!-- Breaking change
- [ ] I have added the tag `Backward-Incompatible-Change`.
-->



Diff:
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts
index 1ad4a6433c14..0dd0e4d24229 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts
@@ -132,6 +132,20 @@ test.describe('Glossary tests', () => {
           glossary1.data.terms[0].data.name
         );
 
+        // Check reviewer's notifications before approval
+        await page1.getByTestId('task-notifications').click();
+        await page1.waitForSelector('.ant-dropdown');
+        const firstNotification = page1
+          .locator('.ant-list-items > .ant-list-item')
+          .first();
+
+        await expect(firstNotification).toContainText(
+          `Approval required for ${glossary1.data.terms[0].data.name}`
+        );
+        await expect(firstNotification).toContainText(
+          glossary1.data.fullyQualifiedName
+        );
+
         await approveGlossaryTermTask(page1, glossary1.data.terms[0].data);
         await redirectToHomePage(page1);
         await sidebarClick(page1, SidebarItem.GLOSSARY);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab.less
index 4d7cd9e27e75..699bea99aa46 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab.less
@@ -518,7 +518,6 @@
       content: none;
     }
   }
-
 }
 
 .task-filter-container {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreSearchCard/ExploreSearchCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreSearchCard/ExploreSearchCard.tsx
index 65480edcc409..9e87a4ff3413 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreSearchCard/ExploreSearchCard.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreSearchCard/ExploreSearchCard.tsx
@@ -207,7 +207,7 @@ const ExploreSearchCard: React.FC<ExploreSearchCardProps> = forwardRef<
           {!hideBreadcrumbs && (
             <Col className="d-flex justify-between items-center" flex="auto">
               <div className="d-flex gap-2 items-center">
-                {serviceIcon}
+                {breadcrumbs.length > 0 && serviceIcon}
                 <div className="entity-breadcrumb" data-testid="category-name">
                   <TitleBreadcrumb
                     className={classNameForBreadcrumb}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
index 0baf5c98f53e..08be523ba45b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
@@ -464,7 +464,7 @@ const NavBar: React.FC = () => {
                   <Badge
                     dot={hasTaskNotification || hasMentionNotification}
                     offset={[-3, 3]}>
-                    <IconBell width={20} />
+                    <IconBell data-testid="task-notifications" width={20} />
                   </Badge>
                 }
                 size="large"
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
index 36cc7ffa8929..217dda54ea69 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
@@ -12,17 +12,21 @@
  */
 
 import { List, Space, Typography } from 'antd';
-import React, { FC } from 'react';
+import React, { FC, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import { Link } from 'react-router-dom';
-import { ThreadType } from '../../generated/entity/feed/thread';
+import { EntityType } from '../../enums/entity.enum';
+import { TaskType, ThreadType } from '../../generated/entity/feed/thread';
 import {
   formatDateTime,
   getRelativeTime,
 } from '../../utils/date-time/DateTimeUtils';
+import { getEntityLinkFromType } from '../../utils/EntityUtils';
 import { entityDisplayName, prepareFeedLink } from '../../utils/FeedUtils';
+import Fqn from '../../utils/Fqn';
 import { getTaskDetailPath } from '../../utils/TasksUtils';
 import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
+import { SourceType } from '../SearchedData/SearchedData.interface';
 import { NotificationFeedProp } from './NotificationFeedCard.interface';
 
 const NotificationFeedCard: FC<NotificationFeedProp> = ({
@@ -36,6 +40,50 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
   const { t } = useTranslation();
   const { task: taskDetails } = task ?? {};
 
+  const taskContent = useMemo(() => {
+    if (
+      entityType === 'glossaryTerm' &&
+      task.task?.type === TaskType.RequestApproval
+    ) {
+      return (
+        <>
+          <span className="p-x-xss">{task.message}</span>
+          <Link
+            className='className="p-r-xss"'
+            to={getEntityLinkFromType(
+              task?.entityRef?.fullyQualifiedName ?? '',
+              task?.entityRef?.type as EntityType,
+              task?.entityRef as SourceType
+            )}>
+            <span className="m-r-xss">{task?.entityRef?.displayName}</span>
+          </Link>
+          <span>{t('label.of-lowercase')}</span>
+          <Link
+            to={getEntityLinkFromType(
+              Fqn.split(task?.entityRef?.fullyQualifiedName ?? '')[0],
+              task?.entityRef?.type as EntityType,
+              task?.entityRef as SourceType
+            )}>
+            <span className="m-l-xss">
+              {Fqn.split(task?.entityRef?.fullyQualifiedName ?? '')[0]}
+            </span>
+          </Link>
+        </>
+      );
+    }
+
+    return (
+      <>
+        <span className="p-x-xss">
+          {t('message.assigned-you-a-new-task-lowercase')}
+        </span>
+        <Link to={getTaskDetailPath(task)}>
+          {`#${taskDetails?.id}`} {taskDetails?.type}
+        </Link>
+      </>
+    );
+  }, [entityType, task, taskDetails]);
+
   return (
     <Link
       className="no-underline"
@@ -64,14 +112,7 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
                   </Link>
                 </>
               ) : (
-                <>
-                  <span className="p-x-xss">
-                    {t('message.assigned-you-a-new-task-lowercase')}
-                  </span>
-                  <Link to={getTaskDetailPath(task)}>
-                    {`#${taskDetails?.id}`} {taskDetails?.type}
-                  </Link>
-                </>
+                taskContent
               )}
             </Typography.Paragraph>
             <Typography.Text
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx
index 1532af4458c1..e475b9c1021f 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx
@@ -38,6 +38,12 @@ jest.mock('react-router-dom', () => ({
       <p data-testid="link">{children}</p>
     )),
 }));
+jest.mock('../../utils/EntityUtils', () => ({
+  getEntityLinkFromType: jest.fn().mockReturnValue('/mock-entity-link'),
+}));
+jest.mock('../../utils/Fqn', () => ({
+  split: jest.fn().mockReturnValue(['mockGlossary']),
+}));
 const mockThread = {
   about: 'test',
   id: '33873393-bd68-46e9-bccc-7701c1c41ad6',


Reviews:
user: login: chirag-madlani
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-21T18:22:24Z
user: login: shrushti2000
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-22T15:53:43Z
user: login: shrushti2000
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-22T15:53:45Z
user: login: chirag-madlani
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-23T16:56:20Z

Review comments:
diff_hunk: @@ -65,12 +69,49 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
                 </>
               ) : (
                 <>
-                  <span className="p-x-xss">
-                    {t('message.assigned-you-a-new-task-lowercase')}
-                  </span>
-                  <Link to={getTaskDetailPath(task)}>
-                    {`#${taskDetails?.id}`} {taskDetails?.type}
-                  </Link>
+                  {entityType === 'glossaryTerm' &&
path: openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
user: login: chirag-madlani
body: @shrushti2000 have you check if link redirections are working for notification?
For task and mention both?
created_at: 2025-03-21T18:21:41Z
author_association: COLLABORATOR
diff_hunk: @@ -65,12 +69,49 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
                 </>
               ) : (
                 <>
-                  <span className="p-x-xss">
-                    {t('message.assigned-you-a-new-task-lowercase')}
-                  </span>
-                  <Link to={getTaskDetailPath(task)}>
-                    {`#${taskDetails?.id}`} {taskDetails?.type}
-                  </Link>
+                  {entityType === 'glossaryTerm' &&
path: openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
user: login: chirag-madlani
body: If possible pull computation to useMemo and pass it to render for optimisation 
created_at: 2025-03-21T18:22:17Z
author_association: COLLABORATOR
diff_hunk: @@ -65,12 +69,49 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
                 </>
               ) : (
                 <>
-                  <span className="p-x-xss">
-                    {t('message.assigned-you-a-new-task-lowercase')}
-                  </span>
-                  <Link to={getTaskDetailPath(task)}>
-                    {`#${taskDetails?.id}`} {taskDetails?.type}
-                  </Link>
+                  {entityType === 'glossaryTerm' &&
path: openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
user: login: shrushti2000
body: 
In our existing application , if the user clicks on the notification we are redirecting the user to the task . This behaviour is replicated in this PR as well. This is working for both Tasks and Mentions
Only for glossary term approval , I have added links to the term , glossary and to the task if user clicks in the notification.
created_at: 2025-03-22T15:53:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,12 +69,49 @@ const NotificationFeedCard: FC<NotificationFeedProp> = ({
                 </>
               ) : (
                 <>
-                  <span className="p-x-xss">
-                    {t('message.assigned-you-a-new-task-lowercase')}
-                  </span>
-                  <Link to={getTaskDetailPath(task)}>
-                    {`#${taskDetails?.id}`} {taskDetails?.type}
-                  </Link>
+                  {entityType === 'glossaryTerm' &&
path: openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx
user: login: shrushti2000
body: done
created_at: 2025-03-22T15:53:45Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-23T07:43:18Z
body: <!-- Jest Coverage Comment: ui-coverage-tests -->
# Jest test Coverage

## UI tests summary

| Lines | Statements | Branches | Functions |
| --- | --- | --- | --- |
| <a href="https://github.com/open-metadata/OpenMetadata/blob//README.md"><img alt="Coverage: 63%" src="https://img.shields.io/badge/Coverage-63%25-yellow.svg" /></a><br/> | 63.42% (43813/69082) | 38.68% (17446/45101) | 43.21% (5265/12186) |

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-23T15:03:45Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-metadata-ui&pullRequest=20382) **Quality Gate passed for 'open-metadata-ui'**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=open-metadata-ui&pullRequest=20382&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-metadata-ui&pullRequest=20382&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-metadata-ui&pullRequest=20382&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-metadata-ui&pullRequest=20382&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-metadata-ui&pullRequest=20382&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-metadata-ui&pullRequest=20382)


author_association: NONE