import { useEffect, useState } from "react"; import useEditorStore from "./useEditorStore.js"; import CanvasStage from "./CanvasStage"; import Toolbox from "./Toolbox"; import PropertiesPanel from "./PropertiesPanel"; import LayersPanel from "./LayersPanel"; import { PAGE_SIZES } from "./utils/pageSizes"; import "./Editor.scss"; import { generateHtml } from "./utils/exportToHtml.js"; import { printHtml } from "./utils/printHtml.js"; export default function Editor() { const { undo, redo, deleteSelected, copyElement, pasteElement, exportTemplate, selectedId, setPageSize, page, toggleLayers, showLayers, } = useEditorStore(); const [zoom, setZoom] = useState(0.9); const [selectedPageSize, setSelectedPageSize] = useState("A4"); // Keyboard shortcuts useEffect(() => { const handleKeyDown = (e) => { // Ignore shortcuts when typing in input/textarea const isTyping = e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || e.target.isContentEditable; if (isTyping) return; // Ctrl/Cmd + Z = Undo if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) { e.preventDefault(); undo(); } // Ctrl/Cmd + Shift + Z = Redo if ((e.ctrlKey || e.metaKey) && e.key === "z" && e.shiftKey) { e.preventDefault(); redo(); } // Ctrl/Cmd + Y = Redo (alternative) if ((e.ctrlKey || e.metaKey) && e.key === "y") { e.preventDefault(); redo(); } // Delete/Backspace = Delete selected (only when not typing) if ((e.key === "Delete" || e.key === "Backspace") && selectedId) { e.preventDefault(); deleteSelected(); } // Ctrl/Cmd + C = Copy if ((e.ctrlKey || e.metaKey) && e.key === "c" && selectedId) { e.preventDefault(); copyElement(); } // Ctrl/Cmd + V = Paste if ((e.ctrlKey || e.metaKey) && e.key === "v") { e.preventDefault(); pasteElement(); } // Ctrl/Cmd + D = Duplicate if ((e.ctrlKey || e.metaKey) && e.key === "d" && selectedId) { e.preventDefault(); const { duplicateElement } = useEditorStore.getState(); duplicateElement(selectedId); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [undo, redo, deleteSelected, copyElement, pasteElement, selectedId]); const handleSave = () => { debugger const json = exportTemplate(); const blob = new Blob([json], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "invoice-template.json"; a.click(); URL.revokeObjectURL(url); }; const handlePageSizeChange = (e) => { const sizeName = e.target.value; setSelectedPageSize(sizeName); const size = PAGE_SIZES[sizeName]; setPageSize({ ...size, name: sizeName }); }; const handleZoomIn = () => { setZoom((prev) => Math.min(prev + 0.1, 2)); }; const handleZoomOut = () => { setZoom((prev) => Math.max(prev - 0.1, 0.3)); }; const handleLoadTestTemplate = async () => { const testTemplate = { "elements": [ { "id": "73079a52-87ac-4bd8-a10e-b901e316a911", "type": "text", "x": 30, "y": 40, "text": "Pozomind Technologies", "fontSize": 25, "fontFamily": "Arial", "fontWeight": "normal", "color": "#ff0000", "align": "left", "bold": true, "italic": false, "underline": true, "rotation": 0 }, { "id": "af702c6a-03b0-498b-8511-3737b754c15d", "type": "image", "x": 630, "y": 20, "width": 150, "height": 100, "src": "http://192.168.1.38/upload/getfile?fileId=69cbba1bdb04001678b283b8.png", "rotation": 0 }, { "id": "58ae6bb8-039c-46f3-b5de-6000553a89cb", "type": "text", "x": 30, "y": 80, "text": "51, Aahhaa Restaurant Back Side, Amirtha Nagar,\nStep Colony, Dharga, Hosur, Krishnagiri,\nTamil Nadu - 635126", "fontSize": 18, "fontFamily": "Arial", "fontWeight": "normal", "color": "#000000", "align": "left", "bold": false, "italic": false, "underline": false, "rotation": 0 }, { "id": "969bda69-023b-461e-9c65-7842004d1289", "type": "table", "x": 30, "y": 210, "width": 480, "columns": [ { "key": "sno", "label": "S.No", "width": 50, "visible": true }, { "key": "item", "label": "Item", "width": 150, "visible": true }, { "key": "hsn", "label": "HSN", "width": 60, "visible": true }, { "key": "qty", "label": "Qty", "width": 50, "visible": true }, { "key": "mrp", "label": "MRP", "width": 60, "visible": true }, { "key": "rate", "label": "Rate", "width": 60, "visible": true }, { "key": "dis", "label": "Dis", "width": 50, "visible": true }, { "key": "tax", "label": "Tax %", "width": 60, "visible": true }, { "key": "amt", "label": "Amt", "width": 70, "visible": true } ], "sampleData": [ { "sno": "1", "item": "ITEM 1", "hsn": "01", "qty": "2", "mrp": "100", "rate": "88", "dis": "0", "tax": "5", "amt": "176" }, { "sno": "2", "item": "ITEM 2", "hsn": "02", "qty": "1", "mrp": "350", "rate": "300", "dis": "0", "tax": "5", "amt": "300" }, { "sno": "3", "item": "ITEM 3", "hsn": "03", "qty": "1", "mrp": "250", "rate": "200", "dis": "0", "tax": "0", "amt": "200" }, { "sno": "4", "item": "ITEM 4", "hsn": "04", "qty": "1", "mrp": "50", "rate": "30", "dis": "0", "tax": "18", "amt": "30" }, { "sno": "5", "item": "ITEM 5", "hsn": "05", "qty": "1", "mrp": "50", "rate": "30", "dis": "0", "tax": "40", "amt": "30" } ], "rowHeight": 30, "headerBg": "#f0f0f0", "borderColor": "#000000", "showSampleData": true, "rotation": 0 }, { "id": "098a7eac-5660-40d7-91b2-d7df56fccfd4", "type": "text", "x": 360, "y": 170, "text": "Takeaway", "fontSize": 18, "fontFamily": "Arial", "fontWeight": "normal", "color": "#000000", "align": "left", "bold": false, "italic": false, "underline": false, "rotation": 0 }, { "id": "dcfcd7b5-965c-4aa7-bfa6-9bba2dd1f147", "type": "qrcode", "x": 670, "y": 450, "size": 100, "data": "https://example.com", "rotation": 0 }, { "id": "a2812eeb-f2fa-44df-a4a9-cd70b69a0115", "type": "summary", "x": 30, "y": 460, "fields": [ { "key": "items", "label": "Items", "visible": true, "width": 150 }, { "key": "qty", "label": "Qty", "visible": true, "width": 100 }, { "key": "amount", "label": "Amount", "visible": true, "width": 150 } ], "fontSize": 14, "labelColor": "#000000", "valueColor": "#000000", "spacing": 10, "rotation": 0 }, { "id": "6a4de268-9839-4ee6-b612-c6b4669830ea", "type": "line", "x": 40.82365943816583, "y": 150.00000000000014, "width": 729.4637752663348, "thickness": 2, "color": "#000000", "rotation": 0 } ], "page": { "width": 794, "height": 1123, "name": "A4" } }; const { importTemplate } = useEditorStore.getState(); importTemplate(JSON.stringify(testTemplate)); const data = { companyName: "Pozomind Technologies", address: "123 Main St", items: "10", qty: "25", amount: "₹5000", tableData: [ { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, { sno: "1", item: "Product A", qty: "2", amt: "200" }, ] }; const html = generateHtml(testTemplate, data); await printHtml(html); }; return (