import React, { useState } from "react"; import { Button, Chip, Box, Typography, Dialog, DialogTitle, DialogContent, DialogActions, IconButton, Divider, } from "@mui/material"; import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; import type { FieldConfig } from "../../../types"; import { useAppContext } from "../../../context/AppContext"; import { extractFields } from "../../../transformers/field-config"; import { FormFieldRenderer } from "../FormFieldRenderer"; interface JsonFieldProps { field: FieldConfig; value: any; onChange: (val: any) => void; } export function JsonField({ field, value, onChange }: JsonFieldProps) { const { schemas } = useAppContext(); const [open, setOpen] = useState(false); const refSchema = field.refSchema ? schemas[field.refSchema] : null; const subFields = refSchema ? extractFields(field.refSchema!, refSchema, schemas) : []; const [editValue, setEditValue] = useState(null); const handleOpen = () => { setEditValue(initEditValue(value, field, schemas)); setOpen(true); }; const handleSave = () => { onChange(editValue); setOpen(false); }; const handleCancel = () => { setEditValue(null); setOpen(false); }; const handleClear = () => { onChange(null); setOpen(false); }; const handleAddItem = () => { setEditValue((prev: any[]) => [...(prev || []), buildDefaultShape(subFields, schemas)]); }; const handleRemoveItem = (index: number) => { setEditValue((prev: any[]) => prev.filter((_: any, i: number) => i !== index)); }; const handleItemFieldChange = (index: number, fieldName: string, val: any) => { setEditValue((prev: any[]) => { const next = [...prev]; next[index] = { ...next[index], [fieldName]: val }; return next; }); }; const handleFieldChange = (fieldName: string, val: any) => { setEditValue((prev: any) => ({ ...prev, [fieldName]: val })); }; if (!open) { if (value === null || value === undefined) { return ( ); } if (field.isArray && Array.isArray(value)) { if (value.length === 0) { return ( ); } return ( ); } if (typeof value === "object") { const summary = field.inlineDisplayFormat ? applyInlineFormat(value, field.inlineDisplayFormat) : Object.entries(value) .filter(([, v]) => v != null) .map(([k, v]) => `${k}: ${String(v)}`) .join(" | "); return ( ); } } return ( {field.label} {field.isArray ? ( ) : ( )} ); } function ObjectEditor({ value, subFields, onFieldChange, }: { value: any; subFields: FieldConfig[]; onFieldChange: (name: string, val: any) => void; schemas: Record; }) { return ( {subFields.map((subField) => ( onFieldChange(subField.name, val)} /> ))} ); } function ArrayEditor({ items, subFields, onAddItem, onRemoveItem, onFieldChange, schemas, }: { items: any[]; subFields: FieldConfig[]; onAddItem: () => void; onRemoveItem: (index: number) => void; onFieldChange: (index: number, name: string, val: any) => void; schemas: Record; }) { return ( {items.length === 0 && ( No items added yet. )} {items.map((item, index) => ( Item {index + 1} onRemoveItem(index)}> {subFields.map((subField) => ( onFieldChange(index, subField.name, val)} /> ))} ))} ); } function buildDefaultShape(fields: FieldConfig[], schemas: Record): Record { const shape: Record = {}; for (const f of fields) { if (f.refSchema && !f.fk) { const refSchemaObj = schemas[f.refSchema!]; const nestedFields = refSchemaObj ? extractFields(f.refSchema!, refSchemaObj, schemas) : []; shape[f.name] = f.isArray ? [] : buildDefaultShape(nestedFields, schemas); } else { shape[f.name] = null; } } return shape; } function initEditValue(value: any, field: FieldConfig, schemas: Record): any { if (field.isArray) { return value ? value.map((item: any) => ({ ...item })) : []; } if (value && typeof value === "object") { return { ...value }; } return buildDefaultShape( field.refSchema ? extractFields(field.refSchema, schemas[field.refSchema], schemas) : [], schemas ); } function applyInlineFormat(obj: any, format: string): string { if (!obj || typeof obj !== "object") return String(obj ?? ""); return format.replace(/\{(\w+)\}/g, (_, key) => String(obj[key] ?? "")); }