import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { v4 as uuidv4 } from "uuid"; const createElementByType = (type, count) => { const baseId = uuidv4(); if (type === "text") { return { id: baseId, type: "text", x: 50 + count * 20, y: 50 + count * 20, text: "New Text", fontSize: 18, fontFamily: "Arial", fontWeight: "normal", color: "#000000", align: "left", bold: false, italic: false, underline: false, rotation: 0, }; } if (type === "image") { return { id: baseId, type: "image", x: 50 + count * 20, y: 50 + count * 20, width: 100, height: 100, src: null, rotation: 0, }; } if (type === "qrcode") { return { id: baseId, type: "qrcode", x: 50 + count * 20, y: 50 + count * 20, size: 100, data: "https://example.com", rotation: 0, }; } if (type === "line") { return { id: baseId, type: "line", x: 50, y: 100 + count * 20, width: 400, thickness: 2, color: "#000000", rotation: 0, }; } if (type === "rectangle") { return { id: baseId, type: "rectangle", x: 50 + count * 20, y: 50 + count * 20, width: 200, height: 100, fill: "transparent", stroke: "#000000", strokeWidth: 2, cornerRadius: 0, rotation: 0, }; } if (type === "summary") { return { id: baseId, type: "summary", x: 50, y: 300 + count * 20, 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, }; } if (type === "table") { return { id: baseId, type: "table", x: 50, y: 200 + count * 20, 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, }; } return null; }; const useEditorStore = create( devtools((set, get) => ({ // State elements: [], selectedId: null, clipboard: null, history: [], historyIndex: -1, page: { width: 794, height: 1123, name: "A4", }, showLayers: true, // Actions addElement: (type) => { const state = get(); const newElement = createElementByType(type, state.elements.length); if (!newElement) return; set((state) => ({ elements: [...state.elements, newElement], selectedId: newElement.id, history: [...state.history.slice(0, state.historyIndex + 1), state.elements], historyIndex: state.historyIndex + 1, })); }, updateElement: (id, updates) => { set((state) => ({ elements: state.elements.map((el) => el.id === id ? { ...el, ...updates } : el ), })); }, deleteElement: (id) => { set((state) => ({ elements: state.elements.filter((el) => el.id !== id), selectedId: state.selectedId === id ? null : state.selectedId, })); }, deleteSelected: () => { const { selectedId, deleteElement } = get(); if (selectedId) deleteElement(selectedId); }, setSelectedId: (id) => set({ selectedId: id }), duplicateElement: (id) => { const state = get(); const element = state.elements.find((el) => el.id === id); if (!element) return; const newElement = { ...element, id: uuidv4(), x: element.x + 20, y: element.y + 20, }; set((state) => ({ elements: [...state.elements, newElement], selectedId: newElement.id, })); }, copyElement: () => { const { selectedId, elements } = get(); const element = elements.find((el) => el.id === selectedId); if (element) { set({ clipboard: element }); } }, pasteElement: () => { const { clipboard } = get(); if (!clipboard) return; const newElement = { ...clipboard, id: uuidv4(), x: clipboard.x + 20, y: clipboard.y + 20, }; set((state) => ({ elements: [...state.elements, newElement], selectedId: newElement.id, })); }, moveElement: (id, direction) => { const step = 10; const deltas = { up: { x: 0, y: -step }, down: { x: 0, y: step }, left: { x: -step, y: 0 }, right: { x: step, y: 0 }, }; const delta = deltas[direction]; if (!delta) return; set((state) => ({ elements: state.elements.map((el) => el.id === id ? { ...el, x: el.x + delta.x, y: el.y + delta.y } : el ), })); }, reorderColumns: (elementId, newColumns) => { set((state) => ({ elements: state.elements.map((el) => el.id === elementId ? { ...el, columns: newColumns } : el ), })); }, toggleColumnVisibility: (elementId, columnKey) => { set((state) => ({ elements: state.elements.map((el) => { if (el.id === elementId && el.type === "table") { return { ...el, columns: el.columns.map((col) => col.key === columnKey ? { ...col, visible: !col.visible } : col ), }; } return el; }), })); }, updateColumnWidth: (elementId, columnKey, width) => { set((state) => ({ elements: state.elements.map((el) => { if (el.id === elementId && el.type === "table") { return { ...el, columns: el.columns.map((col) => col.key === columnKey ? { ...col, width } : col ), }; } return el; }), })); }, updateColumnLabel: (elementId, columnKey, label) => { set((state) => ({ elements: state.elements.map((el) => { if (el.id === elementId && el.type === "table") { return { ...el, columns: el.columns.map((col) => col.key === columnKey ? { ...col, label } : col ), }; } return el; }), })); }, // Undo/Redo undo: () => { const { historyIndex, history } = get(); if (historyIndex > 0) { set({ elements: history[historyIndex - 1], historyIndex: historyIndex - 1, }); } }, redo: () => { const { historyIndex, history } = get(); if (historyIndex < history.length - 1) { set({ elements: history[historyIndex + 1], historyIndex: historyIndex + 1, }); } }, // Page settings setPageSize: (size) => { set({ page: { ...get().page, ...size } }); }, toggleLayers: () => { set((state) => ({ showLayers: !state.showLayers })); }, // Save/Load exportTemplate: () => { const { elements, page } = get(); return JSON.stringify({ elements, page }, null, 2); }, importTemplate: (jsonString) => { try { const data = JSON.parse(jsonString); set({ elements: data.elements || [], page: data.page || get().page, selectedId: null, }); } catch (error) { console.error("Invalid template JSON", error); } }, clearCanvas: () => { set({ elements: [], selectedId: null, history: [], historyIndex: -1, }); }, })) ); export default useEditorStore;