diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.interface.ts
index beae0a748df..78152fcea5d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.interface.ts
@@ -30,6 +30,7 @@ export interface SuggestionsContextType {
allSuggestionsUsers: EntityReference[];
onUpdateActiveUser: (user?: EntityReference) => void;
fetchSuggestions: () => void;
+ fetchSuggestionsByUserId: (userId: string, limit?: number) => void;
acceptRejectSuggestion: (
suggestion: Suggestion,
action: SuggestionAction
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.test.tsx
index 1108fdb5c76..e72ac4bc1fe 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.test.tsx
@@ -45,6 +45,7 @@ jest.mock('../../../hooks/useFqn', () => ({
jest.mock('../../../rest/suggestionsAPI', () => ({
getSuggestionsList: jest.fn().mockImplementation(() => Promise.resolve()),
+ getSuggestionsByUserId: jest.fn().mockImplementation(() => Promise.resolve()),
approveRejectAllSuggestions: jest.fn(),
updateSuggestionStatus: jest.fn(),
}));
@@ -144,6 +145,26 @@ describe('SuggestionsProvider', () => {
SuggestionAction.Reject
);
});
+
+ it('calls fetchSuggestionsByUserId when button is clicked', async () => {
+ const { getSuggestionsByUserId } = await import(
+ '../../../rest/suggestionsAPI'
+ );
+
+ render(
+
+
+
+ );
+
+ const fetchByUserIdBtn = screen.getByText('Fetch By User ID');
+ fireEvent.click(fetchByUserIdBtn);
+
+ expect(getSuggestionsByUserId).toHaveBeenCalledWith('test-user-id', {
+ entityFQN: 'mockFQN',
+ limit: 10,
+ });
+ });
});
function TestComponent() {
@@ -151,6 +172,7 @@ function TestComponent() {
acceptRejectAllSuggestions,
onUpdateActiveUser,
acceptRejectSuggestion,
+ fetchSuggestionsByUserId,
} = useSuggestionsContext();
return (
@@ -181,6 +203,9 @@ function TestComponent() {
}>
Reject One
+
>
);
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
index 97be6d74488..e0f41c8d92a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
@@ -35,6 +35,7 @@ import { useFqn } from '../../../hooks/useFqn';
import { usePub } from '../../../hooks/usePubSub';
import {
approveRejectAllSuggestions,
+ getSuggestionsByUserId,
getSuggestionsList,
updateSuggestionStatus,
} from '../../../rest/suggestionsAPI';
@@ -97,6 +98,43 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
[entityFqn, suggestionLimit]
);
+ const fetchSuggestionsByUserId = useCallback(
+ async (userId: string, limit?: number) => {
+ setLoading(true);
+ try {
+ const { data } = await getSuggestionsByUserId(userId, {
+ entityFQN: entityFqn,
+ limit: limit ?? suggestionLimit,
+ });
+
+ // Merge new suggestions with existing ones, removing duplicates by ID
+ setSuggestions((prevSuggestions) => {
+ const existingIds = new Set(prevSuggestions.map((s) => s.id));
+ const newSuggestions = data.filter((s) => !existingIds.has(s.id));
+ const mergedSuggestions = [...prevSuggestions, ...newSuggestions];
+
+ // Update grouped suggestions with merged data
+ const { allUsersList, groupedSuggestions } =
+ getSuggestionByType(mergedSuggestions);
+ setAllSuggestionsUsers(uniqWith(allUsersList, isEqual));
+ setSuggestionsByUser(groupedSuggestions);
+
+ return mergedSuggestions;
+ });
+ } catch (err) {
+ showErrorToast(
+ err as AxiosError,
+ t('server.entity-fetch-error', {
+ entity: t('label.suggestion-lowercase-plural'),
+ })
+ );
+ } finally {
+ setLoading(false);
+ }
+ },
+ [entityFqn, suggestionLimit]
+ );
+
const acceptRejectSuggestion = useCallback(
async (suggestion: Suggestion, status: SuggestionAction) => {
try {
@@ -188,6 +226,7 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
allSuggestionsUsers,
onUpdateActiveUser,
fetchSuggestions,
+ fetchSuggestionsByUserId,
acceptRejectSuggestion,
acceptRejectAllSuggestions,
};
@@ -203,6 +242,7 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
allSuggestionsUsers,
onUpdateActiveUser,
fetchSuggestions,
+ fetchSuggestionsByUserId,
acceptRejectSuggestion,
acceptRejectAllSuggestions,
]);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarouselItem/AvatarCarouselItem.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarouselItem/AvatarCarouselItem.test.tsx
index d0eefcd2203..8203e566cd4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarouselItem/AvatarCarouselItem.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarouselItem/AvatarCarouselItem.test.tsx
@@ -13,6 +13,7 @@
import { render } from '@testing-library/react';
import React from 'react';
import { EntityReference } from '../../../generated/entity/type';
+import { useSuggestionsContext } from '../../Suggestions/SuggestionsProvider/SuggestionsProvider';
import AvatarCarouselItem from './AvatarCarouselItem';
const suggestions = [
@@ -46,6 +47,7 @@ jest.mock('../../Suggestions/SuggestionsProvider/SuggestionsProvider', () => ({
acceptRejectSuggestion: jest.fn(),
selectedUserSuggestions: [],
onUpdateActiveUser: jest.fn(),
+ fetchSuggestionsByUserId: jest.fn(),
})),
__esModule: true,
default: 'SuggestionsProvider',
@@ -101,6 +103,37 @@ describe('AvatarCarouselItem', () => {
expect(onAvatarClick).toHaveBeenCalledWith(index);
});
+ it('calls fetchSuggestionsByUserId when avatar is clicked', () => {
+ const mockFetchSuggestionsByUserId = jest.fn();
+ (useSuggestionsContext as jest.Mock).mockImplementation(() => ({
+ suggestions: suggestions,
+ suggestionsByUser: suggByUser,
+ allSuggestionsUsers: [
+ { id: '1', name: 'Avatar 1', type: 'user' },
+ { id: '2', name: 'Avatar 2', type: 'user' },
+ ],
+ acceptRejectSuggestion: jest.fn(),
+ selectedUserSuggestions: [],
+ onUpdateActiveUser: jest.fn(),
+ fetchSuggestionsByUserId: mockFetchSuggestionsByUserId,
+ }));
+
+ const { getByTestId } = render(
+
+ );
+
+ const button = getByTestId(`avatar-carousel-item-${avatar.id}`);
+ button.click();
+
+ expect(mockFetchSuggestionsByUserId).toHaveBeenCalledWith(avatar.id);
+ });
+
it('sets isActive class when isActive is true', () => {
const { getByTestId } = render(
{
- const { suggestionsByUser } = useSuggestionsContext();
+ const { suggestionsByUser, fetchSuggestionsByUserId } =
+ useSuggestionsContext();
const buttonRef = useRef(null);
avatarBtnRefs.current[index] = buttonRef;
const getUserSuggestionsCount = useCallback(
@@ -42,6 +43,16 @@ const AvatarCarouselItem = ({
[suggestionsByUser]
);
+ const handleAvatarClick = useCallback(() => {
+ // Call the original onAvatarClick function
+ onAvatarClick(index);
+
+ // Fetch suggestions for this specific user
+ if (avatar.id) {
+ fetchSuggestionsByUserId(avatar.id);
+ }
+ }, [onAvatarClick, index, avatar.id, fetchSuggestionsByUserId]);
+
const button = (
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/suggestionsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/suggestionsAPI.ts
index 4d188aa2884..469c509bb23 100644
--- a/openmetadata-ui/src/main/resources/ui/src/rest/suggestionsAPI.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/rest/suggestionsAPI.ts
@@ -25,6 +25,7 @@ const BASE_URL = '/suggestions';
export type ListSuggestionsParams = ListParams & {
entityFQN?: string;
limit?: number;
+ userId?: string;
};
export const getSuggestionsList = async (params?: ListSuggestionsParams) => {
@@ -35,6 +36,20 @@ export const getSuggestionsList = async (params?: ListSuggestionsParams) => {
return response.data;
};
+export const getSuggestionsByUserId = async (
+ userId: string,
+ params?: Omit
+) => {
+ const response = await APIClient.get>(BASE_URL, {
+ params: {
+ ...params,
+ userId,
+ },
+ });
+
+ return response.data;
+};
+
export const updateSuggestionStatus = (
data: Suggestion,
action: SuggestionAction