Skip to content
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
2 changes: 2 additions & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"test:watch": "vitest watch"
},
"dependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@fontsource/geist-mono": "^5.2.7",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-separator": "^1.1.7",
Expand Down
46 changes: 21 additions & 25 deletions apps/frontend/src/components/connection/ClusterConnectionGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { useAppDispatch } from "@/hooks/hooks.ts"
import { Button } from "@/components/ui/button.tsx"
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip.tsx"
import { Typography } from "@/components/ui/typography.tsx"

interface ClusterConnectionGroupProps {
clusterId: string
Expand Down Expand Up @@ -137,19 +138,17 @@ export const ClusterConnectionGroup = ({ clusterId, connections, onEdit }: Clust
<Tooltip>
<TooltipTrigger asChild>
<div className="min-w-0 flex-1 overflow-hidden max-w-[200px]">
<Link
className="block font-mono text-sm text-blue-600 dark:text-blue-400 hover:underline"
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
display: "block",
}}
title={firstNodeAlias || clusterId}
to={`/${clusterId}/${firstConnectedConnection.connectionId}/cluster-topology`}
<Typography
variant="code"
>
{firstNodeAlias || clusterId}
</Link>
<Link
className="hover:underline block truncate"
title={firstNodeAlias || clusterId}
to={`/${clusterId}/${firstConnectedConnection.connectionId}/cluster-topology`}
>
{firstNodeAlias || clusterId}
</Link>
</Typography>
</div>
</TooltipTrigger>
<TooltipContent>
Expand All @@ -158,21 +157,14 @@ export const ClusterConnectionGroup = ({ clusterId, connections, onEdit }: Clust
</Tooltip>
) : (
<div className="min-w-0 flex-1 overflow-hidden max-w-[200px]">
<h3
className="
font-mono text-sm text-gray-900 dark:text-white
cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap"
<Typography
className="hover:underline block truncate"
onClick={() => setIsOpen(!isOpen)}
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
display: "block",
}}
title={firstNodeAlias || clusterId}
variant="code"
>
{firstNodeAlias || clusterId}
</h3>
</Typography>
</div>
)}
<Button
Expand All @@ -194,15 +186,19 @@ export const ClusterConnectionGroup = ({ clusterId, connections, onEdit }: Clust
</Button>
</div>
)}
<div className="text-sm pl-2 text-gray-500 dark:text-gray-400 font-mono truncate" title={`${connections.length} instance${connections.length !== 1 ? "s" : ""}${hasConnectedInstance ? ` - ${connectedCount} connected` : ""}`}>
<Typography
className="pl-2 text-gray-500 dark:text-gray-400 truncate"
title={`${connections.length} instance${connections.length !== 1 ? "s" : ""}${hasConnectedInstance ? ` - ${connectedCount} connected` : ""}`}
variant="code"
>
{connections.length} instance{connections.length !== 1 ? "s" : ""}
{hasConnectedInstance && (
<span className="ml-2 px-2 py-0.5 text-sm bg-teal-100 dark:bg-teal-900/30 text-teal-700
dark:text-teal-400 rounded">
{connectedCount} connected
</span>
)}
</div>
</Typography>
</div>
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions apps/frontend/src/components/connection/Connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import EditForm from "../ui/edit-form.tsx"
import RouteContainer from "../ui/route-container.tsx"
import { Button } from "../ui/button.tsx"
import { EmptyState } from "../ui/empty-state.tsx"
import { SectionHeader } from "../ui/section-header.tsx"
import { Typography } from "../ui/typography.tsx"
import type { ConnectionState } from "@/state/valkey-features/connection/connectionSlice.ts"
import { selectConnections } from "@/state/valkey-features/connection/connectionSelectors.ts"
import { ConnectionEntry } from "@/components/connection/ConnectionEntry.tsx"
Expand Down Expand Up @@ -59,9 +59,9 @@ export function Connection() {
<RouteContainer title="connection">
{/* top header */}
<div className="flex items-center justify-between h-10">
<h1 className="text-xl font-bold flex items-center gap-2 text-gray-700 dark:text-white">
<Typography className="flex items-center gap-2" variant="heading">
<HousePlug /> Connections
</h1>
</Typography>
{hasConnectionsWithHistory && (
<Button
onClick={() => setShowConnectionForm(!showConnectionForm)}
Expand Down Expand Up @@ -95,7 +95,7 @@ export function Connection() {
{/* for clusters */}
{hasClusterGroups && (
<div className="mb-8">
<SectionHeader>Clusters</SectionHeader>
<Typography className="mb-2" variant="bodyLg">Clusters</Typography>
<div>
{Object.entries(clusterGroups).map(([clusterId, clusterConnections]) => (
<ClusterConnectionGroup
Expand All @@ -112,7 +112,7 @@ export function Connection() {
{/* for standalone instances */}
{hasStandaloneConnections && (
<div>
<SectionHeader>Instances</SectionHeader>
<Typography className="mb-2" variant="bodyLg">Instances</Typography>
<div>
{standaloneConnections.map(({ connectionId, connection }) => (
<ConnectionEntry
Expand Down
53 changes: 29 additions & 24 deletions apps/frontend/src/components/connection/ConnectionEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Button } from "@/components/ui/button.tsx"
import { ConnectionStatusBadge } from "@/components/ui/connection-status-badge"
import { ConnectionActionButtons } from "@/components/ui/connection-action-buttons.tsx"
import { Typography } from "@/components/ui/typography.tsx"
import { cn } from "@/lib/utils.ts"
import history from "@/history.ts"
import { useAppDispatch } from "@/hooks/hooks.ts"
Expand Down Expand Up @@ -65,19 +66,21 @@ export const ConnectionEntry = ({
<div>
<div className="flex items-center gap-3 flex-1 min-w-0">
<ConnectionStatusBadge status={statusType} />
<Button
asChild
className={cn(!isConnected && "pointer-events-none opacity-60", "justify-start p-0 h-auto font-mono text-sm truncate")}
variant="link"
>
<Link title={label} to={clusterId ? `/${clusterId}/${connectionId}/dashboard` : `/${connectionId}/dashboard`}>
{label}
</Link>
</Button>
<Typography asChild variant="code">
<Button
asChild
className={cn(!isConnected && "pointer-events-none opacity-60", "justify-start p-0 h-auto truncate")}
variant="link"
>
<Link title={label} to={clusterId ? `/${clusterId}/${connectionId}/dashboard` : `/${connectionId}/dashboard`}>
{label}
</Link>
</Button>
</Typography>
</div>{lastConnectionTime && lastConnectionTime.event === CONNECTED && (
<span className="text-xs text-gray-500 dark:text-gray-400 ml-1 truncate" title={`Last connected: ${new Date(lastConnectionTime.timestamp).toLocaleString()}`}>
<Typography variant="bodyXs">
Last connected: {new Date(lastConnectionTime.timestamp).toLocaleString()}
</span>
</Typography>
)}</div>

{/* action buttons */}
Expand All @@ -104,25 +107,27 @@ export const ConnectionEntry = ({
</div>

<div className="flex-1 min-w-0">
<Button
asChild
className={cn(!isConnected && "pointer-events-none opacity-60", "justify-start p-0 h-auto font-mono text-sm mb-1 truncate")}
variant="link"
>
<Link title={aliasLabel} to={clusterId ? `/${clusterId}/${connectionId}/dashboard` : `/${connectionId}/dashboard`}>
{aliasLabel}
</Link>
</Button>
<Typography asChild variant="code">
<Button
asChild
className={cn(!isConnected && "pointer-events-none opacity-60", "justify-start p-0 h-auto mb-1 truncate")}
variant="link"
>
<Link title={aliasLabel} to={clusterId ? `/${clusterId}/${connectionId}/dashboard` : `/${connectionId}/dashboard`}>
{aliasLabel}
</Link>
</Button>
</Typography>
{connection.connectionDetails.alias && (
<span className="ml-1 font-mono text-xs text-tw-dark-border dark:text-white/50 truncate" title={label}>
<Typography className="ml-1 text-tw-dark-border dark:text-white/50 truncate" title={label} variant="bodyXs">
({label})
</span>)}
</Typography>)}
<div className="flex items-center gap-3">
<ConnectionStatusBadge status={statusType} />
{lastConnectionTime && lastConnectionTime.event === CONNECTED && (
<span className="text-xs text-gray-500 dark:text-gray-400 truncate" title={`Last connected: ${new Date(lastConnectionTime.timestamp).toLocaleString()}`}>
<Typography variant="bodyXs">
Last connected: {new Date(lastConnectionTime.timestamp).toLocaleString()}
</span>
</Typography>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ describe("Text Truncation Improvements", () => {
</TestWrapper>,
)

// Check for last connection time span with truncate class
const lastConnectionSpans = container.querySelectorAll("span.truncate")
const hasLastConnectionSpan = Array.from(lastConnectionSpans).some((span) =>
span.textContent?.includes("Last connected:"),
// Check for last connection time in Typography component (renders as p by default)
const lastConnectionElements = container.querySelectorAll("p")
const hasLastConnectionElement = Array.from(lastConnectionElements).some((p) =>
p.textContent?.includes("Last connected:"),
)
expect(hasLastConnectionSpan).toBe(true)
expect(hasLastConnectionElement).toBe(true)
})

it("should apply truncate class and title to alias display", () => {
Expand All @@ -100,12 +100,12 @@ describe("Text Truncation Improvements", () => {
</TestWrapper>,
)

// Check for alias span with truncate class and title
const aliasSpans = container.querySelectorAll("span.truncate[title]")
const hasAliasSpan = Array.from(aliasSpans).some((span) =>
span.textContent?.includes("(") && span.textContent?.includes(")"),
// Check for alias in Typography component with truncate and title
const aliasElements = container.querySelectorAll("p.truncate[title]")
const hasAliasElement = Array.from(aliasElements).some((p) =>
p.textContent?.includes("(") && p.textContent?.includes(")"),
)
expect(hasAliasSpan).toBe(true)
expect(hasAliasElement).toBe(true)
})
})

Expand Down Expand Up @@ -161,13 +161,13 @@ describe("Text Truncation Improvements", () => {
</TestWrapper>,
)

// Since there are no connected instances, cluster name should be an h3 element
const clusterNameElement = container.querySelector("h3[title]")
// Since there are no connected instances, cluster name should be a code element with Typography
const clusterNameElement = container.querySelector("code[title]")
expect(clusterNameElement).toBeInTheDocument()
expect(clusterNameElement).toHaveAttribute("title")

// Check that it has the ellipsis CSS classes
expect(clusterNameElement).toHaveClass("overflow-hidden", "text-ellipsis", "whitespace-nowrap")
// Check that it has truncate class
expect(clusterNameElement).toHaveClass("truncate")
})

it("should apply truncate class and title to instance count text", () => {
Expand All @@ -190,10 +190,10 @@ describe("Text Truncation Improvements", () => {
</TestWrapper>,
)

// Check for instance count div with truncate class and title
const instanceCountElements = container.querySelectorAll("div.truncate[title]")
const hasInstanceCountElement = Array.from(instanceCountElements).some((div) =>
div.textContent?.includes("instance"),
// Check for instance count in Typography component with code variant (renders as <code> element) with truncate and title
const instanceCountElements = container.querySelectorAll("code.truncate[title]")
const hasInstanceCountElement = Array.from(instanceCountElements).some((code) =>
code.textContent?.includes("instance"),
)
expect(hasInstanceCountElement).toBe(true)
})
Expand Down
5 changes: 4 additions & 1 deletion apps/frontend/src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Panel } from "../ui/panel"
import { Input } from "../ui/input"
import { StatCard } from "../ui/stat-card"
import RouteContainer from "../ui/route-container"
import { Typography } from "../ui/typography"
import { selectData } from "@/state/valkey-features/info/infoSelectors.ts"

export function Dashboard() {
Expand All @@ -30,7 +31,9 @@ export function Dashboard() {
title="Dashboard"
/>
<div className="flex flex-1 items-center justify-center">
<span className="text-gray-500">Loading metrics…</span>
<Typography className="text-gray-500" variant="body">
Loading metrics…
</Typography>
</div>
</div>
)
Expand Down
18 changes: 12 additions & 6 deletions apps/frontend/src/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CircleChevronDown, CircleChevronUp, Dot } from "lucide-react"
import { formatMetricValue, type ValueType } from "@common/src/format-metric-value"
import { TooltipProvider } from "@radix-ui/react-tooltip"
import { TooltipIcon } from "./tooltip-icon"
import { Typography } from "./typography"

interface AccordionProps {
accordionName?: string;
Expand Down Expand Up @@ -66,15 +67,17 @@ export default function Accordion({ accordionName, accordionItems, valueType = "
<TooltipProvider>
<div className="h-14 px-2 py-4 border border-input rounded-md shadow-xs flex items-center gap-2 justify-between">
<div className="flex flex-col gap-1">
<span className="font-semibold text-sm flex items-center gap-1">{accordionName}
<Typography className="flex items-center gap-1" variant="label">
{accordionName}
<TooltipIcon description={accordionDescription} size={16} />
</span>
<span className="text-xs font-light text-tw-dark-border">
</Typography>
<Typography variant="bodyXs">
{searchQuery.trim() && filteredItemCount !== itemCount ? (
<>{filteredItemCount} of {itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
) : (
<>{itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
)}</span>
)}
</Typography>
</div>
<button onClick={() => setIsOpen(!isOpen)}><ToggleIcon className="text-tw-primary cursor-pointer hover:text-tw-primary/80" /></button>
</div>
Expand All @@ -86,12 +89,15 @@ export default function Accordion({ accordionName, accordionItems, valueType = "
{Object.entries(filteredItems).map(([key, value]) => (
<li className="flex justify-between items-center border-b last:border-b-0" key={key}>
<div className="flex items-center gap-1">
<span className="font-normal flex items-center"><Dot className="text-tw-primary" size={30} />{formatKey(key)}</span>
<Typography className="flex items-center" variant="bodySm">
<Dot className="text-tw-primary" size={30} />
{formatKey(key)}
</Typography>
{singleMetricDescriptions[key] && (
<TooltipIcon description={`${singleMetricDescriptions[key].description} ${singleMetricDescriptions[key].unit}` } size={13} />
)}
</div>
<span className="font-light">{formatMetricValue(key, value, valueType)}</span>
<Typography variant="bodySm">{formatMetricValue(key, value, valueType)}</Typography>
</li>
))}
</ul>
Expand Down
Loading
Loading