+ `).join('');
+
+ // Format shipping address for email
+ let shippingAddress = 'No shipping address provided';
+ if (order.shipping_address) {
+ const address = typeof order.shipping_address === 'string' ?
+ JSON.parse(order.shipping_address) : order.shipping_address;
+
+ shippingAddress = `
+ ${address.name || ''}
+ ${address.street || ''}
+ ${address.city || ''}, ${address.state || ''} ${address.zip || ''}
+ ${address.country || ''}
+ `;
+ }
+
+ // Send order confirmation email
+ await emailService.sendOrderConfirmation({
+ to: order.email,
+ first_name: order.first_name || 'Customer',
+ order_id: order.id.substring(0, 8), // first 8 characters of UUID for cleaner display
+ order_date: new Date(order.created_at).toLocaleDateString(),
+ order_total: `$${parseFloat(order.total_amount).toFixed(2)}`,
+ shipping_address: shippingAddress,
+ items_html: itemsHtml
+ });
+
+ console.log(`Order confirmation email sent to ${order.email}`);
+ }
}
}
break;
diff --git a/db/init/22-blog-json.sql b/db/init/22-blog-json.sql
new file mode 100644
index 0000000..04c7c03
--- /dev/null
+++ b/db/init/22-blog-json.sql
@@ -0,0 +1 @@
+ALTER TABLE blog_posts ADD COLUMN IF NOT EXISTS design JSONB;
\ No newline at end of file
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index f1fe796..b3b42c8 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -42,6 +42,7 @@ const BlogPage = lazy(() => import('@pages/BlogPage'));
const BlogDetailPage = lazy(() => import('@pages/BlogDetailPage'));
const AdminBlogPage = lazy(() => import('@pages/Admin/BlogPage'));
const BlogEditPage = lazy(() => import('@pages/Admin/BlogEditPage'));
+const BlogPreviewPage = lazy(() => import('@pages/Admin/BlogPreviewPage'));
const AdminBlogCommentsPage = lazy(() => import('@pages/Admin/BlogCommentsPage'));
const AdminProductReviewsPage = lazy(() => import('@pages/Admin/ProductReviewsPage'));
const EmailTemplatesPage = lazy(() => import('@pages/Admin/EmailTemplatesPage'));
@@ -199,6 +200,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/frontend/src/pages/Admin/BlogEditPage.jsx b/frontend/src/pages/Admin/BlogEditPage.jsx
index 882098b..280caf3 100644
--- a/frontend/src/pages/Admin/BlogEditPage.jsx
+++ b/frontend/src/pages/Admin/BlogEditPage.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import {
Box,
Typography,
@@ -25,25 +25,32 @@ import {
CardActions,
Tooltip,
Breadcrumbs,
- Link
+ Link,
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
} from '@mui/material';
import {
ArrowBack as ArrowBackIcon,
Save as SaveIcon,
Preview as PreviewIcon,
- Delete as DeleteIcon
+ Delete as DeleteIcon,
+ ExpandMore as ExpandMoreIcon,
+ Info as InfoIcon
} from '@mui/icons-material';
import { useNavigate, useParams, Link as RouterLink } from 'react-router-dom';
import { useAdminBlogPost, useCreateBlogPost, useUpdateBlogPost, useBlogCategories } from '@hooks/blogHooks';
import { useAuth } from '@hooks/reduxHooks';
import ImageUploader from '@components/ImageUploader';
import imageUtils from '@utils/imageUtils';
+import EmailEditor from 'react-email-editor';
const BlogEditPage = () => {
const { id } = useParams();
const navigate = useNavigate();
const isNewPost = !id;
const { userData } = useAuth();
+ const emailEditorRef = useRef(null);
// Form state
const [formData, setFormData] = useState({
@@ -54,13 +61,15 @@ const BlogEditPage = () => {
tags: [],
featuredImagePath: '',
status: 'draft',
- publishNow: false
+ publishNow: false,
+ design: null // New field to store email editor design JSON
});
// Validation state
const [errors, setErrors] = useState({});
const [notificationOpen, setNotificationOpen] = useState(false);
const [notification, setNotification] = useState({ type: 'success', message: '' });
+ const [isEditorReady, setIsEditorReady] = useState(false);
// Fetch blog post if editing
const {
@@ -112,6 +121,120 @@ const BlogEditPage = () => {
setFormData(prev => ({ ...prev, featuredImagePath: '' }));
};
+ // Email editor ready handler
+ const onEditorReady = () => {
+ setIsEditorReady(true);
+ console.log('Email editor is ready');
+
+ // If editing existing post with design data, load it
+ if (formData.design && emailEditorRef.current) {
+ emailEditorRef.current.editor.loadDesign(formData.design);
+ }
+ // If post has content but no design, create default design with the content
+ else if (formData.content && emailEditorRef.current) {
+ const defaultDesign = {
+ body: {
+ rows: [
+ {
+ cells: [1],
+ columns: [
+ {
+ contents: [
+ {
+ type: "html",
+ values: {
+ html: formData.content,
+ containerPadding: "10px"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ };
+ emailEditorRef.current.editor.loadDesign(defaultDesign);
+ }
+ // For new posts, load a simple default template
+ else if (emailEditorRef.current) {
+ const defaultTemplate = {
+ body: {
+ rows: [
+ {
+ cells: [1],
+ columns: [
+ {
+ contents: [
+ {
+ type: "html",
+ values: {
+ containerPadding: "10px",
+ textAlign: "left",
+ html: `
Exploring the Wonders of Nature
+
A journey through fascinating landscapes and natural phenomena
+
+
+
+
The Majesty of Mountains
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. Cras porttitor metus justo, vitae facilisis massa tempus sit amet. Nullam sit amet ipsum at magna tempus dignissim. Donec et nisi sed dolor scelerisque bibendum ut sit amet dolor.
+
Proin gravida nibh vel velit auctor aliquet. Aenean sollicitudin, lorem quis bibendum auctor, nisi elit consequat ipsum, nec sagittis sem nibh id elit. Duis sed odio sit amet nibh vulputate cursus a sit amet mauris. Morbi accumsan ipsum velit. Nam nec tellus a odio tincidunt auctor a ornare odio.
+
+
+
+
+
+
+
The Serenity of Waterfalls
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
+
+
+
+
+
+
Embracing Biodiversity
+
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
+
+
+
+
+
Wildlife in natural habitat
+
+
+
+
Macro photography reveals tiny worlds
+
+
+
+
Vibrant flora adds color to our world
+
+
+
+
+
"In all things of nature there is something of the marvelous." — Aristotle
+
+
+
Conservation Efforts
+
Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?