diff --git a/package-lock.json b/package-lock.json
index a159e96b..ac07d86a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"tailwind-merge": "^2.5.5",
"tailwind-variants": "^0.3.0",
"tailwindcss-animate": "^1.0.7",
+ "zod": "^3.24.1",
"zustand": "^5.0.3"
},
"devDependencies": {
@@ -12564,7 +12565,6 @@
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
diff --git a/package.json b/package.json
index 45af9315..0592f1c1 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"tailwind-merge": "^2.5.5",
"tailwind-variants": "^0.3.0",
"tailwindcss-animate": "^1.0.7",
+ "zod": "^3.24.1",
"zustand": "^5.0.3"
},
"devDependencies": {
diff --git a/src/features/alerts/components/search-field-alerts.tsx b/src/features/alerts/components/search-field-alerts.tsx
new file mode 100644
index 00000000..7d084de6
--- /dev/null
+++ b/src/features/alerts/components/search-field-alerts.tsx
@@ -0,0 +1,31 @@
+import {
+ FieldGroup,
+ Input,
+ SearchField,
+ SearchFieldClearButton,
+} from "@stacklok/ui-kit";
+import { useAlertsFilterSearchParams } from "../hooks/use-alerts-filter-search-params";
+import { SearchMd } from "@untitled-ui/icons-react";
+
+export function SearchFieldAlerts() {
+ const { setSearch, state } = useAlertsFilterSearchParams();
+
+ return (
+ setSearch(value.toLowerCase().trim())}
+ >
+
+ }
+ />
+
+
+
+ );
+}
diff --git a/src/features/alerts/components/switch-malicious-alerts-filter.tsx b/src/features/alerts/components/switch-malicious-alerts-filter.tsx
new file mode 100644
index 00000000..78f4e99e
--- /dev/null
+++ b/src/features/alerts/components/switch-malicious-alerts-filter.tsx
@@ -0,0 +1,34 @@
+import { TooltipTrigger, Switch, Tooltip } from "@stacklok/ui-kit";
+import {
+ AlertsFilterView,
+ useAlertsFilterSearchParams,
+} from "../hooks/use-alerts-filter-search-params";
+
+export function SwitchMaliciousAlertsFilter() {
+ const { setView, state } = useAlertsFilterSearchParams();
+
+ return (
+
+ {
+ switch (isSelected) {
+ case true:
+ return setView(AlertsFilterView.MALICIOUS);
+ case false:
+ return setView(AlertsFilterView.ALL);
+ default:
+ return isSelected satisfies never;
+ }
+ }}
+ >
+ Malicious Packages
+
+
+
+ Filter by malicious packages
+
+
+ );
+}
diff --git a/src/features/alerts/components/table-alerts.tsx b/src/features/alerts/components/table-alerts.tsx
index 899ddbe4..683a8be4 100644
--- a/src/features/alerts/components/table-alerts.tsx
+++ b/src/features/alerts/components/table-alerts.tsx
@@ -2,33 +2,28 @@ import { formatDistanceToNow } from "date-fns";
import {
Cell,
Column,
- Input,
Row,
- SearchField,
Table,
TableBody,
- FieldGroup,
TableHeader,
- SearchFieldClearButton,
Badge,
Button,
ResizableTableContainer,
} from "@stacklok/ui-kit";
-import { Switch } from "@stacklok/ui-kit";
import { AlertConversation, QuestionType } from "@/api/generated";
-import { Tooltip, TooltipTrigger } from "@stacklok/ui-kit";
import {
sanitizeQuestionPrompt,
parsingPromptText,
getIssueDetectedType,
} from "@/lib/utils";
import { useAlertSearch } from "@/hooks/useAlertSearch";
-import { useCallback } from "react";
-import { useNavigate, useSearchParams } from "react-router-dom";
-import { useFilteredAlerts } from "@/hooks/useAlertsData";
+import { useNavigate } from "react-router-dom";
import { useClientSidePagination } from "@/hooks/useClientSidePagination";
import { TableAlertTokenUsage } from "./table-alert-token-usage";
-import { Key01, PackageX, SearchMd } from "@untitled-ui/icons-react";
+import { Key01, PackageX } from "@untitled-ui/icons-react";
+import { SearchFieldAlerts } from "./search-field-alerts";
+import { useQueryGetWorkspaceAlertTable } from "../hooks/use-query-get-workspace-alerts-table";
+import { SwitchMaliciousAlertsFilter } from "./switch-malicious-alerts-filter";
const getTitle = (alert: AlertConversation) => {
const prompt = alert.conversation;
@@ -80,18 +75,9 @@ function IssueDetectedCellContent({ alert }: { alert: AlertConversation }) {
}
export function TableAlerts() {
- const {
- isMaliciousFilterActive,
- setIsMaliciousFilterActive,
- setSearch,
- search,
- page,
- nextPage,
- prevPage,
- } = useAlertSearch();
+ const { page, nextPage, prevPage } = useAlertSearch();
const navigate = useNavigate();
- const [searchParams, setSearchParams] = useSearchParams();
- const { data: filteredAlerts = [] } = useFilteredAlerts();
+ const { data: filteredAlerts = [] } = useQueryGetWorkspaceAlertTable();
const { dataView, hasNextPage, hasPreviousPage } = useClientSidePagination(
filteredAlerts,
@@ -99,37 +85,6 @@ export function TableAlerts() {
15,
);
- const handleToggleFilter = useCallback(
- (isChecked: boolean) => {
- if (isChecked) {
- searchParams.set("maliciousPkg", "true");
- searchParams.delete("search");
- setSearch("");
- } else {
- searchParams.delete("maliciousPkg");
- }
- setSearchParams(searchParams);
- setIsMaliciousFilterActive(isChecked);
- },
- [setSearchParams, setSearch, searchParams, setIsMaliciousFilterActive],
- );
-
- const handleSearch = useCallback(
- (value: string) => {
- if (value) {
- searchParams.set("search", value);
- searchParams.delete("maliciousPkg");
- setSearch(value);
- setIsMaliciousFilterActive(false);
- } else {
- searchParams.delete("search");
- setSearch("");
- }
- setSearchParams(searchParams);
- },
- [searchParams, setIsMaliciousFilterActive, setSearch, setSearchParams],
- );
-
return (
<>
@@ -141,37 +96,8 @@ export function TableAlerts() {
-
-
-
- Malicious Packages
-
-
-
- Filter by malicious packages
-
-
-
-
handleSearch(value.toLowerCase().trim())}
- >
-
- }
- />
-
-
-
+
+
diff --git a/src/features/alerts/hooks/use-alerts-filter-search-params.ts b/src/features/alerts/hooks/use-alerts-filter-search-params.ts
new file mode 100644
index 00000000..6fe5c200
--- /dev/null
+++ b/src/features/alerts/hooks/use-alerts-filter-search-params.ts
@@ -0,0 +1,52 @@
+import { useCallback } from "react";
+import { useSearchParams } from "react-router-dom";
+import { z } from "zod";
+
+export enum AlertsFilterView {
+ ALL = "all",
+ MALICIOUS = "malicious",
+ SECRETS = "secrets",
+}
+
+const alertsFilterSchema = z.object({
+ search: z.string().optional(),
+ view: z.nativeEnum(AlertsFilterView),
+});
+
+type AlertsFilterSchema = z.output
;
+
+const DEFAULT_FILTER: AlertsFilterSchema = {
+ view: AlertsFilterView.ALL,
+};
+
+export const useAlertsFilterSearchParams = () => {
+ const [searchParams, setSearchParams] = useSearchParams(
+ new URLSearchParams(DEFAULT_FILTER),
+ );
+
+ const setView = useCallback(
+ (view: AlertsFilterView) => {
+ setSearchParams((prev) => {
+ if (view) prev.set("view", view);
+ if (!view) prev.delete("view");
+ return prev;
+ });
+ },
+ [setSearchParams],
+ );
+
+ const setSearch = useCallback(
+ (query: string | null) => {
+ setSearchParams((prev) => {
+ if (query !== null) prev.set("search", query);
+ if (query == null || query === "") prev.delete("search");
+ return prev;
+ });
+ },
+ [setSearchParams],
+ );
+
+ const state = alertsFilterSchema.parse(Object.fromEntries(searchParams));
+
+ return { state, setView, setSearch };
+};
diff --git a/src/features/alerts/hooks/use-query-get-workspace-alerts-malicious-pkg.ts b/src/features/alerts/hooks/use-query-get-workspace-alerts-malicious-pkg.ts
index ecb44d84..d3c4e2ff 100644
--- a/src/features/alerts/hooks/use-query-get-workspace-alerts-malicious-pkg.ts
+++ b/src/features/alerts/hooks/use-query-get-workspace-alerts-malicious-pkg.ts
@@ -1,15 +1,13 @@
-import {
- AlertConversation,
- V1GetWorkspaceAlertsResponse,
-} from "@/api/generated";
-import { filterAlertsCritical } from "../lib/filter-alerts-critical";
+import { V1GetWorkspaceAlertsResponse } from "@/api/generated";
import { isAlertMalicious } from "../lib/is-alert-malicious";
import { useQueryGetWorkspaceAlerts } from "./use-query-get-workspace-alerts";
+import { multiFilter } from "@/lib/multi-filter";
+import { isAlertCritical } from "../lib/is-alert-critical";
// NOTE: This needs to be a stable function reference to enable memo-isation of
// the select operation on each React re-render.
-function select(data: V1GetWorkspaceAlertsResponse): AlertConversation[] {
- return filterAlertsCritical(data).filter(isAlertMalicious);
+function select(data: V1GetWorkspaceAlertsResponse) {
+ return multiFilter(data, [isAlertCritical, isAlertMalicious]);
}
export function useQueryGetWorkspaceAlertsMaliciousPkg() {
diff --git a/src/features/alerts/hooks/use-query-get-workspace-alerts-secrets.ts b/src/features/alerts/hooks/use-query-get-workspace-alerts-secrets.ts
index ee5b7a47..5e617173 100644
--- a/src/features/alerts/hooks/use-query-get-workspace-alerts-secrets.ts
+++ b/src/features/alerts/hooks/use-query-get-workspace-alerts-secrets.ts
@@ -1,15 +1,13 @@
-import {
- V1GetWorkspaceAlertsResponse,
- AlertConversation,
-} from "@/api/generated";
-import { filterAlertsCritical } from "../lib/filter-alerts-critical";
+import { V1GetWorkspaceAlertsResponse } from "@/api/generated";
import { isAlertSecret } from "../lib/is-alert-secret";
import { useQueryGetWorkspaceAlerts } from "./use-query-get-workspace-alerts";
+import { multiFilter } from "@/lib/multi-filter";
+import { isAlertCritical } from "../lib/is-alert-critical";
// NOTE: This needs to be a stable function reference to enable memo-isation of
-// the select operation on each React re-render.
-function select(data: V1GetWorkspaceAlertsResponse): AlertConversation[] {
- return filterAlertsCritical(data).filter(isAlertSecret);
+// the select operation on each React re-render
+function select(data: V1GetWorkspaceAlertsResponse) {
+ return multiFilter(data, [isAlertCritical, isAlertSecret]);
}
export function useQueryGetWorkspaceAlertSecrets() {
diff --git a/src/features/alerts/hooks/use-query-get-workspace-alerts-table.ts b/src/features/alerts/hooks/use-query-get-workspace-alerts-table.ts
new file mode 100644
index 00000000..42094205
--- /dev/null
+++ b/src/features/alerts/hooks/use-query-get-workspace-alerts-table.ts
@@ -0,0 +1,43 @@
+import { V1GetWorkspaceAlertsResponse } from "@/api/generated";
+import { useQueryGetWorkspaceAlerts } from "./use-query-get-workspace-alerts";
+import { useCallback } from "react";
+import {
+ AlertsFilterView,
+ useAlertsFilterSearchParams,
+} from "./use-alerts-filter-search-params";
+import { multiFilter } from "@/lib/multi-filter";
+import { isAlertCritical } from "../lib/is-alert-critical";
+import { isAlertMalicious } from "../lib/is-alert-malicious";
+import { isAlertSecret } from "../lib/is-alert-secret";
+import { doesAlertIncludeSearch } from "../lib/does-alert-include-search";
+import { isAlertConversation } from "../lib/is-alert-conversation";
+
+const FILTER: Record<
+ AlertsFilterView,
+ (alert: V1GetWorkspaceAlertsResponse[number]) => boolean
+> = {
+ all: () => true,
+ malicious: isAlertMalicious,
+ secrets: isAlertSecret,
+};
+
+export function useQueryGetWorkspaceAlertTable() {
+ const { state } = useAlertsFilterSearchParams();
+
+ // NOTE: This needs to be a stable function reference to enable memo-isation
+ // of the select operation on each React re-render.
+ const select = useCallback(
+ (data: V1GetWorkspaceAlertsResponse) => {
+ return multiFilter(data, [
+ isAlertCritical,
+ isAlertConversation,
+ FILTER[state.view],
+ ]).filter((alert) => doesAlertIncludeSearch(alert, state.search ?? null));
+ },
+ [state.search, state.view],
+ );
+
+ return useQueryGetWorkspaceAlerts({
+ select,
+ });
+}
diff --git a/src/features/alerts/lib/does-alert-include-search.ts b/src/features/alerts/lib/does-alert-include-search.ts
new file mode 100644
index 00000000..8b91b89b
--- /dev/null
+++ b/src/features/alerts/lib/does-alert-include-search.ts
@@ -0,0 +1,38 @@
+import {
+ AlertConversation,
+ V1GetWorkspaceAlertsResponse,
+} from "@/api/generated";
+
+export function doesAlertIncludeSearch(
+ alert: V1GetWorkspaceAlertsResponse[number],
+ searchQuery: string | null,
+): alert is AlertConversation {
+ if (alert == null) return false;
+ if (searchQuery === null) return true;
+
+ const trigger_type: string = alert.trigger_type;
+ const trigger_string: string | null =
+ typeof alert.trigger_string === "string" ? alert.trigger_string : null;
+
+ let malicious_pkg_name: string | null = null;
+ let malicious_pkg_type: string | null = null;
+
+ if (
+ alert.trigger_string !== null &&
+ typeof alert.trigger_string === "object" &&
+ "name" in alert.trigger_string &&
+ typeof alert.trigger_string.name === "string" &&
+ "type" in alert.trigger_string &&
+ typeof alert.trigger_string.type === "string"
+ ) {
+ malicious_pkg_name = alert.trigger_string.name;
+ malicious_pkg_type = alert.trigger_string.type;
+ }
+
+ return [
+ trigger_type,
+ trigger_string,
+ malicious_pkg_name,
+ malicious_pkg_type,
+ ].some((i) => i?.toLowerCase().includes(searchQuery));
+}
diff --git a/src/features/alerts/lib/filter-alerts-critical.ts b/src/features/alerts/lib/filter-alerts-critical.ts
deleted file mode 100644
index 5537728d..00000000
--- a/src/features/alerts/lib/filter-alerts-critical.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import {
- AlertConversation,
- V1GetWorkspaceAlertsResponse,
-} from "@/api/generated";
-
-export function filterAlertsCritical(
- alerts: V1GetWorkspaceAlertsResponse | undefined,
-): AlertConversation[] {
- return (alerts ?? [])
- .filter(
- (alert): alert is AlertConversation =>
- alert !== null &&
- alert.trigger_category === "critical" &&
- alert?.conversation.question_answers.every(
- (item) => item.answer && item.question,
- ),
- )
- .sort(
- (a, b) =>
- new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
- );
-}
diff --git a/src/features/alerts/lib/is-alert-conversation.ts b/src/features/alerts/lib/is-alert-conversation.ts
new file mode 100644
index 00000000..fdcf23cc
--- /dev/null
+++ b/src/features/alerts/lib/is-alert-conversation.ts
@@ -0,0 +1,14 @@
+import {
+ AlertConversation,
+ V1GetWorkspaceAlertsResponse,
+} from "@/api/generated";
+
+export function isAlertConversation(
+ alert: V1GetWorkspaceAlertsResponse[number],
+): alert is AlertConversation {
+ return Boolean(
+ alert?.conversation.question_answers.every(
+ (item) => item.answer && item.question,
+ ),
+ );
+}
diff --git a/src/features/alerts/lib/is-alert-critical.ts b/src/features/alerts/lib/is-alert-critical.ts
new file mode 100644
index 00000000..d4ea3e46
--- /dev/null
+++ b/src/features/alerts/lib/is-alert-critical.ts
@@ -0,0 +1,10 @@
+import {
+ AlertConversation,
+ V1GetWorkspaceAlertsResponse,
+} from "@/api/generated";
+
+export function isAlertCritical(
+ alert: V1GetWorkspaceAlertsResponse[number],
+): alert is AlertConversation {
+ return alert !== null && alert.trigger_category === "critical";
+}
diff --git a/src/features/alerts/lib/is-alert-malicious.ts b/src/features/alerts/lib/is-alert-malicious.ts
index 77ad4f2a..2c9e10ed 100644
--- a/src/features/alerts/lib/is-alert-malicious.ts
+++ b/src/features/alerts/lib/is-alert-malicious.ts
@@ -1,14 +1,13 @@
import { AlertConversation } from "@/api/generated";
-export function isAlertMalicious({
- trigger_string,
- trigger_category,
-}: AlertConversation) {
+export function isAlertMalicious(
+ alert: AlertConversation | null,
+): alert is AlertConversation {
return (
- trigger_category === "critical" &&
- trigger_string !== null &&
- typeof trigger_string === "object" &&
- "status" in trigger_string &&
- trigger_string.status === "malicious"
+ alert?.trigger_category === "critical" &&
+ alert.trigger_string !== null &&
+ typeof alert.trigger_string === "object" &&
+ "status" in alert.trigger_string &&
+ alert.trigger_string.status === "malicious"
);
}
diff --git a/src/features/alerts/lib/is-alert-secret.ts b/src/features/alerts/lib/is-alert-secret.ts
index dbaf329a..9e7fd87a 100644
--- a/src/features/alerts/lib/is-alert-secret.ts
+++ b/src/features/alerts/lib/is-alert-secret.ts
@@ -1,8 +1,8 @@
-import { AlertConversation } from "@/api/generated";
+import { V1GetWorkspaceAlertsResponse } from "@/api/generated";
-export function isAlertSecret({
- trigger_type,
- trigger_category,
-}: AlertConversation) {
- return trigger_category === "critical" && trigger_type === "codegate-secrets";
+export function isAlertSecret(alert: V1GetWorkspaceAlertsResponse[number]) {
+ return (
+ alert?.trigger_category === "critical" &&
+ alert.trigger_type === "codegate-secrets"
+ );
}
diff --git a/src/hooks/useAlertsData.ts b/src/hooks/useAlertsData.ts
deleted file mode 100644
index b2e966ad..00000000
--- a/src/hooks/useAlertsData.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { useQuery } from "@tanstack/react-query";
-import {
- AlertConversation,
- v1GetWorkspaceAlerts,
- V1GetWorkspaceAlertsData,
-} from "@/api/generated";
-import { getMaliciousPackage } from "@/lib/utils";
-import { MaliciousPkgType, TriggerType } from "@/types";
-import { useAlertSearch } from "./useAlertSearch";
-import { useActiveWorkspaceName } from "@/features/workspace/hooks/use-active-workspace-name";
-
-const fetchAlerts = async (
- options: V1GetWorkspaceAlertsData,
-): Promise => {
- const { data } = await v1GetWorkspaceAlerts(options);
-
- const results = (data ?? [])
- .filter((alert): alert is AlertConversation => alert !== null)
- .filter((alert) => alert.trigger_category === "critical")
- .filter((alert) =>
- alert?.conversation.question_answers.every(
- (item) => item.answer && item.question,
- ),
- )
- .sort(
- (a, b) =>
- new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
- );
-
- return results;
-};
-
-/** @deprecated — incrementally aiming to replace this with more granular
- * approach - AMG */
-export const useAlertsData = ({ ...args } = {}) => {
- const { data: activeWorkspaceName } = useActiveWorkspaceName();
-
- const options: V1GetWorkspaceAlertsData = {
- path: {
- workspace_name: activeWorkspaceName ?? "default",
- },
- };
-
- return useQuery({
- queryKey: ["useAlertsData", options],
- queryFn: () => fetchAlerts(options),
- ...args,
- });
-};
-
-export const useFilteredAlerts = () => {
- const { isMaliciousFilterActive, search } = useAlertSearch();
-
- return useAlertsData({
- // NOTE: Inlined select will run on every render, we'll remove this over
- // time though - AMG
- select: (
- data: Exclude["data"], undefined>,
- ) => {
- return data
- .filter((alert) => {
- const maliciousPkg = getMaliciousPackage(alert.trigger_string);
- const maliciousPkgName =
- typeof maliciousPkg === "object"
- ? maliciousPkg?.type
- : maliciousPkg;
-
- const maliciousPkgType =
- typeof maliciousPkg === "object"
- ? maliciousPkg?.name
- : maliciousPkg;
-
- return (
- maliciousPkgName?.toLowerCase().includes(search) ||
- maliciousPkgType?.toLowerCase().includes(search) ||
- alert.trigger_type?.toLowerCase().includes(search)
- );
- })
- .filter((alert) => {
- if (!isMaliciousFilterActive) {
- return true;
- }
-
- return (
- typeof alert.trigger_string === "object" &&
- (alert.trigger_type as TriggerType) === "codegate-context-retriever"
- );
- })
- .sort(
- (a, b) =>
- new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
- );
- },
- });
-};
-
-export function useMaliciousPackagesChartData() {
- const { data: alerts = [] } = useAlertsData();
-
- return alerts
- .filter((item) => typeof item.trigger_string === "object")
- .filter((item) => item.trigger_type === "codegate-context-retriever")
- .map((item) => item.trigger_string as MaliciousPkgType);
-}
diff --git a/src/lib/multi-filter.ts b/src/lib/multi-filter.ts
new file mode 100644
index 00000000..59a847bc
--- /dev/null
+++ b/src/lib/multi-filter.ts
@@ -0,0 +1,20 @@
+/**
+ * Utility function for chaining multiple predicates when filtering an array,
+ * avoiding iterating over an array more than once. Intended to aid in composing
+ * filtering behavior, particularly when querying data from the server with react-query.
+ *
+ * Not intended to be used to chain filters together, but rather to apply
+ * multiple filter criteria to an array in a single pass.
+ *
+ * @example
+ * const data = [0,1,2,3,4,5]
+ * const result = multiFilter(data, [(d) => !!d, (d) => d > 3])
+ */
+export function multiFilter(
+ array: T[] | undefined,
+ predicates: ((i: T) => boolean)[],
+): T[] {
+ if (!array) return [];
+
+ return array.filter((i) => predicates.every((p) => p(i) === true));
+}
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index b1e13234..07a499a6 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,7 +1,6 @@
import { AlertConversation, Conversation } from "@/api/generated/types.gen";
import { isAlertSecret } from "@/features/alerts/lib/is-alert-secret";
import { isAlertMalicious } from "@/features/alerts/lib/is-alert-malicious";
-import { MaliciousPkgType, TriggerType } from "@/types";
import { format, isToday, isYesterday } from "date-fns";
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
@@ -97,45 +96,6 @@ export function groupPromptsByRelativeDate(prompts: Conversation[]) {
return grouped;
}
-export function getAllIssues(alerts: AlertConversation[]) {
- const groupedTriggerCounts = alerts.reduce>(
- (acc, alert) => {
- const triggerType: TriggerType = alert.trigger_type;
- if (triggerType) {
- acc[triggerType] = (acc[triggerType] || 0) + 1;
- }
- return acc;
- },
- {},
- );
-
- const maxCount = Math.max(...Object.values(groupedTriggerCounts));
-
- const sortedTagCounts = Object.entries(groupedTriggerCounts).sort(
- ([, countA], [, countB]) => countB - countA,
- );
- return { maxCount, sortedTagCounts };
-}
-
-export function getMaliciousPackages() {
- const packageCounts = ([] as { packages: [] }[]).reduce<
- Record
- >((acc, prompt) => {
- (prompt?.packages ?? []).forEach((pkg) => {
- acc[pkg] = (acc[pkg] || 0) + 1;
- });
- return acc;
- }, {});
-
- const chartData = Object.entries(packageCounts).map(([pkg, count]) => ({
- id: pkg,
- label: pkg,
- value: count,
- }));
-
- return chartData;
-}
-
export function sanitizeQuestionPrompt({
question,
answer,
@@ -164,20 +124,6 @@ export function sanitizeQuestionPrompt({
}
}
-export function getMaliciousPackage(
- value: AlertConversation["trigger_string"],
-): string | (MaliciousPkgType & { [key: string]: string }) | null {
- if (typeof value === "string") {
- return value;
- }
-
- if (typeof value === "object" && value !== null) {
- return value as MaliciousPkgType;
- }
-
- return null;
-}
-
export function getIssueDetectedType(
alert: AlertConversation,
): "malicious_package" | "leaked_secret" | null {
diff --git a/src/routes/route-dashboard.tsx b/src/routes/route-dashboard.tsx
index 4f469fdd..c8345091 100644
--- a/src/routes/route-dashboard.tsx
+++ b/src/routes/route-dashboard.tsx
@@ -1,31 +1,10 @@
import { Separator } from "@stacklok/ui-kit";
-import { useEffect } from "react";
-import { useSearchParams } from "react-router-dom";
-import { useAlertsData } from "@/hooks/useAlertsData";
-import { useAlertSearch } from "@/hooks/useAlertSearch";
import { TableAlerts } from "@/features/alerts/components/table-alerts";
import { AlertsSummaryMaliciousPkg } from "@/features/alerts/components/alerts-summary-malicious-pkg";
import { AlertsSummaryWorkspaceTokenUsage } from "@/features/alerts/components/alerts-summary-workspace-token-usage";
import { AlertsSummaryMaliciousSecrets } from "@/features/alerts/components/alerts-summary-secrets";
export function RouteDashboard() {
- const [searchParams] = useSearchParams();
-
- const { setIsMaliciousFilterActive, setSearch } = useAlertSearch();
-
- const { data: alerts = [] } = useAlertsData();
-
- useEffect(() => {
- const isMaliciousFilterActive = searchParams.get("maliciousPkg") === "true";
- const searchFilterParam = searchParams.get("search");
- if (isMaliciousFilterActive && alerts.length > 0) {
- setIsMaliciousFilterActive(true);
- }
- if (searchFilterParam && alerts.length > 0) {
- setSearch(searchFilterParam);
- }
- }, [searchParams, setIsMaliciousFilterActive, setSearch, alerts]);
-
return (