Skip to content

Commit 8d91923

Browse files
committed
Dynamic Entity json handling
1 parent 280de2a commit 8d91923

1 file changed

Lines changed: 65 additions & 11 deletions

File tree

  • apps/api-manager/src/routes/(protected)/dynamic-entities/system/[id]/crud

apps/api-manager/src/routes/(protected)/dynamic-entities/system/[id]/crud/+page.svelte

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@
179179
return idField ? recordData[idField] : null;
180180
}
181181
182+
// Render a field value for display. Objects/arrays (json fields) are
183+
// stringified so they don't show as "[object Object]".
184+
function displayValue(value: any): string {
185+
if (value === undefined || value === null) return "-";
186+
if (typeof value === "object") return JSON.stringify(value);
187+
return String(value);
188+
}
189+
182190
let dataRecords = $state(data.dataRecords || []);
183191
let searchQuery = $state("");
184192
let showCreateModal = $state(false);
@@ -217,7 +225,14 @@
217225
formData = {};
218226
const recordData = record ? getRecordData(record) : null;
219227
Object.keys(properties).forEach((fieldName) => {
220-
formData[fieldName] = recordData ? recordData[fieldName] : "";
228+
const rawValue = recordData ? recordData[fieldName] : "";
229+
// json fields are edited as text, so render existing objects/arrays as
230+
// pretty-printed JSON for the textarea.
231+
if (properties[fieldName].type === "json" && rawValue && typeof rawValue === "object") {
232+
formData[fieldName] = JSON.stringify(rawValue, null, 2);
233+
} else {
234+
formData[fieldName] = rawValue;
235+
}
221236
});
222237
validationErrors = {};
223238
}
@@ -276,6 +291,18 @@
276291
case "boolean":
277292
// Accept true/false or convert from string
278293
break;
294+
case "json":
295+
// Must parse to a JSON object or array (OBP rejects bare strings/numbers)
296+
if (typeof value === "object") break;
297+
try {
298+
const parsed = JSON.parse(value);
299+
if (typeof parsed !== "object" || parsed === null) {
300+
return "Must be a JSON object or array";
301+
}
302+
} catch (e) {
303+
return e instanceof Error ? `Invalid JSON: ${e.message}` : "Invalid JSON";
304+
}
305+
break;
279306
case "DATE_WITH_DAY":
280307
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
281308
return "Must be in format YYYY-MM-DD";
@@ -339,6 +366,13 @@
339366
// Convert to number (JavaScript/JSON doesn't distinguish int vs float)
340367
converted[fieldName] = Number(value);
341368
break;
369+
case "json":
370+
// Parse the textarea contents into a real JSON object/array.
371+
// If the value is already an object (e.g. loaded from an existing
372+
// record), pass it through unchanged.
373+
converted[fieldName] =
374+
typeof value === "string" ? JSON.parse(value) : value;
375+
break;
342376
default:
343377
// Keep as string
344378
converted[fieldName] = String(value);
@@ -576,6 +610,8 @@
576610
case "integer":
577611
case "number":
578612
return "number";
613+
case "json":
614+
return "textarea";
579615
case "DATE_WITH_DAY":
580616
return "text";
581617
default:
@@ -939,10 +975,7 @@
939975
<td
940976
class="max-w-xs truncate px-6 py-4 text-sm text-gray-900 dark:text-gray-100"
941977
>
942-
{recordData[fieldName] !== undefined &&
943-
recordData[fieldName] !== null
944-
? String(recordData[fieldName])
945-
: "-"}
978+
{displayValue(recordData[fieldName])}
946979
</td>
947980
{/each}
948981
<td class="whitespace-nowrap px-6 py-4 text-right text-sm">
@@ -1037,7 +1070,7 @@
10371070
class="sticky top-0 flex items-center justify-between border-b border-gray-200 bg-white p-6 dark:border-gray-700 dark:bg-gray-800"
10381071
>
10391072
<h2 class="text-xl font-semibold text-gray-900 dark:text-gray-100">
1040-
Create New Record
1073+
Create New {entityName} Record
10411074
</h2>
10421075
<button
10431076
type="button"
@@ -1100,6 +1133,16 @@
11001133
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
11011134
/>
11021135
</div>
1136+
{:else if inputType === "textarea"}
1137+
<textarea
1138+
id="create-{fieldName}"
1139+
bind:value={formData[fieldName]}
1140+
rows="8"
1141+
placeholder={fieldDef.example
1142+
? JSON.stringify(fieldDef.example, null, 2)
1143+
: '{ }'}
1144+
class="mt-2 block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 font-mono text-sm text-gray-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100"
1145+
></textarea>
11031146
{:else}
11041147
<input
11051148
type={inputType}
@@ -1233,6 +1276,16 @@
12331276
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
12341277
/>
12351278
</div>
1279+
{:else if inputType === "textarea"}
1280+
<textarea
1281+
id="edit-{fieldName}"
1282+
bind:value={formData[fieldName]}
1283+
rows="8"
1284+
placeholder={fieldDef.example
1285+
? JSON.stringify(fieldDef.example, null, 2)
1286+
: '{ }'}
1287+
class="mt-2 block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 font-mono text-sm text-gray-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100"
1288+
></textarea>
12361289
{:else}
12371290
<input
12381291
type={inputType}
@@ -1337,11 +1390,12 @@
13371390
<span class="ml-1 text-xs text-red-600">(Required)</span>
13381391
{/if}
13391392
</dt>
1340-
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
1341-
{recordData[fieldName] !== undefined &&
1342-
recordData[fieldName] !== null
1343-
? String(recordData[fieldName])
1344-
: "-"}
1393+
<dd class="mt-1 whitespace-pre-wrap break-words font-mono text-sm text-gray-900 dark:text-gray-100">
1394+
{properties[fieldName].type === "json" &&
1395+
recordData[fieldName] &&
1396+
typeof recordData[fieldName] === "object"
1397+
? JSON.stringify(recordData[fieldName], null, 2)
1398+
: displayValue(recordData[fieldName])}
13451399
</dd>
13461400
</div>
13471401
{/each}

0 commit comments

Comments
 (0)