diff --git a/BACKEND/controllers/savedForLater.controller.js b/BACKEND/controllers/savedForLater.controller.js index aafa165..182360b 100644 --- a/BACKEND/controllers/savedForLater.controller.js +++ b/BACKEND/controllers/savedForLater.controller.js @@ -61,7 +61,6 @@ export const removeFromSavedForLater = async (req, res, next) => { if (!productId) { return next(new AppError("Product ID is required", 400)); } - let savedList = await SavedForLater.findOne({ user: req.user._id }); if (!savedList) { diff --git a/BACKEND/models/order.model.js b/BACKEND/models/order.model.js index 753e00e..7d6145f 100644 --- a/BACKEND/models/order.model.js +++ b/BACKEND/models/order.model.js @@ -62,6 +62,15 @@ const orderSchema = new mongoose.Schema({ type: Date, default: null, }, + deliveryStatus: { + type: String, + enum: ["pending", "shipped", "delivered"], + default: "pending", + }, + deliveryDate: { + type: Date, + default: null, + }, }, { timestamps: true }); // Indexes for faster lookups diff --git a/BACKEND/models/user.model.js b/BACKEND/models/user.model.js index fd1479a..be7fba6 100644 --- a/BACKEND/models/user.model.js +++ b/BACKEND/models/user.model.js @@ -81,19 +81,22 @@ const userSchema = new mongoose.Schema( default: null, }, + role: { + type: String, + enum: ['user', 'admin'], + default: 'user', + }, referralCode: { type: String, unique: true, sparse: true, trim: true, }, - referredBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User', default: null, }, - walletBalance: { type: Number, default: 0, diff --git a/FRONTEND/applyDemo.cjs b/FRONTEND/applyDemo.cjs new file mode 100644 index 0000000..07e068d --- /dev/null +++ b/FRONTEND/applyDemo.cjs @@ -0,0 +1,48 @@ +const fs = require('fs'); + +// App.jsx +let appPath = 'src/App.jsx'; +let appContent = fs.readFileSync(appPath, 'utf8'); +appContent = appContent.replace('import { useKeyboardShortcuts } from "./hooks/useKeyboardShortcuts";', 'import { useKeyboardShortcuts } from "./hooks/useKeyboardShortcuts";\nimport { ToastContainer } from "./utils/toastService";'); +appContent = appContent.replace('', '\n '); +fs.writeFileSync(appPath, appContent); + +// ErrorBoundary.jsx +let ebPath = 'src/components/ui/ErrorBoundary.jsx'; +let ebContent = fs.readFileSync(ebPath, 'utf8'); +ebContent = ebContent.replace('console.error("ErrorBoundary caught:", error, info.componentStack);', 'console.error("ErrorBoundary caught:", error, info.componentStack);\n // Sentry.captureException(error, { extra: info });\n // LogRocket.captureException(error, { extra: info });'); +fs.writeFileSync(ebPath, ebContent); + +// HomePage.jsx +let hpPath = 'src/pages/HomePage.jsx'; +let hpContent = fs.readFileSync(hpPath, 'utf8'); +hpContent = hpContent.replace('import FilterPanel from "../components/ui/FilterPanel";', 'import FilterPanel from "../components/ui/FilterPanel";\nimport { notify } from "../utils/toastService";\n\nconst CrashTest = ({ shouldCrash }) => {\n if (shouldCrash) {\n throw new Error("Simulated Frontend Crash for Demo!");\n }\n return null;\n};\n'); +hpContent = hpContent.replace('const [totalProducts, setTotalProducts] = useState(0);', 'const [totalProducts, setTotalProducts] = useState(0);\n const [shouldCrash, setShouldCrash] = useState(false);'); + +let buttonsStr = ` + + Current Products🚀 + + + + + + + `; + +hpContent = hpContent.replace(' \n \n Current Products🚀\n ', buttonsStr); +fs.writeFileSync(hpPath, hpContent); + +console.log("Applied demo changes"); diff --git a/FRONTEND/cleanup.cjs b/FRONTEND/cleanup.cjs new file mode 100644 index 0000000..3309797 --- /dev/null +++ b/FRONTEND/cleanup.cjs @@ -0,0 +1,24 @@ +const fs = require('fs'); + +let hpPath = 'src/pages/HomePage.jsx'; +let c = fs.readFileSync(hpPath, 'utf8'); + +const importRegex = /import \{ notify \} from "\.\.\/utils\/toastService";\r?\n\r?\nconst CrashTest = \(\{ shouldCrash \}\) => \{[\s\S]*?if \(shouldCrash\) \{[\s\S]*?throw new Error\("Simulated Frontend Crash for Demo!"\);[\s\S]*?\}[\s\S]*?return null;[\s\S]*?\};\r?\n/; +c = c.replace(importRegex, ''); + +const stateRegex = /const \[shouldCrash, setShouldCrash\] = useState\(false\);\r?\n\s*/; +c = c.replace(stateRegex, ''); + +const buttonsRegex = /[\s\S]*? + + + + ); })} )} + {savedItems && savedItems.length > 0 && ( + + + {t("cart.savedForLater") || "Saved for Later"} ({savedItems.length}) + + {savedItems.map((item) => ( + + + {item.name} + + {formatPrice(item.price, currency, rates)} + + + + + + + + ))} + + + )} diff --git a/FRONTEND/src/pages/HomePage.jsx b/FRONTEND/src/pages/HomePage.jsx index 2d4b448..2e987db 100644 --- a/FRONTEND/src/pages/HomePage.jsx +++ b/FRONTEND/src/pages/HomePage.jsx @@ -17,6 +17,8 @@ import { formatPrice } from '../utils/currency'; import RecentlyViewedCarousel from "../components/ui/RecentlyViewedCarousel"; import FilterPanel from "../components/ui/FilterPanel"; + + const ProductCardSkeleton = () => { const bg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.200", "gray.700"); @@ -52,6 +54,7 @@ const HomePage = () => { const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalProducts, setTotalProducts] = useState(0); + const limit = 10; const [filters, setFilters] = useState({ diff --git a/FRONTEND/src/pages/Login.jsx b/FRONTEND/src/pages/Login.jsx index de2d846..dd616c3 100644 --- a/FRONTEND/src/pages/Login.jsx +++ b/FRONTEND/src/pages/Login.jsx @@ -23,8 +23,8 @@ import { showErrorToast, showWarningToast, } from "../utils/toastHelpers"; +import { useSavedForLaterStore } from "../store/savedForLater"; import { useAuth } from '../context/AuthContext'; - function Login() { const [email, setEmail] = useState('') const [password, setPassword] = useState('') @@ -61,6 +61,9 @@ function Login() { login(data.token, data.user) + // Sync local saved items with the backend profile + await useSavedForLaterStore.getState().syncWithBackend(); + showSuccessToast( toast, 'Login successful!', diff --git a/FRONTEND/updateNavbar.cjs b/FRONTEND/updateNavbar.cjs new file mode 100644 index 0000000..0f8b8be --- /dev/null +++ b/FRONTEND/updateNavbar.cjs @@ -0,0 +1,105 @@ +const fs = require('fs'); +const path = 'src/components/ui/Navbar.jsx'; +let lines = fs.readFileSync(path, 'utf8').split(/\r?\n/); + +// We will find lines to replace and insert. + +// 1. Add `useSavedForLater` import. +let importIdx = lines.findIndex(l => l.includes('import { useCart }')); +if (importIdx !== -1 && !lines.find(l => l.includes('useSavedForLater'))) { + lines.splice(importIdx + 1, 0, 'import { useSavedForLater } from "../../store/savedForLater";'); + lines.splice(importIdx + 2, 0, 'import { Divider } from "@chakra-ui/react";'); +} + +// 2. Add `useSavedForLater` hook inside Navbar. +let cartHookIdx = lines.findIndex(l => l.includes('const { cartItems, removeFromCart, totalPrice, emptyCart } = useCart();')); +if (cartHookIdx !== -1) { + lines[cartHookIdx] = ' const { cartItems, removeFromCart, totalPrice, emptyCart, addToCart } = useCart();'; + lines.splice(cartHookIdx + 1, 0, ' const { savedItems, saveForLater, removeFromSaved } = useSavedForLater();'); +} + +// 3. Add orchestration methods before totalItemsCount +let totalItemsIdx = lines.findIndex(l => l.includes('const totalItemsCount = cartItems.reduce')); +if (totalItemsIdx !== -1 && !lines.find(l => l.includes('handleSaveForLater'))) { + const orchestrator = [ + ' const handleSaveForLater = async (item) => {', + ' await saveForLater(item);', + ' removeFromCart(item._id);', + ' };', + '', + ' const handleMoveToCart = async (item) => {', + ' const res = addToCart(item, item.quantity || 1);', + ' if (res.status === "added") {', + ' await removeFromSaved(item._id);', + ' } else if (res.status === "capped") {', + ' toast({ title: "Stock Limit", description: "You can\'t add more of this item.", status: "warning", duration: 3000 });', + ' } else if (res.status === "out_of_stock") {', + ' toast({ title: "Out of Stock", description: "This item is currently out of stock.", status: "error", duration: 3000 });', + ' }', + ' };', + '' + ]; + lines.splice(totalItemsIdx, 0, ...orchestrator); +} + +// 4. Update the Remove button exactly +let currentPriceIdx = lines.findIndex(l => l.includes('currentPrice, currency, rates')); +if (currentPriceIdx !== -1) { + let btnStart = currentPriceIdx + 3; + if (lines[btnStart].includes('', + ' ', + ' ', + ' ' + ); + } +} + +// 5. Add Saved Items section before +let drawerBodyEndIdx = lines.lastIndexOf(' '); +if (drawerBodyEndIdx !== -1 && !lines.find(l => l.includes('savedItems.length > 0'))) { + const savedItemsBlock = [ + ' {savedItems && savedItems.length > 0 && (', + ' ', + ' ', + ' {t("cart.savedForLater") || "Saved for Later"} ({savedItems.length})', + ' ', + ' {savedItems.map((item) => (', + ' ', + ' ', + ' {item.name}', + ' ', + ' {formatPrice(item.price, currency, rates)}', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ))}', + ' ', + ' ', + ' )}' + ]; + lines.splice(drawerBodyEndIdx, 0, ...savedItemsBlock); +} + +fs.writeFileSync(path, lines.join('\n')); +console.log('Navbar updated completely via arrays.');