diff --git a/src/components/Assets/AssetList/AssetEntry/index.tsx b/src/components/Assets/AssetList/AssetEntry/index.tsx
index c17655e08..b71f766d8 100644
--- a/src/components/Assets/AssetList/AssetEntry/index.tsx
+++ b/src/components/Assets/AssetList/AssetEntry/index.tsx
@@ -1,14 +1,26 @@
-import { useTheme } from "styled-components";
+import { DefaultTheme, useTheme } from "styled-components";
import { getInsightImportanceColor } from "../../../../utils/getInsightImportanceColor";
import { getInsightTypeInfo } from "../../../../utils/getInsightTypeInfo";
import { getInsightTypeOrderPriority } from "../../../../utils/getInsightTypeOrderPriority";
import { timeAgo } from "../../../../utils/timeAgo";
+import { GlobeIcon } from "../../../common/icons/GlobeIcon";
import { getAssetTypeInfo } from "../../utils";
import * as s from "./styles";
import { AssetEntryProps } from "./types";
+const getServiceIconColor = (theme: DefaultTheme) => {
+ switch (theme.mode) {
+ case "light":
+ return "#4d668a";
+ case "dark":
+ case "dark-jetbrains":
+ return "#dadada";
+ }
+};
+
export const AssetEntry = (props: AssetEntryProps) => {
const theme = useTheme();
+ const serviceIconColor = getServiceIconColor(theme);
const handleLinkClick = () => {
props.onAssetLinkClick(props.entry);
@@ -69,10 +81,11 @@ export const AssetEntry = (props: AssetEntryProps) => {
-
+
Services
+
{props.entry.serviceName}
@@ -83,15 +96,6 @@ export const AssetEntry = (props: AssetEntryProps) => {
)}
-
- Last
-
- {timeAgo(lastSeenDateTime)}
- ago
-
-
-
-
Performance
@@ -101,7 +105,15 @@ export const AssetEntry = (props: AssetEntryProps) => {
)}
-
+
+
+
+ Last
+
+ {timeAgo(lastSeenDateTime)}
+ ago
+
+
Slowest 5%
@@ -113,7 +125,7 @@ export const AssetEntry = (props: AssetEntryProps) => {
)}
-
+
);
diff --git a/src/components/Assets/AssetList/AssetEntry/styles.ts b/src/components/Assets/AssetList/AssetEntry/styles.ts
index b45aa6634..bb0a04c45 100644
--- a/src/components/Assets/AssetList/AssetEntry/styles.ts
+++ b/src/components/Assets/AssetList/AssetEntry/styles.ts
@@ -74,7 +74,7 @@ export const InsightIconContainer = styled(AssetTypeIconContainer)`
export const StatsContainer = styled.div`
display: flex;
- gap: 15px 12px;
+ gap: 16px 12px;
flex-wrap: wrap;
font-size: 10px;
line-height: 12px;
@@ -84,20 +84,12 @@ export const Stats = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
+ width: 120px;
`;
-export const StatsColumn = styled.div`
+export const StatsRow = styled.div`
display: flex;
- flex-direction: column;
- gap: 12px;
-
- &:first-child {
- width: 35%;
- }
-
- &:nth-child(2) {
- width: 35%;
- }
+ gap: 16px;
`;
export const ServicesContainer = styled.div`
@@ -107,7 +99,7 @@ export const ServicesContainer = styled.div`
`;
export const ServiceName = styled.div`
- padding: 4px 6px;
+ padding: 4px 0;
border-radius: 23px;
line-height: 8px;
overflow: hidden;
@@ -122,15 +114,6 @@ export const ServiceName = styled.div`
return "#dadada";
}
}};
- background: ${({ theme }) => {
- switch (theme.mode) {
- case "light":
- return "#e9eef4";
- case "dark":
- case "dark-jetbrains":
- return "#2e2e2e";
- }
- }};
`;
export const ValueContainer = styled.div`
diff --git a/src/components/Assets/AssetList/index.tsx b/src/components/Assets/AssetList/index.tsx
index 52afab239..c681771c1 100644
--- a/src/components/Assets/AssetList/index.tsx
+++ b/src/components/Assets/AssetList/index.tsx
@@ -6,6 +6,7 @@ import { PopoverContent } from "../../common/Popover/PopoverContent";
import { PopoverTrigger } from "../../common/Popover/PopoverTrigger";
import { ChevronIcon } from "../../common/icons/ChevronIcon";
import { MagnifierIcon } from "../../common/icons/MagnifierIcon";
+import { SortIcon } from "../../common/icons/SortIcon";
import { Direction } from "../../common/icons/types";
import { getAssetTypeInfo } from "../utils";
import { AssetEntry as AssetEntryComponent } from "./AssetEntry";
@@ -14,6 +15,7 @@ import {
AssetListProps,
ExtendedAssetEntryWithServices,
SORTING_CRITERION,
+ SORTING_ORDER,
Sorting
} from "./types";
@@ -23,6 +25,8 @@ const sortEntries = (
): ExtendedAssetEntryWithServices[] => {
entries = [...entries];
+ const isDesc = sorting.order === SORTING_ORDER.DESC;
+
const sortByName = (
a: ExtendedAssetEntryWithServices,
b: ExtendedAssetEntryWithServices,
@@ -83,39 +87,31 @@ const sortEntries = (
).length;
return (
- (sorting.isDesc
+ (isDesc
? aHighestImportance - bHighestImportance
: bHighestImportance - aHighestImportance) ||
- (sorting.isDesc
+ (isDesc
? bMostImportantInsightCount - aMostImportantInsightCount
: aMostImportantInsightCount - bMostImportantInsightCount) ||
- sortByName(a, b, sorting.isDesc)
+ sortByName(a, b, isDesc)
);
});
case SORTING_CRITERION.PERFORMANCE:
- return entries.sort((a, b) =>
- sortByPercentile(a, b, 0.5, sorting.isDesc)
- );
+ return entries.sort((a, b) => sortByPercentile(a, b, 0.5, isDesc));
case SORTING_CRITERION.SLOWEST_FIVE_PERCENT:
- return entries.sort((a, b) =>
- sortByPercentile(a, b, 0.95, sorting.isDesc)
- );
+ return entries.sort((a, b) => sortByPercentile(a, b, 0.95, isDesc));
case SORTING_CRITERION.LATEST:
return entries.sort((a, b) => {
const aDateTime = new Date(a.lastSpanInstanceInfo.startTime).valueOf();
const bDateTime = new Date(b.lastSpanInstanceInfo.startTime).valueOf();
return (
- (sorting.isDesc ? bDateTime - aDateTime : aDateTime - bDateTime) ||
- sortByName(a, b, sorting.isDesc)
+ (isDesc ? bDateTime - aDateTime : aDateTime - bDateTime) ||
+ sortByName(a, b, isDesc)
);
});
case SORTING_CRITERION.NAME:
- return entries.sort((a, b) =>
- sorting.isDesc
- ? sortByName(b, a, sorting.isDesc)
- : sortByName(a, b, sorting.isDesc)
- );
+ return entries.sort((a, b) => sortByName(a, b, isDesc));
default:
return entries;
}
@@ -151,13 +147,44 @@ const getSortingMenuChevronColor = (theme: DefaultTheme) => {
}
};
+const getSortIconColor = (theme: DefaultTheme, selected: boolean) => {
+ if (selected) {
+ switch (theme.mode) {
+ case "light":
+ return "#f1f5fa";
+ case "dark":
+ case "dark-jetbrains":
+ return "#dadada";
+ }
+ }
+ switch (theme.mode) {
+ case "light":
+ return "#828797";
+ case "dark":
+ case "dark-jetbrains":
+ return "#dadada";
+ }
+};
+
+const getDefaultSortingOrder = (
+ criterion: SORTING_CRITERION
+): SORTING_ORDER => {
+ switch (criterion) {
+ case SORTING_CRITERION.NAME:
+ return SORTING_ORDER.ASC;
+ case SORTING_CRITERION.PERFORMANCE:
+ case SORTING_CRITERION.SLOWEST_FIVE_PERCENT:
+ case SORTING_CRITERION.CRITICAL_INSIGHTS:
+ case SORTING_CRITERION.LATEST:
+ default:
+ return SORTING_ORDER.DESC;
+ }
+};
+
export const AssetList = (props: AssetListProps) => {
- const [sorting, setSorting] = useState<{
- criterion: SORTING_CRITERION;
- isDesc: boolean;
- }>({
+ const [sorting, setSorting] = useState({
criterion: SORTING_CRITERION.CRITICAL_INSIGHTS,
- isDesc: true
+ order: SORTING_ORDER.DESC
});
const [isSortingMenuOpen, setIsSortingMenuOpen] = useState(false);
const [searchInputValue, setSearchInputValue] = useState("");
@@ -184,12 +211,15 @@ export const AssetList = (props: AssetListProps) => {
if (sorting.criterion === value) {
setSorting({
...sorting,
- isDesc: !sorting.isDesc
+ order:
+ sorting.order === SORTING_ORDER.DESC
+ ? SORTING_ORDER.ASC
+ : SORTING_ORDER.DESC
});
} else {
setSorting({
criterion: value as SORTING_CRITERION,
- isDesc: false
+ order: getDefaultSortingOrder(value as SORTING_CRITERION)
});
}
handleSortingMenuToggle();
@@ -199,6 +229,13 @@ export const AssetList = (props: AssetListProps) => {
props.onAssetLinkClick(entry);
};
+ const handleSortingOrderToggleOptionButtonClick = (order: SORTING_ORDER) => {
+ setSorting({
+ ...sorting,
+ order
+ });
+ };
+
const assetTypeInfo = getAssetTypeInfo(props.assetTypeId);
const entries: ExtendedAssetEntryWithServices[] = useMemo(
@@ -260,14 +297,14 @@ export const AssetList = (props: AssetListProps) => {
placement={"bottom-start"}
>
-
+
Sort by
{sorting.criterion}
-
+
+
+ {[SORTING_ORDER.DESC, SORTING_ORDER.ASC].map((order) => {
+ const isSelected = sorting.order === order;
+ const iconColor = getSortIconColor(theme, isSelected);
+
+ return (
+ handleSortingOrderToggleOptionButtonClick(order)}
+ >
+
+
+
+
+ );
+ })}
+
{sortedEntries.length > 0 ? (
diff --git a/src/components/Assets/AssetList/styles.ts b/src/components/Assets/AssetList/styles.ts
index f2a726753..67042bec0 100644
--- a/src/components/Assets/AssetList/styles.ts
+++ b/src/components/Assets/AssetList/styles.ts
@@ -1,4 +1,9 @@
import styled from "styled-components";
+import {
+ SORTING_ORDER,
+ SortingMenuButtonProps,
+ SortingOrderOptionProps
+} from "./types";
export const Container = styled.div`
display: flex;
@@ -45,6 +50,7 @@ export const Toolbar = styled.div`
display: flex;
justify-content: space-between;
padding: 8px;
+ gap: 4px;
`;
export const PopoverContainer = styled.div`
@@ -70,7 +76,7 @@ export const SearchInput = styled.input`
font-size: 10px;
padding: 4px 4px 4px 18px;
border-radius: 4px;
- width: 140px;
+ width: 70px;
outline: none;
caret-color: ${({ theme }) => {
switch (theme.mode) {
@@ -132,14 +138,17 @@ export const SearchInput = styled.input`
}
`;
-export const SortingMenuContainer = styled.div`
+export const SortingMenuButton = styled.button`
+ background: none;
+ cursor: pointer;
display: flex;
gap: 2px;
font-weight: 500;
font-size: 10px;
line-height: 12px;
align-items: center;
- height: 20px;
+ border-radius: 4px;
+ padding: 4px 8px;
color: ${({ theme }) => {
switch (theme.mode) {
case "light":
@@ -149,6 +158,41 @@ export const SortingMenuContainer = styled.div`
return "#9b9b9b";
}
}};
+ border: 1px solid
+ ${({ theme, isOpen }) => {
+ if (isOpen) {
+ switch (theme.mode) {
+ case "light":
+ return "#7891d0";
+ case "dark":
+ case "dark-jetbrains":
+ return "#9b9b9b";
+ }
+ }
+
+ switch (theme.mode) {
+ case "light":
+ return "#d0d6eb";
+ case "dark":
+ case "dark-jetbrains":
+ return "#49494d";
+ }
+ }};
+
+ &:hover,
+ &:focus {
+ outline: none;
+ border: 1px solid
+ ${({ theme }) => {
+ switch (theme.mode) {
+ case "light":
+ return "#7891d0";
+ case "dark":
+ case "dark-jetbrains":
+ return "#9b9b9b";
+ }
+ }};
+ }
`;
export const SortingLabel = styled.span`
@@ -167,6 +211,41 @@ export const SortingLabel = styled.span`
}};
`;
+export const SortingOrderToggle = styled.div`
+ display: flex;
+ border-radius: 4px;
+ padding: 4px;
+ gap: 4px;
+ border: 1px solid
+ ${({ theme }) => {
+ switch (theme.mode) {
+ case "light":
+ return "#b9c0d4";
+ case "dark":
+ case "dark-jetbrains":
+ return "#49494d";
+ }
+ }};
+`;
+
+export const SortingOrderToggleOptionButton = styled.button`
+ border: none;
+ outline: none;
+ padding: 0;
+ border-radius: 2px;
+ cursor: pointer;
+ background: ${({ selected }) => (selected ? "#3538cd" : "transparent")};
+`;
+
+export const SortingOrderIconContainer = styled.div<{
+ sortingOrder: SORTING_ORDER;
+}>`
+ display: flex;
+ transform: scaleY(
+ ${({ sortingOrder }) => (sortingOrder === SORTING_ORDER.DESC ? -1 : 1)}
+ );
+`;
+
export const ItemsCount = styled.span`
margin-left: auto;
color: ${({ theme }) => {
diff --git a/src/components/Assets/AssetList/types.ts b/src/components/Assets/AssetList/types.ts
index a993f6192..606681505 100644
--- a/src/components/Assets/AssetList/types.ts
+++ b/src/components/Assets/AssetList/types.ts
@@ -19,7 +19,20 @@ export enum SORTING_CRITERION {
NAME = "Name"
}
+export enum SORTING_ORDER {
+ ASC = "asc",
+ DESC = "desc"
+}
+
export interface Sorting {
criterion: SORTING_CRITERION;
- isDesc: boolean;
+ order: SORTING_ORDER;
+}
+
+export interface SortingMenuButtonProps {
+ isOpen: boolean;
+}
+
+export interface SortingOrderOptionProps {
+ selected: boolean;
}
diff --git a/src/components/InstallationWizard/InstallStep/index.tsx b/src/components/InstallationWizard/InstallStep/index.tsx
index db8713352..99f72cadd 100644
--- a/src/components/InstallationWizard/InstallStep/index.tsx
+++ b/src/components/InstallationWizard/InstallStep/index.tsx
@@ -132,7 +132,7 @@ export const InstallStep = (props: InstallStepProps) => {
openLinkInDefaultBrowser(DOCKER_DESKTOP_URL)}>
Docker Desktop
{" "}
- 4.10.0 or later installed)
+ 4.10.0 or higher installed)
{
+ switch (theme.mode) {
+ case "light":
+ return "#f1f5fa";
+ case "dark":
+ case "dark-jetbrains":
+ return "#383838";
+ }
+ }};
+ }
+
+ &:active {
+ color: ${({ theme }) => {
+ switch (theme.mode) {
+ case "light":
+ return "#4d668a";
+ case "dark":
+ case "dark-jetbrains":
+ return "#e2e7ff";
+ }
+ }};
+ background: ${({ theme }) => {
+ switch (theme.mode) {
+ case "light":
+ return "#e9eef4";
+ case "dark":
+ case "dark-jetbrains":
+ return "#49494d";
+ }
+ }};
+ }
`;
diff --git a/src/components/common/Popover/PopoverContent/index.tsx b/src/components/common/Popover/PopoverContent/index.tsx
index 0e56bb390..dbc8b8319 100644
--- a/src/components/common/Popover/PopoverContent/index.tsx
+++ b/src/components/common/Popover/PopoverContent/index.tsx
@@ -5,7 +5,7 @@ import {
FloatingPortal,
useMergeRefs
} from "@floating-ui/react";
-import { CSSProperties, ForwardedRef, forwardRef, HTMLProps } from "react";
+import { CSSProperties, ForwardedRef, HTMLProps, forwardRef } from "react";
import { usePopoverContext } from "../hooks";
const PopoverContentComponent = (
@@ -26,7 +26,9 @@ const PopoverContentComponent = (
position: context.strategy,
top: context.y ?? 0,
left: context.x ?? 0,
- width: "max-content",
+ width:
+ context.elements.reference?.getBoundingClientRect().width,
+ outline: "none",
...props.style
} as CSSProperties
}
diff --git a/src/components/common/Popover/PopoverTrigger/index.tsx b/src/components/common/Popover/PopoverTrigger/index.tsx
index 260d427b8..77f294323 100644
--- a/src/components/common/Popover/PopoverTrigger/index.tsx
+++ b/src/components/common/Popover/PopoverTrigger/index.tsx
@@ -10,7 +10,6 @@ import {
Ref
} from "react";
import { usePopoverContext } from "../hooks";
-import * as s from "./styles";
import { PopoverTriggerProps } from "./types";
const PopoverTriggerComponent = (
@@ -43,15 +42,14 @@ const PopoverTriggerComponent = (
}
return (
-
{children}
-
+
);
};
diff --git a/src/components/common/Popover/PopoverTrigger/styles.ts b/src/components/common/Popover/PopoverTrigger/styles.ts
deleted file mode 100644
index f228829d5..000000000
--- a/src/components/common/Popover/PopoverTrigger/styles.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import styled from "styled-components";
-
-export const Button = styled.button`
- background: none;
- border: none;
- display: flex;
- padding: 0;
- cursor: pointer;
-`;
diff --git a/src/components/common/icons/GlobeIcon.tsx b/src/components/common/icons/GlobeIcon.tsx
new file mode 100644
index 000000000..ebd616304
--- /dev/null
+++ b/src/components/common/icons/GlobeIcon.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { useIconProps } from "./hooks";
+import { IconProps } from "./types";
+
+const GlobeIconComponent = (props: IconProps) => {
+ const { size, color } = useIconProps(props);
+
+ return (
+
+ );
+};
+
+export const GlobeIcon = React.memo(GlobeIconComponent);
diff --git a/src/components/common/icons/SortIcon.tsx b/src/components/common/icons/SortIcon.tsx
new file mode 100644
index 000000000..aae2fe52f
--- /dev/null
+++ b/src/components/common/icons/SortIcon.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { useIconProps } from "./hooks";
+import { IconProps } from "./types";
+
+const SortIconComponent = (props: IconProps) => {
+ const { size, color } = useIconProps(props);
+
+ return (
+
+ );
+};
+
+export const SortIcon = React.memo(SortIconComponent);