Backend
from flask import Flask, jsonify, request
import os
from flask_cors import CORS
import mysql.connector
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
CORS(app)
conn = mysql.connector.connect(
host = os.getenv("DB_HOST", "localhost"),
port = int(os.getenv("DB_PORT", 3306)),
user = os.getenv("DB_USER"),
password = os.getenv("DB_PASSWORD"),
database = os.getenv("DB_NAME", "task_db")
)
cursor = conn.cursor(dictionary = True)
# ---------- REGISTER ----------
@app.post("/api/register")
def register():
try:
data = request.json or {}
firstName = data.get("firstName", "").strip()
lastName = data.get("lastName", "").strip()
email = data.get("email", "").strip()
password = data.get("password", "").strip()
confirmPassword = data.get("confirmPassword", "").strip()
if not firstName:
return jsonify({"success": False, "message": "First name is required."}), 400
if not lastName:
return jsonify({"success": False, "message": "Last name is required."}), 400
if not email:
return jsonify({"success": False, "message": "Email is required."}), 400
if not password:
return jsonify({"success": False, "message": "Password is required."}), 400
if password != confirmPassword:
return jsonify({"success": False, "message": "Passwords do not match."}), 400
# Check duplicate email
cursor.execute("SELECT id FROM user WHERE email = %s", (email,))
if cursor.fetchone():
return jsonify({"success": False, "message": "Email already registered."}), 400
# Insert new user
cursor.execute("""
INSERT INTO user (role_id, firstName, lastName, email, password_hash)
VALUES (%s, %s, %s, %s, %s)
""", (2, firstName, lastName, email, password))
conn.commit()
# Get new user ID
cursor.execute("SELECT LAST_INSERT_ID() AS id")
new_user = cursor.fetchone()
return jsonify({
"success": True,
"message": "User registered successfully!",
"user": { "id": new_user["id"] }
}), 200
except Exception as e:
print("REGISTER ERROR:", e)
return jsonify({"success": False, "message": "Server error"}), 500
# ---------- LOGIN ----------
@app.post("/api/login")
def login():
try:
data = request.json or {}
email = data.get("email", "").strip()
password = data.get("password", "").strip()
if not email:
return jsonify({"success": False, "message": "Email is required."}), 400
if not password:
return jsonify({"success": False, "message": "Password is required."}), 400
cursor.execute("SELECT * FROM user WHERE email = %s", (email,))
user = cursor.fetchone()
if not user:
return jsonify({"success": False, "message": "Email not found."}), 400
if user["password_hash"] != password:
return jsonify({"success": False, "message": "Incorrect password."}), 400
return jsonify({
"success": True,
"message": "Login successful.",
"user": {
"id": user["id"],
"firstName": user["firstName"],
"lastName": user["lastName"],
"email": user["email"],
"phone": user.get("phone"),
"userPoints": 0 # You don't have this column yet
}
}), 200
except Exception as e:
print("LOGIN ERROR:", e)
return jsonify({"success": False, "message": "Server error"}), 500
# ---------- GET PROFILE ----------
@app.get("/api/user/profile")
def get_profile():
try:
user_id = request.args.get("userId")
if not user_id:
return jsonify({"success": False, "message": "Missing userId"}), 400
cursor.execute("""
SELECT id, firstName, lastName, email, phone
FROM user
WHERE id = %s
""", (user_id,))
user = cursor.fetchone()
if not user:
return jsonify({"success": False, "message": "User not found"}), 404
# Add userPoints manually (not in DB)
user["userPoints"] = 0
return jsonify({"success": True, "user": user}), 200
except Exception as e:
print("PROFILE ERROR:", e)
return jsonify({"success": False, "message": "Server error"}), 500
# ---------- UPDATE PROFILE ----------
@app.post("/api/user/update")
def update_profile():
try:
data = request.json or {}
user_id = data.get("userId")
if not user_id:
return jsonify({"success": False, "message": "Missing userId"}), 400
firstName = data.get("firstName", "").strip()
lastName = data.get("lastName", "").strip()
email = data.get("email", "").strip()
phone = data.get("phone", "").strip()
cursor.execute("""
UPDATE user
SET firstName=%s, lastName=%s, email=%s, phone=%s
WHERE id=%s
""", (firstName, lastName, email, phone, user_id))
conn.commit()
return jsonify({"success": True, "message": "Profile updated"}), 200
except Exception as e:
print("UPDATE ERROR:", e)
return jsonify({"success": False, "message": "Server error"}), 500
if __name__ == "__main__":
app.run(debug=True)
login
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import "./login.css";
import Chicken from "../../assets/chicken.png";
export default function Login() {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleLogin = async () => {
try {
const res = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
const data = await res.json();
if (!data.success) {
setError(data.message);
return;
}
// ⭐ Save userId
localStorage.setItem("userId", data.user.id);
// ⭐ Redirect
navigate("/profile");
} catch (err) {
setError("Something went wrong.");
}
};
return (
<div className="body">
<div className="loginContainer">
<div className="mainLoginContainer">
<h1 className="loginHeader">Login</h1>
<div className="loginTop">
<input
className="email"
placeholder="Email"
value={email}
type="email"
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="password"
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{error && <div className="errorPopup">{error}</div>}
<div className="loginBottom">
<button className="loginSubmit" onClick={handleLogin}>
Login
</button>
</div>
</div>
</div>
<div className="imgContainer">
<img src={Chicken} alt="chicken" className="chickenImg" />
</div>
</div>
);
}
profile
import { useState, useEffect } from "react";
import { products } from "../../data/products.js";
import "../profile/profile.css";
export default function Profile() {
const userId = localStorage.getItem("userId");
// 1️⃣ All hooks at the top (safe)
const [user, setUser] = useState(null);
const [orders, setOrders] = useState([]);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [phone, setPhone] = useState("");
// 2️⃣ Fetch user
useEffect(() => {
if (!userId) return;
fetch(`/api/user/profile?userId=${userId}`)
.then(res => res.json())
.then(data => {
if (data.success) {
setUser(data.user);
}
});
setOrders([
{ id: 101, status: "delivered", total_price: 25.50, created_at: "2026-03-19" },
{ id: 102, status: "processing", total_price: 14.99, created_at: "2026-03-20" }
]);
}, [userId]);
// 3️⃣ Sync form fields AFTER user loads
useEffect(() => {
if (user) {
setFirstName(user.firstName);
setLastName(user.lastName);
setEmail(user.email);
setPhone(user.phone || "");
}
}, [user]);
// 4️⃣ Safe loading state (AFTER all hooks)
if (!user) return <p>Loading...</p>;
// 5️⃣ Update profile
const handleUpdateProfile = (e) => {
e.preventDefault();
fetch("/api/user/update", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId,
firstName,
lastName,
email,
phone
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
alert("Profile updated!");
}
});
};
return (
<div className="profilePage">
<div className="topSection">
<div className="welcomeBox">
<h2>Welcome {user.firstName}!</h2>
<p className="subtitle">Recommended products for you</p>
<div className="carouselWrapper">
{products.map((p) => (
<div key={p.id} className="carouselItem">
<img src={p.image} alt={p.name} />
<h4>{p.name}</h4>
<p>£{p.price}</p>
<button>Add to basket</button>
</div>
))}
</div>
</div>
<div className="loyaltyBox">
<h3>Loyalty Card</h3>
<p className="points">{user.userPoints} Points</p>
<p className="info">Points are added when you shop online</p>
<p className="nextReward">Next Reward 1000 Points: 50% discount</p>
<div className="progressWrapper">
<div className="progressBar">
<div
className="progressFill"
style={{ width: `${(user.userPoints / 1000) * 100}%` }}
></div>
</div>
<p className="progressText">
{user.userPoints} / 1000 points
</p>
</div>
<button className="continueBtn">Continue shopping</button>
</div>
</div>
<div className="banner">
<h2>Shop with our best deals</h2>
</div>
<div className="recentOrdersSection">
<h3>Your Recent Orders</h3>
{orders.length === 0 ? (
<p>You have no recent orders.</p>
) : (
<div className="ordersList">
{orders.map((o) => (
<div key={o.id} className="orderCard">
<p><strong>Order #{o.id}</strong></p>
<p>Status: {o.status}</p>
<p>Total: £{o.total_price}</p>
<p>Date: {o.created_at}</p>
</div>
))}
</div>
)}
</div>
<div className="editProfileSection">
<h3>Edit Profile</h3>
<form className="editForm" onSubmit={handleUpdateProfile}>
<input
type="text"
placeholder="First Name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<input
type="text"
placeholder="Last Name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="text"
placeholder="Phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
<button type="submit">Save Changes</button>
</form>
</div>
</div>
);
}
sign up
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import "./signUp.css";
import Bag from "../../assets/bag.png";
export default function SignUp() {
const navigate = useNavigate();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const handleRegister = async (e) => {
e.preventDefault(); // ⭐ IMPORTANT FIX
try {
const res = await fetch("/api/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
firstName,
lastName,
email,
password,
confirmPassword
}),
});
const data = await res.json();
if (!data.success) {
setError(data.message);
return;
}
// Save user ID
localStorage.setItem("userId", data.user.id);
navigate("/profile");
} catch (err) {
setError("Network error. Please try again.");
}
};
return (
<div className="body">
<div className="signUpContainer">
<div className="mainSUContainer">
<h1 className="signUpHeader">Sign Up</h1>
<form className="signUpTop" onSubmit={handleRegister}>
<input
className="firstName"
placeholder="First Name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<input
className="lastName"
placeholder="Last Name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
<input
className="email"
placeholder="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="password"
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<input
className="confirmPassword"
placeholder="Confirm Password"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
{error && <div className="errorPopup">{error}</div>}
<div className="signUpBottom">
<button className="signUpSubmit" type="submit">
Sign Up
</button>
</div>
</form>
</div>
</div>
<div className="imgContainer">
<img src={Bag} alt="bag" className="bagImg" />
</div>
</div>
);
}
Top comments (0)