Skip to content

Commit ee763c1

Browse files
committed
feat: Merge branch 'feature/new-readme' of https://github.com/palad-in/keep into feature/new-readme
2 parents b094e0f + 4cc96f9 commit ee763c1

7 files changed

+200
-189
lines changed

keep-ui/app/alerts/alert-table-alert-facets.tsx

+76-73
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useCallback, useEffect } from "react";
22
import {
33
AlertFacetsProps,
44
FacetValue,
@@ -12,11 +12,10 @@ import {
1212
import { useLocalStorage } from "utils/hooks/useLocalStorage";
1313
import { AlertDto } from "./models";
1414
import {
15-
DynamicFacet,
1615
DynamicFacetWrapper,
1716
AddFacetModal,
1817
} from "./alert-table-facet-dynamic";
19-
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
18+
import { PlusIcon } from "@heroicons/react/24/outline";
2019

2120
export const AlertFacets: React.FC<AlertFacetsProps> = ({
2221
alerts,
@@ -76,88 +75,92 @@ export const AlertFacets: React.FC<AlertFacetsProps> = ({
7675
setFacetFilters(newFilters);
7776
};
7877

79-
const getFacetValues = (key: keyof AlertDto | string): FacetValue[] => {
80-
const filteredAlerts = getFilteredAlertsForFacet(
81-
alerts,
82-
facetFilters,
83-
key,
84-
timeRange
85-
);
86-
const valueMap = new Map<string, number>();
87-
let nullCount = 0;
78+
const getFacetValues = useCallback(
79+
(key: keyof AlertDto | string): FacetValue[] => {
80+
const filteredAlerts = getFilteredAlertsForFacet(
81+
alerts,
82+
facetFilters,
83+
key,
84+
timeRange
85+
);
86+
const valueMap = new Map<string, number>();
87+
let nullCount = 0;
8888

89-
filteredAlerts.forEach((alert) => {
90-
let value;
89+
filteredAlerts.forEach((alert) => {
90+
let value;
9191

92-
// Handle nested keys like "labels.host"
93-
if (typeof key === "string" && key.includes(".")) {
94-
const [parentKey, childKey] = key.split(".");
95-
const parentValue = alert[parentKey as keyof AlertDto];
92+
// Handle nested keys like "labels.host"
93+
if (typeof key === "string" && key.includes(".")) {
94+
const [parentKey, childKey] = key.split(".");
95+
const parentValue = alert[parentKey as keyof AlertDto];
9696

97-
if (
98-
typeof parentValue === "object" &&
99-
parentValue !== null &&
100-
!Array.isArray(parentValue) &&
101-
!(parentValue instanceof Date)
102-
) {
103-
value = (parentValue as Record<string, unknown>)[childKey];
97+
if (
98+
typeof parentValue === "object" &&
99+
parentValue !== null &&
100+
!Array.isArray(parentValue) &&
101+
!(parentValue instanceof Date)
102+
) {
103+
value = (parentValue as Record<string, unknown>)[childKey];
104+
} else {
105+
value = undefined;
106+
}
104107
} else {
105-
value = undefined;
108+
value = alert[key as keyof AlertDto];
106109
}
107-
} else {
108-
value = alert[key as keyof AlertDto];
109-
}
110110

111-
if (Array.isArray(value)) {
112-
if (value.length === 0) {
113-
nullCount++;
111+
if (Array.isArray(value)) {
112+
if (value.length === 0) {
113+
nullCount++;
114+
} else {
115+
value.forEach((v) => {
116+
valueMap.set(v, (valueMap.get(v) || 0) + 1);
117+
});
118+
}
119+
} else if (value !== undefined && value !== null) {
120+
const strValue = String(value);
121+
valueMap.set(strValue, (valueMap.get(strValue) || 0) + 1);
114122
} else {
115-
value.forEach((v) => {
116-
valueMap.set(v, (valueMap.get(v) || 0) + 1);
117-
});
123+
nullCount++;
118124
}
119-
} else if (value !== undefined && value !== null) {
120-
const strValue = String(value);
121-
valueMap.set(strValue, (valueMap.get(strValue) || 0) + 1);
122-
} else {
123-
nullCount++;
124-
}
125-
});
126-
127-
let values = Array.from(valueMap.entries()).map(([label, count]) => ({
128-
label,
129-
count,
130-
isSelected:
131-
facetFilters[key]?.includes(label) || !facetFilters[key]?.length,
132-
}));
125+
});
133126

134-
if (["assignee", "incident"].includes(key as string) && nullCount > 0) {
135-
values.push({
136-
label: "n/a",
137-
count: nullCount,
127+
let values = Array.from(valueMap.entries()).map(([label, count]) => ({
128+
label,
129+
count,
138130
isSelected:
139-
facetFilters[key]?.includes("n/a") || !facetFilters[key]?.length,
140-
});
141-
}
131+
facetFilters[key]?.includes(label) || !facetFilters[key]?.length,
132+
}));
142133

143-
if (key === "severity") {
144-
values.sort((a, b) => {
145-
if (a.label === "n/a") return 1;
146-
if (b.label === "n/a") return -1;
147-
const orderDiff = getSeverityOrder(a.label) - getSeverityOrder(b.label);
148-
if (orderDiff !== 0) return orderDiff;
149-
return b.count - a.count;
150-
});
151-
} else {
152-
values.sort((a, b) => {
153-
if (a.label === "n/a") return 1;
154-
if (b.label === "n/a") return -1;
155-
return b.count - a.count;
156-
});
157-
}
134+
if (["assignee", "incident"].includes(key as string) && nullCount > 0) {
135+
values.push({
136+
label: "n/a",
137+
count: nullCount,
138+
isSelected:
139+
facetFilters[key]?.includes("n/a") || !facetFilters[key]?.length,
140+
});
141+
}
158142

159-
return values;
160-
};
143+
if (key === "severity") {
144+
values.sort((a, b) => {
145+
if (a.label === "n/a") return 1;
146+
if (b.label === "n/a") return -1;
147+
const orderDiff =
148+
getSeverityOrder(a.label) - getSeverityOrder(b.label);
149+
if (orderDiff !== 0) return orderDiff;
150+
return b.count - a.count;
151+
});
152+
} else {
153+
values.sort((a, b) => {
154+
if (a.label === "n/a") return 1;
155+
if (b.label === "n/a") return -1;
156+
return b.count - a.count;
157+
});
158+
}
159+
160+
return values;
161+
},
162+
[alerts, facetFilters, timeRange]
163+
);
161164

162165
const staticFacets = [
163166
"severity",

keep-ui/app/alerts/alert-table-facet-types.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ export interface FacetProps {
3434
export interface AlertFacetsProps {
3535
alerts: AlertDto[];
3636
facetFilters: FacetFilters;
37-
setFacetFilters: (filters: FacetFilters) => void;
37+
setFacetFilters: (
38+
filters: FacetFilters | ((filters: FacetFilters) => FacetFilters)
39+
) => void;
3840
dynamicFacets: DynamicFacet[];
39-
setDynamicFacets: (facets: DynamicFacet[]) => void;
41+
setDynamicFacets: (
42+
facets: DynamicFacet[] | ((facets: DynamicFacet[]) => DynamicFacet[])
43+
) => void;
4044
onDelete: (facetKey: string) => void;
4145
className?: string;
4246
table: Table<AlertDto>;

keep-ui/app/alerts/alert-table-facet-value.tsx

+59-63
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from "react";
1+
import React from "react";
22
import { Icon } from "@tremor/react";
33
import Image from "next/image";
44
import { Text } from "@tremor/react";
@@ -22,8 +22,6 @@ export const FacetValue: React.FC<FacetValueProps> = ({
2222
showIcon = false,
2323
facetFilters,
2424
}) => {
25-
const [isHovered, setIsHovered] = useState(false);
26-
2725
const handleCheckboxClick = (e: React.MouseEvent) => {
2826
e.stopPropagation();
2927
onSelect(label, false, false);
@@ -51,8 +49,6 @@ export const FacetValue: React.FC<FacetValueProps> = ({
5149
<div
5250
className="flex items-center px-2 py-1 hover:bg-gray-100 rounded-sm cursor-pointer group"
5351
onClick={handleCheckboxClick}
54-
onMouseEnter={() => setIsHovered(true)}
55-
onMouseLeave={() => setIsHovered(false)}
5652
>
5753
<div className="flex items-center min-w-[24px]">
5854
<input
@@ -65,67 +61,67 @@ export const FacetValue: React.FC<FacetValueProps> = ({
6561
/>
6662
</div>
6763

68-
{showIcon && (
69-
<div className="flex items-center min-w-[24px] ml-2">
70-
{facetKey === "source" && (
71-
<Image
72-
className="inline-block"
73-
alt={label}
74-
height={16}
75-
width={16}
76-
title={label}
77-
src={
78-
label.includes("@")
79-
? "/icons/mailgun-icon.png"
80-
: `/icons/${label}-icon.png`
81-
}
82-
/>
83-
)}
84-
{facetKey === "severity" && (
85-
<AlertSeverity severity={label as Severity} />
86-
)}
87-
{facetKey === "assignee" && (
88-
<Icon
89-
icon={UserCircleIcon}
90-
size="sm"
91-
className="text-gray-600 !p-0"
92-
/>
93-
)}
94-
{facetKey === "status" && (
95-
<Icon
96-
icon={getStatusIcon(label)}
97-
size="sm"
98-
color={getStatusColor(label)}
99-
className="!p-0"
100-
/>
101-
)}
102-
{facetKey === "dismissed" && (
103-
<Icon
104-
icon={label === "true" ? BellSlashIcon : BellIcon}
105-
size="sm"
106-
className="text-gray-600 !p-0"
107-
/>
108-
)}
109-
{facetKey === "incident" && (
110-
<Icon icon={FireIcon} size="sm" className="text-gray-600 !p-0" />
111-
)}
112-
</div>
113-
)}
114-
115-
<div className="flex-1 min-w-0 mx-2" title={label}>
64+
<div className="flex-1 flex items-center min-w-0" title={label}>
65+
{showIcon && (
66+
<div className="flex items-center min-w-[24px]">
67+
{facetKey === "source" && (
68+
<Image
69+
className="inline-block"
70+
alt={label}
71+
height={16}
72+
width={16}
73+
title={label}
74+
src={
75+
label.includes("@")
76+
? "/icons/mailgun-icon.png"
77+
: `/icons/${label}-icon.png`
78+
}
79+
/>
80+
)}
81+
{facetKey === "severity" && (
82+
<AlertSeverity severity={label as Severity} />
83+
)}
84+
{facetKey === "assignee" && (
85+
<Icon
86+
icon={UserCircleIcon}
87+
size="sm"
88+
className="text-gray-600 !p-0"
89+
/>
90+
)}
91+
{facetKey === "status" && (
92+
<Icon
93+
icon={getStatusIcon(label)}
94+
size="sm"
95+
color={getStatusColor(label)}
96+
className="!p-0"
97+
/>
98+
)}
99+
{facetKey === "dismissed" && (
100+
<Icon
101+
icon={label === "true" ? BellSlashIcon : BellIcon}
102+
size="sm"
103+
className="text-gray-600 !p-0"
104+
/>
105+
)}
106+
{facetKey === "incident" && (
107+
<Icon icon={FireIcon} size="sm" className="text-gray-600 !p-0" />
108+
)}
109+
</div>
110+
)}
116111
<Text className="truncate">{label}</Text>
117112
</div>
118113

119-
<div className="flex-shrink-0 w-8 text-right">
120-
{isHovered ? (
121-
<button
122-
onClick={handleActionClick}
123-
className="text-xs text-orange-600 hover:text-orange-800 w-full"
124-
>
125-
{isExclusivelySelected() ? "All" : "Only"}
126-
</button>
127-
) : (
128-
count > 0 && <Text className="text-xs text-gray-500">{count}</Text>
114+
<div className="flex-shrink-0 w-8 text-right flex justify-end">
115+
<button
116+
onClick={handleActionClick}
117+
className="text-xs text-orange-600 hover:text-orange-800 hidden group-hover:block"
118+
>
119+
{isExclusivelySelected() ? "All" : "Only"}
120+
</button>
121+
{count > 0 && (
122+
<Text className="text-xs text-gray-500 group-hover:hidden">
123+
{count}
124+
</Text>
129125
)}
130126
</div>
131127
</div>

keep-ui/app/alerts/alert-table-facet.tsx

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { useState } from "react";
2-
import { Icon, Title } from "@tremor/react";
3-
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
1+
import React from "react";
2+
import { Title } from "@tremor/react";
3+
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/20/solid";
44
import { FacetProps } from "./alert-table-facet-types";
55
import { FacetValue } from "./alert-table-facet-value";
66
import { useLocalStorage } from "utils/hooks/useLocalStorage";
@@ -32,18 +32,16 @@ export const Facet: React.FC<FacetProps> = ({
3232
v.label.toLowerCase().includes(filter.toLowerCase())
3333
);
3434

35+
const Icon = isOpen ? ChevronDownIcon : ChevronRightIcon;
36+
3537
return (
3638
<div className="pb-2 border-b border-gray-200">
3739
<div
3840
className="flex items-center justify-between px-2 py-2 cursor-pointer hover:bg-gray-50"
3941
onClick={() => setIsOpen(!isOpen)}
4042
>
4143
<div className="flex items-center space-x-2">
42-
<Icon
43-
icon={isOpen ? ChevronDownIcon : ChevronRightIcon}
44-
size="sm"
45-
className="text-gray-600 !p-0"
46-
/>
44+
<Icon className="size-5 -m-0.5 text-gray-600" />
4745
<Title className="text-sm">{name}</Title>
4846
</div>
4947
</div>

keep-ui/app/alerts/alert-table-headers.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { getColumnsIds } from "./alert-table-utils";
3030
import classnames from "classnames";
3131
import { FaArrowUp, FaArrowDown, FaArrowRight } from "react-icons/fa";
3232
import clsx from "clsx";
33+
import { getCommonPinningStylesAndClassNames } from "@/components/ui/table/utils";
3334

3435
interface DraggableHeaderCellProps {
3536
header: Header<AlertDto, unknown>;
@@ -67,6 +68,9 @@ const DraggableHeaderCell = ({
6768
: "grab",
6869
};
6970

71+
// TODO: fix multiple pinned columns
72+
// const { style, className } = getCommonPinningStylesAndClassNames(column);
73+
7074
return (
7175
<TableHeaderCell
7276
className={clsx(

0 commit comments

Comments
 (0)