Json-Generator

master
Raaj 2024-08-21 14:25:31 +05:30
parent 428c223c78
commit 043c96a3fc
20 changed files with 20865 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

20269
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "json-generator",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@mui/material": "^5.16.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

38
src/App.css Normal file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

18
src/App.js Normal file
View File

@ -0,0 +1,18 @@
import React, { useState } from "react";
import FormGenerator from "./components/FormGenerator";
import DisplayJson from "./components/DisplayJson";
import { Container, CssBaseline } from "@mui/material";
function App() {
const [json, setJson] = useState("");
return (
<Container component="main" maxWidth="md">
<CssBaseline />
<FormGenerator onGenerate={setJson} />
{json && <DisplayJson json={json} />}
</Container>
);
}
export default App;

8
src/App.test.js Normal file
View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,26 @@
import React from "react";
import { Paper, Typography, Button } from "@mui/material";
const DisplayJson = ({ json }) => {
const downloadJson = () => {
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "config.json";
a.click();
URL.revokeObjectURL(url);
};
return (
<Paper style={{ padding: "20px" }}>
<Typography variant="h6">Generated JSON</Typography>
<pre>{json}</pre>
<Button variant="contained" color="primary" onClick={downloadJson}>
Download JSON
</Button>
</Paper>
);
};
export default DisplayJson;

View File

