Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 3 additions & 3 deletions apps/frontend/src/components/key-browser/KeyBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,15 @@ export function KeyBrowser() {
className="flex-1"
label="Total Keys"
tooltip={
<TooltipIcon description="Total number of keys in the database" size={14} />
<TooltipIcon description="Total number of keys scanned/fetched" size={14} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would "sampled" be a better word here than "scanned/fetched"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, sampled is much better. Thanks

}
value={totalKeys}
/>
<StatCard
className="flex-1"
label="Memory Usage"
tooltip={
<TooltipIcon description="Memory used by all keys in the database" size={14} />
<TooltipIcon description="Memory used by keys scanned/fetched" size={14} />
}
value={formatBytes(totalMemoryUsage)}
/>
Expand Down Expand Up @@ -206,7 +206,7 @@ export function KeyBrowser() {
disabled={loading}
onChange={(e) => setSearchPattern(e.target.value)}
onClear={handleClearSearch}
placeholder="Search keys (use * to search patterns like user:*)"
placeholder="Search keys (use * to search patterns like user:* and press Enter)"
value={searchPattern}
/>
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Key, Trash, X } from "lucide-react"
import { Key, Trash, TriangleAlert, X } from "lucide-react"
import { useState } from "react"
import { convertTTL } from "@common/src/ttl-conversion"
import { formatBytes } from "@common/src/bytes-conversion"
Expand All @@ -17,6 +17,7 @@ import { useAppDispatch } from "@/hooks/hooks"
import { deleteKeyRequested } from "@/state/valkey-features/keys/keyBrowserSlice"
import { CustomTooltip } from "@/components/ui/tooltip"
import { Typography } from "@/components/ui/typography"
import { Alert, AlertDescription } from "@/components/ui/alert"

interface BaseKeyInfo {
name: string;
Expand Down Expand Up @@ -157,9 +158,11 @@ export default function KeyDetails({ selectedKey, selectedKeyInfo, connectionId,
</div>

{selectedKeyInfo.elementsWarning ? (
<div className="text-center text-muted-foreground py-8">
<Typography variant="bodySm">{selectedKeyInfo.elementsWarning}</Typography>
</div>
<Alert variant="warning">
<TriangleAlert className="w-4 h-4" />
<AlertDescription>{selectedKeyInfo.elementsWarning}
</AlertDescription>
</Alert>
) : (
<>
{/* show different key types */}
Expand Down
15 changes: 6 additions & 9 deletions apps/frontend/src/components/key-browser/key-types.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Trash, Check, TriangleAlert } from "lucide-react"
import { Alert, AlertDescription } from "../ui/alert"
import { Button } from "../ui/button"
import { Input } from "../ui/input"
import { Label } from "../ui/label"
Expand Down Expand Up @@ -310,18 +311,14 @@ export function JsonFields({ value, setValue, jsonModuleAvailable = false }: Jso
<Label htmlFor="json-value">JSON Value *</Label>

{/* JSON Module Indicator */}
<div className={`flex gap-2 px-3 py-2 rounded bg-primary/20 ${
jsonModuleAvailable
? "text-teal-500"
: "text-red-400"
}`}>
{jsonModuleAvailable ? <Check size={14} /> : <TriangleAlert size={14} />}
<Typography variant="bodyXs">
<Alert variant={jsonModuleAvailable ? "success" : "warning"}>
{jsonModuleAvailable ? <Check className="h-4 w-4" /> : <TriangleAlert className="h-4 w-4" />}
<AlertDescription>
{jsonModuleAvailable
? "JSON module is available"
: "JSON module is not loaded on this Valkey instance"}
</Typography>
</div>
</AlertDescription>
</Alert>

<Textarea
className="min-h-[150px] font-mono text-sm"
Expand Down
66 changes: 66 additions & 0 deletions apps/frontend/src/components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm transition-colors " +
"[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-3 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default:
"bg-background text-foreground border-border",
destructive:
"border-destructive text-destructive bg-destructive/10",
success:
"border-green-500 text-green-700 dark:text-green-400 bg-green-50 dark:bg-green-500/10",
warning:
"border-yellow-500 text-yellow-700 dark:text-yellow-400 bg-yellow-50 dark:bg-yellow-500/10",
},
},
defaultVariants: {
variant: "default",
},
},
)

interface AlertProps
extends React.ComponentProps<"div">,
VariantProps<typeof alertVariants> {}

function Alert({ className, variant, ...props }: AlertProps) {
return (
<div
className={cn(alertVariants({ variant }), className)}
data-slot="alert"
role="alert"
{...props}
/>
)
}

type AlertTitleProps = React.HTMLAttributes<HTMLHeadingElement>

function AlertTitle({ className, ...props }: AlertTitleProps) {
return (
<h5
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
data-slot="alert-title"
{...props}
/>
)
}

type AlertDescriptionProps = React.HTMLAttributes<HTMLParagraphElement>

function AlertDescription({ className, ...props }: AlertDescriptionProps) {
return (
<div
className={cn("text-sm [&_p]:leading-relaxed", className)}
data-slot="alert-description"
{...props}
/>
)
}

export { Alert, AlertTitle, AlertDescription }
18 changes: 10 additions & 8 deletions apps/frontend/src/components/ui/connection-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Loader2, X } from "lucide-react"
import { AlertTriangle, Loader2, X } from "lucide-react"
import { type FormEvent } from "react"
import * as Dialog from "@radix-ui/react-dialog"
import { MAX_CONNECTIONS } from "@common/src/constants.ts"
import { Alert, AlertDescription } from "./alert.tsx"
import { Button } from "./button.tsx"
import { Input } from "./input.tsx"
import { Typography } from "./typography.tsx"
Expand Down Expand Up @@ -62,9 +63,9 @@ export function ConnectionModal({
</Dialog.Description>

{errorMessage && (
<div className="mt-4 p-1 bg-primary/20 border rounded">
<Typography className="text-red-500" variant="bodySm">{errorMessage}</Typography>
</div>
<Alert className="mt-4" variant="destructive">
<AlertDescription>{errorMessage}</AlertDescription>
</Alert>
)}

<form className="space-y-4 mt-4" onSubmit={onSubmit}>
Expand Down Expand Up @@ -183,12 +184,13 @@ export function ConnectionModal({
)}

{showConnectionLimitWarning && (
<div className="mt-4 p-2 bg-yellow-100 border rounded">
<Typography variant="bodyXs">
<Alert className="mt-4" variant="warning">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
You've reached the maximum of {MAX_CONNECTIONS} active connections.
Please disconnect one before connecting to another.
</Typography>
</div>
</AlertDescription>
</Alert>
)}

<div className="pt-2 text-sm">
Expand Down
5 changes: 3 additions & 2 deletions apps/frontend/src/components/ui/delete-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Trash, X } from "lucide-react"
import { Button } from "./button"
import { Typography } from "./typography"

interface DeleteModalProps {
itemName: string;
Expand All @@ -18,10 +19,10 @@ export default function DeleteModal({
<div className="flex flex-col items-start gap-1 min-h-24 w-60 bg-white dark:bg-tw-dark-primary
border border-tw-dark-border rounded p-2 text-sm font-thin shadow-xl absolute top-full mt-1 right-0 z-100">
<div className="flex flex-row justify-between w-full">
<span>{message}</span>
<Typography variant={"label"}>{message}</Typography>
<X className="hover:text-primary cursor-pointer" onClick={onCancel} size={16} />
</div>
<span className="font-semibold break-words w-full">{itemName}</span>
<Typography className="break-words w-full" variant={"code"}>{itemName}</Typography>
<Button onClick={onConfirm} variant={"destructiveGhost"}>
<Trash size={12} /> Delete
</Button>
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/components/ui/search-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
"placeholder:text-muted-foreground",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"disabled:cursor-not-allowed disabled:opacity-50",
"[&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden",
className,
)}
data-slot="search-input"
Expand All @@ -33,7 +34,7 @@ const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
/>
{showClearButton && hasValue && onClear && (
<button
className="absolute right-3 text-primary hover:text-primary/80 transition-colors"
className="absolute right-3 text-tw-primary hover:text-tw-primary/80 transition-colors"
onClick={onClear}
type="button"
>
Expand Down
Loading