Skip to content

Instantly share code, notes, and snippets.

@pankajbisht
Last active November 20, 2024 15:46
Show Gist options
  • Save pankajbisht/51fe011f34ae7df61d2e10c11538c58c to your computer and use it in GitHub Desktop.
Save pankajbisht/51fe011f34ae7df61d2e10c11538c58c to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css'; // Quill theme CSS
import mammoth from 'mammoth'; // Mammoth.js for DOCX to HTML conversion
import { Box, Container, Typography, Button, CircularProgress, Snackbar, IconButton, Stack } from '@mui/material';
import { styled } from '@mui/system';
import Brightness4Icon from '@mui/icons-material/Brightness4'; // Icon for Dark Mode
import Brightness7Icon from '@mui/icons-material/Brightness7'; // Icon for Light Mode
const DocDataMerging = () => {
const [editorContent, setEditorContent] = useState(''); // Right panel: Editable Quill content
const [previewContent, setPreviewContent] = useState(''); // Left panel: Preview Quill content
const [file, setFile] = useState(null); // File state
const [showHtml, setShowHtml] = useState(false); // Toggle to show HTML code
const [loading, setLoading] = useState(false); // File upload loading state
const [openSnackbar, setOpenSnackbar] = useState(false); // Snackbar state
const [darkMode, setDarkMode] = useState(false); // Dark mode state
// Example dynamic data to merge
const data = {
user: {
name: 'John Doe',
accountNumber: '1234567890',
address: {
city: 'New York',
street: '5th Avenue'
},
registrationDate: '2024-05-22',
isActive: true,
balance: 1000
},
items: [
{ name: 'Item 1', price: 100 },
{ name: 'Item 2', price: 150 }
]
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
if (selectedFile) {
setLoading(true);
setFile(selectedFile);
processFile(selectedFile);
}
};
const processFile = (file) => {
const reader = new FileReader();
reader.onload = async () => {
const arrayBuffer = reader.result;
// Convert DOCX to HTML using Mammoth
try {
const result = await mammoth.convertToHtml({ arrayBuffer });
const htmlContent = result.value;
// Set initial content for both preview and editor
setEditorContent(htmlContent); // Right panel: Initially set
setPreviewContent(htmlContent); // Left panel: Initially set
setLoading(false); // Stop loading
} catch (err) {
console.error('Error processing DOCX file:', err);
setLoading(false); // Stop loading
}
};
reader.readAsArrayBuffer(file); // Read file as ArrayBuffer
};
const handleMerge = () => {
const mergedPreviewContent = mergeData(editorContent, data); // Use the current editor content
setPreviewContent(mergedPreviewContent);
};
const mergeData = (htmlContent, data) => {
let mergedContent = htmlContent;
mergedContent = mergedContent.replace(/{#items}[\s\S]*?{\/items}/g, (match) => {
return handleItems(data.items);
});
mergedContent = mergedContent.replace(/{(.*?)}/g, (match, placeholder) => {
if (placeholder.startsWith('formatDate')) {
const dateFormat = placeholder.split(' ')[1].replace(/'/g, '');
const date = new Date(data.user.registrationDate);
return formatDate(date, dateFormat);
}
if (placeholder.startsWith('currency')) {
return currency(data.user.balance);
}
if (placeholder.includes('?')) {
return evaluateCondition(placeholder, data);
}
return getValueFromData(placeholder, data) || match;
});
return mergedContent;
};
const handleItems = (items) => {
if (items.length === 0) return '';
return items.map(item => `${item.name} - $${item.price}`).join(', ');
};
const evaluateCondition = (condition, data) => {
try {
const replacedCondition = condition.replace(/{(.*?)}/g, (match, placeholder) => {
return getValueFromData(placeholder, data) || match;
});
return eval(replacedCondition);
} catch (err) {
console.error('Error evaluating condition:', err);
return condition;
}
};
const getValueFromData = (placeholder, data) => {
const parts = placeholder.split('.');
let result = data;
for (let part of parts) {
result = result[part];
if (result === undefined) return null;
}
return result;
};
const formatDate = (date, format) => {
const options = { year: 'numeric', month: 'numeric', day: 'numeric' };
return date.toLocaleDateString(undefined, options);
};
const currency = (value) => {
return `$${value.toFixed(2)}`;
};
const toggleDarkMode = () => {
setDarkMode(!darkMode);
};
return (
<Container maxWidth="lg" sx={{ padding: '20px', backgroundColor: darkMode ? '#333' : '#fff', color: darkMode ? '#fff' : '#000', minHeight: '100vh' }}>
{/* Header Section */}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px', borderBottom: '2px solid #ccc', paddingBottom: '10px' }}>
<Typography variant="h4">Doc Data Merging</Typography>
<Stack direction="row" spacing={2} alignItems="center">
{/* File Upload moved to Header */}
<input
type="file"
accept=".docx"
onChange={handleFileChange}
style={{ display: 'none' }}
id="upload-button"
/>
<label htmlFor="upload-button">
<Button variant="contained" component="span">
Upload DOCX
</Button>
</label>
<Button variant="contained" onClick={handleMerge} disabled={loading}>
Merge Data
</Button>
<Button variant="outlined" onClick={() => setShowHtml(!showHtml)}>
{showHtml ? 'Hide HTML' : 'Show HTML'}
</Button>
<IconButton onClick={toggleDarkMode}>
{darkMode ? (
<Brightness7Icon sx={{ color: '#fff' }} />
) : (
<Brightness4Icon sx={{ color: '#000' }} />
)}
</IconButton>
</Stack>
</Box>
<Box sx={{ display: 'flex', height: '85vh', boxShadow: 3, borderRadius: '8px', overflow: 'hidden', backgroundColor: darkMode ? '#444' : '#f5f5f5' }}>
{/* Left Panel: Read-only Quill Editor (Preview) */}
<Box sx={{ flex: 1, padding: '20px', borderRight: '2px solid #ccc' }}>
<Typography variant="h6" sx={{ mb: 2 }}>Preview</Typography>
{showHtml ? (
<Box sx={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word', backgroundColor: '#f4f4f4', padding: '10px', borderRadius: '4px' }}>
<Typography variant="body2" sx={{ fontFamily: 'monospace' }}>
{previewContent}
</Typography>
</Box>
) : (
<ReactQuill
value={previewContent}
readOnly={true}
theme="snow"
style={{ height: '60vh' }}
/>
)}
</Box>
{/* Right Panel: Editable Quill Editor (Edit Mode) */}
<Box sx={{ flex: 1, padding: '20px' }}>
<Typography variant="h6" sx={{ mb: 2 }}>Edit Document</Typography>
{loading && <CircularProgress sx={{ display: 'block', margin: '20px auto' }} />}
<ReactQuill
value={editorContent}
onChange={setEditorContent}
theme="snow"
style={{ height: '60vh' }}
/>
</Box>
</Box>
<Snackbar
open={openSnackbar}
autoHideDuration={3000}
onClose={() => setOpenSnackbar(false)}
message="File uploaded successfully!"
/>
</Container>
);
};
export default DocDataMerging;
// src/App.js
import React from 'react';
import DocDataMerging from './DocDataMerging';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
});
const App = () => {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<DocDataMerging />
</ThemeProvider>
);
};
export default App;
{
"name": "blockly-hello-world",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.7",
"@mui/material": "^6.1.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"dayjs": "^1.11.13",
"docxtemplater": "^3.53.0",
"html-docx-js": "^0.3.1",
"mammoth": "^1.8.0",
"pizzip": "^3.1.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-quill": "^2.0.0",
"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"
]
}
}
{
"name": "blockly-hello-world",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.7",
"@mui/material": "^6.1.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"dayjs": "^1.11.13",
"docxtemplater": "^3.53.0",
"html-docx-js": "^0.3.1",
"mammoth": "^1.8.0",
"pizzip": "^3.1.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-quill": "^2.0.0",
"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"
]
}
}
import React, { useEffect, useRef, useState } from 'react';
import * as Blockly from 'blockly/core';
import 'blockly/blocks';
import { javascriptGenerator } from 'blockly/javascript';
const BlocklyWorkspace = () => {
const workspaceRef = useRef(null);
const [output, setOutput] = useState(''); // State to store the output
const [data, setData] = useState({
user: {
name: 'John Doe',
accountNumber: '1234567890',
address: {
city: 'New York',
street: '5th Avenue'
},
registrationDate: '2024-05-22',
isActive: true,
balance: 1000
},
items: [
{ name: 'Item 1', price: 100 },
{ name: 'Item 2', price: 150 }
]
});
useEffect(() => {
const workspace = Blockly.inject(workspaceRef.current, {
toolbox: `
<xml>
<category name="User">
<block type="user_getname"></block>
<block type="user_getbalance"></block>
<block type="user_setbalance">
<value name="BALANCE">
<block type="math_number">
<field name="NUM">1000</field>
</block>
</value>
</block>
</category>
<category name="Items">
<block type="item_getname">
<value name="ITEM_INDEX">
<block type="math_number">
<field name="NUM">0</field>
</block>
</value>
</block>
<block type="item_getprice">
<value name="ITEM_INDEX">
<block type="math_number">
<field name="NUM">0</field>
</block>
</value>
</block>
</category>
</xml>
`,
grid: {
spacing: 20,
length: 3,
colour: '#ccc',
},
});
return () => workspace.dispose();
}, []);
// Blockly block definitions with output
Blockly.Blocks['user_getname'] = {
init: function () {
this.appendDummyInput().appendField('Get user name');
this.setOutput(true, 'String');
this.setColour(230);
this.setTooltip('Returns the user\'s name');
},
};
Blockly.Blocks['user_getbalance'] = {
init: function () {
this.appendDummyInput().appendField('Get user balance');
this.setOutput(true, 'Number');
this.setColour(230);
this.setTooltip('Returns the user\'s balance');
},
};
Blockly.Blocks['user_setbalance'] = {
init: function () {
this.appendValueInput('BALANCE')
.setCheck('Number')
.appendField('Set user balance to');
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip('Sets the user\'s balance');
}
};
Blockly.Blocks['item_getname'] = {
init: function () {
this.appendValueInput('ITEM_INDEX')
.setCheck('Number')
.appendField('Get item name at index');
this.setOutput(true, 'String');
this.setColour(230);
this.setTooltip('Returns the item name at the specified index');
},
};
Blockly.Blocks['item_getprice'] = {
init: function () {
this.appendValueInput('ITEM_INDEX')
.setCheck('Number')
.appendField('Get item price at index');
this.setOutput(true, 'Number');
this.setColour(230);
this.setTooltip('Returns the item price at the specified index');
},
};
// JavaScript code generation for each block
javascriptGenerator.forBlock['user_getname'] = function () {
return [`data.user.name`, javascriptGenerator.ORDER_ATOMIC];
};
javascriptGenerator.forBlock['user_getbalance'] = function () {
return [`data.user.balance`, javascriptGenerator.ORDER_ATOMIC];
};
javascriptGenerator.forBlock['user_setbalance'] = function (block) {
const value_balance = javascriptGenerator.valueToCode(block, 'BALANCE', javascriptGenerator.ORDER_ATOMIC);
// Directly update the state without returning any value
return `setData(prevData => ({ ...prevData, user: { ...prevData.user, balance: ${value_balance} } }));\n`;
};
javascriptGenerator.forBlock['item_getname'] = function (block) {
const value_index = javascriptGenerator.valueToCode(block, 'ITEM_INDEX', javascriptGenerator.ORDER_ATOMIC);
return [`data.items[${value_index}].name`, javascriptGenerator.ORDER_ATOMIC];
};
javascriptGenerator.forBlock['item_getprice'] = function (block) {
const value_index = javascriptGenerator.valueToCode(block, 'ITEM_INDEX', javascriptGenerator.ORDER_ATOMIC);
return [`data.items[${value_index}].price`, javascriptGenerator.ORDER_ATOMIC];
};
// Run code and update output
const runCode = () => {
const code = javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace());
if (code) {
try {
// Execute the generated code and get the result
const result = eval(code); // Using eval to execute the generated code
if (result !== undefined) {
setOutput(result); // Update the output state with the result
} else {
setOutput("Action executed successfully, but no return value.");
}
} catch (error) {
setOutput(`Error: ${error.message}`);
}
}
};
// Automatically run code periodically
useEffect(() => {
const intervalId = setInterval(() => {
// Generate code from Blockly workspace
const generatedCode = javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace());
//setCode(generatedCode); // Store the code in the state
// Execute the generated code
eval(generatedCode); // Execute the code (using eval for simplicity)
}, 2000); // Run every 2 seconds for demo
return () => clearInterval(intervalId);
}, []);
return (
<div>
<div ref={workspaceRef} style={{ height: '400px', width: '100%' }}></div>
<button onClick={runCode}>Run Code</button>
<div>
<strong>Output:</strong>
<pre>{output !== undefined ? output : "No output"}</pre> {/* Display the output here */}
</div>
</div>
);
};
export default BlocklyWorkspace;
// src/App.js
import React, { useState } from 'react';
import BlocklyWorkspace from './BlocklyWorkspace';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { Characters } from './Characters';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
});
const App = () => {
const [commands, setCommands] = useState([]);
const handleRunCode = (code) => {
const commandList = code.split("\n").filter((line) => line !== "");
setCommands(commandList);
};
return (
<div>
<ThemeProvider theme={theme}>
<CssBaseline />
<BlocklyWorkspace onRunCode={handleRunCode} />
<Characters commands={commands} />
</ThemeProvider>
</div>
);
};
export default App;
import { useState } from "react";
export const Characters = ({ commands }) => {
const [position, setPosition] = useState(0);
const [rotation, setRotation] = useState(0);
const executeCommands = () => {
commands.forEach((command) => {
if (command.startsWith('moveSteps')) {
const steps = parseInt(command.match(/\((.*)\)/)[1]);
moveSteps(steps);
} else if (command === 'moveLeft()' || command === 'moveLeft();') {
moveLeft();
} else if (command === 'moveRight()' || command === 'moveRight();') {
moveRight();
} else if (command === 'turnLeft()' || command === 'turnLeft();') {
turnLeft();
} else if (command === 'turnRight()' || command === 'turnRight();') {
turnRight();
}
});
};
const moveSteps = (steps) => {
setPosition((prev) => prev + steps);
};
const moveLeft = () => {
setPosition((prev) => prev - 10);
};
const moveRight = () => {
setPosition((prev) => prev + 10);
};
const turnLeft = () => {
setRotation((prev) => prev - 45);
};
const turnRight = () => {
setRotation((prev) => prev + 45);
};
return (
<div>
<div className="character-container">
<img
src={'https://img.freepik.com/premium-vector/cute-unicon-monster-doodles_38841-90.jpg?w=360'}
alt="Character"
style={{ marginLeft: position, transform: `rotate(${rotation}deg)` }}
height={'100px'}
/>
</div>
<button onClick={executeCommands}>Execute Commands</button>
</div>
);
};
import { useState } from "react";
export const Characters = ({ commands }) => {
const [position, setPosition] = useState(0);
const [rotation, setRotation] = useState(0);
const executeCommands = () => {
commands.forEach((command) => {
if (command.startsWith('moveSteps')) {
const steps = parseInt(command.match(/\((.*)\)/)[1]);
moveSteps(steps);
} else if (command === 'moveLeft()' || command === 'moveLeft();') {
moveLeft();
} else if (command === 'moveRight()' || command === 'moveRight();') {
moveRight();
} else if (command === 'turnLeft()' || command === 'turnLeft();') {
turnLeft();
} else if (command === 'turnRight()' || command === 'turnRight();') {
turnRight();
}
});
};
const moveSteps = (steps) => {
setPosition((prev) => prev + steps);
};
const moveLeft = () => {
setPosition((prev) => prev - 10);
};
const moveRight = () => {
setPosition((prev) => prev + 10);
};
const turnLeft = () => {
setRotation((prev) => prev - 45);
};
const turnRight = () => {
setRotation((prev) => prev + 45);
};
return (
<div>
<div className="character-container">
<img
src={'https://img.freepik.com/premium-vector/cute-unicon-monster-doodles_38841-90.jpg?w=360'}
alt="Character"
style={{ marginLeft: position, transform: `rotate(${rotation}deg)` }}
height={'100px'}
/>
</div>
<button onClick={executeCommands}>Execute Commands</button>
</div>
);
};
// src/App.js
import React, { useState } from 'react';
import BlocklyWorkspace from './BlocklyWorkspace';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { Characters } from './Characters';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
});
const App = () => {
const [commands, setCommands] = useState([]);
const handleRunCode = (code) => {
const commandList = code.split("\n").filter((line) => line !== "");
setCommands(commandList);
};
return (
<div>
<ThemeProvider theme={theme}>
<CssBaseline />
<BlocklyWorkspace onRunCode={handleRunCode} />
<Characters commands={commands} />
</ThemeProvider>
</div>
);
};
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment