diff --git a/app/client/src/ce/pages/Applications/index.tsx b/app/client/src/ce/pages/Applications/index.tsx
index b52921360fe2..c418f4e16984 100644
--- a/app/client/src/ce/pages/Applications/index.tsx
+++ b/app/client/src/ce/pages/Applications/index.tsx
@@ -60,6 +60,7 @@ import {
MenuItem as ListItem,
Text,
TextType,
+ FontWeight,
} from "@appsmith/ads-old";
import { loadingUserWorkspaces } from "pages/Applications/ApplicationLoaders";
import PageWrapper from "pages/common/PageWrapper";
@@ -395,6 +396,59 @@ export const textIconStyles = (props: { color: string; hover: string }) => {
`;
};
+const WorkspaceItemRow = styled.a<{ disabled?: boolean; selected?: boolean }>`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ text-decoration: none;
+ padding: 0px var(--ads-spaces-6);
+ background-color: ${(props) =>
+ props.selected ? "var(--ads-v2-color-bg-muted)" : "transparent"};
+ .${Classes.TEXT} {
+ color: var(--ads-v2-color-fg);
+ }
+ .${Classes.ICON} {
+ svg {
+ path {
+ fill: var(--ads-v2-color-fg);
+ }
+ }
+ }
+ height: 38px;
+
+ ${(props) =>
+ !props.disabled
+ ? `
+ &:hover {
+ text-decoration: none;
+ cursor: pointer;
+ background-color: var(--ads-v2-color-bg-subtle);
+ border-radius: var(--ads-v2-border-radius);
+ }`
+ : `
+ &:hover {
+ text-decoration: none;
+ cursor: default;
+ }
+ `}
+`;
+
+const WorkspaceIconContainer = styled.span`
+ display: flex;
+ align-items: center;
+
+ .${Classes.ICON} {
+ margin-right: var(--ads-spaces-5);
+ }
+`;
+
+const WorkspaceLogoImage = styled.img`
+ width: 16px;
+ height: 16px;
+ object-fit: contain;
+ margin-right: var(--ads-spaces-5);
+`;
+
export function WorkspaceMenuItem({
isFetchingWorkspaces,
selected,
@@ -403,6 +457,7 @@ export function WorkspaceMenuItem({
}: any) {
const history = useHistory();
const location = useLocation();
+ const [imageError, setImageError] = React.useState(false);
const handleWorkspaceClick = () => {
const workspaceId = workspace?.id;
@@ -414,8 +469,50 @@ export function WorkspaceMenuItem({
}
};
+ const handleImageError = () => {
+ setImageError(true);
+ };
+
if (!workspace.id) return null;
+ const hasLogo = workspace?.logoUrl && !imageError;
+ const displayText = isFetchingWorkspaces
+ ? workspace?.name
+ : workspace?.name?.length > 22
+ ? workspace.name.slice(0, 22).concat(" ...")
+ : workspace?.name;
+
+ // Use custom component when there's a logo, otherwise use ListItem
+ if (hasLogo && !isFetchingWorkspaces) {
+ const showTooltip = workspace?.name && workspace.name.length > 22;
+ const itemContent = (
+
+
+
+
+ {displayText}
+
+
+
+ );
+
+ return showTooltip ? (
+
+ {itemContent}
+
+ ) : (
+ itemContent
+ );
+ }
+
return (
workspace.id === action.payload.id,
+ );
+
+ if (searchWorkspaceIndex !== -1) {
+ draftState.searchEntities.workspaces[searchWorkspaceIndex] = {
+ ...draftState.searchEntities.workspaces[searchWorkspaceIndex],
+ ...action.payload,
+ };
+ }
+ }
},
[ReduxActionErrorTypes.SAVE_WORKSPACE_ERROR]: (
draftState: WorkspaceReduxState,
diff --git a/app/client/src/pages/common/SearchBar/WorkspaceSearchItems.tsx b/app/client/src/pages/common/SearchBar/WorkspaceSearchItems.tsx
index 74eeee3dd2f4..f2cbdd92ea2a 100644
--- a/app/client/src/pages/common/SearchBar/WorkspaceSearchItems.tsx
+++ b/app/client/src/pages/common/SearchBar/WorkspaceSearchItems.tsx
@@ -1,6 +1,6 @@
import type { Workspace } from "ee/constants/workspaceConstants";
import { Icon, Text } from "@appsmith/ads";
-import React from "react";
+import React, { useState } from "react";
import { useHistory } from "react-router";
import styled from "styled-components";
@@ -15,14 +15,68 @@ export const SearchListItem = styled.div`
}
`;
+const WorkspaceLogoImage = styled.img`
+ width: 16px;
+ height: 16px;
+ min-width: 16px;
+ min-height: 16px;
+ object-fit: contain;
+ margin-right: 8px;
+`;
+
interface Props {
workspacesList: Workspace[] | undefined;
setIsDropdownOpen: (isOpen: boolean) => void;
}
+interface WorkspaceItemProps {
+ workspace: Workspace;
+ setIsDropdownOpen: (isOpen: boolean) => void;
+}
+
+const WorkspaceItem = ({
+ setIsDropdownOpen,
+ workspace,
+}: WorkspaceItemProps) => {
+ const history = useHistory();
+ const [imageError, setImageError] = useState(false);
+ const hasLogo = workspace.logoUrl && !imageError;
+
+ const handleImageError = () => {
+ setImageError(true);
+ };
+
+ return (
+ {
+ setIsDropdownOpen(false);
+ history.push(`/applications?workspaceId=${workspace?.id}`);
+ }}
+ >
+ {hasLogo ? (
+
+ ) : (
+
+ )}
+
+ {workspace.name}
+
+
+ );
+};
+
const WorkspaceSearchItems = (props: Props) => {
const { setIsDropdownOpen, workspacesList } = props;
- const history = useHistory();
if (!workspacesList || workspacesList?.length === 0) return null;
@@ -32,24 +86,11 @@ const WorkspaceSearchItems = (props: Props) => {
Workspaces
{workspacesList.map((workspace: Workspace) => (
- {
- setIsDropdownOpen(false);
- history.push(`/applications?workspaceId=${workspace?.id}`);
- }}
- >
-
-
- {workspace.name}
-
-
+ setIsDropdownOpen={setIsDropdownOpen}
+ workspace={workspace}
+ />
))}
);
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Workspace.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Workspace.java
index 49f8fc3afa82..2485a8a40f84 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Workspace.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Workspace.java
@@ -71,6 +71,11 @@ public static String toSlug(String text) {
@JsonView(Views.Public.class)
public String getLogoUrl() {
+ // If there's no logo, return null instead of constructing a URL like "/api/v1/assets/null"
+ // This prevents the frontend from making pointless requests to load a non-existent image
+ if (logoAssetId == null || logoAssetId.isEmpty()) {
+ return null;
+ }
return Url.ASSET_URL + "/" + logoAssetId;
}