diff --git a/backend/controllers/transaction.js b/backend/controllers/transaction.js index ace31bb..1c0fe58 100644 --- a/backend/controllers/transaction.js +++ b/backend/controllers/transaction.js @@ -6,16 +6,16 @@ exports.getTransactionWithPagination = async (req, res) => { const offset = (page - 1) * limit; try { const [data, _] = await db.execute( - `SELECT T.TransactionID, DATE_FORMAT(T.Date, '%b-%d-%Y %h:%i %p') as Date, T.PaymentStatus, U.Name as UserName, P.Name as ProductName + `SELECT T.TransactionID, DATE_FORMAT(T.Date, '%b-%d-%Y %h:%i %p') as Date, T.PaymentStatus, U.Name as UserName, P.Name as ProductName FROM Transaction T LEFT JOIN User U ON T.UserID = U.UserID LEFT JOIN Product P ON T.ProductID = P.ProductID ORDER BY T.TransactionID ASC LIMIT ? OFFSET ?`, - [limit.toString(), offset.toString()] + [limit.toString(), offset.toString()], ); const [result] = await db.execute( - "SELECT COUNT(*) AS count FROM Transaction" + "SELECT COUNT(*) AS count FROM Transaction", ); const { count: total } = result[0]; return res.json({ data, total }); @@ -29,7 +29,7 @@ exports.removeTransation = async (req, res) => { try { const [result] = await db.execute( "DELETE FROM Transaction WHERE TransactionID = ?;", - [id.toString()] + [id.toString()], ); return res.json({ message: "Remove transaction successfully!" }); } catch (error) { @@ -38,3 +38,188 @@ exports.removeTransation = async (req, res) => { .json({ error: "Cannot remove transactions from database!" }); } }; +// Create a new transaction +exports.createTransaction = async (req, res) => { + const { userID, productID, date, paymentStatus } = req.body; + + try { + // Check if the transaction already exists for the same user and product + const [existingTransaction] = await db.execute( + `SELECT TransactionID FROM Transaction WHERE UserID = ? AND ProductID = ?`, + [userID, productID], + ); + + if (existingTransaction.length > 0) { + return res.status(400).json({ + success: false, + message: "Transaction already exists for this user and product", + }); + } + + // Format the date + const formattedDate = new Date(date) + .toISOString() + .slice(0, 19) + .replace("T", " "); + + // Insert the new transaction + const [result] = await db.execute( + `INSERT INTO Transaction (UserID, ProductID, Date, PaymentStatus) + VALUES (?, ?, ?, ?)`, + [userID, productID, formattedDate, paymentStatus], + ); + + res.json({ + success: true, + message: "Transaction created successfully", + transactionID: result.insertId, + }); + } catch (error) { + console.error("Error creating transaction:", error); + res.status(500).json({ error: "Could not create transaction" }); + } +}; + +// Get all transactions for a given product +exports.getTransactionsByProduct = async (req, res) => { + const { productID } = req.params; + + try { + const [transactions] = await db.execute( + `SELECT + T.TransactionID, + T.UserID, + T.ProductID, + T.Date, + T.PaymentStatus, + P.Name AS ProductName, + MIN(I.URL) AS Image_URL + FROM Transaction T + JOIN Product P ON T.ProductID = P.ProductID + LEFT JOIN Image_URL I ON P.ProductID = I.ProductID + GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`, + ); + + res.json({ + success: true, + transactions, + }); + } catch (error) { + console.error("Error fetching transactions by product:", error); + res.status(500).json({ error: "Could not retrieve transactions" }); + } +}; + +// Get all transactions for a given user +exports.getTransactionsByUser = async (req, res) => { + const { userID } = req.body; + + try { + const [transactions] = await db.execute( + `SELECT + T.TransactionID, + T.UserID, + T.ProductID, + T.Date, + T.PaymentStatus, + P.Name AS ProductName, + I.URL AS Image_URL + FROM Transaction T + JOIN Product P ON T.ProductID = P.ProductID + LEFT JOIN Image_URL I ON P.ProductID = I.ProductID + WHERE T.UserID = ?`, + [userID], + ); + + res.json({ + success: true, + transactions, + }); + } catch (error) { + console.error("Error fetching transactions by user:", error); + res.status(500).json({ error: "Could not retrieve transactions" }); + } +}; + +// Get all transactions in the system +exports.getAllTransactions = async (req, res) => { + try { + const [transactions] = await db.execute( + `SELECT + T.TransactionID, + T.UserID, + T.ProductID, + T.Date, + T.PaymentStatus, + P.Name AS ProductName, + MIN(I.URL) AS Image_URL + FROM Transaction T + JOIN Product P ON T.ProductID = P.ProductID + LEFT JOIN Image_URL I ON P.ProductID = I.ProductID + GROUP BY T.TransactionID, T.UserID, T.ProductID, T.Date, T.PaymentStatus, P.Name`, + ); + + res.json({ + success: true, + transactions, + }); + } catch (error) { + console.error("Error fetching all transactions:", error); + res.status(500).json({ error: "Could not retrieve transactions" }); + } +}; + +// Update the payment status of a transaction +exports.updatePaymentStatus = async (req, res) => { + const { transactionID, paymentStatus } = req.body; + + try { + const [result] = await db.execute( + `UPDATE Transaction + SET PaymentStatus = ? + WHERE TransactionID = ?`, + [paymentStatus, transactionID], + ); + + if (result.affectedRows === 0) { + return res + .status(404) + .json({ success: false, message: "Transaction not found" }); + } + + res.json({ + success: true, + message: "Payment status updated successfully", + }); + } catch (error) { + console.error("Error updating payment status:", error); + res.status(500).json({ error: "Could not update payment status" }); + } +}; + +// Delete a transaction +exports.deleteTransaction = async (req, res) => { + const { transactionID } = req.body; + + try { + const [result] = await db.execute( + `DELETE FROM Transaction + WHERE TransactionID = ?`, + [transactionID], + ); + + if (result.affectedRows === 0) { + return res + .status(404) + .json({ success: false, message: "Transaction not found" }); + } + + res.json({ + success: true, + message: "Transaction deleted successfully", + }); + } catch (error) { + console.error("Error deleting transaction:", error); + res.status(500).json({ error: "Could not delete transaction" }); + } +}; diff --git a/backend/index.js b/backend/index.js index b06f1fc..08d260d 100644 --- a/backend/index.js +++ b/backend/index.js @@ -9,7 +9,9 @@ const searchRouter = require("./routes/search"); const recommendedRouter = require("./routes/recommendation"); const history = require("./routes/history"); const review = require("./routes/review"); + const categoryRouter = require("./routes/category"); + const transactionRouter = require("./routes/transaction"); const { generateEmailTransporter } = require("./utils/mail"); @@ -44,9 +46,9 @@ app.use("/api/search", searchRouter); app.use("/api/engine", recommendedRouter); app.use("/api/history", history); app.use("/api/review", review); -app.use("/api/category", categoryRouter); app.use("/api/transaction", transactionRouter); app.use("/api/category", categoryRouter); +app.use("/api/transaction", transactionRouter); // Set up a scheduler to run cleanup every hour clean_up_time = 30 * 60 * 1000; diff --git a/backend/routes/transaction.js b/backend/routes/transaction.js index 0788b96..c29611e 100644 --- a/backend/routes/transaction.js +++ b/backend/routes/transaction.js @@ -1,11 +1,39 @@ +// routes/transaction.js const express = require("express"); const { - getTransactionWithPagination, - removeTransation, + createTransaction, + getTransactionsByProduct, + getTransactionsByUser, + getAllTransactions, + updatePaymentStatus, + deleteTransaction, } = require("../controllers/transaction"); - const router = express.Router(); +// logging middleware +router.use((req, res, next) => { + console.log(`Incoming ${req.method} ${req.originalUrl}`); + next(); +}); + +// Create a new transaction +router.post("/createTransaction", createTransaction); + +// Get all transactions for a specific product +router.get("/getTransactionsByProduct/:productID", getTransactionsByProduct); + +// Get all transactions for a specific user +router.post("/getTransactionsByUser", getTransactionsByUser); + +// Get all transactions in the system +router.post("/getAllTransactions", getAllTransactions); + +// Update payment status on a transaction +router.patch("/updatePaymentStatus", updatePaymentStatus); + +// Delete a transaction +router.delete("/deleteTransaction", deleteTransaction); + router.get("/getTransactions", getTransactionWithPagination); router.delete("/:id", removeTransation); diff --git a/backend/utils/database.js b/backend/utils/database.js index 379d5df..7e9a4c9 100644 --- a/backend/utils/database.js +++ b/backend/utils/database.js @@ -4,6 +4,7 @@ const pool = mysql.createPool({ host: "localhost", user: "root", database: "Marketplace", + password: "12345678" }); // const pool = mysql.createPool( diff --git a/backend/utils/helper.js b/backend/utils/helper.js index 406a897..1d39b48 100644 --- a/backend/utils/helper.js +++ b/backend/utils/helper.js @@ -18,7 +18,7 @@ async function sendVerificationEmail(email, verificationCode) { // Clean up expired verification codes (run this periodically) function cleanupExpiredCodes() { - db_con.query( + db.query( "DELETE FROM AuthVerification WHERE Date < DATE_SUB(NOW(), INTERVAL 15 MINUTE) AND Authenticated = 0", (err, result) => { if (err) { diff --git a/frontend/src/pages/ProductDetail.jsx b/frontend/src/pages/ProductDetail.jsx index ebec8f2..e5b1143 100644 --- a/frontend/src/pages/ProductDetail.jsx +++ b/frontend/src/pages/ProductDetail.jsx @@ -414,8 +414,45 @@ const ProductDetail = () => {
diff --git a/frontend/src/pages/Transactions.jsx b/frontend/src/pages/Transactions.jsx index c38b5fc..e4051de 100644 --- a/frontend/src/pages/Transactions.jsx +++ b/frontend/src/pages/Transactions.jsx @@ -1,8 +1,213 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { Link } from "react-router-dom"; +import { Calendar, CreditCard, Trash2 } from "lucide-react"; const Transactions = () => { - return
; + const [transactions, setTransactions] = useState([]); + + useEffect(() => { + const fetchTransactions = async () => { + try { + const response = await fetch( + "http://localhost:3030/api/transaction/getAllTransactions", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ userID: 1 }), // replace with actual userID + } + ); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const { transactions: txData } = await response.json(); + if (!Array.isArray(txData)) return; + + setTransactions( + txData.map((tx) => ({ + id: tx.TransactionID, + productId: tx.ProductID, + name: tx.ProductName || "Unnamed Product", + price: tx.Price != null ? parseFloat(tx.Price) : null, + image: tx.Image_URL || "/default-image.jpg", + date: tx.Date, + status: tx.PaymentStatus, + })) + ); + } catch (error) { + console.error("Failed to fetch transactions:", error); + } + }; + + fetchTransactions(); + }, []); + + const deleteTransaction = async (id) => { + try { + const res = await fetch( + "http://localhost:3030/api/transaction/deleteTransaction", + { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ transactionID: id }), + } + ); + const data = await res.json(); + if (data.success) { + setTransactions((prev) => prev.filter((tx) => tx.id !== id)); + } else { + console.error("Delete failed:", data.message); + } + } catch (err) { + console.error("Error deleting transaction:", err); + } + }; + + const formatDate = (dateString) => { + const d = new Date(dateString); + return d.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + }; + + return ( +
+
+

My Transactions

+
+ + {transactions.length === 0 ? ( +
+ +

+ No transactions found +

+

+ Once transactions are created, they’ll appear here. +

+ + Browse Listings + +
+ ) : ( + <> +
+ {transactions.map((tx) => ( +
+ {/* Delete Button */} + + + +
+ {tx.image ? ( + {tx.name} + ) : ( +
No image
+ )} +
+
+

+ {tx.name} +

+ {tx.price !== null && ( +

+ ${tx.price.toFixed(2)} +

+ )} +
+ + {formatDate(tx.date)} +
+

+ Status: {tx.status} +

+
+ +
+ ))} +
+ +
+ Showing {transactions.length}{" "} + {transactions.length === 1 ? "transaction" : "transactions"} +
+ + )} + + +
+ ); }; export default Transactions;