Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
Empty file added App.css
Empty file.
99 changes: 99 additions & 0 deletions App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import SearchBar from './components/SearchBar';

function App() {
const [products, setProducts] = useState([]);
const [selectedCategory, setSelectedCategory] = useState('');
const [sortByPrice, setSortByPrice] = useState('');
const [pagination, setPagination] = useState({ page: 1, limit: 10 });
const [searchTerm, setSearchTerm] = useState('');
const [totalPages, setTotalPages] = useState(0);

useEffect(() => {
fetchProducts();
}, [selectedCategory, sortByPrice, pagination, searchTerm]);

const fetchProducts = () => {
let url = 'http://localhost:8000/products';
const params = {
...pagination,
category: selectedCategory,
price: sortByPrice,
q: searchTerm
};

axios.get(url, { params })
.then(response => {
// Update products and total pages
setProducts(response.data.products);
setTotalPages(response.data.totalPages);
})
.catch(error => {
console.error('Error fetching products:', error);
});
};


const handleCategoryChange = (category) => {
setSelectedCategory(category);
};

const handleSortChange = (sortBy) => {
setSortByPrice(sortBy);
};

const handleSearch = (searchTerm) => {
setSearchTerm(searchTerm);
setPagination({ page: 1, limit: 10 }); // Reset pagination when searching
};

const handleNextPage = () => {
if (pagination.page < totalPages) {
setPagination(prevPagination => ({
...prevPagination,
page: prevPagination.page + 1
}));
}
};

const handlePrevPage = () => {
if (pagination.page > 1) {
setPagination(prevPagination => ({
...prevPagination,
page: prevPagination.page - 1
}));
}
};

return (
<div className="container">
<h1 className="text-center mt-3 mb-5">Products</h1>
<SearchBar
onSearch={handleSearch}
onCategoryChange={handleCategoryChange}
onSortChange={handleSortChange}
/>
<div className="row">
{products.map(product => (
<div key={product._id} className="col-md-4 mb-4">
<div className="card">
<img src={product.image} className="card-img-top" alt={product.name} />
<div className="card-body">
<h5 className="card-title">{product.name}</h5>
<p className="card-text">Price: ${product.price}</p>
</div>
</div>
</div>
))}
</div>
<div className="pagination mt-4">
<button className="btn btn-primary me-2" onClick={handlePrevPage}>Previous</button>
<span>Page {pagination.page} of {totalPages}</span>
<button className="btn btn-primary ms-2" onClick={handleNextPage}>Next</button>
</div>
</div>
);
}

export default App;
14 changes: 14 additions & 0 deletions components/Product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

function Product({ product }) {
return (
<div className="product">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>Category: {product.category}</p>
<p>Price: ${product.price}</p>
</div>
);
}

export default Product;
18 changes: 18 additions & 0 deletions components/ProductList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

function ProductList({ products }) {
return (
<div className="product-list">
{products.map(product => (
<div key={product._id} className="product">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
<p>Category: {product.category}</p>
</div>
))}
</div>
);
}

export default ProductList;
82 changes: 82 additions & 0 deletions components/SearchBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState } from 'react';

function SearchBar({ onSearch, onCategoryChange, onSortChange }) {
const [searchTerm, setSearchTerm] = useState('');
const [sortByPrice, setSortByPrice] = useState('');
const [selectedCategory, setSelectedCategory] = useState('');


// Hardcoded categories: Consider updating them to dynamic for improved scalability potential in the future.
const categories = [
"Health",
"Shoes",
"Outdoors",
"Movies",
"Grocery",
"Computers",
"Beauty",
"Industrial",
"Electronics",
"Toys",
"Music",
"Games",
"Clothing",
"Automotive",
"Jewelery"
];

const handleSearch = () => {
// Pass all search parameters to the onSearch function
onSearch(searchTerm, sortByPrice, selectedCategory);
};

return (
<div className="search-bar mb-4">
<div className="row g-3">
<div className="col-md-4">
<input
type="text"
className="form-control"
placeholder="Search products..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="col-md-3">
<select
className="form-select"
value={sortByPrice}
onChange={(e) => {
setSortByPrice(e.target.value);
onSortChange(e.target.value);
}}
>
<option value="">Sort by Price</option>
<option value="lowest">Lowest to Highest</option>
<option value="highest">Highest to Lowest</option>
</select>
</div>
<div className="col-md-3">
<select
className="form-select"
value={selectedCategory}
onChange={(e) => {
setSelectedCategory(e.target.value);
onCategoryChange(e.target.value);
}}
>
<option value="">All Categories</option>
{categories.map(category => (
<option key={category} value={category}>{category}</option>
))}
</select>
</div>
<div className="col-md-2">
<button className="btn btn-primary" onClick={handleSearch}>Search</button>
</div>
</div>
</div>
);
}

export default SearchBar;
13 changes: 13 additions & 0 deletions index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
21 changes: 21 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './store/store';
import 'bootstrap/dist/css/bootstrap.min.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
12 changes: 12 additions & 0 deletions models/products.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const ProductSchema = new Schema({
categories: String,
name: String,
price: Number,
image: String,
reviews:[{ type: mongoose.Schema.Types.ObjectId, ref: 'Review' }]
});

module.exports = mongoose.model("Product", ProductSchema);
9 changes: 9 additions & 0 deletions models/reviews.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const mongoose = require('mongoose');

const reviewSchema = new mongoose.Schema({
userName: String,
text: String,
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' }
});

module.exports = mongoose.model('Review', reviewSchema);
16 changes: 16 additions & 0 deletions node_modules/.bin/mime

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions node_modules/.bin/mime.cmd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions node_modules/.bin/mime.ps1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading