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 (