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

refactor: alerts feed UI loading #2414

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6da3062
refactor: useHydratedSession instead of built-in useSession
Kiryous Nov 6, 2024
bf4f340
wip: temporary change the api url to production to test frontend
Kiryous Nov 7, 2024
1b85d7f
polish: skeleton for facets
Kiryous Nov 7, 2024
8094ff9
refactor: keep the session from server until update is loaded
Kiryous Nov 7, 2024
92f71e7
refactor: useMemo instead of useEffect in usePresetAlerts
Kiryous Nov 7, 2024
c5b4aa1
refactor: useMounted hook
Kiryous Nov 7, 2024
75637de
refactor: use the Alerts component directly; do not do client redirec…
Kiryous Nov 7, 2024
e0bac3c
fix: skeleton logic in AlertsTableBody
Kiryous Nov 7, 2024
91aa8ec
fix: useMounted to avoid hydration issues
Kiryous Nov 7, 2024
b818b30
chore: temporary change the API url to point directly at production s…
Kiryous Nov 7, 2024
1d16343
polish: skeleton for facets
Kiryous Nov 7, 2024
5d6aef8
refactor: useMemo instead of useEffect in usePresetAlerts
Kiryous Nov 7, 2024
6959cd8
refactor: useMounted hook
Kiryous Nov 7, 2024
e364f07
refactor: use the Alerts component directly; do not do client redirec…
Kiryous Nov 7, 2024
885ed5d
fix: skeleton logic in AlertsTableBody
Kiryous Nov 7, 2024
7f7e792
fix: useMounted to avoid hydration issues
Kiryous Nov 7, 2024
9cdf653
chore: temporary change the API url to point directly at production s…
Kiryous Nov 7, 2024
afa8967
Merge branch 'refactor/2413-alerts-feed-ui-loading' of github.com:kee…
Kiryous Nov 7, 2024
352b51f
polish: remove callout "Getting your alerts" since skeleton playing t…
Kiryous Nov 7, 2024
1e2ef32
refactor: fetch config server side
Kiryous Nov 7, 2024
ca4acee
fix: add config-provider
Kiryous Nov 7, 2024
1871750
fix: config
Kiryous Nov 7, 2024
ba60c98
chore: add sentry to keep-ui
Kiryous Nov 7, 2024
873fc26
fix: middleware matcher
Kiryous Nov 7, 2024
56d222e
fix: try to enable onRequestError
Kiryous Nov 7, 2024
8f3f075
refactor: replace swr with context for useConfig
Kiryous Nov 7, 2024
dbb3696
fix: check if window is present in all (🀞) occurrences
Kiryous Nov 7, 2024
ebb6d3e
revert: hydrated session
Kiryous Nov 7, 2024
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
3 changes: 3 additions & 0 deletions keep-ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ jspm_packages

app/topology/mock-topology-data.tsx
.vercel

# Sentry Config File
.env.sentry-build-plugin
4 changes: 2 additions & 2 deletions keep-ui/app/alerts/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import AlertsPage from "../alerts.client";
import Alerts from "../alerts";

type PageProps = {
params: { id: string };
searchParams: { [key: string]: string | string[] | undefined };
};

export default function Page({ params }: PageProps) {
return <AlertsPage presetName={params.id} />;
return <Alerts presetName={params.id} />;
}

