added review functionality
This commit is contained in:
@@ -14,6 +14,7 @@ exports.addToFavorite = async (req, res) => {
|
|||||||
success: true,
|
success: true,
|
||||||
message: "Product added to favorites successfully",
|
message: "Product added to favorites successfully",
|
||||||
});
|
});
|
||||||
|
console.log(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding favorite product:", error);
|
console.error("Error adding favorite product:", error);
|
||||||
return res.json({ error: "Could not add favorite product" });
|
return res.json({ error: "Could not add favorite product" });
|
||||||
|
|||||||
@@ -8,6 +8,29 @@ const Home = () => {
|
|||||||
const [recommended, setRecommended] = useState([]);
|
const [recommended, setRecommended] = useState([]);
|
||||||
const [history, sethistory] = useState([]);
|
const [history, sethistory] = useState([]);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||||
|
|
||||||
|
const handleLinkClick = async (id) => {
|
||||||
|
// Example: append to localStorage or call analytics
|
||||||
|
const response = await fetch(
|
||||||
|
"http://localhost:3030/api/product/add_fav_product",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
userID: storedUser.ID,
|
||||||
|
productsID: id,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error("Failed to fetch products");
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
console.log(`Add Product -> History: ${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchrecomProducts = async () => {
|
const fetchrecomProducts = async () => {
|
||||||
@@ -298,6 +321,7 @@ const Home = () => {
|
|||||||
<Link
|
<Link
|
||||||
key={listing.id}
|
key={listing.id}
|
||||||
to={`/product/${listing.id}`}
|
to={`/product/${listing.id}`}
|
||||||
|
onClick={() => handleLinkClick(listing.id)}
|
||||||
className="bg-white border border-gray-200 hover:shadow-md transition-shadow w-70 flex-shrink-0 relative"
|
className="bg-white border border-gray-200 hover:shadow-md transition-shadow w-70 flex-shrink-0 relative"
|
||||||
>
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ProductDetail = () => {
|
|||||||
reviews: null,
|
reviews: null,
|
||||||
});
|
});
|
||||||
const [isFavorite, setIsFavorite] = useState(false);
|
const [isFavorite, setIsFavorite] = useState(false);
|
||||||
const [showContactForm, setShowContactForm] = useState(false);
|
const [contactForm, showContactForm, setShowContactForm] = useState(false);
|
||||||
const [currentImage, setCurrentImage] = useState(0);
|
const [currentImage, setCurrentImage] = useState(0);
|
||||||
const [reviews, setReviews] = useState([]);
|
const [reviews, setReviews] = useState([]);
|
||||||
const [showReviewForm, setShowReviewForm] = useState(false);
|
const [showReviewForm, setShowReviewForm] = useState(false);
|
||||||
@@ -417,81 +417,15 @@ const ProductDetail = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
{/* <button
|
||||||
onClick={() => setShowContactForm(!showContactForm)}
|
onClick={() => setShowContactForm(!showContactForm)}
|
||||||
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
||||||
>
|
>
|
||||||
Contact Seller
|
Contact Seller
|
||||||
</button>
|
</button> */}
|
||||||
|
|
||||||
{showContactForm && (
|
|
||||||
<div className="border border-gray-200 p-4 mb-4">
|
|
||||||
<h3 className="font-medium text-gray-800 mb-2">
|
|
||||||
Contact Seller
|
|
||||||
</h3>
|
|
||||||
<form onSubmit={handleSendMessage}>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label htmlFor="email" className="block text-gray-700 mb-1">
|
|
||||||
Email <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
value={contactForm.email}
|
|
||||||
onChange={handleContactInputChange}
|
|
||||||
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label htmlFor="phone" className="block text-gray-700 mb-1">
|
|
||||||
Phone Number <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
value={contactForm.phone}
|
|
||||||
onChange={handleContactInputChange}
|
|
||||||
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label
|
|
||||||
htmlFor="message"
|
|
||||||
className="block text-gray-700 mb-1"
|
|
||||||
>
|
|
||||||
Message
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="message"
|
|
||||||
value={contactForm.message}
|
|
||||||
onChange={handleContactInputChange}
|
|
||||||
placeholder="Hi, is this item still available?"
|
|
||||||
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
|
||||||
rows="3"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4"
|
|
||||||
>
|
|
||||||
Send Contact Info
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowContactForm(false)}
|
|
||||||
className="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="pt-4 border-t border-gray-200">
|
<div className="pt-4 border-t border-gray-200">
|
||||||
|
{/* Seller Info */}
|
||||||
<div className="flex items-center mb-3">
|
<div className="flex items-center mb-3">
|
||||||
<div className="mr-3">
|
<div className="mr-3">
|
||||||
<div className="h-12 w-12 rounded-full bg-gray-200 flex items-center justify-center">
|
<div className="h-12 w-12 rounded-full bg-gray-200 flex items-center justify-center">
|
||||||
@@ -507,23 +441,33 @@ const ProductDetail = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
|
||||||
<div>
|
{/* Contact Options */}
|
||||||
<span className="font-medium">Rating:</span>{" "}
|
{showContactForm && (
|
||||||
{product.seller?.rating ? (
|
<div className="mt-4 border border-gray-200 p-4">
|
||||||
<div className="flex items-center">
|
<h3 className="font-medium text-gray-800 mb-2">
|
||||||
{renderStars(product.seller.rating)}
|
Contact Seller
|
||||||
<span className="ml-1">{product.seller.rating}/5</span>
|
</h3>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
|
<a
|
||||||
|
href={`tel:${contactForm.phone}`}
|
||||||
|
className="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 text-center "
|
||||||
|
>
|
||||||
|
Call Seller
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={`mailto:${contactForm.email}`}
|
||||||
|
className="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 text-center "
|
||||||
|
>
|
||||||
|
Email Seller
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
"N/A"
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Reviews Section */}
|
{/* Reviews Section */}
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
|
|||||||
@@ -415,6 +415,60 @@ const Settings = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Privacy Section */}
|
||||||
|
<div className="bg-white border border-gray-200 mb-6">
|
||||||
|
<div className="border-b border-gray-200 p-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Shield className="h-5 w-5 text-gray-500 mr-2" />
|
||||||
|
<h2 className="text-lg font-medium text-gray-800">Privacy</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex justify-between items-center pb-4 border-b border-gray-100">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<Search className="h-5 w-5 text-gray-500 mr-2 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-gray-800">Search History</h3>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Delete all your search history on StudentMarket
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteHistory("search")}
|
||||||
|
className="bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium py-2 px-4 flex items-center"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-1" />
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<History className="h-5 w-5 text-gray-500 mr-2 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-gray-800">
|
||||||
|
Browsing History
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Delete all your browsing history on StudentMarket
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteHistory("browsing")}
|
||||||
|
className="bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium py-2 px-4 flex items-center"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-1" />
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Delete Account (Danger Zone) */}
|
{/* Delete Account (Danger Zone) */}
|
||||||
<div className="bg-white border border-red-200 mb-6">
|
<div className="bg-white border border-red-200 mb-6">
|
||||||
<div className="border-b border-red-200 p-4 bg-red-50">
|
<div className="border-b border-red-200 p-4 bg-red-50">
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# pip install mysql.connector
|
# pip install mysql.connector
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
from sklearn.metrics.pairwise import cosine_similarity
|
from sklearn.metrics.pairwise import cosine_similarity
|
||||||
@@ -15,6 +17,28 @@ def database():
|
|||||||
)
|
)
|
||||||
return db_connection
|
return db_connection
|
||||||
|
|
||||||
|
def get_popular_products():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def delete_user_recommendation(userID, Array):
|
||||||
|
db_con = database()
|
||||||
|
cursor = db_con.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
for item in Array:
|
||||||
|
#Product ID starts form index 1
|
||||||
|
item_value = item + 1
|
||||||
|
print(item_value)
|
||||||
|
# Use parameterized queries to prevent SQL injection
|
||||||
|
cursor.execute(f"INTO Recommendation (UserID, RecommendedProductID) VALUES ({userID}, {item_value});")
|
||||||
|
|
||||||
|
db_con.commit()
|
||||||
|
|
||||||
|
#results = cursor.fetchall()
|
||||||
|
#print(results)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_all_products():
|
def get_all_products():
|
||||||
|
|
||||||
@@ -89,9 +113,9 @@ def get_recommendations(user_id, top_n=10):
|
|||||||
# Get all products and user history with their category vectors
|
# Get all products and user history with their category vectors
|
||||||
all_products = get_all_products()
|
all_products = get_all_products()
|
||||||
user_history = get_user_history(user_id)
|
user_history = get_user_history(user_id)
|
||||||
# if not user_history:
|
if not user_history:
|
||||||
# # Cold start: return popular products
|
#Cold start: return popular products
|
||||||
# return get_popular_products(top_n)
|
return get_popular_products(top_n)
|
||||||
# Calculate similarity between all products and user history
|
# Calculate similarity between all products and user history
|
||||||
user_profile = np.mean(user_history, axis=0) # Average user preferences
|
user_profile = np.mean(user_history, axis=0) # Average user preferences
|
||||||
similarities = cosine_similarity([user_profile], all_products)
|
similarities = cosine_similarity([user_profile], all_products)
|
||||||
@@ -128,8 +152,8 @@ def history_upload(userID, anrr):
|
|||||||
db_con.commit()
|
db_con.commit()
|
||||||
|
|
||||||
# If you need results, you'd typically fetch them after a SELECT query
|
# If you need results, you'd typically fetch them after a SELECT query
|
||||||
# results = cursor.fetchall()
|
#results = cursor.fetchall()
|
||||||
# print(results)
|
#print(results)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Flask, request, jsonify
|
from flask import Flask, request, jsonify
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from app import get_recommendations
|
from app import get_recommendations
|
||||||
from app import history_upload
|
|
||||||
|
|
||||||
import time
|
#import time
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|||||||
Reference in New Issue
Block a user