diff --git a/backend/index.js b/backend/index.js
index bf23680..17ee0be 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -2,6 +2,7 @@ import express, { json } from "express";
import cors from "cors";
import mysql from "mysql2";
import nodemailer from "nodemailer";
+
import crypto from "crypto";
import jwt from "jsonwebtoken";
@@ -33,7 +34,7 @@ const transporter = nodemailer.createTransport({
port: 465,
auth: {
user: "campusplug@zohomailcloud.ca", //Zoho email
- pass: "NzaZ7FFKNh18", //Zoho password
+ pass: "e0YRrNSeJZQd", //Zoho password
},
});
@@ -74,7 +75,8 @@ app.post("/send-verification", async (req, res) => {
if (results.length > 0) {
// Update existing record
db_con.query(
- "UPDATE AuthVerification SET VerificationCode = ?, Authenticated = FALSE, Date = CURRENT_TIMESTAMP WHERE Email = ?",
+ `UPDATE AuthVerification SET VerificationCode = ?, Authenticated = FALSE, Date = CURRENT_TIMESTAMP
+ WHERE Email = ?`,
[verificationCode, email],
async (err) => {
if (err) {
@@ -138,7 +140,7 @@ app.post("/verify-code", (req, res) => {
// Check verification code
db_con.query(
- "SELECT * FROM AuthVerification WHERE Email = ? AND VerificationCode = ? AND Authenticated = FALSE AND Date > DATE_SUB(NOW(), INTERVAL 15 MINUTE)",
+ "SELECT * FROM AuthVerification WHERE Email = ? AND VerificationCode = ? AND Authenticated = 0 AND Date > DATE_SUB(NOW(), INTERVAL 15 MINUTE)",
[email, code],
(err, results) => {
if (err) {
@@ -179,11 +181,11 @@ app.post("/verify-code", (req, res) => {
});
// Create a users and Complete user registration after verification
-app.post("/complete-registration", (req, res) => {
+app.post("/complete-signup", (req, res) => {
const data = req.body;
db_con.query(
- `SELECT * FROM AuthVerification WHERE Email = ${data.email} AND Authenticated = 1`,
+ `SELECT * FROM AuthVerification WHERE Email = '${data.email}' AND Authenticated = 1;`,
(err, results) => {
if (err) {
console.error("Database error:", err);
@@ -196,7 +198,7 @@ app.post("/complete-registration", (req, res) => {
// Create the user
db_con.query(
`INSERT INTO User (Name, Email, UCID, Password, Phone, Address)
- VALUES (${data.name}, ${data.email}, ${data.UCID}, ${data.password}, ${data.phone}, ${data.address})`,
+ VALUES ('${data.name}', '${data.email}', '${data.UCID}', '${data.password}', '${data.phone}', '${data.address}')`,
(err, result) => {
if (err) {
console.error("Error creating user:", err);
@@ -215,7 +217,7 @@ app.post("/complete-registration", (req, res) => {
// Delete verification record
db_con.query(
- `DELETE FROM AuthVerification WHERE Email = ${data.email}`,
+ `DELETE FROM AuthVerification WHERE Email = '${data.email}'`,
(deleteErr) => {
if (deleteErr) {
console.error("Error deleting verification:", deleteErr);
@@ -223,7 +225,10 @@ app.post("/complete-registration", (req, res) => {
res.json({
success: true,
message: "User registration completed successfully",
- userId: result.insertId,
+ name: data.name,
+ email: data.email,
+ UCID: data.UCID,
+ phone: data.phone,
});
},
);
@@ -243,9 +248,7 @@ function cleanupExpiredCodes() {
if (err) {
console.error("Error cleaning up expired codes:", err);
} else {
- console.log(
- `Cleaned up ${result.affectedRows} expired verification codes`,
- );
+ console.log(`Cleaned up ${results} expired verification codes`);
}
},
);
@@ -253,7 +256,7 @@ function cleanupExpiredCodes() {
// Set up a scheduler to run cleanup every hour
setInterval(cleanupExpiredCodes, 60 * 60 * 1000);
-//TODO: Fetch all users data:
+//Fetch all users data:
app.get("/fetch_all_users", (req, res) => {
db_con.query("SELECT * FROM User;", (err, data) => {
if (err) {
@@ -264,7 +267,7 @@ app.get("/fetch_all_users", (req, res) => {
});
});
-//TODO: Fetch One user Data:
+//Fetch One user Data:
app.post("/find_user", (req, res) => {
const { email, password } = req.body;
@@ -310,7 +313,82 @@ app.post("/find_user", (req, res) => {
});
//TODO: Update A uses Data:
-//TODO: Delete A uses Data:
+app.post("/update", (req, res) => {
+ const { userId, ...updateData } = req.body;
+
+ if (!userId) {
+ return res.status(400).json({ error: "User ID is required" });
+ }
+
+ // Create query dynamically based on provided fields
+ const updateFields = [];
+ const values = [];
+
+ Object.entries(updateData).forEach(([key, value]) => {
+ // Only include fields that are actually in the User table
+ if (["Name", "Email", "Password", "Phone", "UCID"].includes(key)) {
+ updateFields.push(`${key} = ?`);
+ values.push(value);
+ }
+ });
+
+ if (updateFields.length === 0) {
+ return res.status(400).json({ error: "No valid fields to update" });
+ }
+
+ // Add userId to values array
+ values.push(userId);
+
+ const query = `UPDATE User SET ${updateFields.join(", ")} WHERE UserID = ?`;
+
+ db_con.query(query, values, (err, result) => {
+ if (err) {
+ console.error("Error updating user:", err);
+ return res.status(500).json({ error: "Could not update user" });
+ }
+
+ if (result.affectedRows === 0) {
+ return res.status(404).json({ error: "User not found" });
+ }
+
+ res.json({ success: true, message: "User updated successfully" });
+ });
+});
+
+//Delete A uses Data:
+app.post("/delete", (req, res) => {
+ const { userId } = req.body;
+
+ if (!userId) {
+ return res.status(400).json({ error: "User ID is required" });
+ }
+
+ // Delete from UserRole first (assuming foreign key constraint)
+ db_con.query("DELETE FROM UserRole WHERE UserID = ?", [userId], (err) => {
+ if (err) {
+ console.error("Error deleting user role:", err);
+ return res.status(500).json({ error: "Could not delete user role" });
+ }
+
+ // Then delete from User table
+ db_con.query(
+ "DELETE FROM User WHERE UserID = ?",
+ [userId],
+ (err, result) => {
+ if (err) {
+ console.error("Error deleting user:", err);
+ return res.status(500).json({ error: "Could not delete user" });
+ }
+
+ if (result.affectedRows === 0) {
+ return res.status(404).json({ error: "User not found" });
+ }
+
+ res.json({ success: true, message: "User deleted successfully" });
+ },
+ );
+ });
+});
app.listen(3030, () => {
console.log(`Running Backend on http://localhost:3030/`);
diff --git a/frontend/index.html b/frontend/index.html
index a9abed9..ce26f74 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1,13 +1,17 @@
-
-
-
-
- Campus Plug
-
-
-
-
-
+
+
+
+
+ Campus
+
+
+
+
+
diff --git a/frontend/public/20191227_012601_0000.png b/frontend/public/20191227_012601_0000.png
new file mode 100644
index 0000000..e4b0445
Binary files /dev/null and b/frontend/public/20191227_012601_0000.png differ
diff --git a/frontend/public/Ucalgary.png b/frontend/public/Ucalgary.png
new file mode 100644
index 0000000..f5d5fe9
Binary files /dev/null and b/frontend/public/Ucalgary.png differ
diff --git a/frontend/public/icon.png b/frontend/public/icon.png
deleted file mode 100644
index 31164be..0000000
Binary files a/frontend/public/icon.png and /dev/null differ
diff --git a/frontend/public/icon/apple-touch-icon.png b/frontend/public/icon/apple-touch-icon.png
new file mode 100644
index 0000000..b69ea23
Binary files /dev/null and b/frontend/public/icon/apple-touch-icon.png differ
diff --git a/frontend/public/icon/favicon.ico b/frontend/public/icon/favicon.ico
new file mode 100644
index 0000000..3c826fc
Binary files /dev/null and b/frontend/public/icon/favicon.ico differ
diff --git a/frontend/public/icon/icon-192-maskable.png b/frontend/public/icon/icon-192-maskable.png
new file mode 100644
index 0000000..5913259
Binary files /dev/null and b/frontend/public/icon/icon-192-maskable.png differ
diff --git a/frontend/public/icon/icon-192.png b/frontend/public/icon/icon-192.png
new file mode 100644
index 0000000..97f48c7
Binary files /dev/null and b/frontend/public/icon/icon-192.png differ
diff --git a/frontend/public/icon/icon-512-maskable.png b/frontend/public/icon/icon-512-maskable.png
new file mode 100644
index 0000000..4066f48
Binary files /dev/null and b/frontend/public/icon/icon-512-maskable.png differ
diff --git a/frontend/public/icon/icon-512.png b/frontend/public/icon/icon-512.png
new file mode 100644
index 0000000..c4bd39c
Binary files /dev/null and b/frontend/public/icon/icon-512.png differ
diff --git a/frontend/public/market.jpg b/frontend/public/market.jpg
new file mode 100644
index 0000000..9b7fe14
Binary files /dev/null and b/frontend/public/market.jpg differ
diff --git a/frontend/public/university-of-calgary-logo.png b/frontend/public/university-of-calgary-logo.png
new file mode 100644
index 0000000..875b098
Binary files /dev/null and b/frontend/public/university-of-calgary-logo.png differ
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index ede7618..e89937a 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -29,6 +29,11 @@ function App() {
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(false);
+ // New verification states
+ const [verificationStep, setVerificationStep] = useState("initial"); // 'initial', 'code-sent', 'verifying'
+ const [tempUserData, setTempUserData] = useState(null);
+ const [verificationCode, setVerificationCode] = useState("");
+
// Auto-hide image on smaller screens
useEffect(() => {
const handleResize = () => {
@@ -38,17 +43,162 @@ function App() {
setShowImage(true);
}
};
-
- // Initial check
handleResize();
- // Listen for window resize
window.addEventListener("resize", handleResize);
- // Cleanup
return () => window.removeEventListener("resize", handleResize);
}, []);
+ // Send verification code
+ const sendVerificationCode = async (userData) => {
+ try {
+ setIsLoading(true);
+ setError("");
+
+ console.log("Sending verification code to:", userData.email);
+
+ // Make API call to send verification code
+ const response = await fetch("http://localhost:3030/send-verification", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ email: userData.email,
+ // Add any other required fields
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to send verification code");
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ // Store temporary user data
+ setTempUserData(userData);
+ // Move to verification step
+ setVerificationStep("code-sent");
+ console.log("Verification code sent successfully");
+ return true;
+ } else {
+ setError(result.message || "Failed to send verification code");
+ return false;
+ }
+ } catch (err) {
+ console.error("Error sending verification code:", err);
+ setError("Failed to send verification code. Please try again.");
+ return false;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Verify code
+ const verifyCode = async (code) => {
+ try {
+ setIsLoading(true);
+ setError("");
+
+ console.log("Verifying code:", code);
+
+ // Make API call to verify code
+ const response = await fetch("http://localhost:3030/verify-code", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ email: tempUserData.email,
+ code: code,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to verify code");
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ console.log("Code verified successfully");
+ // Proceed to complete signup
+ return await completeSignUp(tempUserData);
+ } else {
+ setError(result.message || "Invalid verification code");
+ return false;
+ }
+ } catch (err) {
+ console.error("Error verifying code:", err);
+ setError("Failed to verify code. Please try again.");
+ return false;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Complete signup
+ const completeSignUp = async (userData) => {
+ try {
+ setIsLoading(true);
+ setError("");
+
+ console.log("Completing signup for:", userData.email);
+ console.log(userData);
+
+ // Make API call to complete signup
+ const response = await fetch("http://localhost:3030/complete-signup", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(userData),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to complete signup");
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ // Create user object from API response
+ const newUser = {
+ name: result.name || userData.name,
+ email: result.email || userData.email,
+ UCID: result.UCID || userData.ucid,
+ phone: result.phone || userData.phone,
+ };
+
+ // Set authenticated user
+ setUser(newUser);
+ setIsAuthenticated(true);
+
+ // Save to localStorage to persist across refreshes
+ localStorage.setItem("isAuthenticated", "true");
+ localStorage.setItem("user", JSON.stringify(newUser));
+
+ // Reset verification steps
+ setVerificationStep("initial");
+ setTempUserData(null);
+
+ console.log("Signup completed successfully");
+ return true;
+ } else {
+ setError(result.message || "Failed to complete signup");
+ return false;
+ }
+ } catch (err) {
+ console.error("Error completing signup:", err);
+ setError("Failed to complete signup. Please try again.");
+ return false;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
@@ -63,31 +213,30 @@ function App() {
setError("Email and password are required");
setIsLoading(false);
return;
+ } else if (!formValues.email.endsWith("@ucalgary.ca")) {
+ setError("Please use your UCalgary email address (@ucalgary.ca)");
+ setIsLoading(false);
+ return;
}
-
try {
if (isSignUp) {
- // Handle Sign Up
+ // Handle Sign Up with verification
console.log("Sign Up Form Data:", formValues);
- // You could add API endpoint for registration here
- // For now, we'll just simulate a successful registration
+ // Create a user object with the form data
const newUser = {
name: formValues.name,
email: formValues.email,
- ucid: formValues.ucid,
+ UCID: formValues.ucid,
phone: formValues.phone,
+ password: formValues.password, // This will be needed for the final signup
+ address: "NOT_GIVEN",
+ client: 1,
+ admin: 0,
};
- // Set authenticated user
- setUser(newUser);
- setIsAuthenticated(true);
-
- // Save to localStorage to persist across refreshes
- localStorage.setItem("isAuthenticated", "true");
- localStorage.setItem("user", JSON.stringify(newUser));
-
- console.log("New user registered:", newUser);
+ // Send verification code
+ await sendVerificationCode(newUser);
} else {
// Handle Login with API
console.log("Login Attempt:", {
@@ -144,10 +293,25 @@ function App() {
}
};
+ // Handle code verification submission
+ const handleVerifySubmit = async (e) => {
+ e.preventDefault();
+ const code = e.target.verificationCode.value;
+
+ if (!code) {
+ setError("Verification code is required");
+ return;
+ }
+
+ await verifyCode(code);
+ };
+
const handleLogout = () => {
// Clear authentication state
setIsAuthenticated(false);
setUser(null);
+ setVerificationStep("initial");
+ setTempUserData(null);
// Clear localStorage
localStorage.removeItem("isAuthenticated");
@@ -159,6 +323,20 @@ function App() {
const toggleAuthMode = () => {
setIsSignUp(!isSignUp);
setError(""); // Clear errors when switching modes
+ setVerificationStep("initial"); // Reset verification step
+ };
+
+ // Resend verification code
+ const handleResendCode = async () => {
+ if (tempUserData) {
+ await sendVerificationCode(tempUserData);
+ }
+ };
+
+ // Back to signup form
+ const handleBackToSignup = () => {
+ setVerificationStep("initial");
+ setError("");
};
// Login component
@@ -168,9 +346,9 @@ function App() {
{showImage && (
@@ -185,10 +363,18 @@ function App() {
- {isSignUp ? "Create Account" : "Welcome Back"}
+ {isSignUp
+ ? verificationStep === "initial"
+ ? "Create Account"
+ : "Verify Your Email"
+ : "Welcome Back"}
- {isSignUp ? "Set up your new account" : "Sign in to your account"}
+ {isSignUp
+ ? verificationStep === "initial"
+ ? "Set up your new account"
+ : "Enter the verification code sent to your email"
+ : "Sign in to your account"}
@@ -199,132 +385,191 @@ function App() {
)}
-
+ )}
+
+ {/* Verification Code Form */}
+ {isSignUp && verificationStep === "code-sent" && (
+
+ )}
-
-
- Password
-
-
+ {/* Auth mode toggle */}
+ {verificationStep === "initial" && (
+
+
+ {isSignUp
+ ? "Already have an account?"
+ : "Don't have an account?"}{" "}
+
+ {isSignUp ? "Sign in" : "Sign up"}
+
+
-
-
-
- {isLoading
- ? "Please wait..."
- : isSignUp
- ? "Create Account"
- : "Sign In"}
-
-
-
-
-
-
- {isSignUp
- ? "Already have an account?"
- : "Don't have an account?"}{" "}
-
- {isSignUp ? "Sign in" : "Sign up"}
-
-
-
+ )}
diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg
deleted file mode 100644
index 6c87de9..0000000
--- a/frontend/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx
index 48832f4..036a137 100644
--- a/frontend/src/components/Navbar.jsx
+++ b/frontend/src/components/Navbar.jsx
@@ -1,18 +1,18 @@
-import { useState } from 'react';
-import { Link } from 'react-router-dom';
-import UserDropdown from './UserDropdown';
-import { Search, Heart } from 'lucide-react';
+import { useState } from "react";
+import { Link } from "react-router-dom";
+import UserDropdown from "./UserDropdown";
+import { Search, Heart } from "lucide-react";
const Navbar = ({ onLogout, userName }) => {
- const [searchQuery, setSearchQuery] = useState('');
-
+ const [searchQuery, setSearchQuery] = useState("");
+
const handleSearchChange = (e) => {
setSearchQuery(e.target.value);
};
-
+
const handleSearchSubmit = (e) => {
e.preventDefault();
- console.log('Searching for:', searchQuery);
+ console.log("Searching for:", searchQuery);
// TODO: Implement search functionality
};
@@ -23,10 +23,17 @@ const Navbar = ({ onLogout, userName }) => {
{/* Logo */}
-
Campus Plug
+
+
+ Campus Plug
+
-
+
{/* Search Bar */}
-
+
{/* User Navigation */}
{/* Favorites Button */}
-
+
{/* User Profile */}
@@ -60,4 +70,4 @@ const Navbar = ({ onLogout, userName }) => {
);
};
-export default Navbar;
\ No newline at end of file
+export default Navbar;
diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx
index 75f6c80..d98a998 100644
--- a/frontend/src/pages/Home.jsx
+++ b/frontend/src/pages/Home.jsx
@@ -1,74 +1,86 @@
-import { useState } from 'react';
-import { Link, useNavigate } from 'react-router-dom';
-import { Tag, Book, Laptop, Sofa, Utensils, Gift, Heart } from 'lucide-react';
+import { useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { Tag, Book, Laptop, Sofa, Utensils, Gift, Heart } from "lucide-react";
const Home = () => {
const navigate = useNavigate();
// Same categories
const categories = [
- { id: 1, name: 'Textbooks', icon:
},
- { id: 2, name: 'Electronics', icon:
},
- { id: 3, name: 'Furniture', icon:
},
- { id: 4, name: 'Kitchen', icon:
},
- { id: 5, name: 'Other', icon:
},
+ { id: 1, name: "Textbooks", icon:
},
+ { id: 2, name: "Electronics", icon:
},
+ { id: 3, name: "Furniture", icon:
},
+ { id: 4, name: "Kitchen", icon:
},
+ { id: 5, name: "Other", icon:
},
];
-
+
// Same listings data
const [listings, setListings] = useState([
{
id: 0,
- title: 'Dell XPS 16 Laptop',
+ title: "Dell XPS 16 Laptop",
price: 850,
- category: 'Electronics',
- image: 'image1.avif',
- condition: 'Good',
- seller: 'Michael T.',
- datePosted: '5d ago',
+ category: "Electronics",
+ image: "image1.avif",
+ condition: "Good",
+ seller: "Michael T.",
+ datePosted: "5d ago",
isFavorite: true,
},
]);
-
+
// Toggle favorite status
const toggleFavorite = (id, e) => {
e.preventDefault(); // Prevent navigation when clicking the heart icon
setListings(
listings.map((listing) =>
- listing.id === id ? { ...listing, isFavorite: !listing.isFavorite } : listing
- )
+ listing.id === id
+ ? { ...listing, isFavorite: !listing.isFavorite }
+ : listing,
+ ),
);
};
const handleSelling = () => {
- navigate('/selling');
+ navigate("/selling");
+ };
- }
-
return (
- {/* Hero Section */}
-
-
-
+ {/* Hero Section with School Background */}
+
+ {/* Background Image - Positioned at bottom */}
+
+
+ {/* Dark overlay for better text readability */}
+
+
+ {/* Content */}
+
+
Buy and Sell on Campus
-
- The marketplace exclusively for university students. Find everything you need or sell what you don't.
+
+ The marketplace exclusively for university students. Find everything
+ you need or sell what you don't.
-
-
+
Post an Item
-
-
+
{/* Categories */}
-
+ {/*
Categories
{categories.map((category) => (
-
{
{category.icon}
- {category.name}
+
+ {category.name}
+
))}
-
-
+
*/}
+
{/* Recent Listings */}
-
Recent Listings
-
+
+ Recent Listings
+
+
{listings.map((listing) => (
{
className="bg-white border border-gray-200 hover:shadow-md transition-shadow"
>
-
+
toggleFavorite(listing.id, e)}
className="absolute top-2 right-2 p-1 bg-white rounded-full shadow-sm"
>
-
+
{listing.title}
- ${listing.price}
+
+ ${listing.price}
+
-
+
{listing.category}
•
{listing.condition}
-
+
- {listing.datePosted}
- {listing.seller}
+
+ {listing.datePosted}
+
+
+ {listing.seller}
+
@@ -134,4 +162,4 @@ const Home = () => {
);
};
-export default Home;
\ No newline at end of file
+export default Home;