Skip to content

Commit 619979d

Browse files
authored
Merge pull request #11 from ghazi-git/bulk-update
bulk-update
2 parents f6893e8 + 1dce3d3 commit 619979d

File tree

4 files changed

+67
-33
lines changed

4 files changed

+67
-33
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
55

66
## [Unreleased]
77

8+
### Added
9+
10+
- Allow bulk updating the selected objects from the table.
11+
812
## [1.1.2] - 2025-12-10
913

1014
### Fixed

src/devtools/components/main-content/object-store-view/AddObjectsButton.tsx

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Schema, ValidationError, Validator } from "jsonschema";
22
import { PrismEditor } from "prism-code-editor";
3-
import { createEffect, createSignal, onMount, Show, untrack } from "solid-js";
3+
import {
4+
createEffect,
5+
createMemo,
6+
createSignal,
7+
onMount,
8+
Show,
9+
} from "solid-js";
410

511
import UnstyledButton from "@/devtools/components/buttons/UnstyledButton";
612
import DatatypeValidationCheckbox from "@/devtools/components/main-content/object-store-view/DatatypeValidationCheckbox";
@@ -17,7 +23,7 @@ import {
1723
parseJSONFromUser,
1824
} from "@/devtools/utils/json-editor";
1925
import {
20-
NewObject,
26+
SerializedObject,
2127
TableColumn,
2228
TableColumnValue,
2329
TableRow,
@@ -26,14 +32,20 @@ import {
2632
import styles from "./AddObjectsButton.module.css";
2733

2834
export default function AddObjectsButton() {
29-
const { createData } = useTableMutationContext();
35+
const { tableMutationStore, createData } = useTableMutationContext();
3036
const { query, refetch } = useTableContext();
3137
const [validateDatatypes, setValidateDatatypes] = createSignal(true);
3238
const [error, setError] = createSignal<string[]>([]);
3339
let dialogRef!: HTMLDialogElement;
3440
let editorRef!: HTMLDivElement;
3541
let editor: PrismEditor;
3642

43+
const title = () => {
44+
return tableMutationStore.selectedObjects.length > 0
45+
? "Edit Object(s)"
46+
: "Add Objects";
47+
};
48+
3749
const onSaveClick = async () => {
3850
setError([]);
3951
const value = editor.value.trim();
@@ -53,7 +65,7 @@ export default function AddObjectsButton() {
5365
}
5466

5567
if (!query.data?.columns?.length) {
56-
const msg = `Unable to add any object due to inability to determine the object store key.`;
68+
const msg = `Unable to save any object due to inability to determine the object store key.`;
5769
setError([msg]);
5870
return;
5971
}
@@ -69,7 +81,7 @@ export default function AddObjectsButton() {
6981
return;
7082
}
7183

72-
const newObjects = getNewObjects(parsedObj as TableRow[], cols);
84+
const newObjects = serializeObjects(parsedObj as TableRow[], cols);
7385
try {
7486
await createData({
7587
requestID: generateRequestID(),
@@ -79,58 +91,61 @@ export default function AddObjectsButton() {
7991
});
8092
refetch();
8193
dialogRef.close();
82-
editor.setOptions({ value: getSampleValue(cols) });
94+
editor.setOptions({ value: editorData() });
8395
} catch (e) {
8496
const msg = e instanceof Error ? e.message : DATA_MUTATION_ERROR_MSG;
8597
setError([msg]);
8698
}
8799
};
88100

89101
onMount(() => {
90-
editor = createJSONEditor(
91-
editorRef,
92-
getSampleValue(query.data?.columns || []),
93-
);
102+
editor = createJSONEditor(editorRef, "");
94103
});
95-
createEffect(() => {
104+
const editorData = createMemo(() => {
105+
const columns = query.data?.columns;
96106
const activeStore = query.data?.activeStore;
97-
if (activeStore) {
98-
const columns = untrack(() => query.data?.columns || []);
99-
editor.setOptions({ value: getSampleValue(columns) });
100-
} else {
101-
editor.setOptions({ value: "" });
107+
108+
if (columns && tableMutationStore.selectedObjects.length > 0) {
109+
return stringifyData(tableMutationStore.selectedObjects, columns);
110+
} else if (columns && activeStore) {
111+
const data = getSampleValue(columns);
112+
return stringifyData(data, columns);
102113
}
114+
return "";
115+
});
116+
createEffect(() => {
117+
editor.setOptions({ value: editorData() });
103118
});
104119

105120
return (
106121
<>
107122
<UnstyledButton
108123
class={styles["dialog-trigger"]}
109124
command="show-modal"
110-
commandfor="add-objects-modal"
125+
commandfor="add-edit-objects-modal"
111126
>
112-
Add Objects
127+
{title()}
113128
</UnstyledButton>
114129
<dialog
115130
ref={dialogRef}
116-
id="add-objects-modal"
131+
id="add-edit-objects-modal"
117132
class={styles.dialog}
118133
onClose={() => setError([])}
119134
>
120135
<header>
121-
<h2>Add Objects</h2>
136+
<h2>{title()}</h2>
122137
<UnstyledButton
123138
title="Close Modal"
124139
aria-label="Close Modal"
125140
command="close"
126-
commandfor="add-objects-modal"
141+
commandfor="add-edit-objects-modal"
127142
>
128143
<CloseIcon />
129144
</UnstyledButton>
130145
</header>
131146
<div ref={editorRef} />
132147
<div class={styles.hint}>
133-
<div>The json value entered must be an array of objects.</div>
148+
<div>The JSON value entered must be an array of objects.</div>
134149
<div>
135150
Use ctrl+M/ctrl+shift+M(Mac) to toggle the use of Tab for
136151
indentation.
@@ -150,7 +165,7 @@ export default function AddObjectsButton() {
150165
/>
151166
</Show>
152167
<footer>
153-
<UnstyledButton command="close" commandfor="add-objects-modal">
168+
<UnstyledButton command="close" commandfor="add-edit-objects-modal">
154169
Cancel
155170
</UnstyledButton>
156171
<UnstyledButton
@@ -186,7 +201,22 @@ function getSampleValue(columns: TableColumn[]) {
186201
return [column.name, value] as [string, TableColumnValue];
187202
});
188203
const obj = Object.fromEntries(keyValuePairs);
189-
return JSON.stringify([obj], null, 2);
204+
return [obj];
205+
}
206+
207+
function stringifyData(data: TableRow[], columns: TableColumn[]) {
208+
// sort the data keys according to columns' order
209+
const objects = data.map((row) => {
210+
const keyValuePairs = columns.map((col) => [col.name, row[col.name]]);
211+
return Object.fromEntries(keyValuePairs);
212+
});
213+
214+
return JSON.stringify(
215+
objects,
216+
// replace undefined with null
217+
(_, val) => (val === undefined ? null : val),
218+
2,
219+
);
190220
}
191221

192222
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -238,10 +268,10 @@ function generateErrorMsg(error: ValidationError) {
238268
return `${error.property.replace("instance", "object")} ${error.message}`;
239269
}
240270

241-
function getNewObjects(
271+
function serializeObjects(
242272
parsedObj: TableRow[],
243273
cols: TableColumn[],
244-
): NewObject[] {
274+
): SerializedObject[] {
245275
return parsedObj.map((row) => {
246276
return Object.entries(row).map(([colName, colValue]) => {
247277
const col = cols.find((col) => col.name === colName);

src/devtools/utils/inspected-window-data-create.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,14 @@ function insertObjects(
8686
const objStore = tx.objectStore(storeName);
8787
objects.forEach((obj) => {
8888
try {
89-
const addRequest = objStore.add(obj);
90-
addRequest.onerror = () => {
91-
console.error("data-creation: add error", addRequest.error);
89+
const putRequest = objStore.put(obj);
90+
putRequest.onerror = () => {
91+
console.error("data-creation: put error", putRequest.error);
9292
// better to just return the indexedDB error given that many things
9393
// can go wrong
94-
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/add#exceptions
94+
// https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put#exceptions
9595
const msg =
96-
addRequest.error?.message ??
96+
putRequest.error?.message ??
9797
`Unable to insert the object=${JSON.stringify(obj)}.`;
9898
reject(new Error(msg));
9999
};

src/devtools/utils/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ export interface DataCreationRequest {
128128
requestID: string;
129129
dbName: string;
130130
storeName: string;
131-
objects: NewObject[];
131+
objects: SerializedObject[];
132132
}
133133

134-
export type NewObject = {
134+
export type SerializedObject = {
135135
name: string;
136136
value: JSONSerializable;
137137
datatype: TableColumnDatatype;

0 commit comments

Comments
 (0)