├── Backend ├── README.md ├── .gitignore ├── config.npmrc ├── routes │ ├── historyRoutes.js │ ├── productRoutes.js │ ├── user_routes.js │ ├── locationRoutes.js │ ├── companyRoutes.js │ └── analyticsRoutes.js ├── db │ └── user_db.js ├── middlewares │ └── user_auth.js ├── utils │ └── user_utils.js ├── package.json ├── models │ ├── company_model.js │ ├── locations_models.js │ ├── user_model.js │ ├── history_model.js │ └── product_model.js ├── app.js └── controllers │ ├── user_controllers.js │ └── product_controller.js ├── Frontend ├── README.md ├── src │ ├── index.css │ ├── App.jsx │ ├── screens │ │ ├── users │ │ │ ├── components │ │ │ │ ├── ChangeRolePopup.jsx │ │ │ │ └── ManageUserTableRow.jsx │ │ │ └── UserManagementScreen.jsx │ │ ├── login │ │ │ ├── AuthLayout.jsx │ │ │ ├── LoginScreen.jsx │ │ │ └── SignupScreen.jsx │ │ ├── dashboard │ │ │ ├── components │ │ │ │ ├── PieChart.jsx │ │ │ │ └── AnalyticsComponent.jsx │ │ │ ├── DashBoardScreen.jsx │ │ │ └── DashBoardLayout.jsx │ │ ├── product │ │ │ ├── ProductInfoScreen.jsx │ │ │ ├── ProductHistoryScreen.jsx │ │ │ ├── ProductsScreen.jsx │ │ │ ├── AddNewProductScreen.jsx │ │ │ └── ProductEditScreen.jsx │ │ ├── locations │ │ │ ├── NewLocationScreen.jsx │ │ │ ├── LocationsScreen.jsx │ │ │ └── EditLocationScreen.jsx │ │ ├── brands │ │ │ ├── NewBrandsScreen.jsx │ │ │ ├── BrandsScreen.jsx │ │ │ └── EditBrandsScreen.jsx │ │ └── InventoryFormScreen.jsx │ ├── components │ │ ├── PopUpComponenet.jsx │ │ ├── LoadingIndicator.jsx │ │ ├── ShowSuccessMesasge.jsx │ │ ├── ShowErrorMessage.jsx │ │ ├── LogoutButton.jsx │ │ ├── HeaderBar.jsx │ │ ├── SideNavbar.jsx │ │ └── WarrantyExpiringProductsTableComponent.jsx │ ├── assets │ │ ├── menu.svg │ │ ├── user-logo.svg │ │ ├── react.svg │ │ ├── admin-logo.svg │ │ ├── authenticate.svg │ │ └── undraw_empty_re.svg │ ├── main.jsx │ └── router.jsx ├── postcss.config.js ├── vite.config.js ├── tailwind.config.js ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── package.json └── public │ └── vite.svg └── Readme.md /Backend/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Frontend/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /Backend/config.npmrc: -------------------------------------------------------------------------------- 1 | proxy=null 2 | https-proxy=null -------------------------------------------------------------------------------- /Frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /Frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /Frontend/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function App() { 4 | return ( 5 |
App
6 | ) 7 | } 8 | 9 | export default App -------------------------------------------------------------------------------- /Frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /Frontend/src/screens/users/components/ChangeRolePopup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function ChangeRolePopup() { 4 | return ( 5 |
ChangeRolePopup
6 | ) 7 | } 8 | 9 | export default ChangeRolePopup -------------------------------------------------------------------------------- /Frontend/src/components/PopUpComponenet.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function PopUpComponenet() { 4 | return ( 5 |
6 | ) 7 | } 8 | 9 | export default PopUpComponenet -------------------------------------------------------------------------------- /Frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx}", 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | 13 | -------------------------------------------------------------------------------- /Frontend/src/screens/login/AuthLayout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Outlet } from "react-router-dom"; 3 | 4 | function AuthLayout() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | 12 | export default AuthLayout; 13 | -------------------------------------------------------------------------------- /Frontend/src/assets/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env 26 | -------------------------------------------------------------------------------- /Backend/routes/historyRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import HistoryModel from "../models/history_model.js"; 3 | 4 | const historyRouter = express.Router(); 5 | 6 | // historyRouter.get("/", async (req, res) => { 7 | // const { _id } = req.body; 8 | 9 | 10 | // const historyData = await HistoryModel.findById(_id); 11 | // }); 12 | 13 | 14 | 15 | export default historyRouter; 16 | -------------------------------------------------------------------------------- /Backend/db/user_db.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | export const connectdb = () => { 6 | mongoose 7 | .connect(process.env.MONGODB_URI, { 8 | dbName: "ims", 9 | }) 10 | .then((c) => { 11 | console.log(`database connected with ${c.connection.host}`); 12 | }) 13 | .catch((e) => { 14 | console.log(e); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /Frontend/src/components/LoadingIndicator.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function LoadingIndicator() { 4 | return ( 5 |
6 |
7 |
8 | ); 9 | } 10 | 11 | export default LoadingIndicator; 12 | -------------------------------------------------------------------------------- /Frontend/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | import { RouterProvider } from "react-router-dom"; 6 | import router from "./router.jsx"; 7 | 8 | ReactDOM.createRoot(document.getElementById("root")).render( 9 | 10 | 11 | 12 | ); 13 | 14 | -------------------------------------------------------------------------------- /Frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Frontend/src/components/ShowSuccessMesasge.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function ShowSuccessMesasge({ children }) { 4 | return ( 5 |
6 |
7 | {children} 8 |
9 |
10 | ); 11 | } 12 | 13 | export default ShowSuccessMesasge; 14 | -------------------------------------------------------------------------------- /Frontend/src/components/ShowErrorMessage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function ShowErrorMessage({ children }) { 4 | return ( 5 |
6 |
7 | Oops some thing went wrong! {children} 8 |
9 |
10 | ); 11 | } 12 | 13 | export default ShowErrorMessage; 14 | -------------------------------------------------------------------------------- /Backend/middlewares/user_auth.js: -------------------------------------------------------------------------------- 1 | import User from "../models/user_model.js"; 2 | import jwt from "jsonwebtoken"; 3 | 4 | export const isAuthenticated = async (req, res, next) => { 5 | const { token } = req.cookies; 6 | console.log("token", token); 7 | if (!token) { 8 | return res.status(400).json({ 9 | success: false, 10 | message: "user is not found or Login first", 11 | }); 12 | } 13 | 14 | const decode = jwt.verify(token, process.env.SECRET_KEY); 15 | 16 | req.user = await User.findById(decode._id); 17 | next(); 18 | }; 19 | -------------------------------------------------------------------------------- /Backend/utils/user_utils.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | 3 | export const sendcookie = (user, res, message, statuscode = 200) => { 4 | const token = jwt.sign({ _id: user._id }, process.env.SECRET_KEY); 5 | res 6 | .status(statuscode) 7 | .cookie("token", token, { 8 | httpOnly: true, 9 | maxAge: 30 * 60 * 10000, 10 | sameSite: process.env.NODE_ENV === "development" ? "lax" : "none", 11 | secure: process.env.NODE_ENV === "development" ? false : true, 12 | }) 13 | .json({ 14 | success: true, 15 | message, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /Frontend/src/assets/user-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backendapi", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "type": "module", 7 | "engines": { 8 | "node": "18.8.0" 9 | }, 10 | "scripts": { 11 | "start": "node app.js", 12 | "dev": "nodemon app.js" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bcrypt": "^5.1.1", 18 | "cookie-parser": "^1.4.6", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.3.1", 21 | "express": "^4.18.2", 22 | "jsonwebtoken": "^9.0.2", 23 | "mongodb": "^6.2.0", 24 | "mongoose": "^8.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Backend/models/company_model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const companySchema = new mongoose.Schema( 4 | { 5 | name: { type: String, required: true }, 6 | description: { type: String, required: true }, 7 | createdBy: { 8 | ref: "User", 9 | type: mongoose.Schema.Types.ObjectId, 10 | required: true, 11 | }, 12 | 13 | editedBy: { 14 | ref: "User", 15 | type: mongoose.Schema.Types.ObjectId, 16 | }, 17 | }, 18 | 19 | { 20 | timestamps: true, 21 | } 22 | ); 23 | 24 | const LocationModel = mongoose.model("Company", companySchema); 25 | 26 | export default LocationModel; 27 | -------------------------------------------------------------------------------- /Backend/models/locations_models.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const locationSchema = new mongoose.Schema( 4 | { 5 | createdBy: { 6 | ref: "User", 7 | type: mongoose.Schema.Types.ObjectId, 8 | required: true, 9 | }, 10 | 11 | editedBy: { 12 | ref: "User", 13 | type: mongoose.Schema.Types.ObjectId, 14 | }, 15 | 16 | name: { type: String, required: true }, 17 | description: { type: String, required: true }, 18 | }, 19 | { 20 | timestamps: true, 21 | } 22 | ); 23 | 24 | const LocationModel = mongoose.model("Location", locationSchema); 25 | 26 | export default LocationModel; 27 | -------------------------------------------------------------------------------- /Frontend/src/screens/dashboard/components/PieChart.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useRef, useEffect } from "react"; 2 | import { Line, Pie } from "react-chartjs-2"; 3 | import "chart.js/auto"; 4 | 5 | const PieChart = ({ data }) => { 6 | const ref = useRef(null); 7 | useEffect(() => { 8 | return ref?.current?.destroy(); 9 | }, []); 10 | return ( 11 | 23 | ); 24 | }; 25 | 26 | export default PieChart; 27 | -------------------------------------------------------------------------------- /Frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /Backend/models/user_model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const Userschema = new mongoose.Schema( 3 | { 4 | name: { 5 | type: String, 6 | required: true, 7 | }, 8 | email: { 9 | type: String, 10 | required: true, 11 | unique: true, 12 | }, 13 | password: { 14 | type: String, 15 | required: true, 16 | select: false, //password won't included mainly used for sensitive info 17 | }, 18 | role: { 19 | type: String, 20 | enum: ["user", "admin"], 21 | default: "user", 22 | }, 23 | }, 24 | { 25 | timeStamps: true, 26 | } 27 | ); 28 | 29 | const User = mongoose.model("User", Userschema); 30 | 31 | export default User; 32 | -------------------------------------------------------------------------------- /Frontend/src/screens/dashboard/DashBoardScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { Link, useOutletContext } from "react-router-dom"; 4 | import AnalyticsComponent from "./components/AnalyticsComponent"; 5 | import WarrantyExpiringProductsTablesComponent from "../../components/WarrantyExpiringProductsTableComponent"; 6 | 7 | function DashBoardScreen() { 8 | // const [data] = useOutletContext(); 9 | // // console.log("data ", data) 10 | 11 | return ( 12 |
13 |
14 | 15 | 16 |
17 | ); 18 | } 19 | 20 | export default DashBoardScreen; 21 | -------------------------------------------------------------------------------- /Backend/models/history_model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const historySchema = new mongoose.Schema( 4 | { 5 | location: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: "Location", 8 | required: true, 9 | }, 10 | status: [ 11 | { 12 | name: { 13 | type: String, 14 | required: true, 15 | enum: ["repair", "in use", "not in use"], 16 | }, 17 | date: { 18 | type: Date, 19 | default: Date.now(), 20 | }, 21 | }, 22 | ], 23 | }, 24 | { 25 | timestamps: true, 26 | } 27 | ); 28 | 29 | const HistoryModel = mongoose.model("History", historySchema); 30 | 31 | export default HistoryModel; 32 | -------------------------------------------------------------------------------- /Backend/routes/productRoutes.js: -------------------------------------------------------------------------------- 1 | // routes/productRoutes.js 2 | 3 | import express from "express"; 4 | import { 5 | createProduct, 6 | getAllProducts, 7 | getProductById, 8 | updateProductById, 9 | deleteProductById, 10 | getProductHistroy, 11 | } from "../controllers/product_controller.js"; 12 | import { isAuthenticated } from "../middlewares/user_auth.js"; 13 | 14 | const productRouter = express.Router(); 15 | 16 | // Create a new product 17 | productRouter.post("/", isAuthenticated, createProduct); 18 | 19 | // Get all products 20 | productRouter.get("/", getAllProducts); 21 | 22 | // Get a single product by ID 23 | productRouter.get("/:id", getProductById); 24 | productRouter.get("/:id/history", getProductHistroy); 25 | 26 | // Update a product by ID 27 | productRouter.put("/:id", isAuthenticated, updateProductById); 28 | 29 | // Delete a product by ID 30 | productRouter.delete("/:id", isAuthenticated, deleteProductById); 31 | 32 | //search bar feature 33 | //expired products 34 | export default productRouter; 35 | -------------------------------------------------------------------------------- /Frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "front-end", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.6.8", 14 | "chartjs": "^0.3.24", 15 | "dagre": "^0.8.5", 16 | "localforage": "^1.10.0", 17 | "match-sorter": "^6.3.4", 18 | "react": "^18.2.0", 19 | "react-chartjs-2": "^5.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-icons": "^5.2.1", 22 | "react-router-dom": "^6.23.1", 23 | "reactflow": "^11.11.3", 24 | "sort-by": "^1.2.0" 25 | }, 26 | "devDependencies": { 27 | "@types/react": "^18.2.66", 28 | "@types/react-dom": "^18.2.22", 29 | "@vitejs/plugin-react": "^4.2.1", 30 | "autoprefixer": "^10.4.19", 31 | "eslint": "^8.57.0", 32 | "eslint-plugin-react": "^7.34.1", 33 | "eslint-plugin-react-hooks": "^4.6.0", 34 | "eslint-plugin-react-refresh": "^0.4.6", 35 | "postcss": "^8.4.38", 36 | "tailwindcss": "^3.4.3", 37 | "vite": "^5.2.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Backend/routes/user_routes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | getAllUsers, 4 | getMyprofile, 5 | login, 6 | logout, 7 | register, 8 | } from "../controllers/user_controllers.js"; 9 | import { isAuthenticated } from "../middlewares/user_auth.js"; 10 | import User from "../models/user_model.js"; 11 | 12 | const router = express.Router(); 13 | 14 | router.post("/new", register); 15 | router.post("/login", login); 16 | router.get("/logout", logout); 17 | 18 | router.get("/all", getAllUsers); 19 | 20 | router.patch("/chage-role", isAuthenticated, async (req, res) => { 21 | const { targetUserId, role } = req.body; 22 | const user = req.user; 23 | if (!targetUserId) { 24 | return res.status(404).json({ message: "please provide target user id" }); 25 | } 26 | if (user.role !== "admin") { 27 | return res 28 | .status(400) 29 | .json({ message: "Not authorized to make this call" }); 30 | } 31 | 32 | await User.findByIdAndUpdate(targetUserId, { 33 | $set: { 34 | role: role, 35 | }, 36 | }); 37 | 38 | return res.status(200).json({ message: "success" }); 39 | }); 40 | 41 | router.get("/me", isAuthenticated, getMyprofile); 42 | 43 | export default router; 44 | -------------------------------------------------------------------------------- /Backend/models/product_model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const Productschema = new mongoose.Schema({ 4 | title: { 5 | type: String, 6 | required: true, 7 | }, 8 | description: { 9 | type: String, 10 | }, 11 | serialNo: { 12 | type: String, 13 | required: true, 14 | }, 15 | createdBy: { 16 | type: mongoose.Schema.ObjectId, 17 | ref: "User", 18 | required: true, 19 | }, 20 | createdAt: { 21 | type: Date, 22 | default: Date.now(), 23 | }, 24 | rackMountable: { 25 | type: Boolean, 26 | default: false, 27 | }, 28 | isPart: { 29 | type: Boolean, 30 | default: false, 31 | }, 32 | manufacturer: { 33 | type: mongoose.Schema.Types.ObjectId, 34 | ref: "Company", 35 | }, 36 | 37 | model: { 38 | type: String, 39 | required: true, 40 | }, 41 | 42 | dateOfPurchase: { 43 | type: Date, 44 | required: true, 45 | }, 46 | warrantyMonths: { 47 | type: Number, 48 | required: true, 49 | }, 50 | 51 | user: { 52 | type: String, 53 | required: true, 54 | enum: ["normal user", "department", "admin"], 55 | default: "normal user", 56 | }, 57 | 58 | history: [ 59 | { 60 | type: mongoose.Schema.Types.ObjectId, 61 | ref: "History", 62 | }, 63 | ], 64 | }); 65 | 66 | const Product = mongoose.model("Product", Productschema); 67 | 68 | export default Product; 69 | -------------------------------------------------------------------------------- /Frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Frontend/src/components/LogoutButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { IoIosLogOut } from "react-icons/io"; 3 | import LoadingIndicator from "./LoadingIndicator"; 4 | import axios from "axios"; 5 | import { useNavigate } from "react-router-dom"; 6 | import { SERVER_URL } from "../router"; 7 | 8 | function LogoutButton() { 9 | const [isLoading, setLoading] = useState(false); 10 | const navigator = useNavigate(); 11 | 12 | const handleLogout = async () => { 13 | try { 14 | setLoading(true); 15 | const { data, status } = await axios.get( 16 | `${SERVER_URL}/api/v1/users/logout`, 17 | { 18 | withCredentials: true, 19 | headers: { 20 | "Content-Type": "application/json", 21 | }, 22 | } 23 | ); 24 | 25 | if (status === 200) { 26 | navigator("/", { replace: true }); 27 | window.location.reload(); 28 | } 29 | } catch (error) { 30 | console.error("Something went wrong:", error); 31 | } finally { 32 | setLoading(false); 33 | } 34 | }; 35 | return ( 36 | 52 | ); 53 | } 54 | 55 | export default LogoutButton; 56 | -------------------------------------------------------------------------------- /Backend/routes/locationRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import LocationModel from "../models/locations_models.js"; 3 | import { isAuthenticated } from "../middlewares/user_auth.js"; 4 | 5 | const locationRouter = express.Router(); 6 | 7 | locationRouter.get("/", async (req, res) => { 8 | const locations = await LocationModel.find() 9 | .populate("createdBy") 10 | .populate("editedBy"); 11 | 12 | return res.status(200).json(locations || []); 13 | }); 14 | 15 | locationRouter.get("/:id", async (req, res) => { 16 | const locations = await LocationModel.findById(req.params.id) 17 | .populate("createdBy") 18 | .populate("editedBy"); 19 | 20 | return res.status(200).json(locations || []); 21 | }); 22 | 23 | locationRouter.patch("/:id", isAuthenticated, async (req, res, next) => { 24 | try { 25 | const { name, description } = req.body; 26 | const editedBy = req.user._id; 27 | await LocationModel.findByIdAndUpdate(req.params.id, { 28 | $set: { 29 | name, 30 | description, 31 | editedBy, 32 | }, 33 | }); 34 | 35 | return res.status(200).json(); 36 | } catch (e) { 37 | throw next(e); 38 | } 39 | }); 40 | 41 | locationRouter.post("/", isAuthenticated, async (req, res, next) => { 42 | try { 43 | const { name, description } = req.body; 44 | const user_id = req.user._id; 45 | const location = new LocationModel({ 46 | createdBy: user_id, 47 | name, 48 | description, 49 | }); 50 | 51 | await location.save(); 52 | return res.status(200).json({ message: "Success" }); 53 | } catch (e) { 54 | throw next(e); 55 | } 56 | }); 57 | 58 | export default locationRouter; 59 | -------------------------------------------------------------------------------- /Backend/app.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { connectdb } from "./db/user_db.js"; 3 | import userRouter from "./routes/user_routes.js"; 4 | import dotenv from "dotenv"; 5 | import cookieParser from "cookie-parser"; 6 | import cors from "cors"; 7 | import productRouter from "./routes/productRoutes.js"; 8 | import companyRouter from "./routes/companyRoutes.js"; 9 | import locationRouter from "./routes/locationRoutes.js"; 10 | import analyticsRoutes from "./routes/analyticsRoutes.js"; 11 | 12 | dotenv.config(); 13 | 14 | const app = express(); 15 | app.use( 16 | cors({ 17 | // methods: ["GET", "POST", "PUT", "PATCH", "DELETE"], 18 | // credentials: true, 19 | // preflightContinue: false, 20 | origin: true, 21 | methods: ["GET", "POST", "PUT", "PATCH", "DELETE"], 22 | credentials: true, 23 | }) 24 | ); 25 | 26 | app.use(cookieParser()); 27 | app.use(express.json()); 28 | app.use(express.urlencoded()); 29 | connectdb(); 30 | 31 | app.use("/api/v1/users", userRouter); 32 | app.use("/api/v1/products", productRouter); 33 | app.use("/api/v1/brands", companyRouter); 34 | app.use("/api/v1/location", locationRouter); 35 | app.use("/api/v1/analytics", analyticsRoutes); 36 | 37 | app.use(express.urlencoded({ extended: true })); 38 | 39 | // console.log(process.env.FRONTEND_URL); 40 | 41 | app.get("/", (req, res) => { 42 | res.send("

working nicely

"); 43 | }); 44 | 45 | app.use((error, req, res, next) => { 46 | console.log(error, error.message); 47 | return res.status(400).json({ message: "internal server error" }); 48 | }); 49 | 50 | app.listen(process.env.PORT, () => { 51 | console.log( 52 | `server is working at port:${process.env.PORT} in ${process.env.NODE_ENV} mode` 53 | ); 54 | }); 55 | -------------------------------------------------------------------------------- /Backend/routes/companyRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import CompanyModel from "../models/company_model.js"; 3 | import { isAuthenticated } from "../middlewares/user_auth.js"; 4 | 5 | const companyRouter = express.Router(); 6 | 7 | companyRouter.get("/", async (req, res, next) => { 8 | try { 9 | const locations = await CompanyModel.find() 10 | .populate("createdBy") 11 | .populate("editedBy"); 12 | 13 | return res.status(200).json(locations || []); 14 | } catch (e) { 15 | throw next(e); 16 | } 17 | }); 18 | companyRouter.get("/:id", async (req, res, next) => { 19 | try { 20 | const locations = await CompanyModel.findById(req.params.id) 21 | .populate("createdBy") 22 | .populate("editedBy"); 23 | 24 | return res.status(200).json(locations); 25 | } catch (e) { 26 | throw next(e); 27 | } 28 | }); 29 | 30 | companyRouter.patch("/:id", isAuthenticated, async (req, res, next) => { 31 | try { 32 | const { name, description } = req.body; 33 | const editedBy = req.user._id; 34 | 35 | await CompanyModel.findByIdAndUpdate(req.params.id, { 36 | $set: { 37 | name, 38 | description, 39 | editedBy, 40 | }, 41 | }); 42 | 43 | return res.status(200).json({ message: "Success" }); 44 | } catch (e) { 45 | throw next(e); 46 | } 47 | }); 48 | 49 | companyRouter.post("/", isAuthenticated, async (req, res, next) => { 50 | try { 51 | const { name, description } = req.body; 52 | const user_id = req.user._id; 53 | 54 | const location = new CompanyModel({ 55 | createdBy: user_id, 56 | name, 57 | description, 58 | }); 59 | 60 | await location.save(); 61 | return res.status(200).json({ message: "Success" }); 62 | } catch (e) { 63 | throw next(e); 64 | } 65 | }); 66 | 67 | export default companyRouter; 68 | -------------------------------------------------------------------------------- /Frontend/src/screens/users/components/ManageUserTableRow.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState } from "react"; 3 | import { NavLink } from "react-router-dom"; 4 | import { SERVER_URL } from "../../../router"; 5 | 6 | function ManageUserTableRow({ user, role }) { 7 | const [userRole, setUserRole] = useState(user.role); 8 | const [isLoading, setLoading] = useState(false); 9 | async function changeRoleAPicall() { 10 | try { 11 | setLoading(true); 12 | await axios.patch( 13 | `${SERVER_URL}/api/v1/users/chage-role`, 14 | { 15 | targetUserId: user._id, 16 | role: userRole==="user" ? "admin":"user", 17 | }, 18 | { 19 | withCredentials: "include", 20 | headers: { 21 | "Content-Type": "application/json", 22 | }, 23 | } 24 | ); 25 | console.log(userRole) 26 | setUserRole((s) => (s === "admin" ? "user" : "admin")); 27 | } catch (e) { 28 | console.log(e); 29 | setUserRole((s) => (s === "admin" ? "user" : "admin")); 30 | } finally { 31 | setLoading(false); 32 | } 33 | } 34 | async function changeRole() { 35 | // setUserRole((s) => (s === "admin" ? "user" : "admin")); 36 | await changeRoleAPicall(); 37 | } 38 | return ( 39 | 40 | {user.name} 41 | {user.email} 42 | 43 | 50 | {userRole} 51 | 52 | 53 | 54 | {user && role === "admin" && ( 55 | 56 | 66 | 67 | )} 68 | 69 | ); 70 | } 71 | 72 | export default ManageUserTableRow; 73 | -------------------------------------------------------------------------------- /Frontend/src/components/HeaderBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Link, useNavigate } from "react-router-dom"; 3 | import adminLogo from "../assets/admin-logo.svg"; 4 | import userLogo from "../assets/user-logo.svg"; 5 | import axios from "axios"; 6 | 7 | function HeaderBar({ user }) { 8 | const [showMenu, setShowMenu] = useState(false); 9 | const navigator = useNavigate(); 10 | 11 | const handleMenuClick = () => { 12 | setShowMenu(!showMenu); 13 | }; 14 | 15 | const [isLoading, setLoading] = useState(false); 16 | 17 | return ( 18 | <> 19 | {isLoading && ( 20 |
21 |
22 |

Loading please wait...

23 |
24 | )} 25 | {!isLoading && ( 26 |
27 |
28 |

29 | Inventory Management 30 |

31 |
32 |
33 |
34 | User Logo 39 |
40 |

41 | {user.name} 42 |

43 | {user.email} 44 |
45 |
46 | 65 |
66 |
67 |
68 | )} 69 | 70 | ); 71 | } 72 | 73 | export default HeaderBar; 74 | -------------------------------------------------------------------------------- /Backend/routes/analyticsRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import ProductModel from "../models/product_model.js"; 3 | import HistoryModel from "../models/history_model.js"; 4 | 5 | const analyticsRoutes = express.Router(); 6 | 7 | analyticsRoutes.get("/expiring", async (req, res) => { 8 | try { 9 | const { months = 1 } = req.query; 10 | const expiringProducts = await getExpiringProducts(parseInt(months)); 11 | res.status(200).json(expiringProducts); 12 | } catch (error) { 13 | res.status(500).json({ error: error.message }); 14 | } 15 | }); 16 | 17 | const getExpiringProducts = async (months) => { 18 | const currentDate = new Date(); 19 | const expirationDate = new Date(currentDate); 20 | expirationDate.setMonth(expirationDate.getMonth() + months); 21 | 22 | const expiringProducts = await ProductModel.find({ 23 | dateOfPurchase: { $lte: expirationDate }, 24 | }) 25 | .populate("createdBy") 26 | .populate({ 27 | path: "history", 28 | populate: { 29 | path: "location", 30 | }, 31 | }) 32 | .populate("manufacturer"); 33 | 34 | return expiringProducts; 35 | }; 36 | 37 | analyticsRoutes.get("/", async (req, res) => { 38 | try { 39 | const useby = await getProductUsageByUser(); 40 | const expiry = await getWarrantyStatus(); 41 | const status = await getProductStatus(); 42 | 43 | const analytics = { useby, expiry, status }; 44 | 45 | res.status(200).json(analytics); 46 | } catch (error) { 47 | res.status(500).json({ error: error.message }); 48 | } 49 | }); 50 | 51 | // Function to get product usage by user 52 | const getProductUsageByUser = async () => { 53 | const result = await ProductModel.aggregate([ 54 | { 55 | $group: { 56 | _id: "$user", 57 | count: { $sum: 1 }, 58 | }, 59 | }, 60 | ]); 61 | 62 | const labels = result.map((item) => item._id); 63 | const data = result.map((item) => item.count); 64 | 65 | return { title: "Products used by", labels, data }; 66 | }; 67 | 68 | // Function to get warranty status 69 | const getWarrantyStatus = async () => { 70 | const result = await ProductModel.aggregate([ 71 | { 72 | $project: { 73 | status: { 74 | $cond: [ 75 | { $lte: ["$dateOfPurchase", new Date()] }, 76 | "in warranty", 77 | "not in warranty", 78 | ], 79 | }, 80 | }, 81 | }, 82 | { 83 | $group: { 84 | _id: "$status", 85 | count: { $sum: 1 }, 86 | }, 87 | }, 88 | ]); 89 | 90 | const labels = result.map((item) => item._id); 91 | const data = result.map((item) => item.count); 92 | 93 | return { title: "Warranty", labels, data }; 94 | }; 95 | 96 | const getProductStatus = async () => { 97 | const result = await HistoryModel.aggregate([ 98 | { 99 | $unwind: "$status", 100 | }, 101 | { 102 | $group: { 103 | _id: "$status.name", 104 | count: { $sum: 1 }, 105 | }, 106 | }, 107 | ]); 108 | 109 | const labels = result.map((item) => item._id); 110 | const data = result.map((item) => item.count); 111 | 112 | return { title: "Product Status", labels, data }; 113 | }; 114 | 115 | export default analyticsRoutes; 116 | -------------------------------------------------------------------------------- /Frontend/src/screens/dashboard/components/AnalyticsComponent.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from "react"; 2 | import { Pie, Bar } from "react-chartjs-2"; 3 | import axios from "axios"; 4 | 5 | import "chart.js/auto"; 6 | import { SERVER_URL } from "../../../router"; 7 | export const AnalyticsComponent = () => { 8 | const [loading, setLoading] = useState(true); 9 | const [analyticsData, setAnalyticsData] = useState(null); 10 | const ref1 = useRef(null); 11 | const ref2 = useRef(null); 12 | const ref3 = useRef(null); 13 | useEffect(() => { 14 | const fetchData = async () => { 15 | try { 16 | const response = await axios.get( 17 | `${SERVER_URL}/api/v1/analytics/` 18 | ); 19 | setAnalyticsData(response.data); 20 | setLoading(false); 21 | } catch (error) { 22 | console.error("Error fetching data:", error); 23 | setLoading(false); 24 | } 25 | }; 26 | 27 | fetchData(); 28 | 29 | return () => { 30 | ref1.current = null; 31 | ref2.current = null; 32 | ref3.current = null; 33 | }; 34 | }, []); 35 | 36 | return ( 37 |
38 | {loading ? ( 39 |
40 |
41 | Loading... 42 |
43 |
44 | ) : ( 45 | <> 46 |
47 |

48 | {analyticsData.useby.title} 49 |

50 | 62 |
63 |
64 |

65 | {analyticsData.expiry.title} 66 |

67 | 79 |
80 |
81 |

82 | {analyticsData.status.title} 83 |

84 | 96 |
97 | 98 | )} 99 |
100 | ); 101 | }; 102 | 103 | export default AnalyticsComponent; 104 | -------------------------------------------------------------------------------- /Frontend/src/router.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter } from "react-router-dom"; 2 | import DashBoardLayout from "./screens/dashboard/DashBoardLayout"; 3 | import LoginScreen from "./screens/login/LoginScreen"; 4 | import SignupScreen from "./screens/login/SignupScreen"; 5 | import DashBoardScreen from "./screens/dashboard/DashBoardScreen"; 6 | 7 | import AuthLayout from "./screens/login/AuthLayout"; 8 | import InventoryFormScreen from "./screens/InventoryFormScreen"; 9 | import ProductInfoScreen from "./screens/product/ProductInfoScreen"; 10 | import AddNewProductScreen from "./screens/product/AddNewProductScreen"; 11 | import ProductEditScreen from "./screens/product/ProductEditScreen"; 12 | import PrductsScreen from "./screens/product/ProductsScreen"; 13 | import LocationsScreen from "./screens/locations/LocationsScreen"; 14 | import NewLocationScreen from "./screens/locations/NewLocationScreen"; 15 | import EditLocationScreen from "./screens/locations/EditLocationScreen"; 16 | 17 | import BrandsScreen from "./screens/brands/BrandsScreen"; 18 | import NewBrandsScreen from "./screens/brands/NewBrandsScreen"; 19 | import EditBrandsScreen from "./screens/brands/EditBrandsScreen"; 20 | import UserManagementScreen from "./screens/users/UserManagementScreen"; 21 | import ProductHistoryScreen from "./screens/product/ProductHistoryScreen"; 22 | const router = createBrowserRouter([ 23 | { 24 | path: "/", 25 | element: , 26 | children: [ 27 | { 28 | path: "", 29 | element: , 30 | }, 31 | // { 32 | // path: "add-item", 33 | // element: , 34 | // }, 35 | { 36 | path: "add-product", 37 | element: , 38 | }, 39 | { 40 | path: "products/:id", 41 | element: , 42 | }, 43 | { 44 | path: "products/:id/edit", 45 | element: , 46 | }, 47 | 48 | // new routes 49 | 50 | { 51 | path: "/products", 52 | element: , 53 | }, 54 | { path: "/products/new", element: }, 55 | { path: "/products/edit/:id", element: }, 56 | { path: "/products/history/:id", element: }, 57 | 58 | // brands 59 | 60 | { 61 | path: "/brands", 62 | element: , 63 | }, 64 | { 65 | path: "/brands/new", 66 | element: , 67 | }, 68 | { 69 | path: "/brands/edit/:id", 70 | element: , 71 | }, 72 | 73 | // locations 74 | { 75 | path: "/locations", 76 | element: , 77 | }, 78 | { 79 | path: "/locations/new", 80 | element: , 81 | }, 82 | { 83 | path: "/locations/edit/:id", 84 | element: , 85 | }, 86 | // users 87 | { 88 | path: "/users", 89 | element: , 90 | }, 91 | ], 92 | }, 93 | { 94 | path: "/auth", 95 | element: , 96 | children: [ 97 | { 98 | index: true, 99 | path: "", 100 | element: , 101 | }, 102 | { 103 | path: "signup", 104 | element: , 105 | }, 106 | ], 107 | }, 108 | ]); 109 | 110 | export default router; 111 | export const SERVER_URL = 112 | import.meta.env.VITE_MODE === "DEV" 113 | ? import.meta.env.VITE_LOCAL 114 | : import.meta.env.VITE_SERVER; 115 | -------------------------------------------------------------------------------- /Frontend/src/screens/dashboard/DashBoardLayout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import HeaderBar from "../../components/HeaderBar"; 3 | import { Link, Outlet, useNavigate } from "react-router-dom"; 4 | import axios from "axios"; 5 | import DashBoardScreen from "./DashBoardScreen"; 6 | import loginLogo from "../../assets/authenticate.svg"; 7 | import SideNavbar from "../../components/SideNavbar"; 8 | import { SERVER_URL } from "../../router"; 9 | 10 | function DashBoardLayout() { 11 | const navigator = useNavigate(); 12 | const [data, setData] = useState(null); 13 | const [user, setUser] = useState(null); 14 | const [isLoading, setLoading] = useState(true); 15 | 16 | useEffect(() => { 17 | fetchUserInfo(); 18 | }, []); 19 | const fetchUserInfo = async () => { 20 | try { 21 | const { data, status } = await axios.get( 22 | `${SERVER_URL}/api/v1/users/me`, 23 | 24 | { 25 | withCredentials: true, 26 | headers: { 27 | "Content-Type": "application/json", 28 | }, 29 | } 30 | ); 31 | console.log(data); 32 | if (status === 400) { 33 | // navigator("/auth"); 34 | } 35 | if (status === 200) { 36 | setData(data); 37 | setUser(data.user); 38 | setLoading(false); 39 | } 40 | } catch (e) { 41 | // navigator("/auth"); 42 | console.log(e); 43 | } finally { 44 | setLoading(false); 45 | } 46 | }; 47 | return ( 48 | <> 49 | {!data &&
} 50 | {!data && ( 51 |
52 | Your are not authenticated please login to continue 53 |
54 | )} 55 | 56 | {/* loading spinner animation */} 57 | {isLoading && ( 58 |
59 |
60 |
61 |

Loading

62 |
63 |
64 | )} 65 | 66 | {/* showing the data from data base */} 67 | {data && } 68 | {data && ( 69 |
70 |
71 | 72 |
73 | 74 |
75 | 76 |
77 | 78 |
79 |
80 | )} 81 | 82 | {/* home screen logout component */} 83 | {!data && ( 84 |
85 |
86 | Image 87 |
88 | 92 | Login Now 93 | 94 |
95 |
96 |
97 | )} 98 | 99 | ); 100 | } 101 | 102 | export default DashBoardLayout; 103 | -------------------------------------------------------------------------------- /Frontend/src/screens/product/ProductInfoScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import axios from "axios"; // Import Axios 4 | import { SERVER_URL } from "../../router"; 5 | 6 | function ProductInfoScreen() { 7 | const params = useParams(); 8 | 9 | const [isLoading, setLoading] = useState(true); // Adjusted loading state 10 | const [isError, setError] = useState(null); 11 | const [isSuccess, setSuccess] = useState(false); 12 | const [product, setProduct] = useState(null); // Initialize product as null 13 | 14 | useEffect(() => { 15 | getProductInfo(); 16 | }, [params.id]); // Include params.id in the dependency array 17 | 18 | const getProductInfo = async () => { 19 | try { 20 | setLoading(true); // Set loading state to true 21 | const { data, status } = await axios.get( 22 | `${SERVER_URL}/api/v1/products/${params.id}`, 23 | { 24 | withCredentials: true, // Change "include" to true 25 | headers: { 26 | "Content-Type": "application/json", 27 | }, 28 | } 29 | ); 30 | console.log(data); 31 | if (status === 200) { 32 | setSuccess(true); 33 | setProduct(data); 34 | } else { 35 | throw new Error(data.error); 36 | } 37 | } catch (error) { 38 | setError("Error while fetching product information"); 39 | console.error(error); 40 | } finally { 41 | setLoading(false); // Set loading state to false 42 | } 43 | }; 44 | 45 | // JSX for rendering product information 46 | return ( 47 |
48 | {isError && ( 49 |
50 |
51 |
52 | {isError}{" "} 53 | 54 | Click here to reload! 55 | 56 |
57 |
58 |
59 | )} 60 | {isLoading && ( 61 |
62 |
63 |
64 |

Loading

65 |
66 |
67 | )} 68 | {isSuccess && product && ( 69 |
70 |

{product.title}

71 |

{product.description}

72 |

Serial No: {product.serialNo}

73 |

74 | Manufacturer: {product.manufacturer} 75 |

76 |

Model: {product.model}

77 |

78 | Date of Purchase:{" "} 79 | {new Date(product.dateOfPurchase).toLocaleDateString()} 80 |

81 |

82 | Warranty Months: {product.warrantyMonths} 83 |

84 |

Status: {product.status}

85 |

User: {product.user}

86 |

87 | Created By: {product.createdBy.name} 88 |

89 | {/* Render other product information */} 90 |
91 | )} 92 |
93 | ); 94 | } 95 | 96 | export default ProductInfoScreen; 97 | -------------------------------------------------------------------------------- /Frontend/src/components/SideNavbar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link, NavLink } from "react-router-dom"; 3 | 4 | import { FaBlog, FaSleigh } from "react-icons/fa"; 5 | import { IoMdHome } from "react-icons/io"; 6 | import { AiFillProduct } from "react-icons/ai"; 7 | 8 | import { LuShoppingBag } from "react-icons/lu"; 9 | import { MdCategory } from "react-icons/md"; 10 | import { SiBrandfolder } from "react-icons/si"; 11 | import { BsCollectionFill } from "react-icons/bs"; 12 | 13 | import { FcAdvertising } from "react-icons/fc"; 14 | import { RiCoupon2Fill } from "react-icons/ri"; 15 | 16 | import { IoIosSettings } from "react-icons/io"; 17 | import { FcRatings } from "react-icons/fc"; 18 | import { FaUserAlt } from "react-icons/fa"; 19 | import { BiCart, BiLocationPlus, BiUser } from "react-icons/bi"; 20 | import LogoutButton from "./LogoutButton"; 21 | 22 | function SideNavbar() { 23 | const quickLinks = { 24 | title: "QUICK LINKS", 25 | links: [ 26 | { link: "", name: "Dashboard", icon: , end: true }, 27 | { 28 | link: "/products/new", 29 | name: "New Product", 30 | icon: , 31 | end: true, 32 | }, 33 | { 34 | link: "/brands/new", 35 | name: "New Brand", 36 | icon: , 37 | end: true, 38 | }, 39 | { 40 | link: "/locations/new", 41 | name: "New Location", 42 | icon: , 43 | end: true, 44 | }, 45 | ], 46 | }; 47 | const catalogLinks = { 48 | title: "CATALOG", 49 | links: [ 50 | { 51 | link: "/products", 52 | name: "Products", 53 | icon: , 54 | end: false, 55 | }, 56 | { link: "/brands", name: "Brands", icon: , end: false }, 57 | { 58 | link: "/locations", 59 | name: "Locations", 60 | icon: , 61 | end: false, 62 | }, 63 | { link: "/users", name: "User Management", icon: , end: false }, 64 | ], 65 | }; 66 | 67 | const links = [quickLinks, catalogLinks]; 68 | return ( 69 |
70 | {links.map((link, index) => ( 71 |
72 |

73 | {link.title} 74 |

75 |
76 | {link.links.map((_link, idx) => ( 77 | 82 | `${ 83 | prop.isActive 84 | ? "text-teal-600 border-l-4 border-l-teal-600 rounded-sm bg-slate-50" 85 | : "hover:bg-slate-50 hover:text-teal-800" 86 | } pl-6 py-2 font-semibold text-slate-700 flex items-center gap-3` 87 | } 88 | > 89 | {_link.icon} 90 | {_link.name} 91 | 92 | ))} 93 |
94 |
95 | ))} 96 | 97 | 98 |
99 | ); 100 | } 101 | 102 | export default SideNavbar; 103 | 104 | { 105 | /*
106 | 112 | 113 | Settings 114 | 115 |
116 | */ 117 | } 118 | -------------------------------------------------------------------------------- /Backend/controllers/user_controllers.js: -------------------------------------------------------------------------------- 1 | import User from "../models/user_model.js"; 2 | import bcrypt from "bcrypt"; 3 | import { sendcookie } from "../utils/user_utils.js"; 4 | 5 | export const getAllUsers = async (req, res) => { 6 | try { 7 | const { 8 | page = 1, 9 | itemsPerPage = 10, 10 | search = "", 11 | role, // Role parameter for filtering 12 | } = req.query; 13 | 14 | // Define the search query 15 | const searchQuery = { 16 | $or: [ 17 | { name: { $regex: search, $options: "i" } }, 18 | { email: { $regex: search, $options: "i" } }, 19 | ], 20 | }; 21 | 22 | // Include role-based filtering if role parameter is provided 23 | if (role) { 24 | searchQuery.role = role; 25 | } 26 | 27 | // Calculate the number of documents to skip 28 | const skipItems = (page - 1) * itemsPerPage; 29 | 30 | // Find users based on search query, role filter, and pagination 31 | const users = await User.find(searchQuery) 32 | .skip(skipItems) 33 | .limit(parseInt(itemsPerPage)); 34 | 35 | // Count total number of users for pagination 36 | const totalUsers = await User.countDocuments(searchQuery); 37 | const totalPages = Math.ceil(totalUsers / itemsPerPage); 38 | 39 | res.status(200).json({ 40 | success: true, 41 | users, 42 | totalPages, 43 | currentPage: page, 44 | }); 45 | } catch (error) { 46 | res.status(500).json({ error: error.message }); 47 | } 48 | }; 49 | 50 | export const login = async (req, res, next) => { 51 | try { 52 | const { email, password } = req.body; 53 | const user = await User.findOne({ email }).select("+password"); 54 | if (!user) { 55 | return res.status(404).json({ 56 | success: false, 57 | message: "Register first", 58 | }); 59 | } 60 | 61 | const encrypted = await bcrypt.compare(password, user.password); 62 | if (!encrypted) { 63 | return res.status(404).json({ 64 | success: false, 65 | message: "your password is incorrect", 66 | }); 67 | } 68 | 69 | sendcookie(user, res, `welcome back,${user.name}`, 201); 70 | } catch (e) { 71 | throw next(e); 72 | } 73 | }; 74 | 75 | export const register = async (req, res, next) => { 76 | try { 77 | const { name, email, password } = req.body; 78 | let user = await User.findOne({ email }); 79 | if (user) { 80 | return res.status(400).json({ 81 | success: false, 82 | message: "user already exists", 83 | }); 84 | } 85 | 86 | const hashedpassword = await bcrypt.hash(password, 10); 87 | user = await User.create({ name, email, password: hashedpassword }); 88 | 89 | sendcookie(user, res, "successfully registered", 201); 90 | } catch (e) { 91 | throw next(e); 92 | } 93 | }; 94 | 95 | export const getMyprofile = async (req, res, next) => { 96 | try { 97 | // 2nd method by {Abhisekhsuru} 98 | // const { id } = req.query; 99 | // const user = await User.findById(id); 100 | //without authentication 101 | const user = await User.findById(req.user._id); 102 | res.status(200).json({ 103 | success: true, 104 | message: `your profile is found ${req.user.name}`, 105 | user: user, 106 | }); 107 | } catch (e) { 108 | throw next(e); 109 | } 110 | }; 111 | 112 | export const logout = async (req, res, next) => { 113 | try { 114 | res 115 | .cookie("token", null, { 116 | expires: new Date(0), 117 | sameSite: process.env.NODE_ENV === "development" ? "lax" : "none", 118 | secure: process.env.NODE_ENV === "development" ? false : true, 119 | }) 120 | .status(200) 121 | .json({ 122 | success: true, 123 | message: "Successfully logout", 124 | user: req.user, 125 | }); 126 | } catch (e) { 127 | throw next(e); 128 | } 129 | }; 130 | -------------------------------------------------------------------------------- /Frontend/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Frontend/src/screens/locations/NewLocationScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Link, useParams } from "react-router-dom"; 3 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 4 | import ShowSuccessMesasge from "../../components/ShowSuccessMesasge"; 5 | import LoadingIndicator from "../../components/LoadingIndicator"; 6 | import axios from "axios"; 7 | import { SERVER_URL } from "../../router"; 8 | 9 | function NewLocationScreen() { 10 | const [success, setSuccess] = useState(false); 11 | const [uploading, setUploading] = useState(false); 12 | const [data, setData] = useState({}); 13 | const [isError, setError] = useState(""); 14 | 15 | function onchangeHandler(e) { 16 | e.preventDefault(); 17 | const name = e.target.name; 18 | const value = e.target.value; 19 | 20 | setData({ ...data, [name]: value }); 21 | } 22 | async function handleUpdate(e) { 23 | e.preventDefault(); 24 | try { 25 | setError(""); 26 | setUploading(true); 27 | 28 | const {} = await axios.post( 29 | `${SERVER_URL}/api/v1/location/`, 30 | data, 31 | { 32 | withCredentials: true, 33 | headers: { 34 | "Content-Type": "application/json", 35 | }, 36 | } 37 | ); 38 | setSuccess(true); 39 | } catch (e) { 40 | setError(e); 41 | console.log(e); 42 | } finally { 43 | setUploading(false); 44 | } 45 | } 46 | return ( 47 |
48 |

Add New Location

49 | 50 | {isError && ( 51 | reload} 53 | /> 54 | )} 55 | {success && ( 56 | <> 57 |
58 |
59 |

60 | Updated Successfullly{" "} 61 | 62 | goto Home 63 | 64 |

65 |
66 |
67 | 68 | )} 69 | 70 |
71 |
onchangeHandler(e)} 73 | onSubmit={handleUpdate} 74 | className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" 75 | > 76 |
77 | 83 | 91 |
92 |
93 | 99 | 107 |
108 |
109 | 116 |
117 |
118 |
119 |
120 | ); 121 | } 122 | 123 | export default NewLocationScreen; 124 | -------------------------------------------------------------------------------- /Frontend/src/screens/brands/NewBrandsScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Link, useParams } from "react-router-dom"; 3 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 4 | import ShowSuccessMesasge from "../../components/ShowSuccessMesasge"; 5 | import LoadingIndicator from "../../components/LoadingIndicator"; 6 | import axios from "axios"; 7 | import { SERVER_URL } from "../../router"; 8 | 9 | function NewBrandsScreen() { 10 | const params = useParams(); 11 | const [success, setSuccess] = useState(false); 12 | const [uploading, setUploading] = useState(false); 13 | const [data, setData] = useState({}); 14 | const [isError, setError] = useState(""); 15 | 16 | function onchangeHandler(e) { 17 | e.preventDefault(); 18 | const name = e.target.name; 19 | const value = e.target.value; 20 | 21 | setData({ ...data, [name]: value }); 22 | } 23 | async function handleUpdate(e) { 24 | e.preventDefault(); 25 | try { 26 | setError(""); 27 | setUploading(true); 28 | 29 | const {} = await axios.post( 30 | `${SERVER_URL}/api/v1/brands/`, 31 | data, 32 | { 33 | withCredentials: true, 34 | headers: { 35 | "Content-Type": "application/json", 36 | }, 37 | } 38 | ); 39 | setSuccess(true); 40 | } catch (e) { 41 | setError(e); 42 | console.log(e); 43 | } finally { 44 | setUploading(false); 45 | } 46 | } 47 | return ( 48 |
49 |

Add New Brand

50 | 51 | {isError && ( 52 | reload} 54 | /> 55 | )} 56 | {success && ( 57 | <> 58 |
59 |
60 |

61 | Updated Successfullly{" "} 62 | 63 | goto Home 64 | 65 |

66 |
67 |
68 | 69 | )} 70 | 71 |
72 |
onchangeHandler(e)} 74 | onSubmit={handleUpdate} 75 | className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" 76 | > 77 |
78 | 84 | 92 |
93 |
94 | 100 | 108 |
109 |
110 | 117 |
118 |
119 |
120 |
121 | ); 122 | } 123 | 124 | export default NewBrandsScreen; 125 | -------------------------------------------------------------------------------- /Frontend/src/assets/admin-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Frontend/src/components/WarrantyExpiringProductsTableComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import EmptyData from "../assets/undraw_empty_re.svg"; 3 | import axios from "axios"; 4 | import { Link } from "react-router-dom"; 5 | import { SERVER_URL } from "../router"; 6 | 7 | function WarrantyExpiringProductsTablesComponent({ uid }) { 8 | const [isLoading, setLoading] = useState(true); 9 | const [isError, setError] = useState(null); 10 | const [inventoryData, setInventoryData] = useState([]); 11 | 12 | useEffect(() => { 13 | fetchInventory(); 14 | }, []); 15 | 16 | const fetchInventory = async () => { 17 | try { 18 | const { data } = await axios.get(`${SERVER_URL}/api/v1/analytics/expiring`, { 19 | withCredentials: true, 20 | headers: { 21 | "Content-Type": "application/json", 22 | }, 23 | }); 24 | setInventoryData(data); 25 | setLoading(false); 26 | } catch (error) { 27 | console.error("Error fetching inventory data:", error); 28 | setError("Error fetching inventory data"); 29 | setLoading(false); 30 | } 31 | }; 32 | 33 | const calculateMonthsDifference = (date1, date2) => { 34 | const diffInMs = new Date(date2) - new Date(date1); 35 | return Math.round(diffInMs / (1000 * 60 * 60 * 24 * 30.44)); // Approximate number of days in a month 36 | }; 37 | 38 | return ( 39 |
40 | {isLoading ? ( 41 |
Loading...
42 | ) : isError ? ( 43 |
Error: {isError}
44 | ) : inventoryData.length === 0 ? ( 45 |
46 | Empty Data 47 |

No data available

48 |
49 | ) : ( 50 |
51 | 52 | 53 | 54 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 | {inventoryData.map((item, index) => ( 73 | 74 | 77 | 80 | 83 | 86 | 91 | 92 | ))} 93 | 94 |
55 | Product 56 | 58 | Serial Number 59 | 61 | Warranty Months / Purchase Date 62 | 64 | Status 65 | 67 | Action 68 |
75 | {item.title} 76 | 78 | {item.serialNo} 79 | 81 | {item.warrantyMonths} / {item.dateOfPurchase.split("T")[0]} 82 | 84 | {item.history[0].status[0].name} 85 | 87 | 88 | View 89 | 90 |
95 |
96 | )} 97 |
98 | ); 99 | } 100 | 101 | export default WarrantyExpiringProductsTablesComponent; 102 | -------------------------------------------------------------------------------- /Frontend/src/screens/brands/BrandsScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import LoadingIndicator from "../../components/LoadingIndicator"; 4 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 5 | import { IoMailOutline } from "react-icons/io5"; 6 | import { FaUser } from "react-icons/fa"; 7 | import { Link, NavLink } from "react-router-dom"; 8 | import { SERVER_URL } from "../../router"; 9 | 10 | function BrandsScreen() { 11 | const [isLoading, setLoading] = useState(true); 12 | const [data, setData] = useState(null); 13 | const [isError, setError] = useState(""); 14 | 15 | useEffect(() => { 16 | getDataFromApi(); 17 | }, []); 18 | async function getDataFromApi() { 19 | try { 20 | const { data } = await axios.get(`${SERVER_URL}/api/v1/brands`); 21 | setData(data); 22 | } catch (e) { 23 | setError(e); 24 | } finally { 25 | setLoading(false); 26 | } 27 | } 28 | return ( 29 |
30 |
31 |

All Brands

32 | 36 | Add New Brand 37 | 38 |
39 |
40 | 41 | {isLoading && } 42 | 43 | {isError && ( 44 | reload} 46 | /> 47 | )} 48 | 49 | {data && ( 50 |
51 | {data.map((location) => ( 52 |
53 | 54 |
55 | ))} 56 |
57 | )} 58 |
59 | ); 60 | } 61 | 62 | function LoactionCard({ data }) { 63 | return ( 64 |
65 |

{data.name}

66 |

{data.description}

67 | 68 | {data.editedBy ? ( 69 | <> 70 |
71 |
72 |
73 |
74 |

75 | 76 |

77 | {data.editedBy.name} 78 |

79 |

80 | 81 | Edited By 82 | 83 |
84 |

85 | 86 | {data.editedBy.email} 87 |

88 |
89 |
90 | 91 | ) : ( 92 | data.createdBy && ( 93 | <> 94 |
95 |
96 |
97 |
98 |

99 | 100 |

101 | {data.createdBy.name} 102 |

103 |

104 | 105 | Created By 106 | 107 |
108 |

109 | 110 | {data.createdBy.email} 111 |

112 |
113 |
114 | 115 | ) 116 | )} 117 | 118 | 122 | Edit 123 | 124 |
125 | ); 126 | } 127 | 128 | export default BrandsScreen; 129 | -------------------------------------------------------------------------------- /Frontend/src/screens/locations/LocationsScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import LoadingIndicator from "../../components/LoadingIndicator"; 4 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 5 | import { IoMailOutline } from "react-icons/io5"; 6 | import { FaUser } from "react-icons/fa"; 7 | import { Link, NavLink } from "react-router-dom"; 8 | import { SERVER_URL } from "../../router"; 9 | 10 | function LocationsScreen() { 11 | const [isLoading, setLoading] = useState(true); 12 | const [data, setData] = useState(null); 13 | const [isError, setError] = useState(""); 14 | 15 | useEffect(() => { 16 | getDataFromApi(); 17 | }, []); 18 | async function getDataFromApi() { 19 | try { 20 | const { data } = await axios.get(`${SERVER_URL}/api/v1/location`); 21 | setData(data); 22 | } catch (e) { 23 | setError(e); 24 | } finally { 25 | setLoading(false); 26 | } 27 | } 28 | return ( 29 |
30 |
31 |

All Locations

32 | 36 | Add New Location 37 | 38 |
39 |
40 | 41 | {isLoading && } 42 | 43 | {isError && ( 44 | reload} 46 | /> 47 | )} 48 | 49 | {data && ( 50 |
51 | {data.map((location) => ( 52 |
53 | 54 |
55 | ))} 56 |
57 | )} 58 |
59 | ); 60 | } 61 | 62 | function LoactionCard({ data }) { 63 | return ( 64 |
65 |

{data.name}

66 |

{data.description}

67 | 68 | {data.editedBy ? ( 69 | <> 70 |
71 |
72 |
73 |
74 |

75 | 76 |

{data.editedBy.name}

77 |

78 | 79 | Edited By 80 | 81 |
82 |

83 | 84 | {data.editedBy.email} 85 |

86 |
87 |
88 | 89 | ) : ( 90 | data.createdBy && ( 91 | <> 92 |
93 |
94 |
95 |
96 |

97 | 98 |

99 | {data.createdBy.name} 100 |

101 |

102 | 103 | Created By 104 | 105 |
106 |

107 | 108 | {data.createdBy.email} 109 |

110 |
111 |
112 | 113 | ) 114 | )} 115 | 116 | 120 | Edit 121 | 122 |
123 | ); 124 | } 125 | 126 | export default LocationsScreen; 127 | -------------------------------------------------------------------------------- /Frontend/src/screens/brands/EditBrandsScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Link, useParams } from "react-router-dom"; 3 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 4 | import ShowSuccessMesasge from "../../components/ShowSuccessMesasge"; 5 | import LoadingIndicator from "../../components/LoadingIndicator"; 6 | import axios from "axios"; 7 | import { SERVER_URL } from "../../router"; 8 | 9 | function EditBrandsScreen() { 10 | const params = useParams(); 11 | const [isLoading, setLoading] = useState(true); 12 | const [success, setSuccess] = useState(false); 13 | const [uploading, setUploading] = useState(false); 14 | const [data, setData] = useState({}); 15 | const [isError, setError] = useState(""); 16 | 17 | useEffect(() => { 18 | getDataFromApi(); 19 | }, []); 20 | async function getDataFromApi() { 21 | try { 22 | setError(""); 23 | 24 | const { data } = await axios.get( 25 | `${SERVER_URL}/api/v1/brands/${params.id}` 26 | ); 27 | setData(data); 28 | } catch (e) { 29 | setError(e); 30 | console.log(e); 31 | } finally { 32 | setLoading(false); 33 | } 34 | } 35 | 36 | function onchangeHandler(e) { 37 | e.preventDefault(); 38 | const name = e.target.name; 39 | const value = e.target.value; 40 | 41 | setData({ ...data, [name]: value }); 42 | } 43 | async function handleUpdate(e) { 44 | e.preventDefault(); 45 | try { 46 | setError(""); 47 | setUploading(true); 48 | 49 | const {} = await axios.patch( 50 | `${SERVER_URL}/api/v1/brands/${params.id}`, 51 | data, 52 | { 53 | withCredentials: true, 54 | headers: { 55 | "Content-Type": "application/json", 56 | }, 57 | } 58 | ); 59 | setSuccess(true); 60 | } catch (e) { 61 | setError(e); 62 | console.log(e); 63 | } finally { 64 | setUploading(false); 65 | } 66 | } 67 | 68 | return ( 69 |
70 |

Brands Edit

71 | {isLoading && } 72 | 73 | {isError && ( 74 | 77 | reload 78 | 79 | } 80 | /> 81 | )} 82 | {success && ( 83 | 86 | Updated Successfullly{" "} 87 | 88 | goto Home 89 | 90 |

91 | } 92 | /> 93 | )} 94 | 95 | {data && !isError && ( 96 |
97 |
onchangeHandler(e)} 99 | onSubmit={handleUpdate} 100 | className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" 101 | > 102 |
103 | 109 | 117 |
118 |
119 | 125 | 133 |
134 |
135 | 146 |
147 |
148 |
149 | )} 150 |
151 | ); 152 | } 153 | 154 | export default EditBrandsScreen; 155 | -------------------------------------------------------------------------------- /Frontend/src/screens/locations/EditLocationScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Link, useParams } from "react-router-dom"; 3 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 4 | import ShowSuccessMesasge from "../../components/ShowSuccessMesasge"; 5 | import LoadingIndicator from "../../components/LoadingIndicator"; 6 | import axios from "axios"; 7 | import { SERVER_URL } from "../../router"; 8 | 9 | function EditLocationScreen() { 10 | const params = useParams(); 11 | const [isLoading, setLoading] = useState(true); 12 | const [success, setSuccess] = useState(false); 13 | const [uploading, setUploading] = useState(false); 14 | const [data, setData] = useState({ 15 | _id: "663c84f0479858cdb4a0ca9c", 16 | name: "sector 1", 17 | description: "about sector 1 some random content", 18 | createdAt: "2024-05-09T08:10:24.852Z", 19 | updatedAt: "2024-05-09T08:10:24.852Z", 20 | __v: 0, 21 | }); 22 | const [isError, setError] = useState(""); 23 | 24 | useEffect(() => { 25 | getDataFromApi(); 26 | }, []); 27 | async function getDataFromApi() { 28 | try { 29 | setError(""); 30 | 31 | const { data } = await axios.get( 32 | `${SERVER_URL}/api/v1/location/${params.id}` 33 | ); 34 | setData(data); 35 | } catch (e) { 36 | setError(e); 37 | console.log(e); 38 | } finally { 39 | setLoading(false); 40 | } 41 | } 42 | 43 | function onchangeHandler(e) { 44 | e.preventDefault(); 45 | const name = e.target.name; 46 | const value = e.target.value; 47 | 48 | setData({ ...data, [name]: value }); 49 | } 50 | async function handleUpdate(e) { 51 | e.preventDefault(); 52 | try { 53 | setError(""); 54 | setUploading(true); 55 | 56 | const {} = await axios.patch( 57 | `${SERVER_URL}/api/v1/location/${params.id}`, 58 | data, 59 | { 60 | withCredentials: true, 61 | headers: { 62 | "Content-Type": "application/json", 63 | }, 64 | } 65 | ); 66 | setSuccess(true); 67 | } catch (e) { 68 | setError(e); 69 | console.log(e); 70 | } finally { 71 | setUploading(false); 72 | } 73 | } 74 | 75 | return ( 76 |
77 | {isLoading && } 78 | 79 | {isError && ( 80 | 83 | reload 84 | 85 | } 86 | /> 87 | )} 88 | {success && ( 89 | 92 | Updated Successfullly{" "} 93 | 94 | goto Home 95 | 96 |

97 | } 98 | /> 99 | )} 100 | 101 | {data && !isError && ( 102 |
103 |
onchangeHandler(e)} 105 | onSubmit={handleUpdate} 106 | className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" 107 | > 108 |
109 | 115 | 123 |
124 |
125 | 131 | 139 |
140 |
141 | 152 |
153 |
154 |
155 | )} 156 |
157 | ); 158 | } 159 | 160 | export default EditLocationScreen; 161 | -------------------------------------------------------------------------------- /Frontend/src/screens/login/LoginScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState } from "react"; 3 | import { Link, useNavigate } from "react-router-dom"; 4 | import { SERVER_URL } from "../../router"; 5 | 6 | function LoginScreen() { 7 | 8 | const [formData, setData] = useState({ 9 | email: "", 10 | password: "", 11 | }); 12 | 13 | const history = useNavigate(); // Use useNavigate instead of useHistory 14 | 15 | function handInputChange(e) { 16 | setData({ ...formData, [e.target.id]: e.target.value }); 17 | } 18 | 19 | async function handleSignIn(e) { 20 | e.preventDefault(); 21 | 22 | try { 23 | const { data, status } = await axios.post( 24 | `${SERVER_URL}/api/v1/users/login`, 25 | formData, 26 | { 27 | withCredentials: "include", 28 | headers: { 29 | "Content-Type": "application/json", 30 | }, 31 | } 32 | ); 33 | 34 | if (status === 201) { 35 | // console.log(response.headers.Set-Cookie) 36 | // alert("Login success"); 37 | // Redirect to home upon successful login 38 | history("/"); // Use history function to redirect 39 | } else { 40 | alert("Wrong credentials. Check Email and password "); 41 | } 42 | } catch (error) { 43 | console.error("Something went wrong:", error); // Corrected console.log to console.error 44 | alert("Something went wrong"); 45 | } 46 | } 47 | 48 | return ( 49 |
50 |
51 |
52 | 55 |

56 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat 57 | eligendi perspiciatis, sapiente si. 58 |

59 |

60 | Don't have an account? 61 | 62 | Get Started! 63 | 64 |

65 |

66 | Read our{" "} 67 | 68 | terms 69 | {" "} 70 | and{" "} 71 | 72 | conditions 73 | 74 |

75 |
76 |
77 |

78 | Account Login 79 |

80 |
handleSignIn(e)} 84 | > 85 |
86 | 92 | 100 |
101 |
102 |
103 | 109 |
110 | 117 |
118 |
119 | 124 | 130 |
131 |
132 | 138 |
139 |
140 |
141 |
142 |
143 | ); 144 | } 145 | 146 | export default LoginScreen; 147 | -------------------------------------------------------------------------------- /Frontend/src/screens/login/SignupScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { Link, useNavigate } from "react-router-dom"; 4 | import { SERVER_URL } from "../../router"; 5 | 6 | function SignupScreen() { 7 | const navigator = useNavigate(); 8 | 9 | const [formData, setData] = useState({ 10 | name: "", 11 | email: "", 12 | password: "", 13 | }); 14 | 15 | function handInputChange(e) { 16 | console.log(e.target.id, e.target.value); 17 | 18 | setData({ ...formData, [e.target.id]: e.target.value }); 19 | } 20 | 21 | async function handleSignIN(e) { 22 | e.preventDefault(); 23 | try { 24 | const { data, status } = await axios.post( 25 | `${SERVER_URL}/api/v1/users/new`, 26 | formData 27 | ); 28 | 29 | console.log(data); 30 | 31 | if (status === 201) { 32 | // alert("Account Created"); 33 | navigator("/auth", { replace: true }); 34 | } else { 35 | alert("Something went wrong"); 36 | } 37 | } catch (e) { 38 | console.log(e); 39 | alert("User Already exists with same email"); 40 | } 41 | } 42 | 43 | return ( 44 |
45 |
46 |
47 | 50 |

51 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat 52 | eligendi perspiciatis, sapiente si. 53 |

54 |

55 | Already have an account? 56 | 57 | Get Started! 58 | 59 |

60 |

61 | Read our{" "} 62 | 63 | terms 64 | {" "} 65 | and{" "} 66 | 67 | conditions 68 | 69 |

70 |
71 |
72 |

73 | Account Signup 74 |

75 |
handleSignIN(e)} 79 | > 80 |
81 | 84 | 92 |
93 |
94 | 100 | 108 |
109 |
110 |
111 | 117 |
118 | 125 |
126 |
127 | 132 | 138 |
139 |
140 | 146 |
147 |
148 |
149 |
150 |
151 | ); 152 | } 153 | 154 | export default SignupScreen; 155 | -------------------------------------------------------------------------------- /Frontend/src/assets/authenticate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Backend/controllers/product_controller.js: -------------------------------------------------------------------------------- 1 | // controllers/productController.js 2 | 3 | import HistoryModel from "../models/history_model.js"; 4 | import LocationModel from "../models/locations_models.js"; 5 | import Product from "../models/product_model.js"; 6 | 7 | export const createProduct = async (req, res) => { 8 | if (req.user.role !== "admin") { 9 | return res.status(403).json({ message: "not authorized" }); 10 | } 11 | try { 12 | const { 13 | locationId, 14 | status, 15 | title, 16 | description, 17 | serialNo, 18 | rackMountable, 19 | isPart, 20 | manufacturer, 21 | model, 22 | warrantyMonths, 23 | dateOfPurchase, 24 | user, 25 | } = req.body; 26 | 27 | const history = new HistoryModel({ 28 | location: locationId, 29 | 30 | status: [ 31 | { 32 | name: status, 33 | }, 34 | ], 35 | }); 36 | 37 | await history.save(); 38 | 39 | const product = new Product({ 40 | title, 41 | description, 42 | serialNo, 43 | dateOfPurchase, 44 | createdBy: req.user._id, 45 | rackMountable, 46 | isPart, 47 | manufacturer, 48 | model, 49 | warrantyMonths, 50 | user, 51 | history: [history._id], 52 | }); 53 | 54 | await product.save(); 55 | res.status(201).json(product); 56 | } catch (error) { 57 | res.status(400).json({ error: error.message }); 58 | } 59 | }; 60 | export const getAllProducts = async (req, res) => { 61 | try { 62 | const { 63 | page = 1, 64 | itemsperpage = 10, 65 | search = "", 66 | manufacturer, 67 | } = req.query; 68 | 69 | let regex = new RegExp(search, "i"); 70 | 71 | const query = { 72 | $or: [ 73 | { title: regex }, 74 | { description: regex }, 75 | { serialNo: regex }, 76 | { model: regex }, 77 | ], 78 | }; 79 | 80 | if (manufacturer) { 81 | query.manufacturer = mongoose.Types.ObjectId(manufacturer); 82 | } 83 | 84 | const skipItems = (page - 1) * itemsperpage; 85 | 86 | const totalCount = await Product.countDocuments(query); 87 | const pages_count = Math.ceil(totalCount / itemsperpage); 88 | 89 | const products = await Product.find(query) 90 | .skip(skipItems) 91 | .limit(parseInt(itemsperpage)) 92 | .populate({ 93 | path: "history", 94 | populate: { 95 | path: "location", 96 | }, 97 | }) 98 | .populate("manufacturer"); 99 | 100 | res.status(200).json({ 101 | data: products, 102 | pages_count, 103 | currentPage: page, 104 | }); 105 | } catch (error) { 106 | res.status(500).json({ error: error.message }); 107 | } 108 | }; 109 | 110 | // Controller to get a single product by ID 111 | export const getProductById = async (req, res) => { 112 | try { 113 | const product = await Product.findById(req.params.id).populate("createdBy"); 114 | console.log("product", product); 115 | if (!product) { 116 | return res.status(404).json({ error: "Product not found" }); 117 | } 118 | res.status(200).json(product); 119 | } catch (error) { 120 | res.status(500).json({ error: error.message }); 121 | } 122 | }; 123 | export const getProductHistroy = async (req, res) => { 124 | try { 125 | const product = await Product.findById(req.params.id) 126 | .populate("createdBy") 127 | .populate({ 128 | path: "history", 129 | populate: { 130 | path: "location", 131 | }, 132 | }) 133 | .populate("manufacturer"); 134 | 135 | console.log("product", product); 136 | if (!product) { 137 | return res.status(404).json({ error: "Product not found" }); 138 | } 139 | res.status(200).json(product); 140 | } catch (error) { 141 | res.status(500).json({ error: error.message }); 142 | } 143 | }; 144 | 145 | // Controller to update a product by ID 146 | export const updateProductById = async (req, res) => { 147 | const productId = req.params.id; 148 | try { 149 | const { 150 | locationId, 151 | status, 152 | title, 153 | description, 154 | serialNo, 155 | createdBy, 156 | rackMountable, 157 | isPart, 158 | manufacturer, 159 | model, 160 | warrantyMonths, 161 | user, 162 | } = req.body; 163 | 164 | if (locationId) { 165 | // Create a new history entry for the location change 166 | const history = new HistoryModel({ 167 | location: locationId, 168 | status: [ 169 | { 170 | name: status, 171 | }, 172 | ], 173 | }); 174 | 175 | await history.save(); 176 | 177 | // Update product information and push the new history entry 178 | await Product.findByIdAndUpdate(productId, { 179 | $set: { 180 | title, 181 | description, 182 | serialNo, 183 | createdBy, 184 | rackMountable, 185 | isPart, 186 | manufacturer, 187 | model, 188 | warrantyMonths, 189 | user, 190 | }, 191 | $push: { 192 | history: history._id, 193 | }, 194 | }); 195 | } else if (status) { 196 | // Add a new status entry to the existing history 197 | const productRes = await Product.findById(productId); 198 | if (!productRes) { 199 | return res.status(404).json({ error: "Product not found" }); 200 | } 201 | const historyId = productRes.history[0]; 202 | const his = await HistoryModel.findById(historyId); 203 | 204 | // Push the new status object into the history 205 | his.status.push({ 206 | name: status, 207 | }); 208 | 209 | await his.save(); 210 | } else { 211 | console.log("Unhandled condition"); 212 | } 213 | 214 | res.status(200).json({ message: "success" }); 215 | } catch (error) { 216 | res.status(400).json({ error: error.message }); 217 | } 218 | }; 219 | 220 | // Controller to delete a product by ID 221 | export const deleteProductById = async (req, res) => { 222 | try { 223 | const product = await Product.findByIdAndDelete(req.params.id); 224 | if (!product) { 225 | return res.status(404).json({ error: "Product not found" }); 226 | } 227 | res.json({ message: "Product deleted successfully" }); 228 | } catch (error) { 229 | res.status(500).json({ error: error.message }); 230 | } 231 | }; 232 | -------------------------------------------------------------------------------- /Frontend/src/screens/users/UserManagementScreen.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import axios from "axios"; 3 | import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io"; 4 | import LoadingIndicator from "../../components/LoadingIndicator"; 5 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 6 | import { NavLink, useOutletContext } from "react-router-dom"; 7 | import ManageUserTableRow from "./components/ManageUserTableRow"; 8 | import { SERVER_URL } from "../../router"; 9 | 10 | function UserManagementScreen() { 11 | const [data, user] = useOutletContext(); 12 | console.log(user); 13 | const [isLoading, setLoading] = useState(true); 14 | const [itemsPerPage, setItemsPerPage] = useState(10); 15 | const [currentPage, setCurrentPage] = useState(1); 16 | const [totalPages, setTotalPages] = useState(0); 17 | const [users, setUsers] = useState([]); 18 | const [searchTerm, setSearchTerm] = useState(""); 19 | const [error, setError] = useState(null); 20 | const [roleFilter, setRoleFilter] = useState(""); 21 | 22 | useEffect(() => { 23 | getDataFromApi(); 24 | }, [currentPage, itemsPerPage, searchTerm, roleFilter]); 25 | 26 | async function getDataFromApi() { 27 | try { 28 | const { data } = await axios.get( 29 | `${SERVER_URL}/api/v1/users/all`, 30 | { 31 | params: { 32 | page: currentPage, 33 | itemsPerPage, 34 | search: searchTerm, 35 | role: roleFilter, 36 | }, 37 | } 38 | ); 39 | setUsers(data.users); 40 | setTotalPages(data.totalPages); 41 | setLoading(false); 42 | } catch (error) { 43 | setError(error.message); 44 | setLoading(false); 45 | } 46 | } 47 | 48 | function handlePrevPage() { 49 | if (currentPage > 1) { 50 | setCurrentPage(currentPage - 1); 51 | } 52 | } 53 | 54 | function handleNextPage() { 55 | if (currentPage < totalPages) { 56 | setCurrentPage(currentPage + 1); 57 | } 58 | } 59 | 60 | return ( 61 |
62 |
63 |

64 | User Management 65 |

66 |

67 | Here you can modify the privilege of a user [admin/user] 68 |

69 |
70 |
71 | 72 | {error && ( 73 | reload} 75 | message={error} 76 | /> 77 | )} 78 |
79 |
80 |
81 | setSearchTerm(e.target.value)} 87 | /> 88 | 97 |
98 |
99 |
100 |
101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | {user && user.role === "admin" && ( 109 | 110 | )} 111 | 112 | 113 | 114 | {isLoading ? ( 115 | 116 | 119 | 120 | ) : ( 121 | users.map((_user) => ( 122 | 123 | )) 124 | )} 125 | 126 |
NameEmailRoleActions
117 | 118 |
127 |
128 |
129 | setItemsPerPage(e.target.value)} 135 | /> 136 |
Per Page
137 |
138 |
139 | 147 | setCurrentPage(e.target.value)} 153 | /> 154 | 162 |
Total {totalPages} pages
163 |
164 |
165 |
166 |
167 |
168 | ); 169 | } 170 | 171 | export default UserManagementScreen; 172 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Inventory Management System (IMS) 3 | 4 | An Inventory Management System built with Vite, React.js for the frontend, and Node.js, Express, and MongoDB for the backend. 5 | 6 | ## Table of Contents 7 | 8 | - [Features](#features) 9 | - [Folder Structure](#folder-structure) 10 | - [Prerequisites](#prerequisites) 11 | - [Installation](#installation) 12 | - [Running the Application](#running-the-application) 13 | - [Environment Variables](#environment-variables) 14 | - [API Endpoints](#api-endpoints) 15 | - [Contributing](#contributing) 16 | - [License](#license) 17 | 18 | ## Features 19 | 20 | - User authentication and authorization 21 | - Manage products, companies, locations, and brands 22 | - Track product history 23 | - Dashboard with analytics 24 | - Responsive design with Tailwind CSS 25 | 26 | ## Folder Structure 27 | 28 | ```plaintext 29 | C:. 30 | ├───Backend 31 | │ │ .env 32 | │ │ .gitignore 33 | │ │ app.js 34 | │ │ config.npmrc 35 | │ │ package-lock.json 36 | │ │ package.json 37 | │ │ README.md 38 | │ │ 39 | │ ├───controllers 40 | │ │ product_controller.js 41 | │ │ user_controllers.js 42 | │ │ 43 | │ ├───db 44 | │ │ user_db.js 45 | │ │ 46 | │ ├───middlewares 47 | │ │ user_auth.js 48 | │ │ 49 | │ ├───models 50 | │ │ company_model.js 51 | │ │ history_model.js 52 | │ │ locations_models.js 53 | │ │ product_model.js 54 | │ │ user_model.js 55 | │ │ 56 | │ ├───routes 57 | │ │ analyticsRoutes.js 58 | │ │ companyRoutes.js 59 | │ │ historyRoutes.js 60 | │ │ locationRoutes.js 61 | │ │ productRoutes.js 62 | │ │ user_routes.js 63 | │ │ 64 | │ └───utils 65 | │ user_utils.js 66 | │ 67 | └───Frontend 68 | │ .env 69 | │ .eslintrc.cjs 70 | │ .gitignore 71 | │ index.html 72 | │ package-lock.json 73 | │ package.json 74 | │ postcss.config.js 75 | │ README.md 76 | │ tailwind.config.js 77 | │ vite.config.js 78 | │ 79 | ├───public 80 | │ vite.svg 81 | │ 82 | └───src 83 | │ App.jsx 84 | │ index.css 85 | │ main.jsx 86 | │ router.jsx 87 | │ 88 | ├───assets 89 | │ admin-logo.svg 90 | │ authenticate.svg 91 | │ menu.svg 92 | │ react.svg 93 | │ undraw_empty_re.svg 94 | │ user-logo.svg 95 | │ 96 | ├───components 97 | │ HeaderBar.jsx 98 | │ LoadingIndicator.jsx 99 | │ LogoutButton.jsx 100 | │ PopUpComponenet.jsx 101 | │ ShowErrorMessage.jsx 102 | │ ShowSuccessMesasge.jsx 103 | │ SideNavbar.jsx 104 | │ WarrantyExpiringProductsTableComponent.jsx 105 | │ 106 | └───screens 107 | │ InventoryFormScreen.jsx 108 | │ 109 | ├───brands 110 | │ BrandsScreen.jsx 111 | │ EditBrandsScreen.jsx 112 | │ NewBrandsScreen.jsx 113 | │ 114 | ├───dashboard 115 | │ │ DashBoardLayout.jsx 116 | │ │ DashBoardScreen.jsx 117 | │ │ 118 | │ └───components 119 | │ AnalyticsComponent.jsx 120 | │ PieChart.jsx 121 | │ 122 | ├───locations 123 | │ EditLocationScreen.jsx 124 | │ LocationsScreen.jsx 125 | │ NewLocationScreen.jsx 126 | │ 127 | ├───login 128 | │ AuthLayout.jsx 129 | │ LoginScreen.jsx 130 | │ SignupScreen.jsx 131 | │ 132 | ├───product 133 | │ AddNewProductScreen.jsx 134 | │ ProductEditScreen.jsx 135 | │ ProductHistoryScreen.jsx 136 | │ ProductInfoScreen.jsx 137 | │ ProductsScreen.jsx 138 | │ 139 | └───users 140 | │ UserManagementScreen.jsx 141 | │ 142 | └───components 143 | ChangeRolePopup.jsx 144 | ManageUserTableRow.jsx 145 | ``` 146 | 147 | ## Prerequisites 148 | 149 | - Node.js 150 | - npm or yarn 151 | - MongoDB 152 | 153 | ## Installation 154 | 155 | 1. **Clone the repository:** 156 | ```bash 157 | git clone https://github.com/your-username/your-repo.git 158 | cd your-repo 159 | ``` 160 | 161 | 2. **Backend Setup:** 162 | ```bash 163 | cd Backend 164 | npm install 165 | ``` 166 | 167 | 3. **Frontend Setup:** 168 | ```bash 169 | cd ../Frontend 170 | npm install 171 | ``` 172 | 173 | ## Running the Application 174 | 175 | 1. **Run Backend:** 176 | ```bash 177 | cd Backend 178 | npm start 179 | ``` 180 | 181 | 2. **Run Frontend:** 182 | ```bash 183 | cd ../Frontend 184 | npm run dev 185 | ``` 186 | 187 | ## Environment Variables 188 | 189 | Create a `.env` file in the Backend and Frontend directories and configure the following: 190 | 191 | ### Backend `.env`: 192 | ```env 193 | MONGODB_URI=your_mongodb_connection_string 194 | PORT=3000 195 | SECRET_KEY=your_secret_key 196 | NODE_ENV=development 197 | ORIGIN=http://localhost:3000 198 | ``` 199 | 200 | ### Frontend `.env`: 201 | ```env 202 | VITE_SERVER=https://inventory-management-backend-hsaf.onrender.com 203 | VITE_MODE=PROD 204 | VITE_LOCAL=http://localhost:3000 205 | ``` 206 | 207 | ## API Endpoints 208 | 209 | ### User Routes 210 | 211 | - **POST** `/api/v1/users/signup` - Sign up a new user 212 | - **POST** `/api/v1/users/login` - Log in a user 213 | - **GET** `/api/v1/users/logout` - Log out a user 214 | 215 | ### Product Routes 216 | 217 | - **GET** `/api/v1/products` - Get all products 218 | - **POST** `/api/v1/products` - Add a new product 219 | - **PUT** `/api/v1/products/:id` - Update a product 220 | - **DELETE** `/api/v1/products/:id` - Delete a product 221 | 222 | ### History Routes 223 | 224 | - **GET** `/api/v1/history/:productId` - Get product history 225 | 226 | ### Company Routes 227 | 228 | - **GET** `/api/v1/companies` - Get all companies 229 | - **POST** `/api/v1/companies` - Add a new company 230 | 231 | ### Location Routes 232 | 233 | - **GET** `/api/v1/locations` - Get all locations 234 | - **POST** `/api/v1/locations` - Add a new location 235 | 236 | ### Analytics Routes 237 | 238 | - **GET** `/api/v1/analytics` - Get analytics data 239 | 240 | ## Contributing 241 | 242 | Contributions are welcome! Please open an issue or submit a pull request for any changes. 243 | 244 | ## License 245 | 246 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 247 | -------------------------------------------------------------------------------- /Frontend/src/screens/product/ProductHistoryScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import LoadingIndicator from "../../components/LoadingIndicator"; 4 | import ShowErrorMessage from "../../components/ShowErrorMessage"; 5 | import { IoMailOutline } from "react-icons/io5"; 6 | import { FaUser } from "react-icons/fa"; 7 | import { Link, NavLink } from "react-router-dom"; 8 | import { useParams } from "react-router-dom"; 9 | 10 | import { useCallback } from "react"; 11 | import ReactFlow, { 12 | addEdge, 13 | ConnectionLineType, 14 | Panel, 15 | useNodesState, 16 | useEdgesState, 17 | } from "reactflow"; 18 | import dagre from "dagre"; 19 | import { ProductRow } from "./ProductsScreen"; 20 | import { SERVER_URL } from "../../router"; 21 | 22 | function ProductHistoryScreen() { 23 | const params = useParams(); 24 | const [isLoading, setLoading] = useState(true); 25 | const [productData, setData] = useState(null); 26 | const [isError, setError] = useState(""); 27 | 28 | useEffect(() => { 29 | getDataFromApi(); 30 | }, []); 31 | async function getDataFromApi() { 32 | try { 33 | const { data } = await axios.get( 34 | `${SERVER_URL}/api/v1/products/${params.id}/history` 35 | ); 36 | // console.log(data); 37 | setData(data); 38 | } catch (e) { 39 | console.log(e); 40 | setError(e); 41 | } finally { 42 | setLoading(false); 43 | } 44 | } 45 | console.log(productData?.history); 46 | return ( 47 |
48 |
49 |
50 |

