- Complete React app with 7 developer tools - JSON Tool with visual structured editor - Serialize Tool with visual structured editor - URL, Base64, CSV/JSON, Beautifier, Diff tools - Responsive navigation with dropdown menu - Dark/light mode toggle - Mobile-responsive design with sticky header - All tools working with copy/paste functionality
248 lines
7.7 KiB
JavaScript
248 lines
7.7 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Code, AlertCircle, CheckCircle, Edit3 } from 'lucide-react';
|
|
import ToolLayout from '../components/ToolLayout';
|
|
import CopyButton from '../components/CopyButton';
|
|
import StructuredEditor from '../components/StructuredEditor';
|
|
|
|
const JsonTool = () => {
|
|
const [input, setInput] = useState('');
|
|
const [output, setOutput] = useState('');
|
|
const [error, setError] = useState('');
|
|
const [isValid, setIsValid] = useState(null);
|
|
const [editorMode, setEditorMode] = useState('text'); // 'text' or 'visual'
|
|
const [structuredData, setStructuredData] = useState({});
|
|
|
|
const formatJson = () => {
|
|
try {
|
|
const parsed = JSON.parse(input);
|
|
const formatted = JSON.stringify(parsed, null, 2);
|
|
setOutput(formatted);
|
|
setError('');
|
|
setIsValid(true);
|
|
} catch (err) {
|
|
setError(`Invalid JSON: ${err.message}`);
|
|
setOutput('');
|
|
setIsValid(false);
|
|
}
|
|
};
|
|
|
|
const minifyJson = () => {
|
|
try {
|
|
const parsed = JSON.parse(input);
|
|
const minified = JSON.stringify(parsed);
|
|
setOutput(minified);
|
|
setError('');
|
|
setIsValid(true);
|
|
} catch (err) {
|
|
setError(`Invalid JSON: ${err.message}`);
|
|
setOutput('');
|
|
setIsValid(false);
|
|
}
|
|
};
|
|
|
|
const validateJson = () => {
|
|
try {
|
|
JSON.parse(input);
|
|
setError('');
|
|
setIsValid(true);
|
|
setOutput('✅ Valid JSON');
|
|
} catch (err) {
|
|
setError(`Invalid JSON: ${err.message}`);
|
|
setIsValid(false);
|
|
setOutput('');
|
|
}
|
|
};
|
|
|
|
const clearAll = () => {
|
|
setInput('');
|
|
setOutput('');
|
|
setError('');
|
|
setIsValid(null);
|
|
};
|
|
|
|
const handleStructuredDataChange = (newData) => {
|
|
setStructuredData(newData);
|
|
setInput(JSON.stringify(newData, null, 2));
|
|
setError('');
|
|
setIsValid(true);
|
|
};
|
|
|
|
const switchToVisualEditor = () => {
|
|
try {
|
|
const parsed = input ? JSON.parse(input) : {};
|
|
setStructuredData(parsed);
|
|
setEditorMode('visual');
|
|
setError('');
|
|
setIsValid(true);
|
|
} catch (err) {
|
|
setError(`Cannot switch to visual editor: ${err.message}`);
|
|
setIsValid(false);
|
|
}
|
|
};
|
|
|
|
const switchToTextEditor = () => {
|
|
setEditorMode('text');
|
|
};
|
|
|
|
const loadSample = () => {
|
|
const sample = {
|
|
"name": "John Doe",
|
|
"age": 30,
|
|
"email": "john@example.com",
|
|
"address": {
|
|
"street": "123 Main St",
|
|
"city": "New York",
|
|
"zipCode": "10001"
|
|
},
|
|
"hobbies": ["reading", "coding", "traveling"],
|
|
"isActive": true
|
|
};
|
|
setInput(JSON.stringify(sample, null, 2));
|
|
setStructuredData(sample);
|
|
};
|
|
|
|
return (
|
|
<ToolLayout
|
|
title="JSON Encoder/Decoder"
|
|
description="Format, validate, and minify JSON data with syntax highlighting"
|
|
icon={Code}
|
|
>
|
|
{/* Editor Mode Toggle */}
|
|
<div className="flex bg-gray-100 dark:bg-gray-800 rounded-lg p-1 mb-6 w-fit">
|
|
<button
|
|
onClick={switchToTextEditor}
|
|
className={`flex items-center space-x-2 px-4 py-2 rounded-md font-medium transition-colors ${
|
|
editorMode === 'text'
|
|
? 'bg-white dark:bg-gray-700 text-primary-600 shadow-sm'
|
|
: 'text-gray-600 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
<Code className="h-4 w-4" />
|
|
<span>Text Editor</span>
|
|
</button>
|
|
<button
|
|
onClick={switchToVisualEditor}
|
|
className={`flex items-center space-x-2 px-4 py-2 rounded-md font-medium transition-colors ${
|
|
editorMode === 'visual'
|
|
? 'bg-white dark:bg-gray-700 text-primary-600 shadow-sm'
|
|
: 'text-gray-600 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
<Edit3 className="h-4 w-4" />
|
|
<span>Visual Editor</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Controls */}
|
|
<div className="flex flex-wrap gap-3 mb-6">
|
|
<button onClick={formatJson} className="tool-button">
|
|
Format JSON
|
|
</button>
|
|
<button onClick={minifyJson} className="tool-button">
|
|
Minify JSON
|
|
</button>
|
|
<button onClick={validateJson} className="tool-button">
|
|
Validate JSON
|
|
</button>
|
|
<button onClick={loadSample} className="tool-button-secondary">
|
|
Load Sample
|
|
</button>
|
|
<button onClick={clearAll} className="tool-button-secondary">
|
|
Clear All
|
|
</button>
|
|
</div>
|
|
|
|
{/* Status Indicator */}
|
|
{isValid !== null && (
|
|
<div className={`flex items-center space-x-2 p-3 rounded-md mb-4 ${
|
|
isValid
|
|
? 'bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300'
|
|
: 'bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300'
|
|
}`}>
|
|
{isValid ? (
|
|
<CheckCircle className="h-5 w-5" />
|
|
) : (
|
|
<AlertCircle className="h-5 w-5" />
|
|
)}
|
|
<span className="font-medium">
|
|
{isValid ? 'Valid JSON' : 'Invalid JSON'}
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Error Display */}
|
|
{error && (
|
|
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-md p-4 mb-4">
|
|
<div className="flex items-start space-x-2">
|
|
<AlertCircle className="h-5 w-5 text-red-500 mt-0.5" />
|
|
<div>
|
|
<h4 className="text-red-800 dark:text-red-200 font-medium">Error</h4>
|
|
<p className="text-red-700 dark:text-red-300 text-sm mt-1">{error}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Input/Output Grid */}
|
|
<div className={`grid gap-6 ${
|
|
editorMode === 'visual'
|
|
? 'grid-cols-1'
|
|
: 'grid-cols-1 lg:grid-cols-2'
|
|
}`}>
|
|
{/* Input */}
|
|
<div className="space-y-2">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
{editorMode === 'text' ? 'Input JSON' : 'Visual JSON Editor'}
|
|
</label>
|
|
<div className="relative">
|
|
{editorMode === 'text' ? (
|
|
<textarea
|
|
value={input}
|
|
onChange={(e) => setInput(e.target.value)}
|
|
placeholder="Paste your JSON here..."
|
|
className="tool-input h-96"
|
|
/>
|
|
) : (
|
|
<div className="min-h-96">
|
|
<StructuredEditor
|
|
initialData={structuredData}
|
|
onDataChange={handleStructuredDataChange}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Output */}
|
|
<div className="space-y-2">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Output
|
|
</label>
|
|
<div className="relative">
|
|
<textarea
|
|
value={output}
|
|
readOnly
|
|
placeholder="Formatted JSON will appear here..."
|
|
className="tool-input h-96 bg-gray-50 dark:bg-gray-800"
|
|
/>
|
|
{output && <CopyButton text={output} />}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Usage Tips */}
|
|
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-md p-4 mt-6">
|
|
<h4 className="text-blue-800 dark:text-blue-200 font-medium mb-2">Usage Tips</h4>
|
|
<ul className="text-blue-700 dark:text-blue-300 text-sm space-y-1">
|
|
<li>• Use "Format JSON" to beautify and indent your JSON</li>
|
|
<li>• Use "Minify JSON" to compress JSON by removing whitespace</li>
|
|
<li>• Use "Validate JSON" to check if your JSON syntax is correct</li>
|
|
<li>• Click the copy button to copy the output to your clipboard</li>
|
|
</ul>
|
|
</div>
|
|
</ToolLayout>
|
|
);
|
|
};
|
|
|
|
export default JsonTool;
|