export const metadata = {
Expand Down
6 changes: 4 additions & 2 deletions keep-ui/app/alerts/alert-pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Select from "react-select";
import { AlertDto } from "./models";
import { Table } from "@tanstack/react-table";
import { useAlerts } from "utils/hooks/useAlerts";
import { useMounted } from "@/shared/lib/hooks/useMounted";

interface Props {
presetName: string;
Expand Down Expand Up @@ -71,6 +72,7 @@ export default function AlertPagination({
}: Props) {
const { usePresetAlerts } = useAlerts();
const { mutate, isLoading: isValidating } = usePresetAlerts(presetName);
const isMounted = useMounted();

const pageIndex = table.getState().pagination.pageIndex;
const pageCount = table.getPageCount();
Expand Down Expand Up @@ -142,8 +144,8 @@ export default function AlertPagination({
icon={ArrowPathIcon}
color="orange"
size="xs"
disabled={isValidating}
loading={isValidating}
disabled={isValidating || !isMounted}
loading={isValidating || !isMounted}
onClick={async () => await mutate()}
title="Refresh"
/>
Expand Down
14 changes: 14 additions & 0 deletions keep-ui/app/alerts/alert-table-facet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
import { AlertDto, Severity } from "./models";
import AlertSeverity from "./alert-severity";
import clsx from "clsx";
import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";

interface FacetValue {
label: string;
Expand Down Expand Up @@ -195,6 +197,7 @@ interface FacetProps {
onSelect: (value: string, exclusive: boolean, isAllOnly: boolean) => void;
facetKey: string;
facetFilters: FacetFilters;
showSkeleton?: boolean;
}

const Facet: React.FC<FacetProps> = ({
Expand All @@ -203,6 +206,7 @@ const Facet: React.FC<FacetProps> = ({
onSelect,
facetKey,
facetFilters,
showSkeleton = false,
}) => {
const [isOpen, setIsOpen] = useState(true);
const [filter, setFilter] = useState("");
Expand Down Expand Up @@ -258,6 +262,10 @@ const Facet: React.FC<FacetProps> = ({
facetFilters={facetFilters}
/>
))
) : showSkeleton ? (
Array.from({ length: 3 }).map((_, index) => (
<Skeleton key={`skeleton-${index}`} />
))
) : (
<div className="px-2 py-1 text-sm text-gray-500 italic">
No matching values found
Expand All @@ -280,13 +288,15 @@ interface AlertFacetsProps {
isAllOnly: boolean
) => void;
className?: string;
showSkeleton?: boolean;
}

const AlertFacets: React.FC<AlertFacetsProps> = ({
alerts,
facetFilters,
onSelect,
className,
showSkeleton = false,
}) => {
const getFacetValues = (key: keyof AlertDto): FacetValue[] => {
const valueMap = new Map<string, number>();
Expand Down Expand Up @@ -391,6 +401,7 @@ const AlertFacets: React.FC<AlertFacetsProps> = ({
onSelect("severity", value, exclusive, isAllOnly)
}
facetFilters={facetFilters}
showSkeleton={showSkeleton}
/>
<Facet
facetKey="status"
Expand All @@ -400,6 +411,7 @@ const AlertFacets: React.FC<AlertFacetsProps> = ({
onSelect("status", value, exclusive, isAllOnly)
}
facetFilters={facetFilters}
showSkeleton={showSkeleton}
/>
<Facet
facetKey="source"
Expand All @@ -409,6 +421,7 @@ const AlertFacets: React.FC<AlertFacetsProps> = ({
onSelect("source", value, exclusive, isAllOnly)
}
facetFilters={facetFilters}
showSkeleton={showSkeleton}
/>
<Facet
facetKey="assignee"
Expand All @@ -418,6 +431,7 @@ const AlertFacets: React.FC<AlertFacetsProps> = ({
onSelect("assignee", value, exclusive, isAllOnly)
}
facetFilters={facetFilters}
showSkeleton={showSkeleton}
/>
</div>
</div>
Expand Down
21 changes: 7 additions & 14 deletions keep-ui/app/alerts/alert-table.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react";
import { Table, Callout, Card, Icon } from "@tremor/react";
import { useState } from "react";
import { Table, Card } from "@tremor/react";
import { AlertsTableBody } from "./alerts-table-body";
import { AlertDto } from "./models";
import { CircleStackIcon } from "@heroicons/react/24/outline";
import {
getCoreRowModel,
useReactTable,
Expand Down Expand Up @@ -34,6 +33,7 @@ import AlertTabs from "./alert-tabs";
import AlertSidebar from "./alert-sidebar";
import AlertFacets from "./alert-table-facet";
import { FacetFilters } from "./alert-table-facet";
import { useMounted } from "@/shared/lib/hooks/useMounted";

interface PresetTab {
name: string;
Expand Down Expand Up @@ -273,10 +273,12 @@ export function AlertTable({
return acc.concat(alertId);
}, []);

const isMounted = useMounted();

// show skeleton if no alerts are loaded
let showSkeleton = table.getFilteredRowModel().rows.length === 0;
// if showSkeleton and not loading, show empty state
let showEmptyState = !isAsyncLoading && showSkeleton;
let showEmptyState = !isAsyncLoading && showSkeleton && isMounted;

const handleRowClick = (alert: AlertDto) => {
// if presetName is alert-history, do not open sidebar
Expand All @@ -300,6 +302,7 @@ export function AlertTable({
<AlertFacets
className="sticky top-0"
alerts={alerts}
showSkeleton={showSkeleton}
facetFilters={facetFilters}
onSelect={handleFacetSelect}
/>
Expand Down Expand Up @@ -328,16 +331,6 @@ export function AlertTable({
</div>
<Card className="flex-grow h-full flex flex-col p-0">
<div className="flex-grow">
{isAsyncLoading && (
<Callout
title="Getting your alerts..."
icon={CircleStackIcon}
color="gray"
className="m-5"
>
Alerts will show up in this table as they are added to Keep...
</Callout>
)}
{/* For dynamic preset, add alert tabs*/}
{!presetStatic && (
<AlertTabs
Expand Down
20 changes: 19 additions & 1 deletion keep-ui/app/alerts/alerts-table-body.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TableBody, TableRow, TableCell, Card, Button } from "@tremor/react";
import { TableBody, TableRow, TableCell } from "@tremor/react";
import { AlertDto } from "./models";
import "./alerts-table-body.css";
import Skeleton from "react-loading-skeleton";
Expand Down Expand Up @@ -70,6 +70,24 @@ export function AlertsTableBody({
onRowClick(alert);
};

if (showSkeleton) {
return (
<TableBody>
{Array(20)
.fill("")
.map((index, rowIndex) => (
<TableRow key={`row-${index}-${rowIndex}`}>
{table.getAllColumns().map((c, cellIndex) => (
<TableCell key={`cell-${c.id}-${cellIndex}`}>
<Skeleton />
</TableCell>
))}
</TableRow>
))}
</TableBody>
);
}

return (
<TableBody>
{table.getRowModel().rows.map((row) => {
Expand Down
19 changes: 13 additions & 6 deletions keep-ui/app/alerts/alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { useEffect, useMemo, useState } from "react";
import { Preset } from "./models";
import { useAlerts } from "utils/hooks/useAlerts";
Expand All @@ -15,6 +17,7 @@ import { ViewAlertModal } from "./ViewAlertModal";
import { useRouter, useSearchParams } from "next/navigation";
import AlertChangeStatusModal from "./alert-change-status-modal";
import { useAlertPolling } from "utils/hooks/usePusher";
import { useMounted } from "@/shared/lib/hooks/useMounted";

const defaultPresets: Preset[] = [
{
Expand All @@ -25,7 +28,7 @@ const defaultPresets: Preset[] = [
is_noisy: false,
alerts_count: 0,
should_do_noise_now: false,
tags: []
tags: [],
},
{
id: "dismissed",
Expand All @@ -35,7 +38,7 @@ const defaultPresets: Preset[] = [
is_noisy: false,
alerts_count: 0,
should_do_noise_now: false,
tags: []
tags: [],
},
{
id: "groups",
Expand All @@ -45,7 +48,7 @@ const defaultPresets: Preset[] = [
is_noisy: false,
alerts_count: 0,
should_do_noise_now: false,
tags: []
tags: [],
},
{
id: "without-incident",
Expand All @@ -55,7 +58,7 @@ const defaultPresets: Preset[] = [
is_noisy: false,
alerts_count: 0,
should_do_noise_now: false,
tags: []
tags: [],
},
];

Expand Down Expand Up @@ -100,9 +103,14 @@ export default function Alerts({ presetName }: AlertsProps) {
const { data: pollAlerts } = useAlertPolling();
const {
data: alerts = [],
isLoading: isAsyncLoading,
isLoading,
mutate: mutateAlerts,
} = usePresetAlerts(selectedPreset ? selectedPreset.name : "");

const isMounted = useMounted();

const isAsyncLoading = isLoading || !isMounted;

useEffect(() => {
const fingerprint = searchParams?.get("alertPayloadFingerprint");
if (fingerprint) {
Expand Down Expand Up @@ -137,7 +145,6 @@ export default function Alerts({ presetName }: AlertsProps) {
setChangeStatusAlert={setChangeStatusAlert}
mutateAlerts={mutateAlerts}
/>

{selectedPreset && (
<AlertHistory alerts={alerts} presetName={selectedPreset.name} />
)}
Expand Down
19 changes: 19 additions & 0 deletions keep-ui/app/config-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";

import { createContext } from "react";

// Create the context with undefined as initial value
export const ConfigContext = createContext<any | undefined>(undefined);

// Create a provider component
export function ConfigProvider({
children,
config,
}: {
children: React.ReactNode;
config: any;
}) {
return (
<ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
);
}
3 changes: 2 additions & 1 deletion keep-ui/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Title, Subtitle } from "@tremor/react";
import { Button, Text } from "@tremor/react";
import { signOut } from "next-auth/react";
import { KeepApiError } from "@/shared/lib/KeepApiError";
import * as Sentry from "@sentry/nextjs";

export default function ErrorComponent({
error,
Expand All @@ -20,7 +21,7 @@ export default function ErrorComponent({
reset: () => void;
}) {
useEffect(() => {
console.error(error);
Sentry.captureException(error);
}, [error]);

return (
Expand Down
23 changes: 23 additions & 0 deletions keep-ui/app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import * as Sentry from "@sentry/nextjs";
import NextError from "next/error";
import { useEffect } from "react";

export default function GlobalError({ error }: { error: Error & { digest?: string } }) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);

return (
<html>
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
</body>
</html>
);
}
Loading
Loading