@ -0,0 +1,299 @@
import React, { useState, useEffect } from "react";
import { Button, TextField, Grid, Paper, Typography, MenuItem } from "@mui/material";
import { useForm, useFieldArray, Controller } from "react-hook-form";
import JsonSchemaField from "./jsonSchemaField"; // Adjust path as per your file structure
const FormGenerator = ({ onGenerate, initialJson }) => {
const { control, handleSubmit, setValue, getValues } = useForm();
const { fields, append, remove } = useFieldArray({
control,
name: "fields",
});
const [manualJson, setManualJson] = useState(""); // State to hold manually input JSON
useEffect(() => {
if (initialJson) {
setValue("api", initialJson.api);
initialJson.createField.forEach((field) => {
let fieldType = field.type || "text"; // Default type if not specified
append({
label: field.label || "",
key: field.key || "",
type: fieldType,
varient: field.varient || "standard",
search: field.search || {}
});
});
}
}, [initialJson, setValue, append]);
const determineFieldType = (value) => {
if (typeof value === "string") {
// Check if the value is a date/time
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z?$/.test(value)) {
return "datetime-local";
}
// Check if the value is a date
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return "date";
}
// Check if the value is an email
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
return "email";
}
// Check if the value is a phone number
if (/^\+?[1-9]\d{1,14}$/.test(value)) {
return "phone";
}
}
// Check if the value is a number
if (!isNaN(value) && !isNaN(parseFloat(value))) {
return "number";
}
// Default type
return "text";
};
const onSubmit = (data) => {
const jsonData = {
api: data.api,
showField: data.fields.map((field) => ({
label: field.label,
key: field.key,
})),
createField: data.fields.map((field) => {
const fieldData = {
label: field.label,
type: field.type,
varient: field.varient,
name: field.key,
};
if (field.type === "advance-select" || field.type === "simple-select") {
fieldData.search = {
api: field.search.api || "", // Include search API from field data
label: field.search.label || "", // Include search label from field data
key: field.search.key || "" // Include search key from field data
};
}
return fieldData;
}),
editField: data.fields.map((field) => ({
label: field.label,
type: field.type,
varient: field.varient,
name: field.key,
})),
};
onGenerate(JSON.stringify(jsonData, null, 2));
};
const handleManualJsonChange = (event) => {
setManualJson(event.target.value);
};
const addManualJson = () => {
try {
const parsedJson = JSON.parse(manualJson);
// Clear existing fields
while (fields.length > 0) {
remove(0);
}
// Assuming parsedJson is an object with keys representing field names
if (typeof parsedJson === "object" && !Array.isArray(parsedJson)) {
Object.keys(parsedJson).forEach((key) => {
const value = parsedJson[key];
const fieldType = determineFieldType(value); // Determine type based on value
append({
label: key.charAt(0).toUpperCase() + key.slice(1), // Capitalize first letter
key: key, // Use key as the key
type: fieldType, // Set type based on parsed JSON
varient: "standard", // Default varient
search: {} // Default empty search object
});
});
} else {
console.error("Invalid JSON format: Expected an object.");
}
} catch (error) {
console.error("Error parsing JSON:", error);
// Handle invalid JSON input error
}
};
return (
<Paper style={{ padding: "20px" }}>
<Typography varient="h6">Generate JSON Schema</Typography>
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="api"
control={control}
defaultValue=""
render={({ field }) => (
<TextField {...field} label="API Name" fullWidth margin="normal" />
)}
/>
{fields.map((item, index) => (
<Grid container spacing={2} key={item.id}>
<Grid item xs={4}>
<Controller
name={`fields[${index}].label`}
control={control}
defaultValue={item.label}
render={({ field }) => (
<TextField {...field} label="Label" fullWidth margin="normal" />
)}
/>
</Grid>
<Grid item xs={4}>
<Controller
name={`fields[${index}].key`}
control={control}
defaultValue={item.key}
render={({ field }) => (
<TextField {...field} label="Key" fullWidth margin="normal" />
)}
/>
</Grid>
<Grid item xs={4}>
<Controller
name={`fields[${index}].type`}
control={control}
defaultValue={item.type || "text"} // Default to "text" if type is not set
render={({ field }) => (
<TextField
{...field}
select
label="Type"
fullWidth
margin="normal"
onChange={(e) => {
const selectedType = e.target.value;
field.onChange(e); // Update field type
if (selectedType === "advance-select" || selectedType === "simple-select") {
// Set default values for search fields if select types are chosen
setValue(`fields[${index}].search.api`, "company");
setValue(`fields[${index}].search.label`, "Name");
setValue(`fields[${index}].search.key`, "name");
} else {
// Clear search fields for other types
setValue(`fields[${index}].search`, {});
}
}}
>
<MenuItem value="text">Text</MenuItem>
<MenuItem value="select">Select</MenuItem>
<MenuItem value="simple-select">Simple Select</MenuItem>
<MenuItem value="advance-select">Advance Select</MenuItem>
<MenuItem value="datetime-local">Date/Time</MenuItem>
<MenuItem value="checkbox">Checkbox</MenuItem>
<MenuItem value="email">Email</MenuItem>
<MenuItem value="password">Password</MenuItem>
<MenuItem value="phone">Phone</MenuItem>
<MenuItem value="number">Number</MenuItem>
</TextField>
)}
/>
</Grid>
{["advance-select", "simple-select"].includes(item.type) && (
<>
<Grid item xs={4}>
<Controller
name={`fields[${index}].search.api`}
control={control}
defaultValue={item.search.api || "company"}
render={({ field }) => (
<TextField {...field} label="Search API" fullWidth margin="normal" />
)}
/>
</Grid>
<Grid item xs={4}>
<Controller
name={`fields[${index}].search.label`}
control={control}
defaultValue={item.search.label || "Name"}
render={({ field }) => (
<TextField {...field} label="Search Label" fullWidth margin="normal" />
)}
/>
</Grid>
<Grid item xs={4}>
<Controller
name={`fields[${index}].search.key`}
control={control}
defaultValue={item.search.key || "name"}
render={({ field }) => (
<TextField {...field} label="Search Key" fullWidth margin="normal" />
)}
/>
</Grid>
</>
)}
<Grid item xs={12}>
<Controller
name={`fields[${index}].varient`}
control={control}
defaultValue={item.varient || "standard"} // Default to "standard" if varient is not set
render={({ field }) => (
<TextField {...field} label="varient" fullWidth margin="normal" />
)}
/>
</Grid>
<Grid item xs={12}>
<Button type="button" onClick={() => remove(index)}>Remove Field</Button>
</Grid>
</Grid>
))}
<Button type="button" onClick={() => append({ label: "", key: "", type: "text", varient: "standard", search: {} })}>
Add Field
</Button>
<Button type="submit" varient="contained" color="primary">
Generate JSON
</Button>
</form>
{/* Render the JSON Schema field */}
{initialJson && (
<div style={{ marginTop: "20px" }}>
<Typography varient="h6">Initial JSON Schema</Typography>
<JsonSchemaField jsonSchema={initialJson} />
</div>
)}
{/* Add manual JSON input */}
<div style={{ marginTop: "20px" }}>
<Typography varient="h6">Add Manual JSON</Typography>
<TextField
multiline
rows={4}
varient="outlined"
fullWidth
value={manualJson}
onChange={handleManualJsonChange}
placeholder="Enter JSON data here"
margin="normal"
/>
<Button
varient="contained"
color="primary"
onClick={addManualJson}
style={{ marginTop: "10px" }}
>
Add Manual JSON
</Button>
</div>
</Paper>
);
};
export default FormGenerator;

View File

@ -0,0 +1,22 @@
import React from "react";
import { Grid, TextField } from "@mui/material";
const JsonSchemaField = ({ jsonSchema }) => {
return (
<Grid container spacing={2}>
{Object.keys(jsonSchema).map((key) => (
<Grid item xs={4} key={key}>
<TextField
label={key}
defaultValue={jsonSchema[key]}
fullWidth
margin="normal"
InputProps={{ readOnly: true }}
/>
</Grid>
))}
</Grid>
);
};
export default JsonSchemaField;

13
src/index.css Normal file
View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

13
src/reportWebVitals.js Normal file
View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';