Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): better time filtering #2486

Merged
merged 7 commits into from
Nov 14, 2024
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
80 changes: 65 additions & 15 deletions keep-ui/app/alerts/TitleAndFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { DateRangePicker, DateRangePickerValue, Title } from "@tremor/react";
import { AlertDto } from "./models";
import ColumnSelection from "./ColumnSelection";
import { ThemeSelection } from "./ThemeSelection";
import EnhancedDateRangePicker from "@/components/ui/DateRangePicker";
import { useState } from "react";

type Theme = {
[key: string]: string;
Expand All @@ -21,22 +23,65 @@ export const TitleAndFilters = ({
table,
onThemeChange,
}: TableHeaderProps) => {
const onDateRangePickerChange = ({
from: start,
to: end,
}: DateRangePickerValue) => {
table.setColumnFilters((existingFilters) => {
// remove any existing "lastReceived" filters
const filteredArrayFromLastReceived = existingFilters.filter(
({ id }) => id !== "lastReceived"
);
const [timeFrame, setTimeFrame] = useState<{
start: Date | null;
end: Date | null;
paused: boolean;
isFromCalendar: boolean;
}>({
start: null,
end: null,
paused: true,
isFromCalendar: false,
});

return filteredArrayFromLastReceived.concat({
id: "lastReceived",
value: { start, end },
});
const handleTimeFrameChange = (newTimeFrame: {
start: Date | null;
end: Date | null;
paused?: boolean;
isFromCalendar?: boolean;
}) => {
setTimeFrame({
start: newTimeFrame.start,
end: newTimeFrame.end,
paused: newTimeFrame.paused ?? true,
isFromCalendar: newTimeFrame.isFromCalendar ?? false,
});

// Only apply date filter if both start and end dates exist
if (newTimeFrame.start && newTimeFrame.end) {
const adjustedTimeFrame = {
...newTimeFrame,
end: new Date(newTimeFrame.end.getTime()),
paused: newTimeFrame.paused ?? true,
isFromCalendar: newTimeFrame.isFromCalendar ?? false,
};

if (adjustedTimeFrame.isFromCalendar) {
adjustedTimeFrame.end.setHours(23, 59, 59, 999);
}

table.setColumnFilters((existingFilters) => {
const filteredArrayFromLastReceived = existingFilters.filter(
({ id }) => id !== "lastReceived"
);

return filteredArrayFromLastReceived.concat({
id: "lastReceived",
value: {
start: adjustedTimeFrame.start,
end: adjustedTimeFrame.end,
},
});
});
} else {
// Remove date filter if no dates are selected
table.setColumnFilters((existingFilters) =>
existingFilters.filter(({ id }) => id !== "lastReceived")
);
}

table.resetRowSelection();
table.resetPagination();
};

Expand All @@ -46,8 +91,13 @@ export const TitleAndFilters = ({
<Title className="capitalize inline">{presetName}</Title>
</div>
<div className="grid grid-cols-[auto_auto] grid-rows-[auto_auto] gap-4">
<DateRangePicker
onValueChange={onDateRangePickerChange}
<EnhancedDateRangePicker
timeFrame={timeFrame}
setTimeFrame={handleTimeFrameChange}
hasPlay={false}
hasRewind={false}
hasForward={false}
hasZoomOut={false}
enableYearNavigation
/>
<div className="flex items-center">
Expand Down
15 changes: 14 additions & 1 deletion keep-ui/app/alerts/alert-table-alert-facets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export const AlertFacets: React.FC<AlertFacetsProps> = ({
className,
table,
}) => {
const timeRangeFilter = table
.getState()
.columnFilters.find((filter) => filter.id === "lastReceived");

const timeRange = timeRangeFilter?.value as
| { start: Date; end: Date; isFromCalendar: boolean }
| undefined;

const presetName = window.location.pathname.split("/").pop() || "default";

const [isModalOpen, setIsModalOpen] = useLocalStorage<boolean>(
Expand Down Expand Up @@ -69,7 +77,12 @@ export const AlertFacets: React.FC<AlertFacetsProps> = ({
};

const getFacetValues = (key: keyof AlertDto | string): FacetValue[] => {
const filteredAlerts = getFilteredAlertsForFacet(alerts, facetFilters, key);
const filteredAlerts = getFilteredAlertsForFacet(
alerts,
facetFilters,
key,
timeRange
);
const valueMap = new Map<string, number>();
let nullCount = 0;

Expand Down
26 changes: 22 additions & 4 deletions keep-ui/app/alerts/alert-table-facet-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,39 @@ import {
BellSlashIcon,
FireIcon,
} from "@heroicons/react/24/outline";
import { isQuickPresetRange } from "@/components/ui/DateRangePicker";

export const getFilteredAlertsForFacet = (
alerts: AlertDto[],
facetFilters: FacetFilters,
excludeFacet: string
): AlertDto[] => {
currentFacetKey: string,
timeRange?: { start: Date; end: Date; isFromCalendar: boolean }
) => {
return alerts.filter((alert) => {
// Only apply time range filter if both start and end dates exist
if (timeRange?.start && timeRange?.end) {
const lastReceived = new Date(alert.lastReceived);
const rangeStart = new Date(timeRange.start);
const rangeEnd = new Date(timeRange.end);

if (!isQuickPresetRange(timeRange)) {
rangeEnd.setHours(23, 59, 59, 999);
}

if (lastReceived < rangeStart || lastReceived > rangeEnd) {
return false;
}
}

// Then apply facet filters, excluding the current facet
return Object.entries(facetFilters).every(([facetKey, includedValues]) => {
if (facetKey === excludeFacet || includedValues.length === 0) {
// Skip filtering by current facet to avoid circular dependency
if (facetKey === currentFacetKey || includedValues.length === 0) {
return true;
}

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

Expand Down
63 changes: 0 additions & 63 deletions keep-ui/app/alerts/alert-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,69 +210,6 @@ export function AlertTable({
});
});

const handleFacetSelect = (
facetKey: string,
value: string,
exclusive: boolean,
isAllOnly: boolean = false
) => {
setFacetFilters((prev) => {
// Handle All/Only button clicks
if (isAllOnly) {
if (value === "") {
// Reset to include all values (empty array)
return {
...prev,
[facetKey]: [],
};
}

if (exclusive) {
// Only include this value
return {
...prev,
[facetKey]: [value],
};
}
}

// Handle regular checkbox clicks
const currentValues = prev[facetKey] || [];

if (currentValues.length === 0) {
// If no filters, clicking one value means we want to exclude that value
// So we need to include all OTHER values
const allValues = new Set(
alerts
.map((alert) => {
const val = alert[facetKey as keyof AlertDto];
return Array.isArray(val) ? val : [String(val)];
})
.flat()
);
return {
...prev,
[facetKey]: Array.from(allValues).filter((v) => v !== value),
};
}

if (currentValues.includes(value)) {
// Remove value if it's already included
const newValues = currentValues.filter((v) => v !== value);
return {
...prev,
[facetKey]: newValues,
};
} else {
// Add value if it's not included
return {
...prev,
[facetKey]: [...currentValues, value],
};
}
});
};

const table = useReactTable({
data: filteredAlerts,
columns: columns,
Expand Down
Loading
Loading