Product Information

51 |
52 |
53 | 54 | {isLoading && ( 55 |
56 | 57 |
58 | )} 59 | 60 | {isError && ( 61 |
62 | 68 | reload 69 | 70 | } 71 | /> 72 |
73 | )} 74 | 75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | {isLoading ? ( 93 | 94 | 97 | 98 | ) : ( 99 | 100 | 110 | 113 | 116 | 119 | 122 | 125 | 128 | 131 | 134 | 135 | )} 136 | 137 |
DETAILSSERIAL NUMBERUSED BYisPartRACKMOUNTABLEDATE OF PURCHASEMODELWARRANTYMANUFACTURER
95 | 96 |
101 |
102 |
103 | {productData.title} 104 |
105 |

106 | {productData.description} 107 |

108 |
109 |
111 | {productData.serialNo} 112 | 114 | {productData.user} 115 | 117 | {productData.isPart ? "TRUE" : "FALSE"} 118 | 120 | {productData.rackMountable ? "TRUE" : "FALSE"} 121 | 123 | {productData.dateOfPurchase.split("T")[0]} 124 | 126 | {productData.model} 127 | 129 | {productData.warrantyMonths} 130 | 132 | {productData.manufacturer.name} 133 |
138 |
139 |
140 | 141 |
142 |
143 |

