diff --git a/frontends/nextjs/src/components/editors/JsonEditor.tsx b/frontends/nextjs/src/components/editors/JsonEditor.tsx index 7b6beb59a..f6ebcf593 100644 --- a/frontends/nextjs/src/components/editors/JsonEditor.tsx +++ b/frontends/nextjs/src/components/editors/JsonEditor.tsx @@ -1,12 +1,13 @@ -import { useState, useEffect } from 'react' -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui' -import { Button } from '@/components/ui' -import { Alert, AlertDescription } from '@/components/ui' -import { FloppyDisk, X, Warning, ShieldCheck } from '@phosphor-icons/react' +import { useEffect, useState } from 'react' +import { Alert, AlertDescription, Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui' +import { Warning } from '@phosphor-icons/react' import Editor from '@monaco-editor/react' +import { toast } from 'sonner' + +import { SchemaSection } from './json/SchemaSection' +import { Toolbar } from './json/Toolbar' import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner' import { SecurityWarningDialog } from '@/components/organisms/security/SecurityWarningDialog' -import { toast } from 'sonner' interface JsonEditorProps { open: boolean @@ -32,10 +33,12 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json } }, [open, value]) + const parseJson = () => JSON.parse(jsonText) + const handleSave = () => { try { - const parsed = JSON.parse(jsonText) - + const parsed = parseJson() + const scanResult = securityScanner.scanJSON(jsonText) setSecurityScanResult(scanResult) @@ -66,8 +69,7 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json const handleForceSave = () => { try { - const parsed = JSON.parse(jsonText) - onSave(parsed) + onSave(parseJson()) setError(null) setPendingSave(false) setShowSecurityDialog(false) @@ -81,7 +83,7 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json const scanResult = securityScanner.scanJSON(jsonText) setSecurityScanResult(scanResult) setShowSecurityDialog(true) - + if (scanResult.safe) { toast.success('No security issues detected') } else { @@ -91,8 +93,7 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json const handleFormat = () => { try { - const parsed = JSON.parse(jsonText) - setJsonText(JSON.stringify(parsed, null, 2)) + setJsonText(JSON.stringify(parseJson(), null, 2)) setError(null) } catch (err) { setError(err instanceof Error ? err.message : 'Invalid JSON - cannot format') @@ -106,7 +107,7 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json {title} - +
{error && ( @@ -115,16 +116,21 @@ export function JsonEditor({ open, onClose, title, value, onSave, schema }: Json )} - {securityScanResult && securityScanResult.severity !== 'safe' && securityScanResult.severity !== 'low' && !showSecurityDialog && ( - - - - {securityScanResult.issues.length} security {securityScanResult.issues.length === 1 ? 'issue' : 'issues'} detected. - Click Security Scan to review. - - - )} - + {securityScanResult && + securityScanResult.severity !== 'safe' && + securityScanResult.severity !== 'low' && + !showSecurityDialog && ( + + + + {securityScanResult.issues.length} security {securityScanResult.issues.length === 1 ? 'issue' : 'issues'} +  detected. Click Security Scan to review. + + + )} + + +
- - - - - - + diff --git a/frontends/nextjs/src/components/editors/json/SchemaSection.tsx b/frontends/nextjs/src/components/editors/json/SchemaSection.tsx new file mode 100644 index 000000000..b77ca8486 --- /dev/null +++ b/frontends/nextjs/src/components/editors/json/SchemaSection.tsx @@ -0,0 +1,26 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui' + +interface SchemaSectionProps { + schema?: unknown +} + +export function SchemaSection({ schema }: SchemaSectionProps) { + if (!schema) return null + + const formattedSchema = + typeof schema === 'string' ? schema : JSON.stringify(schema, null, 2) + + return ( + + + Schema + Reference for the expected JSON structure + + +
+          {formattedSchema}
+        
+
+
+ ) +} diff --git a/frontends/nextjs/src/components/editors/json/Toolbar.tsx b/frontends/nextjs/src/components/editors/json/Toolbar.tsx new file mode 100644 index 000000000..a642ce6fc --- /dev/null +++ b/frontends/nextjs/src/components/editors/json/Toolbar.tsx @@ -0,0 +1,31 @@ +import { Button, DialogFooter } from '@/components/ui' +import { FloppyDisk, ShieldCheck, X } from '@phosphor-icons/react' + +interface ToolbarProps { + onScan: () => void + onFormat: () => void + onCancel: () => void + onSave: () => void +} + +export function Toolbar({ onScan, onFormat, onCancel, onSave }: ToolbarProps) { + return ( + + + + + + + ) +}