From 5228bf73c9c57e626675f6837d24c1ca9c8ce033 Mon Sep 17 00:00:00 2001
From: Mann Patel <130435633+MannPatel0@users.noreply.github.com>
Date: Mon, 21 Apr 2025 01:01:58 -0600
Subject: [PATCH] refactor for redundant code
---
backend/controllers/product.js | 4 +-
backend/routes/product.js | 3 +-
frontend/src/App.jsx | 251 +++++++-------
frontend/src/api/admin.js | 173 +++++-----
frontend/src/api/client.js | 3 -
frontend/src/components/DashboardNav.jsx | 99 +-----
frontend/src/pages/CategoryDashboard.jsx | 105 ------
frontend/src/pages/Dashboard.jsx | 361 +++++++++++++++++++-
frontend/src/pages/ProductDashboard.jsx | 91 -----
frontend/src/pages/TransactionDashboard.jsx | 91 -----
frontend/src/pages/UserDashboard.jsx | 91 -----
frontend/src/schema.sql | 124 +++++++
mysql-code/Init-Data.sql | 3 +-
mysql-code/Schema.sql | 19 +-
14 files changed, 726 insertions(+), 692 deletions(-)
delete mode 100644 frontend/src/api/client.js
delete mode 100644 frontend/src/pages/CategoryDashboard.jsx
delete mode 100644 frontend/src/pages/ProductDashboard.jsx
delete mode 100644 frontend/src/pages/TransactionDashboard.jsx
delete mode 100644 frontend/src/pages/UserDashboard.jsx
create mode 100644 frontend/src/schema.sql
diff --git a/backend/controllers/product.js b/backend/controllers/product.js
index 44185a5..31ca2ec 100644
--- a/backend/controllers/product.js
+++ b/backend/controllers/product.js
@@ -391,9 +391,9 @@ exports.getProductWithPagination = async (req, res) => {
}
};
-exports.removeProduct = async (req, res) => {
+exports.removeAnyProduct = async (req, res) => {
const { id } = req.params;
-
+ console.log(id);
try {
const [result] = await db.execute(
`DELETE FROM Product WHERE ProductID = ?`,
diff --git a/backend/routes/product.js b/backend/routes/product.js
index 09000cd..5862209 100644
--- a/backend/routes/product.js
+++ b/backend/routes/product.js
@@ -8,6 +8,7 @@ const {
getProductById,
addProduct,
removeProduct,
+ removeAnyProduct,
getProductWithPagination,
myProduct,
updateProduct,
@@ -30,7 +31,7 @@ router.post("/addProduct", addProduct);
router.get("/getProduct", getAllProducts);
//Remove product
-router.delete("/:id", removeProduct);
+router.delete("/any/:id", removeAnyProduct);
//Get products with pagination
router.get("/getProductWithPagination", getProductWithPagination);
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 38d0009..3cd6a94 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -4,6 +4,7 @@ import {
Routes,
Route,
Navigate,
+ useLocation,
} from "react-router-dom";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
@@ -12,14 +13,10 @@ import Selling from "./pages/Selling";
import Transactions from "./pages/Transactions";
import Favorites from "./pages/Favorites";
import ProductDetail from "./pages/ProductDetail";
-import SearchPage from "./pages/SearchPage"; // Make sure to import the SearchPage
-import Dashboard from "./pages/Dashboard";
-import UserDashboard from "./pages/UserDashboard";
-import ProductDashboard from "./pages/ProductDashboard";
+import SearchPage from "./pages/SearchPage";
+import Dashboard from "./pages/Dashboard"; // The single consolidated dashboard component
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
@@ -42,6 +39,18 @@ function App() {
useState(false);
const [recommendations, setRecommendations] = useState([]);
+ // Admin state
+ const [isAdmin, setIsAdmin] = useState(false);
+ const [showAdminDashboard, setShowAdminDashboard] = useState(false);
+
+ // Check URL to determine if we're in admin mode
+ useEffect(() => {
+ // If URL contains /admin, set showAdminDashboard to true
+ if (window.location.pathname.includes("/admin")) {
+ setShowAdminDashboard(true);
+ }
+ }, []);
+
// New verification states
const [verificationStep, setVerificationStep] = useState("initial"); // 'initial', 'code-sent', 'verifying'
const [tempUserData, setTempUserData] = useState(null);
@@ -127,9 +136,6 @@ function App() {
}
};
- const [isAdmin, setIsAdmin] = useState(false);
- const [showAdminDashboard, setShowAdminDashboard] = useState(false);
-
useEffect(() => {
const userInfo = sessionStorage.getItem("user")
? JSON.parse(sessionStorage.getItem("user"))
@@ -142,9 +148,14 @@ function App() {
const handleShowAdminDashboard = () => {
setShowAdminDashboard(true);
+ // Update URL without reloading page
+ window.history.pushState({}, "", "/admin");
};
+
const handleCloseAdminDashboard = () => {
setShowAdminDashboard(false);
+ // Update URL without reloading page
+ window.history.pushState({}, "", "/");
};
// Send verification code
@@ -436,6 +447,7 @@ function App() {
setVerificationStep("initial");
setTempUserData(null);
setRecommendations([]);
+ setShowAdminDashboard(false);
// Clear localStorage
sessionStorage.removeItem("user");
@@ -774,126 +786,119 @@ function App() {
return children;
};
- // If user is admin, show admin naviagtion
- if (showAdminDashboard) {
- return (
-
+ return (
+
+ {/* If admin dashboard should be shown */}
+ {showAdminDashboard ? (
- {/* Admin routes */}
- } />
- } />
- } />
- } />
- }
- />
- } />
+ {/* Single admin route for consolidated dashboard */}
+ } />
+ {/* Any other path in admin mode should go to dashboard */}
+ } />
-
- );
- }
+ ) : (
+ /* Normal user interface */
+
+ {/* Show loading overlay when generating recommendations */}
+ {isGeneratingRecommendations &&
}
- return (
-
-
- {/* Show loading overlay when generating recommendations */}
- {isGeneratingRecommendations &&
}
-
- {/* Only show navbar when authenticated */}
- {isAuthenticated && (
-
- )}
-
- {/* Public routes */}
- : }
- />
- {/* Protected routes */}
-
-
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
-
-
- }
- />
-
-
-
-
-
- }
- />
-
-
-
-
-
- }
- />
-
-
-
-
-
- }
- />
-
-
-
-
-
- }
- />
- {/* Redirect to login for any unmatched routes */}
- }
- />
-
-
+ {/* Only show navbar when authenticated */}
+ {isAuthenticated && (
+
+ )}
+
+ {/* Public routes */}
+ :
+ }
+ />
+ {/* Protected routes */}
+
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+ {/* Redirect to login for any unmatched routes */}
+ }
+ />
+
+
+ )}
);
}
diff --git a/frontend/src/api/admin.js b/frontend/src/api/admin.js
index bea2a15..7aaa648 100644
--- a/frontend/src/api/admin.js
+++ b/frontend/src/api/admin.js
@@ -1,77 +1,20 @@
-import client from "./client";
+// api.js
+import axios from "axios";
+
+const client = axios.create({
+ baseURL: "http://localhost:3030/api",
+});
+
+// Users
export const getUsers = async (page, limit = 10) => {
try {
const { data } = await client.get(
- `/user/getUserWithPagination?page=${page}&limit=${limit}`
+ `/user/getUserWithPagination?page=${page}&limit=${limit}`,
);
return { users: data.users, total: data.total };
} catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
- }
-};
-
-export const getProducts = async (page, limit = 10) => {
- try {
- const { data } = await client.get(
- `/product/getProductWithPagination?limit=${limit}&page=${page}`
- );
-
- return { products: data.products, total: data.totalProd };
- } catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
- }
-};
-
-export const getCategories = async (page, limit = 10) => {
- try {
- const { data } = await client.get(
- `/category/getCategories?page=${page}&limit=${limit}`
- );
- return { data: data.data, total: data.total };
- } catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
- }
-};
-
-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 });
- return { message: data.message };
- } catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
- }
-};
-
-export const removeCategory = async (id) => {
- try {
- const { data } = await client.delete(`/category/${id}`);
- return { message: data.message };
- } catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
+ return handleError(error);
}
};
@@ -80,20 +23,7 @@ export const removeUser = async (id) => {
const { data } = await client.post(`/user/delete`, { userId: id });
return { message: data.message };
} catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
- }
-};
-
-export const removeProduct = async (id) => {
- try {
- const { data } = await client.delete(`/product/${id}`);
- return { message: data.message };
- } catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
+ return handleError(error);
}
};
@@ -102,9 +32,70 @@ export const verifyIsAdmin = async (id) => {
const { data } = await client.get(`/user/isAdmin/${id}`);
return { isAdmin: data.isAdmin };
} catch (error) {
- const { response } = error;
- if (response?.data) return response.data;
- return { error: error.message || error };
+ return handleError(error);
+ }
+};
+
+// Products
+export const getProducts = async (page, limit = 10) => {
+ try {
+ const { data } = await client.get(
+ `/product/getProductWithPagination?limit=${limit}&page=${page}`,
+ );
+ return { products: data.products, total: data.totalProd };
+ } catch (error) {
+ return handleError(error);
+ }
+};
+
+export const removeProduct = async (id) => {
+ try {
+ const { data } = await client.delete(`/product/any/${id}`);
+ return { message: data.message };
+ } catch (error) {
+ return handleError(error);
+ }
+};
+
+// Categories
+export const getCategories = async (page, limit = 10) => {
+ try {
+ const { data } = await client.get(
+ `/category/getCategories?page=${page}&limit=${limit}`,
+ );
+ return { data: data.data, total: data.total };
+ } catch (error) {
+ return handleError(error);
+ }
+};
+
+export const addCategory = async (name) => {
+ try {
+ const { data } = await client.post(`/category/addCategory`, { name });
+ return { message: data.message };
+ } catch (error) {
+ return handleError(error);
+ }
+};
+
+export const removeCategory = async (id) => {
+ try {
+ const { data } = await client.delete(`/category/${id}`);
+ return { message: data.message };
+ } catch (error) {
+ return handleError(error);
+ }
+};
+
+// Transactions
+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) {
+ return handleError(error);
}
};
@@ -113,8 +104,16 @@ export const removeTransaction = async (id) => {
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 };
+ return handleError(error);
}
};
+
+// Shared Error Handler
+const handleError = (error) => {
+ const { response } = error;
+ if (response?.data) return response.data;
+ return { error: error.message || error };
+};
+
+// Optional: export client if you want to use it elsewhere
+export default client;
diff --git a/frontend/src/api/client.js b/frontend/src/api/client.js
deleted file mode 100644
index bcb4831..0000000
--- a/frontend/src/api/client.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import axios from "axios";
-const client = axios.create({ baseURL: "http://localhost:3030/api" });
-export default client;
diff --git a/frontend/src/components/DashboardNav.jsx b/frontend/src/components/DashboardNav.jsx
index 5f19de2..2013901 100644
--- a/frontend/src/components/DashboardNav.jsx
+++ b/frontend/src/components/DashboardNav.jsx
@@ -1,98 +1,15 @@
-import { Link, NavLink } from "react-router-dom";
-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 = () => {
- handleCloseAdminDashboard();
- };
-
return (
-
-
-
- -
-
-
-

-
- Campus Plug
-
-
-
-
- -
-
- (isActive
- ? " text-green-400"
- : "text-white transition-all hover:text-green-200") +
- " flex items-center px-5 text-lg pt-5 "
- }
- >
-
- Users
-
-
- -
-
- (isActive
- ? "text-green-400"
- : "text-white transition-all hover:text-green-200") +
- " flex items-center px-5 text-lg pt-5"
- }
- >
-
- Products
-
-
- -
-
- (isActive
- ? "text-green-400"
- : "text-white transition-all hover:text-green-200") +
- " flex items-center px-5 text-lg pt-5"
- }
- >
-
- Categories
-
-
- -
-
- (isActive
- ? "text-green-400"
- : "text-white transition-all hover:text-green-200") +
- " flex items-center px-5 text-lg pt-5"
- }
- >
-
- Transaction
-
-
-
-
-
- Go back to user page
-
-
+
+
);
}
diff --git a/frontend/src/pages/CategoryDashboard.jsx b/frontend/src/pages/CategoryDashboard.jsx
deleted file mode 100644
index e08c82f..0000000
--- a/frontend/src/pages/CategoryDashboard.jsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import { useEffect, useState } from "react";
-import { getCategories, removeCategory } from "../api/admin";
-import { MdDelete } from "react-icons/md";
-import Pagination from "../components/Pagination";
-import { IoAddCircleSharp } from "react-icons/io5";
-import CategoryForm from "../components/CategoryForm";
-
-export default function CategoryDashboard() {
- const [categories, setCategories] = useState([]);
- const [total, setTotal] = useState(0);
- const [currentPage, setCurrentPage] = useState(1);
-
- let pageLimit = 10;
-
- const [visible, setVisible] = useState(false);
-
- const onChangePage = (page, limit = 10) => {
- setCurrentPage(page);
- fetchCategory(page, limit);
- };
-
- const fetchCategory = (page = 1, limit = 10) => {
- getCategories(page, limit).then(({ data, total }) => {
- setCategories(data);
- setTotal(total);
- });
- };
-
- const notiChange = () => {
- fetchCategory(currentPage);
- };
-
- const handleToggleForm = () => {
- setVisible((curr) => !curr);
- };
-
- const handleRemove = (id) => {
- removeCategory(id)
- .then(() => {
- fetchCategory(currentPage);
- })
- .catch((err) => {
- console.log(err);
- });
- };
-
- //Get user when initialize the component
- useEffect(fetchCategory, []);
-
- return (
-
-
- CATEGORIES
-
-
-
- {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/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index dbcc762..6b991f6 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -1,7 +1,362 @@
-export default function Dashboard() {
+import { useEffect, useState, useCallback } from "react";
+import {
+ getUsers,
+ removeUser,
+ getTransactions,
+ removeTransaction,
+ getProducts,
+ removeProduct,
+ getCategories,
+ removeCategory,
+} from "../api/admin";
+import { MdDelete } from "react-icons/md";
+import { IoAddCircleSharp } from "react-icons/io5";
+import { FaHome } from "react-icons/fa";
+import Pagination from "../components/Pagination";
+import CategoryForm from "../components/CategoryForm";
+import DashboardNav from "../components/DashboardNav";
+import { useNavigate } from "react-router-dom";
+
+// Spinner Component
+const Spinner = () => (
+
+);
+
+// Empty State Component
+const EmptyState = () => (
+
+
+
No data found
+
+ No records are currently available.
+
+
+);
+
+// Generic Dashboard Component
+const Dashboard = ({
+ fetchDataFn,
+ deleteFn,
+ columns,
+ idKey,
+ refreshKey = 0,
+ headerAction = null,
+}) => {
+ const navigate = useNavigate();
+ const [items, setItems] = useState([]);
+ const [total, setTotal] = useState(0);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [loading, setLoading] = useState(true);
+ const pageLimit = 10;
+
+ const fetchItems = useCallback(
+ (page = 1, limit = 10) => {
+ setLoading(true);
+ fetchDataFn(page, limit)
+ .then((res) => {
+ const data =
+ res.users || res.products || res.transactions || res.data || [];
+ setItems(data);
+ setTotal(res.total);
+ })
+ .catch((error) => {
+ console.error("Error fetching data:", error);
+ setItems([]);
+ setTotal(0);
+ })
+ .finally(() => setLoading(false));
+ },
+ [fetchDataFn],
+ );
+
+ const handleRemove = (id) => {
+ if (window.confirm("Are you sure you want to delete this item?")) {
+ setLoading(true);
+ deleteFn(id)
+ .then(() => fetchItems(currentPage))
+ .catch((error) => {
+ console.error("Error removing item:", error);
+ setLoading(false);
+ });
+ }
+ };
+
+ useEffect(() => {
+ fetchItems(currentPage);
+ }, [fetchItems, currentPage, refreshKey]);
+
+ const onChangePage = (page) => {
+ setCurrentPage(page);
+ };
+
+ if (loading) return
;
+
return (
-
- Welcome to admin dashboard
+
+
+
+ Total: {total}
+
+ {headerAction &&
{headerAction}
}
+
+
+ {items.length > 0 ? (
+
+
+
+
+ {columns.map((col) => (
+ |
+ {col.label}
+ |
+ ))}
+
+ Action
+ |
+
+
+
+ {items.map((item) => (
+
+ {columns.map((col) => (
+ |
+ {item[col.key] || "—"}
+ |
+ ))}
+
+
+ |
+
+ ))}
+
+
+
+ ) : (
+
+ )}
+
+ {total > pageLimit && (
+
+ )}
+
+ );
+};
+
+// Main Admin Tabs
+export default function AdminDashboardTabs() {
+ const [activeTab, setActiveTab] = useState(0);
+ const [tabData, setTabData] = useState({
+ users: { loaded: false, data: null },
+ products: { loaded: false, data: null },
+ transactions: { loaded: false, data: null },
+ categories: { loaded: false, data: null },
+ });
+
+ const [visible, setVisible] = useState(false);
+ const [categoryRefreshKey, setCategoryRefreshKey] = useState(0);
+ const toggleForm = () => setVisible((v) => !v);
+
+ // Preload all tab data
+ useEffect(() => {
+ const tabKeys = ["users", "products", "transactions", "categories"];
+ const loadTabData = async (index) => {
+ if (index === tabKeys.length) return;
+
+ setTabData((prev) => ({
+ ...prev,
+ [tabKeys[index]]: { ...prev[tabKeys[index]], loaded: true },
+ }));
+
+ // Load next tab after a short delay
+ setTimeout(() => loadTabData(index + 1), 100);
+ };
+
+ loadTabData(0);
+ }, []);
+
+ const tabs = [
+ {
+ title: "Users",
+ icon: "👥",
+ key: "users",
+ component: () => (
+
+ ),
+ },
+ {
+ title: "Products",
+ icon: "📦",
+ key: "products",
+ component: () => (
+
+ ),
+ },
+ {
+ title: "Transactions",
+ icon: "💰",
+ key: "transactions",
+ component: () => (
+
+ ),
+ },
+ {
+ title: "Categories",
+ icon: "🏷️",
+ key: "categories",
+ component: () => (
+ <>
+
+
+ Add Category
+
+ }
+ />
+ {
+ setCategoryRefreshKey((prev) => prev + 1);
+ toggleForm();
+ }}
+ />
+ >
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+ Admin Dashboard
+
+
+
+ {/* Mobile Tabs */}
+
+
+
+
+ {/* Desktop Tabs */}
+
+ {tabs.map((tab, index) => (
+
+ ))}
+
+
+
+
+ {tabData[tabs[activeTab].key].loaded && tabs[activeTab].component()}
+
+
+
);
}
diff --git a/frontend/src/pages/ProductDashboard.jsx b/frontend/src/pages/ProductDashboard.jsx
deleted file mode 100644
index 35ce13e..0000000
--- a/frontend/src/pages/ProductDashboard.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useEffect, useState } from "react";
-import { getProducts, removeProduct } from "../api/admin";
-import { MdDelete } from "react-icons/md";
-import Pagination from "../components/Pagination";
-
-export default function ProductDashboard() {
- const [products, setProducts] = useState([]);
- const [total, setTotal] = useState(0);
- const [currentPage, setCurrentPage] = useState(1);
-
- let pageLimit = 10;
-
- const onChangePage = (page, limit = 10) => {
- setCurrentPage(page);
- fetchProducts(page, limit);
- };
-
- const fetchProducts = (page = 1, limit = 10) => {
- getProducts(page, limit).then(({ products, total }) => {
- setTotal(total);
- setProducts(products);
- });
- };
-
- const handleRemoveProduct = (id) => {
- removeProduct(id)
- .then((res) => {
- fetchProducts(currentPage);
- })
- .catch((err) => {
- console.log(err);
- });
- };
-
- //Get user when initialize the component
- useEffect(fetchProducts, []);
-
- return (
-
-
- PRODUCTS
-
- {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
deleted file mode 100644
index 167d5ee..0000000
--- a/frontend/src/pages/TransactionDashboard.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-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!
-
- )}
-
- );
-}
diff --git a/frontend/src/pages/UserDashboard.jsx b/frontend/src/pages/UserDashboard.jsx
deleted file mode 100644
index 9cab96d..0000000
--- a/frontend/src/pages/UserDashboard.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useEffect, useState } from "react";
-import { getUsers, removeUser } from "../api/admin";
-import { MdDelete } from "react-icons/md";
-import Pagination from "../components/Pagination";
-
-export default function UserDashboard() {
- const [users, setUsers] = useState([]);
- const [total, setTotal] = useState(0);
- const [currentPage, setCurrentPage] = useState(1);
-
- let pageLimit = 10;
-
- const onChangePage = (page, limit = 10) => {
- setCurrentPage(page);
- fetchUsers(page, limit);
- };
-
- const fetchUsers = (page = 1, limit = 10) => {
- getUsers(page, limit).then(({ users, total }) => {
- setUsers(users);
- setTotal(total);
- });
- };
-
- const handleRemoveUser = (id) => {
- removeUser(id)
- .then((res) => {
- fetchUsers(currentPage);
- })
- .catch((err) => {
- console.log(err);
- });
- };
-
- //Get user when initialize the component
- useEffect(fetchUsers, []);
-
- return (
-
-
- USERS
-
- {users.length > 0 ? (
- <>
- {" "}
-
-
-
- | UserID |
- UCID |
- Name |
- Email |
- Phone |
- Address |
- Action |
-
-
-
- {users.map((user) => (
-
- | {user.UserID} |
- {user.UCID} |
- {user.Name} |
- {user.Email} |
- {user.Phone} |
- {user.Address} |
-
- {
- handleRemoveUser(user.UserID);
- }}
- className="hover:text-red-600 cursor-pointer transition-all text-xl"
- />
- |
-
- ))}
-
-
-
- >
- ) : (
-
- No user exists!
-
- )}
-
- );
-}
diff --git a/frontend/src/schema.sql b/frontend/src/schema.sql
new file mode 100644
index 0000000..8dafcf0
--- /dev/null
+++ b/frontend/src/schema.sql
@@ -0,0 +1,124 @@
+-- MySql Version 9.2.0
+CREATE DATABASE Marketplace;
+
+USE Marketplace;
+
+-- User Entity
+CREATE TABLE User (
+ UserID INT AUTO_INCREMENT PRIMARY KEY,
+ Name VARCHAR(100) NOT NULL,
+ Email VARCHAR(100) UNIQUE NOT NULL,
+ UCID VARCHAR(20) UNIQUE NOT NULL,
+ Password VARCHAR(255) NOT NULL,
+ Phone VARCHAR(20),
+ Address VARCHAR(255)
+);
+
+CREATE TABLE UserRole (
+ UserID INT,
+ Client BOOLEAN DEFAULT True,
+ Admin BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (UserID),
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE
+);
+
+-- Category Entity (must be created before Product or else error)
+CREATE TABLE Category (
+ CategoryID INT AUTO_INCREMENT PRIMARY KEY,
+ Name VARCHAR(255) NOT NULL
+);
+
+-- Product Entity
+CREATE TABLE Product (
+ ProductID INT AUTO_INCREMENT PRIMARY KEY,
+ Name VARCHAR(255) NOT NULL,
+ Price DECIMAL(10, 2) NOT NULL,
+ StockQuantity INT,
+ UserID INT,
+ Description TEXT,
+ CategoryID INT NOT NULL,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID) ON DELETE SET NULL
+);
+
+-- Fixed Image_URL table
+CREATE TABLE Image_URL (
+ URL VARCHAR(255),
+ ProductID INT,
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE
+);
+
+-- Fixed Review Entity (Many-to-One with User, Many-to-One with Product)
+CREATE TABLE Review (
+ ReviewID INT AUTO_INCREMENT PRIMARY KEY,
+ UserID INT,
+ ProductID INT,
+ Comment TEXT,
+ Rating INT CHECK (
+ Rating >= 1
+ AND Rating <= 5
+ ),
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE
+);
+
+-- Transaction Entity (Many-to-One with User, Many-to-One with Product)
+CREATE TABLE Transaction (
+ TransactionID INT PRIMARY KEY,
+ UserID INT,
+ ProductID INT,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PaymentStatus VARCHAR(50),
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE
+);
+
+-- Recommendation Entity (Many-to-One with User, Many-to-One with Product)
+CREATE TABLE Recommendation (
+ RecommendationID_PK INT AUTO_INCREMENT PRIMARY KEY,
+ UserID INT,
+ RecommendedProductID INT,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (RecommendedProductID) REFERENCES Product (ProductID) ON DELETE CASCADE
+);
+
+-- History Entity (Many-to-One with User, Many-to-One with Product)
+CREATE TABLE History (
+ HistoryID INT AUTO_INCREMENT PRIMARY KEY,
+ UserID INT,
+ ProductID INT,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE
+);
+
+-- Favorites Entity (Many-to-One with User, Many-to-One with Product)
+CREATE TABLE Favorites (
+ FavoriteID INT AUTO_INCREMENT PRIMARY KEY,
+ UserID INT,
+ ProductID INT,
+ FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE,
+ UNIQUE (UserID, ProductID)
+);
+
+-- Product-Category Junction Table (Many-to-Many)
+CREATE TABLE Product_Category (
+ ProductID INT,
+ CategoryID INT,
+ PRIMARY KEY (ProductID, CategoryID),
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE CASCADE,
+ FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID) ON DELETE CASCADE
+);
+
+-- Login Authentication table
+CREATE TABLE AuthVerification (
+ UserID INT AUTO_INCREMENT PRIMARY KEY,
+ Email VARCHAR(100) UNIQUE NOT NULL,
+ VerificationCode VARCHAR(6) NOT NULL,
+ Authenticated BOOLEAN DEFAULT FALSE,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP
+);
diff --git a/mysql-code/Init-Data.sql b/mysql-code/Init-Data.sql
index b636c7a..3c7dae5 100644
--- a/mysql-code/Init-Data.sql
+++ b/mysql-code/Init-Data.sql
@@ -101,7 +101,8 @@ VALUES
('Photography Equipment'),
('Event Tickets'),
('Software Licenses'),
- ('Transportation (Car Pool)');
+ ('Transportation (Car Pool)'),
+ ('Other');
-- Insert Products
INSERT INTO
diff --git a/mysql-code/Schema.sql b/mysql-code/Schema.sql
index 508dc7d..e65a325 100644
--- a/mysql-code/Schema.sql
+++ b/mysql-code/Schema.sql
@@ -28,7 +28,6 @@ CREATE TABLE Category (
Name VARCHAR(255) NOT NULL
);
--- Product Entity
CREATE TABLE Product (
ProductID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
@@ -38,8 +37,22 @@ CREATE TABLE Product (
Description TEXT,
CategoryID INT NOT NULL,
Date DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (UserID) REFERENCES User (UserID),
+ FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID)
+);
+
+-- Product Entity
+CREATE TABLE Product (
+ ProductID INT AUTO_INCREMENT PRIMARY KEY,
+ Name VARCHAR(255) NOT NULL,
+ Price DECIMAL(10, 2) NOT NULL,
+ StockQuantity INT,
+ UserID INT,
+ Description TEXT,
+ CategoryID INT,
+ Date DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE SET NULL,
- FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID) ON DELETE SET NULL
+ FOREIGN KEY (CategoryID) REFERENCES Category (CategoryID)
);
-- Fixed Image_URL table
@@ -72,7 +85,7 @@ CREATE TABLE Transaction (
Date DATETIME DEFAULT CURRENT_TIMESTAMP,
PaymentStatus VARCHAR(50),
FOREIGN KEY (UserID) REFERENCES User (UserID) ON DELETE CASCADE,
- FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE SET NULL
+ FOREIGN KEY (ProductID) REFERENCES Product (ProductID) ON DELETE SET NULL
);
-- Recommendation Entity (Many-to-One with User, Many-to-One with Product)