From c12129140f7957f04a2bccd1dbc570d669c082f2 Mon Sep 17 00:00:00 2001 From: Pavlo Kulyk Date: Wed, 17 Jun 2026 13:04:35 +0300 Subject: [PATCH] fix: add documentation for af edit json field plugin --- .../tutorial/09-Plugins/28-json-editor.md | 65 ++++++++++++++++++ .../09-Plugins/json-plugin-example.png | Bin 0 -> 13349 bytes 2 files changed, 65 insertions(+) create mode 100644 adminforth/documentation/docs/tutorial/09-Plugins/28-json-editor.md create mode 100644 adminforth/documentation/docs/tutorial/09-Plugins/json-plugin-example.png diff --git a/adminforth/documentation/docs/tutorial/09-Plugins/28-json-editor.md b/adminforth/documentation/docs/tutorial/09-Plugins/28-json-editor.md new file mode 100644 index 000000000..3e4801177 --- /dev/null +++ b/adminforth/documentation/docs/tutorial/09-Plugins/28-json-editor.md @@ -0,0 +1,65 @@ +--- +title: JSON Editor +description: "Guide to the JSON Editor plugin — add, edit and delete key-value pairs stored as a JSON object in a database column." +slug: /tutorial/Plugins/json-editor +--- + +# JSON Editor + +The JSON Editor plugin replaces the default text input for JSON columns with a structured key-value editor. Users can add, edit, and delete individual pairs without writing raw JSON. + +Values support all standard JSON types: strings, numbers, booleans, `null`, arrays, and nested objects. + +## Installation + +```bash +pnpm install @adminforth/json-editor --save +``` + +## Setting up + +Import and attach the plugin to an existing column in your resource: + +```ts title="./resources/apartments.ts" +//diff-add +import JsonEditorPlugin from '@adminforth/json-editor'; + +export default { + ... + plugins: [ + ... +//diff-add + new JsonEditorPlugin({ +//diff-add + fieldName: 'description', +//diff-add + }), + ], +} +``` + +> The plugin works on columns with type `json`, `string`, or `text`. The stored value must be a JSON object (`{}`). Top-level arrays and primitives are not supported. + +![alt text](json-plugin-example.png) + +## Value types + +Values are entered in JSON notation: + +| Input | Saved as | +|---|---| +| `"hello"` | string | +| `42` | number | +| `true` / `false` | boolean | +| `null` | null | +| `[1, "x", true]` | array | +| `{"key": 1}` | nested object | + +## Validation + +Saving is blocked when: + +- **Duplicate keys** - two rows share the same key name. +- **Invalid JSON value** - the value field is not valid JSON (e.g. `hello` without quotes). The error identifies the row: `Row 2: value is not valid JSON`. + +An empty value field is allowed and saved as an empty string `""`. diff --git a/adminforth/documentation/docs/tutorial/09-Plugins/json-plugin-example.png b/adminforth/documentation/docs/tutorial/09-Plugins/json-plugin-example.png new file mode 100644 index 0000000000000000000000000000000000000000..711add1196c5e696ae9a931b74e3277e36948242 GIT binary patch literal 13349 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 G+y4eQ>UlT- literal 0 HcmV?d00001