Skip to content
Closed
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
10 changes: 8 additions & 2 deletions components/dashboard/device-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
formatDateTime,
formatPowerOnHours,
formatTemperature,
statusBarClass,
summaryAgeClass,
} from "@/lib/format";
import { AppConfig, DeviceSummaryModel, MetricsStatusThreshold } from "@/lib/types";
Expand All @@ -41,18 +42,22 @@ export function DeviceCard({ deviceSummary, settings, threshold, t, variant, onA
pillStatus,
ProtocolIcon,
protocol,
status,
statusLabel,
title,
} = getDeviceCardData(deviceSummary, settings, threshold, t);
const temperatureUnit = settings?.temperature_unit ?? "celsius";

const statusBar = statusBarClass(status);

if (variant === "mobile") {
const handleNavigate = () => onNavigate(deviceHref(deviceSummary.device.wwn));
return (
<div
className={`rounded-lg border bg-card p-4 cursor-pointer transition-colors hover:bg-muted/30 ${failedEmphasis}`}
className={`relative overflow-hidden rounded-lg border bg-card p-4 cursor-pointer transition-colors hover:bg-muted/30 ${failedEmphasis}`}
onClick={handleNavigate}
>
<div className={`absolute inset-x-0 top-0 h-1 ${statusBar}`} />
<div className="flex items-start justify-between gap-3">
<div>
<div className="flex items-center gap-2">
Expand Down Expand Up @@ -136,7 +141,8 @@ export function DeviceCard({ deviceSummary, settings, threshold, t, variant, onA
}

return (
<Card className={`glass-panel ${failedEmphasis}`}>
<Card className={`relative overflow-hidden glass-panel ${failedEmphasis}`}>
<div className={`absolute inset-x-0 top-0 h-1 ${statusBar}`} />
<CardContent className="flex h-full flex-col gap-4 pt-6">
<Link
href={deviceHref(deviceSummary.device.wwn)}
Expand Down
5 changes: 4 additions & 1 deletion components/device/device-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
formatDateTime,
formatPowerOnHours,
formatTemperature,
statusBarClass,
} from "@/lib/format";

interface DeviceSummaryProps {
Expand All @@ -38,13 +39,15 @@ export function DeviceSummary({ device, smart, settings }: DeviceSummaryProps) {
);
const statusLabel = t(`status.${status.replace(": ", "_")}`);
const pillStatus = status === "passed" ? "passed" : status.startsWith("failed") ? "failed" : "unknown";
const statusBar = statusBarClass(status);
const isAta = device.device_protocol?.toUpperCase() === "ATA";
const protocol = device.device_protocol?.toUpperCase() ?? "";
const ProtocolIcon =
protocol === "ATA" ? HardDrive : protocol === "NVME" ? Cpu : protocol === "SCSI" ? Server : HelpCircle;

return (
<Card className="glass-panel">
<Card className="relative overflow-hidden glass-panel">
<div className={`absolute inset-x-0 top-0 h-1 ${statusBar}`} />
<CardHeader className="flex flex-row items-start justify-between gap-4">
<div>
<div className="flex items-center gap-2">
Expand Down
10 changes: 0 additions & 10 deletions components/ui/status-pill.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { CheckCircle2, ShieldAlert } from "lucide-react";

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

interface StatusPillProps {
Expand All @@ -15,13 +13,6 @@ const STATUS_STYLES: Record<StatusPillProps["status"], string> = {
};

export function StatusPill({ status, label, className }: StatusPillProps) {
const icon =
status === "passed" ? (
<CheckCircle2 className="h-4 w-4" />
) : status === "failed" ? (
<ShieldAlert className="h-4 w-4" />
) : null;

return (
<span
className={cn(
Expand All @@ -30,7 +21,6 @@ export function StatusPill({ status, label, className }: StatusPillProps) {
className
)}
>
{icon}
{label}
</span>
);
Expand Down
6 changes: 6 additions & 0 deletions lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,9 @@ export function deviceTitleWithFallback(device: DeviceModel, titleType: Dashboar
if (preferred) titleParts.push(preferred);
return titleParts.join(" - ");
}

export function statusBarClass(status: string) {
if (status === "passed") return "bg-emerald-500";
if (status.startsWith("failed")) return "bg-rose-500";
return "bg-amber-500";
}
19 changes: 19 additions & 0 deletions tests/status-bar-class.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from "vitest";

import { statusBarClass } from "@/lib/format";

describe("statusBarClass", () => {
it("returns green for passed", () => {
expect(statusBarClass("passed")).toBe("bg-emerald-500");
});

it("returns red for failed variants", () => {
expect(statusBarClass("failed")).toBe("bg-rose-500");
expect(statusBarClass("failed: smart")).toBe("bg-rose-500");
expect(statusBarClass("failed: both")).toBe("bg-rose-500");
});

it("returns amber for unknown", () => {
expect(statusBarClass("unknown")).toBe("bg-amber-500");
});
});