Skip to content

Commit 6bbc7b8

Browse files
authored
DX-2455: fix: delete item instead of key when a list item is selected (#49)
* fix: display area badges scroll bug * feat: bump redis to rc.12 * feat: update readme file to include details about project internals * refactor: schema parser to support more errors * fix: update query types and remove $ne * fix: many minor fixes for redis search * fix: loading state of query wizard * fix: minor style fixes for search-empty-state * fix: delete item instead of key when a list item is selected - Add ItemActions component with item-level delete that calls useEditListItem with newKey: undefined - Show ItemActions instead of KeyActions when selectedListItem is set (hash/list/set/zset) - Hide "+" add button when an item is selected; hide all actions for stream items - Pass name to DeleteKeyModal so the modal shows exactly what will be deleted - Improve DeleteKeyModal description: "delete key/item "name"" or "item at index N" for lists * fix: allow adding new entries to stream keys - Make stream form inputs editable when adding a new entry (isNew) - Filter empty strings before passing field array to transformArray to avoid even-length errors * fix: show "Add Item" button text when adding a new list item
1 parent c3762ff commit 6bbc7b8

File tree

6 files changed

+98
-10
lines changed

6 files changed

+98
-10
lines changed

src/components/databrowser/components/delete-key-modal.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export function DeleteKeyModal({
2222
deletionType,
2323
count = 1,
2424
showReindex,
25+
name,
26+
nameLabel,
2527
}: {
2628
children?: React.ReactNode
2729
onDeleteConfirm: (e: MouseEvent, options?: { reindex?: boolean }) => void | Promise<void>
@@ -30,6 +32,8 @@ export function DeleteKeyModal({
3032
deletionType: "item" | "key"
3133
count?: number
3234
showReindex?: boolean
35+
name?: string
36+
nameLabel?: string
3337
}) {
3438
const isPlural = count > 1
3539
const itemLabel = deletionType === "item" ? "Item" : "Key"
@@ -53,7 +57,19 @@ export function DeleteKeyModal({
5357
</DialogTitle>
5458
<DialogDescription className="mt-5">
5559
Are you sure you want to delete{" "}
56-
{isPlural ? `these ${count} ${deletionType}s` : `this ${deletionType}`}?<br />
60+
{name ? (
61+
<>
62+
{`${nameLabel ?? deletionType} `}
63+
<span className="font-medium text-zinc-900 dark:text-zinc-100">
64+
{nameLabel ? name : `"${name}"`}
65+
</span>
66+
</>
67+
) : isPlural ? (
68+
`these ${count} ${deletionType}s`
69+
) : (
70+
`this ${deletionType}`
71+
)}
72+
?<br />
5773
This action cannot be undone.
5874
</DialogDescription>
5975
</DialogHeader>

src/components/databrowser/components/display/display-header.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useTab } from "@/tab-provider"
2-
import { type DataType } from "@/types"
2+
import { type DataType, type ListDataType } from "@/types"
33
import { IconPlus } from "@tabler/icons-react"
44

55
import { Button } from "@/components/ui/button"
@@ -8,6 +8,7 @@ import { SimpleTooltip } from "@/components/ui/tooltip"
88

99
import { TypeTag } from "../type-tag"
1010
import { HeaderTTLBadge, LengthBadge, SizeBadge } from "./header-badges"
11+
import { ItemActions } from "./item-actions"
1112
import { KeyActions } from "./key-actions"
1213

1314
export const DisplayHeader = ({
@@ -21,7 +22,10 @@ export const DisplayHeader = ({
2122
type: DataType
2223
hideTypeTag?: boolean
2324
}) => {
24-
const { setSelectedListItem } = useTab()
25+
const { setSelectedListItem, selectedListItem } = useTab()
26+
27+
const isListType = type !== "string" && type !== "json" && type !== "search" && type !== "stream"
28+
const showItemActions = isListType && Boolean(selectedListItem)
2529

2630
const handleAddItem = () => {
2731
setSelectedListItem({ key: type === "stream" ? "*" : "", isNew: true })
@@ -40,15 +44,19 @@ export const DisplayHeader = ({
4044
</h2>
4145

4246
<div className="flex items-center gap-1">
43-
{type !== "string" && type !== "json" && type !== "search" && (
47+
{type !== "string" && type !== "json" && type !== "search" && !showItemActions && (
4448
<SimpleTooltip content="Add item">
4549
<Button onClick={handleAddItem} size="icon-sm" aria-label="Add item">
4650
<IconPlus className="size-4 text-zinc-500 dark:text-zinc-600" />
4751
</Button>
4852
</SimpleTooltip>
4953
)}
5054

51-
<KeyActions dataKey={dataKey} content={content} type={type} />
55+
{showItemActions ? (
56+
<ItemActions dataKey={dataKey} type={type as ListDataType} />
57+
) : selectedListItem ? undefined : (
58+
<KeyActions dataKey={dataKey} content={content} type={type} />
59+
)}
5260
</div>
5361
</div>
5462

src/components/databrowser/components/display/display-list-edit.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,17 @@ const ListEditForm = ({
8585
{type === "zset" && <NumberFormItem name="value" label={valueLabel} />}
8686

8787
{type !== "list" && (
88-
<FormItem readOnly={type === "stream"} name="key" label={keyLabel} data={itemKey} />
88+
<FormItem
89+
readOnly={type === "stream" && !isNew}
90+
name="key"
91+
label={keyLabel}
92+
data={itemKey}
93+
/>
8994
)}
9095

9196
{type !== "set" && type !== "zset" && (
9297
<FormItem
93-
readOnly={type === "stream"}
98+
readOnly={type === "stream" && !isNew}
9499
name="value"
95100
label={valueLabel}
96101
data={itemValue ?? ""}
@@ -129,8 +134,8 @@ const ListEditForm = ({
129134
(type === "stream" && !isNew)
130135
}
131136
>
132-
<Spinner isLoading={isPending} isLoadingText={"Saving"}>
133-
Save
137+
<Spinner isLoading={isPending} isLoadingText={isNew ? "Adding" : "Saving"}>
138+
{isNew ? "Add Item" : "Save"}
134139
</Spinner>
135140
</Button>
136141
</SimpleTooltip>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useTab } from "@/tab-provider"
2+
import type { ListDataType } from "@/types"
3+
import { IconDotsVertical } from "@tabler/icons-react"
4+
5+
import { Button } from "@/components/ui/button"
6+
import {
7+
DropdownMenu,
8+
DropdownMenuContent,
9+
DropdownMenuItem,
10+
DropdownMenuTrigger,
11+
} from "@/components/ui/dropdown-menu"
12+
13+
import { useEditListItem } from "../../hooks/use-edit-list-item"
14+
import { DeleteKeyModal } from "../delete-key-modal"
15+
16+
export function ItemActions({ dataKey, type }: { dataKey: string; type: ListDataType }) {
17+
const { selectedListItem, setSelectedListItem } = useTab()
18+
const { mutateAsync: editItem } = useEditListItem()
19+
20+
if (!selectedListItem) return
21+
22+
return (
23+
<DropdownMenu modal={false}>
24+
<DropdownMenuTrigger asChild>
25+
<Button size="icon-sm" aria-label="Item actions">
26+
<IconDotsVertical
27+
className="size-4 text-zinc-500 dark:text-zinc-600"
28+
fill="rgb(var(--color-zinc-500))"
29+
/>
30+
</Button>
31+
</DropdownMenuTrigger>
32+
33+
<DropdownMenuContent align="end">
34+
<DeleteKeyModal
35+
deletionType="item"
36+
name={selectedListItem.key}
37+
nameLabel={type === "list" ? "item at index" : undefined}
38+
onDeleteConfirm={async () => {
39+
await editItem({
40+
type,
41+
dataKey,
42+
itemKey: selectedListItem.key,
43+
newKey: undefined,
44+
})
45+
setSelectedListItem(undefined)
46+
}}
47+
>
48+
<DropdownMenuItem
49+
className="text-red-500 focus:bg-red-500 focus:text-white"
50+
onSelect={(e) => e.preventDefault()}
51+
>
52+
Delete item
53+
</DropdownMenuItem>
54+
</DeleteKeyModal>
55+
</DropdownMenuContent>
56+
</DropdownMenu>
57+
)
58+
}

src/components/databrowser/components/display/key-actions.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export function KeyActions({
5858
</DropdownMenuItem>
5959
<DeleteKeyModal
6060
deletionType="key"
61+
name={dataKey}
6162
showReindex={isValuesSearchSelected && type !== "search"}
6263
onDeleteConfirm={async (_e, options) => {
6364
await deleteKey({ keys: [dataKey], reindex: options?.reindex })

src/components/databrowser/hooks/use-edit-list-item.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export const useEditListItem = () => {
8585
}
8686
case "stream": {
8787
if (!isNew || !newKey) throw new Error("Stream data type is not mutable")
88-
const opts = transformArray(newValue?.split("\n") ?? []).map(
88+
const opts = transformArray((newValue?.split("\n") ?? []).filter(Boolean)).map(
8989
({ key, value }) => [key, value] as const
9090
)
9191

0 commit comments

Comments
 (0)