Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.

feat: health check card #62

Merged
merged 10 commits into from
Jan 15, 2025
Merged
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.6",
"@tanstack/react-query": "^5.64.1",
"@types/prismjs": "^1.26.5",
"@types/react-syntax-highlighter": "^15.5.13",
"class-variance-authority": "^0.7.1",
Expand Down Expand Up @@ -81,4 +82,4 @@
"overrides": {
"vite": "^6.0.1"
}
}
}
16 changes: 6 additions & 10 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { useSearchParams } from "react-router-dom";
import { AlertConversation } from "@/api/generated";
import { getMaliciousPackage } from "@/lib/utils";
import { CardCodegateStatus } from "@/features/dashboard/components/card-codegate-status";

const wrapObjectOutput = (input: AlertConversation["trigger_string"]) => {
const data = getMaliciousPackage(input);
Expand Down Expand Up @@ -127,16 +128,11 @@ export function Dashboard() {

return (
<div className="flex-col">
<div className="flex flex-wrap items-center gap-4 w-full">
<div className="min-w-80 w-1/3 h-60">
<BarChart data={alerts} loading={loading} />
</div>
<div className="min-w-80 w-1/4 h-60">
<PieChart data={maliciousPackages} loading={loading} />
</div>
<div className="relative w-[370px] h-60">
<LineChart data={alerts} loading={loading} />
</div>
<div className="grid 2xl:grid-cols-4 sm:grid-cols-2 grid-cols-1 items-stretch gap-4 w-full">
<CardCodegateStatus />
<BarChart data={alerts} loading={loading} />
<PieChart data={maliciousPackages} loading={loading} />
<LineChart data={alerts} loading={loading} />
</div>

<Separator className="my-8" />
Expand Down
41 changes: 24 additions & 17 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react"
import * as React from "react";

import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";

const Card = React.forwardRef<
HTMLDivElement,
Expand All @@ -10,12 +10,12 @@ const Card = React.forwardRef<
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
className,
)}
{...props}
/>
))
Card.displayName = "Card"
));
Card.displayName = "Card";

const CardHeader = React.forwardRef<
HTMLDivElement,
Expand All @@ -26,8 +26,8 @@ const CardHeader = React.forwardRef<
className={cn("flex flex-col space-y-1 p-4", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
));
CardHeader.displayName = "CardHeader";

const CardTitle = React.forwardRef<
HTMLDivElement,
Expand All @@ -37,12 +37,12 @@ const CardTitle = React.forwardRef<
ref={ref}
className={cn(
"text-xl font-semibold leading-none tracking-tight",
className
className,
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
));
CardTitle.displayName = "CardTitle";

const CardDescription = React.forwardRef<
HTMLDivElement,
Expand All @@ -53,16 +53,16 @@ const CardDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
));
CardDescription.displayName = "CardDescription";

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-4 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
));
CardContent.displayName = "CardContent";

const CardFooter = React.forwardRef<
HTMLDivElement,
Expand All @@ -73,7 +73,14 @@ const CardFooter = React.forwardRef<
className={cn("flex items-center p-4 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
));
CardFooter.displayName = "CardFooter";

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { server } from "@/mocks/msw/node";
import { http, HttpResponse } from "msw";
import { expect } from "vitest";
import { CardCodegateStatus } from "../card-codegate-status";
import { render, waitFor } from "@/lib/test-utils";

const renderComponent = () => render(<CardCodegateStatus />);

describe("CardCodegateStatus", () => {
test("renders 'healthy' state", async () => {
server.use(
http.get("*/health", () => HttpResponse.json({ status: "healthy" })),
);

const { getByText } = renderComponent();

await waitFor(
() => {
expect(getByText(/healthy/i)).toBeVisible();
},
{ timeout: 10_000 },
);
});

test("renders 'unhealthy' state", async () => {
server.use(http.get("*/health", () => HttpResponse.json({ status: null })));

const { getByText } = renderComponent();

await waitFor(
() => {
expect(getByText(/unhealthy/i)).toBeVisible();
},
{ timeout: 10_000 },
);
});

test("renders 'error' state", async () => {
server.use(http.get("*/health", () => HttpResponse.error()));

const { getByText } = renderComponent();

await waitFor(
() => {
expect(getByText(/an error occurred/i)).toBeVisible();
},
{ timeout: 10_000 },
);
});
});
Loading
Loading