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
21 changes: 19 additions & 2 deletions apps/frontend/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { LayoutDashboard, Search } from "lucide-react"
import { useParams } from "react-router"
import { formatBytes } from "@common/src/bytes-conversion"
import { Database } from "lucide-react"
import { accordionDescriptions } from "@common/src/dashboard-metrics"
import { singleMetricDescriptions } from "@common/src/dashboard-metrics"
import { AppHeader } from "./ui/app-header"
import Accordion from "./ui/accordion"
import DonutChart from "./ui/donut-chart"
Expand Down Expand Up @@ -43,6 +45,7 @@ export function Dashboard() {
const clientConnectivityMetrics = {
blocked_clients: infoData.blocked_clients,
clients_in_timeout_table: infoData.clients_in_timeout_table,
connected_clients : infoData.connected_clients,
connected_slaves: infoData.connected_slaves,
total_connections_received: infoData.total_connections_received,
evicted_clients: infoData.evicted_clients,
Expand Down Expand Up @@ -115,39 +118,53 @@ export function Dashboard() {
/>
</div>
<Accordion
accordionDescription={accordionDescriptions.memoryUsageMetrics}
accordionItems={memoryUsageMetrics}
accordionName="Memory Usage Metrics"
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="bytes" />
<Accordion
accordionDescription={accordionDescriptions.uptimeMetrics}
accordionItems={upTimeMetrics}
accordionName="Uptime Metrics"
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="mixed" />
<Accordion
accordionDescription={accordionDescriptions.replicationPersistenceMetrics}
accordionItems={replicationPersistenceMetrics}
accordionName="Replication & Persistence Metrics"
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="number" />
<Accordion
accordionDescription={accordionDescriptions.clientConnectivityMetrics}
accordionItems={clientConnectivityMetrics}
accordionName="Client Connectivity Metrics"
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="number" />
<Accordion
accordionDescription={accordionDescriptions.commandExecutionMetrics}
accordionItems={commandExecutionMetrics}
accordionName="Command Execution Metrics"
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="number" />
<Accordion
accordionDescription={accordionDescriptions.dataEffectivenessEvictionMetrics}
accordionItems={dataEffectivenessAndEvictionMetrics}
accordionName="Data Effectiveness & Eviction Metrics"
searchQuery={searchQuery}
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="number" />
<Accordion
accordionDescription={accordionDescriptions.messagingMetrics}
accordionItems={messagingMetrics}
accordionName="Messaging Metrics"
searchQuery={searchQuery}
searchQuery={searchQuery}
singleMetricDescriptions={singleMetricDescriptions}
valueType="number" />
</div>
{/* Chart Area */}
Expand Down
46 changes: 32 additions & 14 deletions apps/frontend/src/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { useState, useMemo, useEffect } from "react"
import { CircleChevronDown, CircleChevronUp, Dot } from "lucide-react"
import { CircleChevronDown, CircleChevronUp, Dot, CircleQuestionMark } from "lucide-react"
import { formatMetricValue, type ValueType } from "@common/src/format-metric-value"
import { TooltipProvider } from "@radix-ui/react-tooltip"
import { CustomTooltip } from "./custom-tooltip"

interface AccordionProps {
accordionName?: string;
accordionItems?: Record<string, number | null>;
valueType?: ValueType;
searchQuery?: string;
accordionDescription?: string;
singleMetricDescriptions?: Record<string, { description: string; unit: string }>;
}

export default function Accordion({ accordionName, accordionItems, valueType = "number", searchQuery = "" }: AccordionProps) {
export default function Accordion({ accordionName, accordionItems, valueType = "number", searchQuery = "",
accordionDescription = "", singleMetricDescriptions = {} }: AccordionProps) {
const [isOpen, setIsOpen] = useState(false)

const ToggleIcon = isOpen ? CircleChevronUp : CircleChevronDown
Expand Down Expand Up @@ -55,25 +60,38 @@ export default function Accordion({ accordionName, accordionItems, valueType = "

return (
<div className="w-full mt-2">
<div className="h-14 px-2 py-4 dark:border-tw-dark-border border rounded flex items-center gap-2 justify-between">
<div className="flex flex-col gap-1">
<span className="font-semibold text-sm">{accordionName}</span>
<span className="text-xs font-light text-tw-dark-border">
{searchQuery.trim() && filteredItemCount !== itemCount ? (
<>{filteredItemCount} of {itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
) : (
<>{itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
)}</span>
<TooltipProvider>
<div className="h-14 px-2 py-4 dark:border-tw-dark-border border rounded 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}
<CustomTooltip description={accordionDescription}>
<CircleQuestionMark className="bg-tw-primary/10 rounded-full text-tw-primary" size={16} />
</CustomTooltip>
</span>
<span className="text-xs font-light text-tw-dark-border">
{searchQuery.trim() && filteredItemCount !== itemCount ? (
<>{filteredItemCount} of {itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
) : (
<>{itemCount} {itemCount === 1 ? "metric" : "metrics"}</>
)}</span>
</div>
<button onClick={() => setIsOpen(!isOpen)}><ToggleIcon className="text-tw-primary cursor-pointer hover:text-tw-primary/80" /></button>
</div>
<button onClick={() => setIsOpen(!isOpen)}><ToggleIcon className="text-tw-primary cursor-pointer hover:text-tw-primary/80" /></button>
</div>
</TooltipProvider>

{isOpen && (
<div className="px-2 py-3 border bg-gray-50 dark:bg-gray-800 text-sm dark:border-tw-dark-border rounded-b">
<ul className="space-y-2">
{Object.entries(filteredItems).map(([key, value]) => (
<li className="flex justify-between items-center hover:bg-tw-primary/20" key={key}>
<span className="font-normal flex items-center"><Dot className="text-tw-primary" size={30} />{formatKey(key)}</span>
<div className="flex items-center gap-1">
<span className="font-normal flex items-center"><Dot className="text-tw-primary" size={30} />{formatKey(key)}</span>
{singleMetricDescriptions[key] && (
<CustomTooltip description={singleMetricDescriptions[key].description} unit={singleMetricDescriptions[key].unit}>
<CircleQuestionMark className="bg-tw-primary/10 rounded-full text-tw-primary" size={13} />
</CustomTooltip>
)}
</div>
<span className="font-light">{formatMetricValue(key, value, valueType)}</span>
</li>
))}
Expand Down
34 changes: 25 additions & 9 deletions apps/frontend/src/components/ui/custom-tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,36 @@ import {

interface CustomTooltipProps {
children: React.ReactNode;
content: string;
content?: string;
description?: string;
unit?: string;
}

export function CustomTooltip({ children, content }: CustomTooltipProps) {
export function CustomTooltip({ children, content, description, unit }: CustomTooltipProps) {
return (
<Tooltip delayDuration={200}>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent
align="center"
className={"bg-tw-primary text-white px-2 py-1 mt-1 rounded text-xs font-light z-10"}
side="bottom"
>
<p>{content}</p>
</TooltipContent>
{content &&
<TooltipContent
align="center"
className={"bg-tw-primary text-white px-2 py-1 mt-1 rounded text-xs font-light z-10"}
side="bottom"
>
<p>{content}</p>
</TooltipContent>
}
{description &&
<TooltipContent
align="center"
className={"bg-tw-primary text-white px-2 py-1 mt-1 rounded text-xs font-light z-10 w-1/2"}
side="right"
>
<p>{description}</p>
{unit && (
<p className="bg-white/20 rounded-full px-1 py-0.5 text-xs mt-1 font-medium text-white inline-block w-fit">{unit}</p>
)}
</TooltipContent>
}
</Tooltip>
)
}
Loading
Loading