diff --git a/backend/controllers/transaction.js b/backend/controllers/transaction.js
new file mode 100644
index 0000000..ace31bb
--- /dev/null
+++ b/backend/controllers/transaction.js
@@ -0,0 +1,40 @@
+const db = require("../utils/database");
+
+exports.getTransactionWithPagination = async (req, res) => {
+ const limit = +req.query?.limit;
+ const page = +req.query?.page;
+ 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
+ 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()]
+ );
+
+ const [result] = await db.execute(
+ "SELECT COUNT(*) AS count FROM Transaction"
+ );
+ const { count: total } = result[0];
+ return res.json({ data, total });
+ } catch (error) {
+ res.json({ error: "Cannot fetch transactions from database!" });
+ }
+};
+
+exports.removeTransation = async (req, res) => {
+ const { id } = req.params;
+ try {
+ const [result] = await db.execute(
+ "DELETE FROM Transaction WHERE TransactionID = ?;",
+ [id.toString()]
+ );
+ return res.json({ message: "Remove transaction successfully!" });
+ } catch (error) {
+ return res
+ .status(500)
+ .json({ error: "Cannot remove transactions from database!" });
+ }
+};
diff --git a/backend/index.js b/backend/index.js
index 5ba3b27..796860f 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -10,6 +10,7 @@ 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");
const {
@@ -44,6 +45,7 @@ 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);
// 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
new file mode 100644
index 0000000..0788b96
--- /dev/null
+++ b/backend/routes/transaction.js
@@ -0,0 +1,12 @@
+const express = require("express");
+const {
+ getTransactionWithPagination,
+ removeTransation,
+} = require("../controllers/transaction");
+
+const router = express.Router();
+
+router.get("/getTransactions", getTransactionWithPagination);
+router.delete("/:id", removeTransation);
+
+module.exports = router;
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index eefdb63..ac28ad5 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -19,6 +19,7 @@ import ProductDashboard from "./pages/ProductDashboard";
import DashboardNav from "./components/DashboardNav";
import CategoryDashboard from "./pages/CategoryDashboard";
import { verifyIsAdmin } from "./api/admin";
+import TransactionDashboard from "./pages/TransactionDashboard";
function App() {
// Authentication state - initialize from localStorage if available
@@ -710,6 +711,10 @@ function App() {
} />
} />
} />
+ }
+ />
} />
diff --git a/frontend/src/api/admin.js b/frontend/src/api/admin.js
index f2728c9..bea2a15 100644
--- a/frontend/src/api/admin.js
+++ b/frontend/src/api/admin.js
@@ -40,6 +40,19 @@ export const getCategories = async (page, limit = 10) => {
}
};
+export const getTransactions = async (page, limit = 10) => {
+ try {
+ const { data } = await client.get(
+ `/transaction/getTransactions?limit=${limit}&page=${page}`
+ );
+ return { transactions: data.data, total: data.total };
+ } catch (error) {
+ const { response } = error;
+ if (response?.data) return response.data;
+ return { error: error.message || error };
+ }
+};
+
export const addCategory = async (name) => {
try {
const { data } = await client.post(`/category/addCategory`, { name: name });
@@ -94,3 +107,14 @@ export const verifyIsAdmin = async (id) => {
return { error: error.message || error };
}
};
+
+export const removeTransaction = async (id) => {
+ try {
+ const { data } = await client.delete(`/transaction/${id}`);
+ return { message: data.message };
+ } catch (error) {
+ const { response } = error;
+ if (response?.data) return response.data;
+ return { error: error.message || error };
+ }
+};
diff --git a/frontend/src/components/DashboardNav.jsx b/frontend/src/components/DashboardNav.jsx
index 75e287c..5f19de2 100644
--- a/frontend/src/components/DashboardNav.jsx
+++ b/frontend/src/components/DashboardNav.jsx
@@ -3,6 +3,7 @@ import { FaUserTag } from "react-icons/fa";
import { FaBoxArchive } from "react-icons/fa6";
import { MdOutlineCategory } from "react-icons/md";
import { FaArrowLeft } from "react-icons/fa";
+import { FaMoneyBillTransfer } from "react-icons/fa6";
export default function DashboardNav({ handleCloseAdminDashboard }) {
const handleClick = () => {
@@ -69,6 +70,20 @@ export default function DashboardNav({ handleCloseAdminDashboard }) {
Categories
+
+
+ (isActive
+ ? "text-green-400"
+ : "text-white transition-all hover:text-green-200") +
+ " flex items-center px-5 text-lg pt-5"
+ }
+ >
+
+ Transaction
+
+
{
removeCategory(id)
- .then((res) => {
+ .then(() => {
fetchCategory(currentPage);
})
.catch((err) => {
@@ -48,7 +48,7 @@ export default function CategoryDashboard() {
useEffect(fetchCategory, []);
return (
-
+
CATEGORIES
@@ -60,35 +60,46 @@ export default function CategoryDashboard() {
-
-
-
- | CategoryID |
- Name |
- Action |
-
-
-
- {categories.map((category) => (
-
- | {category.CategoryID} |
- {category.Name} |
-
- {
- handleRemove(category.CategoryID);
- }}
- className="hover:text-red-600 cursor-pointer transition-all text-xl"
- />
- |
-
- ))}
-
-
-
+ {categories.length > 0 ? (
+ <>
+
+
+
+ | CategoryID |
+ Name |
+ Action |
+
+
+
+ {categories.map((category) => (
+
+ | {category.CategoryID} |
+ {category.Name} |
+
+ {
+ handleRemove(category.CategoryID);
+ }}
+ className="hover:text-red-600 cursor-pointer transition-all text-xl"
+ />
+ |
+
+ ))}
+
+
+
+ >
+ ) : (
+
+ No category exists!
+
+ )}
);
}
diff --git a/frontend/src/pages/ProductDashboard.jsx b/frontend/src/pages/ProductDashboard.jsx
index c3ef4d2..35ce13e 100644
--- a/frontend/src/pages/ProductDashboard.jsx
+++ b/frontend/src/pages/ProductDashboard.jsx
@@ -40,44 +40,52 @@ export default function ProductDashboard() {
PRODUCTS
-
-
-
- | ProductID |
- Name |
- Price |
- Category |
- Seller |
- Action |
-
-
-
- {products.map((product) => (
-
- | {product.ProductID} |
- {product.ProductName} |
- {product.Price} |
- {product.Category ? product.Category : "N/A"} |
- {product.SellerName ? product.SellerName : "N/A"} |
-
- {
- handleRemoveProduct(product.ProductID);
- }}
- className="hover:text-red-600 cursor-pointer transition-all text-xl"
- />
- |
-
- ))}
-
-
-
+ {products.length > 0 ? (
+ <>
+
+
+
+ | ProductID |
+ Name |
+ Price |
+ Category |
+ Seller |
+ Action |
+
+
+
+ {products.map((product) => (
+
+ | {product.ProductID} |
+ {product.ProductName} |
+ {product.Price} |
+ {product.Category ? product.Category : "N/A"} |
+ {product.SellerName ? product.SellerName : "N/A"} |
+
+ {
+ handleRemoveProduct(product.ProductID);
+ }}
+ className="hover:text-red-600 cursor-pointer transition-all text-xl"
+ />
+ |
+
+ ))}
+
+
+
+ >
+ ) : (
+
+ No product exists!
+
+ )}
);
}
diff --git a/frontend/src/pages/TransactionDashboard.jsx b/frontend/src/pages/TransactionDashboard.jsx
new file mode 100644
index 0000000..167d5ee
--- /dev/null
+++ b/frontend/src/pages/TransactionDashboard.jsx
@@ -0,0 +1,91 @@
+import { useEffect, useState } from "react";
+import { getTransactions, removeTransaction } from "../api/admin";
+import { MdDelete } from "react-icons/md";
+import Pagination from "../components/Pagination";
+
+export default function TransactionDashboard() {
+ const [transactions, setTransactions] = useState([]);
+ const [total, setTotal] = useState(0);
+ const [currentPage, setCurrentPage] = useState(1);
+
+ let pageLimit = 10;
+
+ const onChangePage = (page, limit = 10) => {
+ setCurrentPage(page);
+ fetchTransactions(page, limit);
+ };
+
+ const fetchTransactions = (page = 1, limit = 10) => {
+ getTransactions(page, limit).then(({ transactions, total }) => {
+ setTotal(total);
+ setTransactions(transactions);
+ });
+ };
+
+ const handleRemoveTransaction = (id) => {
+ removeTransaction(id)
+ .then(() => {
+ fetchTransactions(currentPage);
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ };
+
+ //Get user when initialize the component
+ useEffect(fetchTransactions, []);
+
+ return (
+
+
+ TRANSACTIONS
+
+ {transactions.length > 0 ? (
+ <>
+
+
+
+ | TransactionID |
+ User |
+ Product |
+ Date |
+ Status |
+ Action |
+
+
+
+ {transactions.map((t) => (
+
+ | {t.TransactionID} |
+ {t.UserName ? t.UserName : "N/A"} |
+ {t.ProductName ? t.ProductName : "N/A"} |
+ {t.Date} |
+ {t.PaymentStatus} |
+
+ {
+ handleRemoveTransaction(t.TransactionID);
+ }}
+ className="hover:text-red-600 cursor-pointer transition-all text-xl"
+ />
+ |
+
+ ))}
+
+
+
+ >
+ ) : (
+
+ No transaction exists!
+
+ )}
+
+ );
+}