-
Notifications
You must be signed in to change notification settings - Fork 167
Eval Pull Request #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Eval Pull Request #170
Changes from all commits
e0d0d78
38afec5
cd1e568
072b564
c02ee9a
461953c
0c5a202
67d5b62
45d8229
5c5064e
6b9dafc
baf857b
13f80b4
70f0347
7450536
6291a98
8541ffe
78d959d
9988785
102feab
c6e189c
c4f25dc
f4dfa18
b5c43dc
c3ee23a
e796075
63da3ac
d8a099a
b042345
fe320dc
a650454
ae643cb
8f9e96f
d54749e
16ac73b
b1a74b6
fb6dfae
3c66309
baf9915
22705d0
9605fb7
42ec250
bd8176e
b48fa6e
9ae2629
a0546c6
7940d41
25a5d4d
65dca31
d1a8863
10d2e7a
458f773
5077701
138ce15
3e43618
98bf657
e861e97
fea2c9e
c967280
8b98cb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # General | ||
| .DS_Store | ||
| .AppleDouble | ||
| .LSOverride | ||
|
|
||
| # Icon must end with two \r | ||
| Icon | ||
|
|
||
|
|
||
| # Thumbnails | ||
| ._* | ||
|
|
||
| # Files that might appear in the root of a volume | ||
| .DocumentRevisions-V100 | ||
| .fseventsd | ||
| .Spotlight-V100 | ||
| .TemporaryItems | ||
| .Trashes | ||
| .VolumeIcon.icns | ||
| .com.apple.timemachine.donotpresent | ||
|
|
||
| # Directories potentially created on remote AFP share | ||
| .AppleDB | ||
| .AppleDesktop | ||
| Network Trash Folder | ||
| Temporary Items | ||
| .apdisk | ||
|
|
||
| .vscode/* | ||
| !.vscode/settings.json | ||
| !.vscode/tasks.json | ||
| !.vscode/launch.json | ||
| !.vscode/extensions.json | ||
|
|
||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
|
|
||
| # Runtime data | ||
| pids | ||
| *.pid | ||
| *.seed | ||
| *.pid.lock | ||
|
|
||
| # Directory for instrumented libs generated by jscoverage/JSCover | ||
| lib-cov | ||
|
|
||
| # Coverage directory used by tools like istanbul | ||
| coverage | ||
|
|
||
| # nyc test coverage | ||
| .nyc_output | ||
|
|
||
| # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
| .grunt | ||
|
|
||
| # Bower dependency directory (https://bower.io/) | ||
| bower_components | ||
|
|
||
| # node-waf configuration | ||
| .lock-wscript | ||
|
|
||
| # Compiled binary addons (https://nodejs.org/api/addons.html) | ||
| build/Release | ||
|
|
||
| # Dependency directories | ||
| node_modules/ | ||
| jspm_packages/ | ||
|
|
||
| # Typescript v1 declaration files | ||
| typings/ | ||
|
|
||
| # Optional npm cache directory | ||
| .npm | ||
|
|
||
| # Optional eslint cache | ||
| .eslintcache | ||
|
|
||
| # Optional REPL history | ||
| .node_repl_history | ||
|
|
||
| # Output of 'npm pack' | ||
| *.tgz | ||
|
|
||
| # Yarn Integrity file | ||
| .yarn-integrity | ||
|
|
||
| # dotenv environment variables file | ||
| .env | ||
|
|
||
| # next.js build output | ||
| .next |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "workbench.colorCustomizations": { | ||
| "activityBar.background": "#402811", | ||
| "titleBar.activeBackground": "#5A3818", | ||
| "titleBar.activeForeground": "#FDFAF7" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,75 @@ | ||
| ## Product List | ||
|
|
||
| # Product List | ||
|
|
||
| This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. | ||
|
|
||
| If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. | ||
|
|
||
| ## Requirements | ||
|
|
||
| Install dependencies | ||
|
|
||
| npm install | ||
|
|
||
| Install required packages | ||
|
|
||
| npm install react-bootstrap bootstrap axios react-icons | ||
|
|
||
| ##Database - MongoDB | ||
|
|
||
| Create a .env file in your backend directory: | ||
| envPORT=8000 | ||
| MONGO_URI=mongodb://localhost/products | ||
|
|
||
| OR use your own MongoDB connection string: | ||
|
|
||
| MONGO_URI=mongodb+srv://username:password@cluster.mongodb.net/products | ||
|
|
||
| ## Running the Application | ||
|
|
||
| Start the Backend Server | ||
| bash# From root directory | ||
|
|
||
| npm run dev | ||
|
|
||
| Server will run on http://localhost:8000 | ||
|
|
||
| Start the Frontend Application | ||
| bash# From frontend directory | ||
| npm run dev | ||
|
|
||
| App will run on http://localhost:3000 | ||
|
|
||
| ### Configuration | ||
|
|
||
| Port Configuration | ||
|
|
||
| Backend: Runs on process.env.PORT || 8000 | ||
| Frontend: Must run on port 3000 (CORS is configured for this port) | ||
|
|
||
| ### Database Configuration | ||
|
|
||
| The MongoDB connection string is set to: | ||
| javascriptconst connectString = process.env.MONGO_URI || "mongodb://localhost/products" | ||
| You can either: | ||
|
|
||
| Use the default local MongoDB setup | ||
| Set your own MONGO_URI in the .env file | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| Common Issues: | ||
| CORS Error: | ||
|
|
||
| Ensure frontend runs on port 3000 | ||
| Check CORS configuration in backend | ||
|
|
||
| Database Connection: | ||
|
|
||
| Verify MongoDB is running locally | ||
| Check MONGO_URI environment variable | ||
|
|
||
| Port Already in Use: | ||
|
|
||
| Change PORT in .env file | ||
|
|
||
| to see products, go to http://localhost:8000/api/products |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const mongoose = require("mongoose"); | ||
|
|
||
| const connectDB = async () => { | ||
| const connectString = process.env.MONGO_URI || "mongodb://localhost/products"; | ||
|
|
||
| try { | ||
| const conn = await mongoose.connect(connectString); | ||
| console.log(`MongoDB connected. Connected to database: ${connectString}`); | ||
| } catch (error) { | ||
| console.error(`Error: ${error.message}`); | ||
| process.exit(1); | ||
| } | ||
| }; | ||
|
|
||
| module.exports = connectDB; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
|
|
||
| const Product = require("../models/product.model.js"); | ||
| const Review = require('../models/review.model.js') | ||
|
|
||
|
|
||
| //gets all products | ||
| const getProducts = async (req, res) => { | ||
| try { | ||
| const productsPerPage = 9; | ||
| const page = parseInt(req.query.page) || 1; | ||
| const category = req.query.category || null; | ||
| const priceSort = req.query.price; | ||
| const search = req.query.search || null; | ||
|
|
||
|
|
||
| let filterProducts = {}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not really clear the label, should be more like filterQuery or something more specific |
||
|
|
||
| if (category) { | ||
| filterProducts.category = { $regex: `^${category}$`, $options: "i" }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dry |
||
| } | ||
|
|
||
| if (search) { | ||
| filterProducts.name = { $regex: search, $options: "i" }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dry There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can store this as a generic const and just use it twice |
||
| } | ||
|
|
||
| let sort = {}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be const |
||
|
|
||
| if (priceSort === "lowest") { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use consts, will help with DRY |
||
| sort.price = 1; | ||
| } else if (priceSort === "highest") { | ||
| sort.price = -1; | ||
| } | ||
|
|
||
| const count = await Product.countDocuments(filterProducts); | ||
|
|
||
| const products = await Product.find(filterProducts) | ||
| .sort(sort) | ||
| .skip((page - 1) * productsPerPage) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. store in a const or a function to give context for this skip |
||
| .limit(productsPerPage); | ||
|
|
||
| res.status(200).json({ | ||
| products, | ||
| count, | ||
| page, | ||
| totalPages: Math.ceil(count / productsPerPage), | ||
| }); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
|
|
||
| const getProductById = async (req, res) => { | ||
| try { | ||
| const { id } = req.params; | ||
| const product = await Product.findById(id); | ||
|
|
||
| if (!product) { | ||
| return res.status(404).json({ message: "Product not found" }); | ||
| } | ||
| res.status(200).json(product); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
|
|
||
| const deleteProductById = async (req, res) => { | ||
| try { | ||
| const { id } = req.params; | ||
| const product = await Product.findByIdAndDelete(id); | ||
| if (!product) { | ||
| return res.status(404).json({ message: "Product not found" }); | ||
| } | ||
| res.status(200).json({ message: "Product has been deleted" }); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
| const createNewProduct = async (req, res) => { | ||
| try { | ||
| const newProduct = await Product.create(req.body); | ||
| res.status(200).json(newProduct); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
| const getReviewsOfAProduct = async (req, res) => { | ||
| try { | ||
| const { productId } = req.params; | ||
|
|
||
| const product = await Product.findById(productId); | ||
|
|
||
| //if no product | ||
| if (!product) { | ||
| return res.status(404).json({ message: "Product not found" }); | ||
| } | ||
|
|
||
| const reviews = await Review.find({ product: productId }).limit(4); | ||
|
|
||
| //if yes prodcut but no reviews | ||
| if (reviews.length === 0) { | ||
| return res.status(404).json({ message: "No reviews available" }); | ||
| } | ||
| res.status(200).json(reviews); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
| const createReviewForAProduct = async (req, res) => { | ||
| try { | ||
| const { productId } = req.params; | ||
| const { userName, text } = req.body; | ||
|
|
||
| if (!userName || !text) { | ||
| return res.status(400).json({ | ||
| message: "username and text are required.", | ||
| }); | ||
| } | ||
|
|
||
| const product = await Product.findById(productId); | ||
|
|
||
| if (!product) { | ||
| return res.status(404).json({ message: "Product not found" }); | ||
| } | ||
|
|
||
| const newReview = await Review.create({ | ||
| userName, | ||
| text, | ||
| product: productId, | ||
| }); | ||
|
|
||
| res.status(201).json({ message: "Review added", review: newReview }); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
| const deleteReviewById = async (req, res) => { | ||
| try { | ||
| const { reviewId } = req.params; | ||
|
|
||
| const review = await Review.findByIdAndDelete(reviewId); | ||
|
|
||
| if (!review) { | ||
| return res.status(404).json({ message: "Review not found" }); | ||
| } | ||
|
|
||
| res.status(200).json({ message: "review has been deleted" }); | ||
| } catch (error) { | ||
| res.status(500).json({ message: error.message }); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| module.exports = { deleteProductById, deleteReviewById, getProductById, getProducts, getReviewsOfAProduct, createNewProduct, createReviewForAProduct} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the null fallback? undefined is not good enough?