diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx
index 33761b58d84..4783d9ad1c7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx
@@ -21,12 +21,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
-import { ReactComponent as DataProductIcon } from '../../../assets/svg/ic-data-product.svg';
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-delete.svg';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.svg';
import { ReactComponent as IconDropdown } from '../../../assets/svg/menu.svg';
import { ReactComponent as StyleIcon } from '../../../assets/svg/style.svg';
-import { DE_ACTIVE_COLOR } from '../../../constants/constants';
import { CustomizeEntityType } from '../../../constants/Customize.constants';
import { EntityField } from '../../../constants/Feeds.constants';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
@@ -65,6 +63,7 @@ import {
import { showErrorToast } from '../../../utils/ToastUtils';
import { useRequiredParams } from '../../../utils/useRequiredParams';
import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable';
+import { EntityAvatar } from '../../common/EntityAvatar/EntityAvatar';
import { ManageButtonItemLabel } from '../../common/ManageButtonContentItem/ManageButtonContentItem.component';
import ResizablePanels from '../../common/ResizablePanels/ResizablePanels';
import TabsLabel from '../../common/TabsLabel/TabsLabel.component';
@@ -510,23 +509,13 @@ const DataProductsDetailsPage = ({
entityType={EntityType.DATA_PRODUCT}
handleFollowingClick={handleFollowingClick}
icon={
- dataProduct.style?.iconURL ? (
-
- ) : (
-
- )
+
}
isFollowing={isFollowing}
isFollowingLoading={isFollowingLoading}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx
index 51c6cea9818..b30d2f0369a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx
@@ -32,8 +32,6 @@ import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-delete.svg';
-import { ReactComponent as DomainIcon } from '../../../assets/svg/ic-domain.svg';
-import { ReactComponent as SubDomainIcon } from '../../../assets/svg/ic-subdomain.svg';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.svg';
import { ReactComponent as IconDropdown } from '../../../assets/svg/menu.svg';
import { ReactComponent as StyleIcon } from '../../../assets/svg/style.svg';
@@ -43,7 +41,7 @@ import { AssetsTabRef } from '../../../components/Glossary/GlossaryTerms/tabs/As
import { AssetsOfEntity } from '../../../components/Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
import EntityNameModal from '../../../components/Modals/EntityNameModal/EntityNameModal.component';
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
-import { DE_ACTIVE_COLOR, ERROR_MESSAGE } from '../../../constants/constants';
+import { ERROR_MESSAGE } from '../../../constants/constants';
import { EntityField } from '../../../constants/Feeds.constants';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
import {
@@ -97,6 +95,7 @@ import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import { useRequiredParams } from '../../../utils/useRequiredParams';
import { useFormDrawerWithRef } from '../../common/atoms/drawer';
import DeleteWidgetModal from '../../common/DeleteWidget/DeleteWidgetModal';
+import { EntityAvatar } from '../../common/EntityAvatar/EntityAvatar';
import { AlignRightIconButton } from '../../common/IconButtons/EditIconButton';
import Loader from '../../common/Loader/Loader';
import { GenericProvider } from '../../Customization/GenericProvider/GenericProvider';
@@ -681,36 +680,14 @@ const DomainDetailsPage = ({
}, [domainFqn, fetchSubDomainsCount]);
const iconData = useMemo(() => {
- if (domain.style?.iconURL) {
- return (
-
- );
- } else if (isSubDomain) {
- return (
-
- );
- }
-
return (
-
);
}, [domain, isSubDomain]);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/EntityAvatar/EntityAvatar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/EntityAvatar/EntityAvatar.tsx
new file mode 100644
index 00000000000..ee3497628f5
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/EntityAvatar/EntityAvatar.tsx
@@ -0,0 +1,123 @@
+/*
+ * 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 { Avatar, useTheme } from '@mui/material';
+import { FC } from 'react';
+import { ReactComponent as SubDomainIcon } from '../../../assets/svg/ic-subdomain.svg';
+import {
+ getDefaultIconForEntityType,
+ ICON_MAP,
+} from '../../../utils/IconUtils';
+
+export interface EntityAvatarProps {
+ entity: {
+ name?: string;
+ displayName?: string;
+ entityType?: string;
+ style?: {
+ color?: string;
+ iconURL?: string;
+ };
+ parent?: {
+ type?: string;
+ };
+ };
+ size?: number;
+ className?: string;
+}
+
+/**
+ * A reusable component for rendering entity avatars with custom icons
+ * Supports URL-based icons, icon names from ICON_MAP, and default icons
+ */
+export const EntityAvatar: FC = ({
+ entity,
+ size = 40,
+ className = 'entity-avatar',
+}) => {
+ const theme = useTheme();
+ const bgColor = entity.style?.color || theme.palette.allShades.brand[600];
+
+ // Check if it's a sub-domain
+ const isSubDomain = entity.parent?.type === 'domain';
+
+ // Check if it's a URL (for Avatar src prop)
+ const isUrl =
+ entity.style?.iconURL &&
+ (entity.style.iconURL.startsWith('http') ||
+ entity.style.iconURL.startsWith('/'));
+
+ if (isUrl) {
+ // For URLs, use Avatar's src prop
+ return (
+
+ );
+ }
+
+ // For icon names, render the icon component
+ const IconComponent = entity.style?.iconURL
+ ? ICON_MAP[entity.style.iconURL]
+ : null;
+
+ if (IconComponent) {
+ return (
+
+
+
+ );
+ }
+
+ // Default icons when no iconURL is provided
+ let DefaultIcon;
+ if (isSubDomain) {
+ DefaultIcon = SubDomainIcon;
+ } else {
+ DefaultIcon = getDefaultIconForEntityType(entity.entityType);
+ }
+
+ return (
+
+
+
+ );
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useCellRenderer.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useCellRenderer.tsx
index 2d6695f177c..3b316facb0a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useCellRenderer.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useCellRenderer.tsx
@@ -11,15 +11,12 @@
* limitations under the License.
*/
-import { Avatar, AvatarGroup, Box, Typography, useTheme } from '@mui/material';
+import { AvatarGroup, Box, Typography, useTheme } from '@mui/material';
import { ReactNode, useMemo } from 'react';
import { EntityType } from '../../../../enums/entity.enum';
import { EntityReference } from '../../../../generated/entity/type';
import { getEntityName } from '../../../../utils/EntityUtils';
-import {
- getDefaultIconForEntityType,
- ICON_MAP,
-} from '../../../../utils/IconUtils';
+import { EntityAvatar } from '../../EntityAvatar/EntityAvatar';
import { ProfilePicture } from '../ProfilePicture';
import { CellRenderer, ColumnConfig } from '../shared/types';
import TagsCell from './TagsCell';
@@ -45,79 +42,9 @@ export const useCellRenderer = <
entityName: (entity: any) => {
const entityName = getEntityName(entity);
- // Generic entity icon (exact copy from useTableRow)
- const getEntityIcon = () => {
- const bgColor =
- entity.style?.color || theme.palette.allShades.brand[600];
-
- // Check if it's a URL (for Avatar src prop)
- const isUrl =
- entity.style?.iconURL &&
- (entity.style.iconURL.startsWith('http') ||
- entity.style.iconURL.startsWith('/'));
-
- if (isUrl) {
- // For URLs, use Avatar's src prop
- return (
-
- );
- }
-
- // For icon names, render the icon component
- const IconComponent = entity.style?.iconURL
- ? ICON_MAP[entity.style.iconURL]
- : null;
- if (IconComponent) {
- return (
-
-
-
- );
- }
-
- // Default icons when no iconURL is provided
- const DefaultIcon = getDefaultIconForEntityType(entity.entityType);
-
- return (
-
-
-
- );
- };
-
return (
- {getEntityIcon()}
+