diff --git a/client/src/App.jsx b/client/src/App.jsx index 8d0ae7a..4f3f532 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -79,6 +79,7 @@ function App() { }; const handleAddTemplate = (template) => { + // Apply template elements to canvas if (template && template.elements) { template.elements.forEach((el, index) => { setTimeout(() => addElement({ ...el }), index * 50); diff --git a/client/src/index.css b/client/src/index.css index a395e25..1eafe41 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -164,6 +164,12 @@ input, textarea, select { max-width: 400px; } +.canvas-actions { + display: flex; + gap: 0.75rem; + align-items: center; +} + .undo-redo-buttons { display: flex; gap: 0.5rem; @@ -196,6 +202,64 @@ input, textarea, select { cursor: not-allowed; } +.export-btn { + padding: 0.5rem 1rem; + background: linear-gradient(135deg, #22c55e, #16a34a); + color: white; + border: none; + border-radius: var(--radius-md); + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.export-btn:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.export-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.export-error { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + background: #fef2f2; + border: 1px solid #fecaca; + border-radius: var(--radius-md); + color: #dc2626; + font-size: 0.875rem; + margin-bottom: 1rem; + width: 100%; + max-width: 400px; +} + +.export-error p { + margin: 0; +} + +.close-error { + background: transparent; + border: none; + color: #dc2626; + cursor: pointer; + font-size: 1.25rem; + padding: 0; + line-height: 1; +} + +.close-error:hover { + color: #991b1b; +} + .canvas-wrapper { margin-bottom: 1rem; } diff --git a/server/index.js b/server/index.js index 62de584..843121b 100644 --- a/server/index.js +++ b/server/index.js @@ -17,7 +17,7 @@ const PORT = process.env.PORT || 3001; // Ensure upload and export directories exist const uploadsDir = join(__dirname, 'uploads'); const exportsDir = join(__dirname, 'exports'); -[uploadsDir, exportsDir].forEach(dir => { +[uploadsDir, exportsDir].forEach((dir) => { if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); }); @@ -39,7 +39,7 @@ const storage = multer.diskStorage({ const ext = file.originalname.split('.').pop(); const filename = `${uuidv4()}.${ext}`; cb(null, filename); - } + }, }); const fileFilter = (req, file, cb) => { @@ -55,8 +55,8 @@ const upload = multer({ storage, fileFilter, limits: { - fileSize: 20 * 1024 * 1024 // 20MB - } + fileSize: 20 * 1024 * 1024, // 20MB + }, }); // Health check endpoint @@ -96,13 +96,13 @@ app.post('/api/upload', upload.single('image'), async (req, res) => { url: originalUrl, filename: req.file.filename, size: req.file.size, - mimetype: req.file.mimetype + mimetype: req.file.mimetype, }, preview: { path: previewPath, url: previewUrl, - filename: previewFilename - } + filename: previewFilename, + }, }); } catch (error) { console.error('Upload error:', error); @@ -192,8 +192,8 @@ app.post('/api/export', async (req, res) => { width: EXPORT_SIZE, height: EXPORT_SIZE, dpi: 300, - sizeInches: '15x15' - } + sizeInches: '15x15', + }, }); } catch (error) { console.error('Export error:', error);