Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions keep-ui/utils/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
// culled from https://github.com/cpvalente/ontime/blob/master/apps/client/src/common/hooks/useLocalStorage.ts

import { useSyncExternalStore } from "react";
import { useMemo, useRef, useSyncExternalStore } from "react";

const STORAGE_EVENT = "keephq";

Expand All @@ -10,7 +10,7 @@ function getSnapshot(key: string): string | null {
if (typeof window === "undefined" || typeof localStorage === "undefined") {
return null;
}

try {
return localStorage.getItem(`keephq-${key}`);
} catch {
Expand All @@ -30,14 +30,15 @@ function getParsedJson<T>(
}

export const useLocalStorage = <T>(key: string, initialValue: T) => {
const localStorageValue = useSyncExternalStore(subscribe, () =>
getSnapshot(key),
const localStorageValue = useSyncExternalStore(
subscribe,
() => getSnapshot(key),
() => JSON.stringify(initialValue)
);
const parsedLocalStorageValue = getParsedJson(
localStorageValue,
initialValue
);
const initialValueRef = useRef(initialValue);
initialValueRef.current = initialValue;

const parsedLocalStorageValue = useMemo(() => getParsedJson(localStorageValue, initialValueRef.current), [localStorageValue]);

/**
* @description Set value to local storage
Expand All @@ -48,7 +49,7 @@ export const useLocalStorage = <T>(key: string, initialValue: T) => {
if (typeof window === "undefined" || typeof localStorage === "undefined") {
return;
}

// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(parsedLocalStorageValue) : value;
Expand Down
114 changes: 59 additions & 55 deletions keep-ui/widgets/alerts-table/ui/alert-table.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useState } from "react";
import { useMemo, useRef, useState } from "react";
import clsx from "clsx";
import { Card, Table } from "@tremor/react";
import {
Expand Down Expand Up @@ -200,66 +200,70 @@ export function AlertTable({
const { toggleAll, areAllGroupsExpanded } = groupExpansionState;
const isGroupingActive = grouping.length > 0;

const filteredAlerts = alerts.filter((alert) => {
// First apply tab filter
if (!tabs[selectedTab].filter(alert)) {
return false;
}

// Then apply facet filters
return Object.entries(facetFilters).every(([facetKey, includedValues]) => {
// If no values are included, don't filter
if (includedValues.length === 0) {
return true;
const filteredAlerts = useMemo(() => {
return alerts.filter((alert) => {
// First apply tab filter
if (!tabs[selectedTab].filter(alert)) {
return false;
}

let value;
if (facetKey.includes(".")) {
// Handle nested keys like "labels.job"
const [parentKey, childKey] = facetKey.split(".");
const parentValue = alert[parentKey as keyof AlertDto];

if (
typeof parentValue === "object" &&
parentValue !== null &&
!Array.isArray(parentValue) &&
!(parentValue instanceof Date)
) {
value = (parentValue as Record<string, unknown>)[childKey];
}
} else {
value = alert[facetKey as keyof AlertDto];
}
// Then apply facet filters
return Object.entries(facetFilters).every(
([facetKey, includedValues]) => {
// If no values are included, don't filter
if (includedValues.length === 0) {
return true;
}

// Handle source array separately
if (facetKey === "source") {
const sources = value as string[];
let value;
if (facetKey.includes(".")) {
// Handle nested keys like "labels.job"
const [parentKey, childKey] = facetKey.split(".");
const parentValue = alert[parentKey as keyof AlertDto];

if (
typeof parentValue === "object" &&
parentValue !== null &&
!Array.isArray(parentValue) &&
!(parentValue instanceof Date)
) {
value = (parentValue as Record<string, unknown>)[childKey];
}
} else {
value = alert[facetKey as keyof AlertDto];
}

// Check if n/a is selected and sources is empty/null
if (includedValues.includes("n/a")) {
return !sources || sources.length === 0;
}
// Handle source array separately
if (facetKey === "source") {
const sources = value as string[];

return (
Array.isArray(sources) &&
sources.some((source) => includedValues.includes(source))
);
}
// Check if n/a is selected and sources is empty/null
if (includedValues.includes("n/a")) {
return !sources || sources.length === 0;
}

// Handle n/a cases for other facets
if (includedValues.includes("n/a")) {
return value === null || value === undefined || value === "";
}
return (
Array.isArray(sources) &&
sources.some((source) => includedValues.includes(source))
);
}

// For non-n/a cases, convert value to string for comparison
// Skip null/undefined values as they should only match n/a
if (value === null || value === undefined || value === "") {
return false;
}
// Handle n/a cases for other facets
if (includedValues.includes("n/a")) {
return value === null || value === undefined || value === "";
}

// For non-n/a cases, convert value to string for comparison
// Skip null/undefined values as they should only match n/a
if (value === null || value === undefined || value === "") {
return false;
}

return includedValues.includes(String(value));
return includedValues.includes(String(value));
}
);
});
});
}, [alerts, facetFilters, selectedTab, tabs]);

const leftPinnedColumns = noisyAlertsEnabled
? ["severity", "checkbox", "status", "source", "name", "noise"]
Expand Down Expand Up @@ -372,8 +376,8 @@ export function AlertTable({
isCreateIncidentWithAIOpen={isCreateIncidentWithAIOpen}
/>
) : (
<AlertPresetManager
table={table}
<AlertPresetManager
table={table}
presetName={presetName}
isGroupingActive={isGroupingActive}
onToggleAllGroups={toggleAll}
Expand All @@ -395,7 +399,7 @@ export function AlertTable({
onDelete={handleFacetDelete}
table={table}
showSkeleton={showSkeleton}
/>
/>
</div>

<div className="flex-1 flex flex-col min-w-0">
Expand Down
Loading