Product History

144 |
145 |
146 | 147 | {productData && ( 148 | 149 | )} 150 |
151 |
152 | ); 153 | } 154 | const HistoryTable = ({ historyInformation }) => { 155 | return ( 156 |
157 |
158 | 159 | 160 | 161 | 164 | 167 | 170 | 173 | 174 | 175 | 176 | {historyInformation.map((history) => ( 177 | 178 | 181 | 184 | 191 | 200 | 201 | ))} 202 | 203 |
162 | Location 163 | 165 | Description 166 | 168 | Status 169 | 171 | Date 172 |
179 | {history.location.name} 180 | 182 | {history.location.description} 183 | 185 | {history.status.map((status, index) => ( 186 |
187 | {status.name} 188 |
189 | ))} 190 |
192 | {history.status.map((status, index) => ( 193 |
194 | 195 | {new Date(status.date).toLocaleDateString()} 196 | 197 |
198 | ))} 199 |
204 |
205 |
206 | ); 207 | }; 208 | 209 | export default ProductHistoryScreen; 210 | -------------------------------------------------------------------------------- /Frontend/src/screens/InventoryFormScreen.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState } from "react"; 3 | import { useOutletContext } from "react-router-dom"; 4 | import { SERVER_URL } from "../router"; 5 | 6 | function InventoryForm() { 7 | const [isLoading, seELoading] = useState(false); 8 | const [isError, setError] = useState(null); 9 | const [isSuccess, setSuccess] = useState(false); 10 | 11 | const [data] = useOutletContext(); 12 | const [formData, setFormData] = useState({ 13 | createdBy: data.user._id, 14 | title: "", 15 | description: "", 16 | serialNo: "", 17 | manufacturer: "", 18 | model: "", 19 | dateOfPurchase: "", 20 | warrantyMonths: "", 21 | status: "none", 22 | user: "none", 23 | rackMountable: false, 24 | isPart: false, 25 | }); 26 | 27 | const handleChange = (e) => { 28 | const { name, value, type, checked } = e.target; 29 | setFormData((prevData) => ({ 30 | ...prevData, 31 | [name]: type === "checkbox" ? checked : value, 32 | })); 33 | }; 34 | 35 | const handleSubmit = async (e) => { 36 | e.preventDefault(); 37 | // Add form submission logic here 38 | console.log(formData); 39 | 40 | try { 41 | if (isError) setError(false); 42 | if (isSuccess) setSuccess(false); 43 | 44 | const { data, status } = await axios.post( 45 | `${SERVER_URL}/api/v1/products/`, 46 | formData, 47 | { 48 | withCredentials: "include", 49 | headers: { 50 | "Content-Type": "application/json", 51 | }, 52 | } 53 | ); 54 | 55 | if (status === 201) { 56 | setSuccess(true); 57 | setFormData({ 58 | ...formData, 59 | title: "", 60 | description: "", 61 | serialNo: "", 62 | manufacturer: "", 63 | model: "", 64 | dateOfPurchase: "", 65 | warrantyMonths: "", 66 | status: "none", 67 | user: "none", 68 | rackMountable: false, 69 | isPart: false, 70 | }); 71 | } 72 | if (status === 400) { 73 | throw new Error(data.error); 74 | } 75 | } catch (e) { 76 | setError("Error while addin new item"); 77 | console.log(e); 78 | } finally { 79 | seELoading(false); 80 | } 81 | }; 82 | 83 | return ( 84 |
85 | {isError && ( 86 |
87 | {isError} 88 |
89 | )} 90 | {isSuccess && ( 91 |
92 | {"New Product added to inventory successfully"} 93 |
94 | )} 95 |
96 | 99 | 108 |
109 |
110 | 113 |