diff --git a/fileStructure.txt b/fileStructure.txt
index 36f478e..68284be 100644
--- a/fileStructure.txt
+++ b/fileStructure.txt
@@ -1,184 +1,236 @@
-project/
-├── frontend/
-│ ├── node_modules/
-│ ├── src/
-│ │ ├── pages/
-│ │ │ ├── Admin/
-│ │ │ │ ├── BrandingPage.jsx
-│ │ │ │ ├── ReportsPage.jsx
-│ │ │ │ ├── ProductEditPage.jsx
-│ │ │ │ ├── OrdersPage.jsx
-│ │ │ │ ├── EmailTemplatesPage.jsx
-│ │ │ │ ├── SettingsPage.jsx
-│ │ │ │ ├── BlogEditPage.jsx
-│ │ │ │ ├── CategoriesPage.jsx
-│ │ │ │ ├── CustomersPage.jsx
-│ │ │ │ ├── CouponsPage.jsx
-│ │ │ │ ├── ProductReviewsPage.jsx
-│ │ │ │ ├── BlogPage.jsx
-│ │ │ │ ├── BlogCommentsPage.jsx
-│ │ │ │ ├── DashboardPage.jsx
-│ │ │ │ └── ProductsPage.jsx
-│ │ │ ├── CheckoutPage.jsx
-│ │ │ ├── CouponEditPage.jsx
-│ │ │ ├── ProductsPage.jsx
-│ │ │ ├── UserOrdersPage.jsx
-│ │ │ ├── CartPage.jsx
-│ │ │ ├── ProductDetailPage.jsx
-│ │ │ ├── BlogDetailPage.jsx
-│ │ │ ├── BlogPage.jsx
-│ │ │ ├── CouponRedemptionsPage.jsx
-│ │ │ ├── HomePage.jsx
-│ │ │ ├── LoginPage.jsx
-│ │ │ ├── PaymentSuccessPage.jsx
-│ │ │ ├── RegisterPage.jsx
-│ │ │ ├── VerifyPage.jsx
-│ │ │ ├── NotFoundPage.jsx
-│ │ │ └── PaymentCancelPage.jsx
-│ │ ├── services/
-│ │ │ ├── emailTemplateService.js
-│ │ │ ├── blogAdminService.js
-│ │ │ ├── adminService.js
-│ │ │ ├── couponService.js
-│ │ │ ├── productService.js
-│ │ │ ├── productReviewService.js
-│ │ │ ├── consentService.js (NEW)
-│ │ │ ├── settingsAdminService.js
-│ │ │ ├── imageService.js
-│ │ │ ├── cartService.js
-│ │ │ ├── categoryAdminService.js
-│ │ │ ├── authService.js
-│ │ │ ├── blogService.js
-│ │ │ └── api.js
-│ │ ├── components/
-│ │ │ ├── ProductReviews.jsx
-│ │ │ ├── OrderStatusDialog.jsx
-│ │ │ ├── ImageUploader.jsx
-│ │ │ ├── CookieConsentPopup.jsx
-│ │ │ ├── CookieSettingsButton.jsx
-│ │ │ ├── Footer.jsx
-│ │ │ ├── CouponInput.jsx
-│ │ │ ├── EmailDialog.jsx
-│ │ │ ├── StripePaymentForm.jsx
-│ │ │ ├── ProductImage.jsx
-│ │ │ ├── ProtectedRoute.jsx
-│ │ │ ├── Notifications.jsx
-│ │ │ └── ProductRatingDisplay.jsx
-│ │ ├── hooks/
-│ │ │ ├── apiHooks.js
-│ │ │ ├── blogHooks.js
-│ │ │ ├── emailTemplateHooks.js
-│ │ │ ├── adminHooks.js
-│ │ │ ├── productReviewHooks.js
-│ │ │ ├── reduxHooks.js
-│ │ │ ├── couponAdminHooks.js
-│ │ │ ├── settingsAdminHooks.js
-│ │ │ ├── categoryAdminHooks.js
-│ │ │ └── brandingHooks.js
-│ │ ├── layouts/
-│ │ │ ├── MainLayout.jsx
-│ │ │ ├── AdminLayout.jsx
-│ │ │ └── AuthLayout.jsx
-│ │ ├── features/
-│ │ │ ├── ui/
-│ │ │ │ └── uiSlice.js
-│ │ │ ├── cart/
-│ │ │ │ └── cartSlice.js
-│ │ │ ├── auth/
-│ │ │ │ └── authSlice.js
-│ │ │ └── theme/
-│ │ │ ├── ThemeProvider.jsx
-│ │ │ └── index.js
-│ │ ├── utils/
-│ │ │ └── imageUtils.js
-│ │ ├── store/
-│ │ │ └── index.js
-│ │ ├── context/
-│ │ │ └── StripeContext.jsx
-│ │ └── assets/
-│ │ ├── App.jsx
-│ │ ├── main.jsx
-│ │ └── config.js
-│ └── public/
-│ ├── favicon.svg
-│ ├── vite.config.js
-│ ├── README.md
-│ ├── Dockerfile
-│ ├── nginx.conf
-│ ├── setup-frontend.sh
-│ ├── index.html
-│ └── .env
+/ (root)
+├── .env
+├── .gitignore
+├── Dockerfile
+├── README.md
+├── config.js
+├── docker-compose.yml
+├── fileStructure.txt
+├── index.html
+├── main.jsx
+├── nginx.conf
+├── package.json
+├── setup-frontend.sh
+├── start.sh
+├── vite.config.js
+│
├── backend/
│ ├── node_modules/
-│ ├── src/
-│ │ ├── routes/
-│ │ │ ├── cart.js
-│ │ │ ├── couponAdmin.js
-│ │ │ ├── emailTemplatesAdmin.js
-│ │ │ ├── productAdmin.js
-│ │ │ ├── blogAdmin.js
-│ │ │ ├── orderAdmin.js
-│ │ │ ├── settingsAdmin.js
-│ │ │ ├── blog.js
-│ │ │ ├── auth.js
-│ │ │ ├── productReviews.js
-│ │ │ ├── stripePayment.js
-│ │ │ ├── products.js
-│ │ │ ├── productReviewsAdmin.js
-│ │ │ ├── blogCommentsAdmin.js
-│ │ │ ├── userAdmin.js
-│ │ │ ├── categoryAdmin.js
-│ │ │ ├── shipping.js
-│ │ │ ├── images.js
-│ │ │ ├── userOrders.js
-│ │ │ ├── publicSettings.js
-│ │ │ └── productAdminImages.js
-│ │ ├── middleware/
-│ │ │ ├── upload.js
-│ │ │ ├── adminAuth.js
-│ │ │ └── auth.js
-│ │ ├── services/
-│ │ │ ├── shippingService.js
-│ │ │ ├── emailService.js
-│ │ │ └── notificationService.js
-│ │ ├── models/
-│ │ │ └── SystemSettings.js
-│ │ └── db/
-│ │ ├── index.js
-│ │ ├── index.js
-│ │ └── config.js
-│ └── public/
-│ ├── uploads/
-│ │ ├── products/
-│ │ └── blog/
-│ ├── package-lock.json
-│ ├── README.md
-│ ├── .env
-│ ├── package.json
-│ ├── Dockerfile
-│ └── .gitignore
-└── db/
- ├── init/
- │ ├── 18-email-templates.sql
- │ ├── 01-schema.sql
- │ ├── 02-seed.sql
- │ ├── 16-blog-schema.sql
- │ ├── 15-coupon.sql
- │ ├── 17-product-reviews.sql
- │ ├── 19-branding-settings.sql (NEW)
- │ ├── 04-product-images.sql
- │ ├── 09-system-settings.sql
- │ ├── 14-product-notifications.sql
- │ ├── 10-payment.sql
- │ ├── 11-notifications.sql
- │ ├── 12-shipping-orders.sql
- │ ├── 08-create-email.sql
- │ ├── 05-admin-role.sql
- │ ├── 03-api-key.sql
- │ ├── 07-user-keys.sql
- │ ├── 13-cart-metadata.sql
- │ └── 06-product-categories.sql
- └── test/
-├── fileStructure.txt
-├── docker-compose.yml
-└── .gitignore
\ No newline at end of file
+│ ├── public/
+│ │ ├── seo-files/
+│ │ ├── uploads/
+│ │ ├── robots.txt
+│ │ └── sitemap.xml
+│ │
+│ └── src/
+│ ├── db/
+│ │ └── index.js
+│ │
+│ ├── middleware/
+│ │ ├── adminAuth.js
+│ │ ├── auth.js
+│ │ ├── seoMiddleware.js
+│ │ └── upload.js
+│ │
+│ ├── models/
+│ │ └── SystemSettings.js
+│ │
+│ ├── routes/
+│ │ ├── auth.js
+│ │ ├── blog.js
+│ │ ├── blogAdmin.js
+│ │ ├── blogCommentsAdmin.js
+│ │ ├── cart.js
+│ │ ├── categoryAdmin.js
+│ │ ├── couponAdmin.js
+│ │ ├── emailCampaignsAdmin.js
+│ │ ├── emailTemplatesAdmin.js
+│ │ ├── emailTracking.js
+│ │ ├── images.js
+│ │ ├── mailingListAdmin.js
+│ │ ├── orderAdmin.js
+│ │ ├── productAdmin.js
+│ │ ├── productAdminImages.js
+│ │ ├── productReviews.js
+│ │ ├── productReviewsAdmin.js
+│ │ ├── products.js
+│ │ ├── publicSettings.js
+│ │ ├── settingsAdmin.js
+│ │ ├── shipping.js
+│ │ ├── stripePayment.js
+│ │ ├── subscribers.js
+│ │ ├── subscribersAdmin.js
+│ │ ├── userAdmin.js
+│ │ └── userOrders.js
+│ │
+│ ├── services/
+│ │ ├── cacheService.js
+│ │ ├── emailService.js
+│ │ ├── notificationService.js
+│ │ ├── queueService.js
+│ │ ├── shippingService.js
+│ │ ├── sitemapService.js
+│ │ └── storageService.js
+│ │
+│ └── uploads/
+│ ├── temp/
+│ ├── config.js
+│ ├── index.js
+│ └── worker.js
+│
+├── db/
+│ ├── init/
+│ │ ├── 01-schema.sql
+│ │ ├── 02-seed.sql
+│ │ ├── 03-api-key.sql
+│ │ ├── 04-product-images.sql
+│ │ ├── 05-admin-role.sql
+│ │ ├── 06-product-categories.sql
+│ │ ├── 07-user-keys.sql
+│ │ ├── 08-create-email.sql
+│ │ ├── 09-system-settings.sql
+│ │ ├── 10-payment.sql
+│ │ ├── 11-notifications.sql
+│ │ ├── 12-shipping-orders.sql
+│ │ ├── 13-cart-metadata.sql
+│ │ ├── 14-product-notifications.sql
+│ │ ├── 15-coupon.sql
+│ │ ├── 16-blog-schema.sql
+│ │ ├── 17-product-reviews.sql
+│ │ ├── 18-email-templates.sql
+│ │ ├── 19-branding-settings.sql
+│ │ ├── 20-mailinglist.sql
+│ │ ├── 21-order-refund.sql
+│ │ └── 22-blog-json.sql
+│ │
+│ └── test/
+│
+└── frontend/
+ ├── node_modules/
+ ├── public/
+ │ ├── seo-files/
+ │ └── favicon.svg
+ │
+ └── src/
+ ├── assets/
+ ├── components/
+ │ ├── CookieConsentPopup.jsx
+ │ ├── CookieSettingsButton.jsx
+ │ ├── CouponInput.jsx
+ │ ├── EmailDialog.jsx
+ │ ├── Footer.jsx
+ │ ├── ImageUploader.jsx
+ │ ├── Notifications.jsx
+ │ ├── OrderStatusDialog.jsx
+ │ ├── ProductImage.jsx
+ │ ├── ProductRatingDisplay.jsx
+ │ ├── ProductReviews.jsx
+ │ ├── ProtectedRoute.jsx
+ │ ├── SEOMetaTags.jsx
+ │ ├── StripePaymentForm.jsx
+ │ └── SubscriptionForm.jsx
+ │
+ ├── context/
+ │ └── StripeContext.jsx
+ │
+ ├── features/
+ │ ├── auth/
+ │ │ └── authSlice.js
+ │ ├── cart/
+ │ │ └── cartSlice.js
+ │ └── ui/
+ │ └── uiSlice.js
+ │
+ ├── hooks/
+ │ ├── adminHooks.js
+ │ ├── apiHooks.js
+ │ ├── blogHooks.js
+ │ ├── brandingHooks.js
+ │ ├── categoryAdminHooks.js
+ │ ├── couponAdminHooks.js
+ │ ├── emailCampaignHooks.js
+ │ ├── emailTemplateHooks.js
+ │ ├── productAdminHooks.js
+ │ ├── productReviewHooks.js
+ │ ├── reduxHooks.js
+ │ ├── settingsAdminHooks.js
+ │ ├── useProductSeo.js
+ │ ├── useSeoMeta.js
+ │ └── useSeoUrl.js
+ │
+ ├── layouts/
+ │ ├── AdminLayout.jsx
+ │ ├── AuthLayout.jsx
+ │ └── MainLayout.jsx
+ │
+ ├── pages/
+ │ ├── Admin/
+ │ │ ├── BlogCommentsPage.jsx
+ │ │ ├── BlogEditPage.jsx
+ │ │ ├── BlogPage.jsx
+ │ │ ├── BlogPreviewPage.jsx
+ │ │ ├── BrandingPage.jsx
+ │ │ ├── CampaignAnalyticsPage.jsx
+ │ │ ├── CampaignSendPage.jsx
+ │ │ ├── CategoriesPage.jsx
+ │ │ ├── CouponsPage.jsx
+ │ │ ├── CustomersPage.jsx
+ │ │ ├── DashboardPage.jsx
+ │ │ ├── EmailCampaignEditorPage.jsx
+ │ │ ├── EmailCampaignsPage.jsx
+ │ │ ├── EmailTemplatesPage.jsx
+ │ │ ├── MailingListsPage.jsx
+ │ │ ├── OrdersPage.jsx
+ │ │ ├── ProductEditPage.jsx
+ │ │ ├── ProductReviewsPage.jsx
+ │ │ ├── ProductsPage.jsx
+ │ │ ├── ReportsPage.jsx
+ │ │ ├── SettingsPage.jsx
+ │ │ └── SubscribersPage.jsx
+ │ │
+ │ ├── BlogDetailPage.jsx
+ │ ├── BlogPage.jsx
+ │ ├── CartPage.jsx
+ │ ├── CheckoutPage.jsx
+ │ ├── CouponEditPage.jsx
+ │ ├── CouponRedemptionsPage.jsx
+ │ ├── HomePage.jsx
+ │ ├── LoginPage.jsx
+ │ ├── NotFoundPage.jsx
+ │ ├── PaymentCancelPage.jsx
+ │ ├── PaymentSuccessPage.jsx
+ │ ├── ProductDetailPage.jsx
+ │ ├── ProductsPage.jsx
+ │ ├── RegisterPage.jsx
+ │ ├── SubscriptionConfirmPage.jsx
+ │ ├── SubscriptionPreferencesPage.jsx
+ │ ├── UnsubscribePage.jsx
+ │ ├── UserOrdersPage.jsx
+ │ └── VerifyPage.jsx
+ │
+ ├── services/
+ │ ├── adminService.js
+ │ ├── api.js
+ │ ├── authService.js
+ │ ├── blogAdminService.js
+ │ ├── blogService.js
+ │ ├── cartService.js
+ │ ├── categoryAdminService.js
+ │ ├── consentService.js
+ │ ├── couponService.js
+ │ ├── emailTemplateService.js
+ │ ├── imageService.js
+ │ ├── productReviewsService.js
+ │ ├── productService.js
+ │ └── settingsAdminService.js
+ │
+ ├── store/
+ │ └── index.js
+ │
+ ├── theme/
+ │ ├── index.js
+ │ └── ThemeProvider.jsx
+ │
+ └── utils/
+ ├── imageUtils.js
+ └── App.jsx
\ No newline at end of file
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 75203b4..e71c098 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -140,8 +140,16 @@ function App() {
} />
- } />
- } />
+ }>
+
+
+ } />
+ }>
+
+
+ } />
} />
} />
} />
diff --git a/frontend/src/components/ProductImage.jsx b/frontend/src/components/ProductImage.jsx
index 87e1aea..82accee 100644
--- a/frontend/src/components/ProductImage.jsx
+++ b/frontend/src/components/ProductImage.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, memo } from 'react';
import { Box } from '@mui/material';
import imageUtils from '@utils/imageUtils';
@@ -11,7 +11,7 @@ import imageUtils from '@utils/imageUtils';
* @param {string} props.placeholderImage - Placeholder image to use when no image is available
* @returns {JSX.Element} - ProductImage component
*/
-const ProductImage = ({
+const ProductImage = memo(({
images,
alt = 'Product image',
sx = {},
@@ -20,7 +20,6 @@ const ProductImage = ({
}) => {
const [imageError, setImageError] = useState(false);
- // Determine which image to use
let imageSrc = placeholderImage;
// Handle different types of image inputs
@@ -52,6 +51,7 @@ const ProductImage = ({
src={imageError ? placeholderImage : imageSrc}
alt={alt}
onError={handleError}
+ loading="lazy"
sx={{
display: 'block',
width: '100%',
@@ -62,6 +62,6 @@ const ProductImage = ({
{...rest}
/>
);
-};
+})
export default ProductImage;
\ No newline at end of file
diff --git a/frontend/src/components/ProductRatingDisplay.jsx b/frontend/src/components/ProductRatingDisplay.jsx
index 18efeba..16f5c04 100644
--- a/frontend/src/components/ProductRatingDisplay.jsx
+++ b/frontend/src/components/ProductRatingDisplay.jsx
@@ -1,10 +1,10 @@
-import React from 'react';
+import React, { memo } from 'react';
import { Box, Typography, Rating } from '@mui/material';
/**
* Component to display product rating in a compact format
*/
-const ProductRatingDisplay = ({ rating, reviewCount, showEmpty = false }) => {
+const ProductRatingDisplay = memo(({ rating, reviewCount, showEmpty = false }) => {
if (!rating && !reviewCount && !showEmpty) {
return null;
}
@@ -22,6 +22,6 @@ const ProductRatingDisplay = ({ rating, reviewCount, showEmpty = false }) => {
);
-};
+})
export default ProductRatingDisplay;
\ No newline at end of file
diff --git a/frontend/src/pages/HomePage.jsx b/frontend/src/pages/HomePage.jsx
index d77dafc..9dfeb3a 100644
--- a/frontend/src/pages/HomePage.jsx
+++ b/frontend/src/pages/HomePage.jsx
@@ -29,23 +29,25 @@ const HomePage = () => {
const featuredProductsData = products && products.length > 0 ? {
"@context": "https://schema.org",
"@type": "ItemList",
- "itemListElement": products.slice(0, 6).map((product, index) => ({
- "@type": "ListItem",
- "position": index + 1,
- "item": {
- "@type": "Product",
- "name": product.name,
- "image": product.images && product.images.length > 0 ?
- imageUtils.getImageUrl(product.images.find(img => img.isPrimary)?.path || product.images[0].path) : null,
- "description": product.description,
- "url": `${window.location.origin}/products/${product.id}`,
- "offers": {
- "@type": "Offer",
- "price": parseFloat(product.price).toFixed(2),
- "priceCurrency": "CAD"
+ "itemListElement": products.slice(0, 6).map((product, index) => {
+ return {
+ "@type": "ListItem",
+ "position": index + 1,
+ "item": {
+ "@type": "Product",
+ "name": product.name,
+ "image": product.images && product.images.length > 0 ?
+ imageUtils.getImageUrl(product.images.find(img => img.isPrimary)?.path || product.images[0].path) : null,
+ "description": product.description,
+ "url": `${window.location.origin}/products/${product.id}`,
+ "offers": {
+ "@type": "Offer",
+ "price": parseFloat(product.price).toFixed(2),
+ "priceCurrency": "CAD"
+ }
}
}
- }))
+ })
} : null;
return (