Everything the inspector will check, ask, and test — with exact answers from your code. Read this before your assessment.
backend-project/src/localhost:5173, parses JSON bodies, calls ensureDbAndTables() to auto-create the DB, then mounts all 4 route groups and starts listening on port 5000.users, stock_in, and stock_out tables with all primary keys and foreign keys. Uses mysql2/promise connection pool.Authorization: Bearer <token> header from every protected request. Verifies the JWT using JWT_SECRET. Attaches req.user (id + username) for use in routes. Returns 401 if token is missing or invalid.frontend-project/src/index.html's #root div using ReactDOM. Imports global CSS. This is where React starts.Protected component that redirects to /login if no user. Wraps everything in AuthProvider.login(token, username) saves to localStorage. logout() clears it. On page refresh, reads from localStorage to restore session. The useAuth() hook gives any component access.http://localhost:5000/api. Request interceptor automatically attaches Authorization: Bearer <token> from localStorage to every request. Exports named functions for all CRUD operations and the report./api/auth/login or /api/auth/register. On success saves token, redirects to /. Fully styled with Tailwind + emerald theme./api/reports/stock-status and renders a table showing: ItemName, TotalQuantityReceived, TotalQuantityIssuedOut, RemainingQuantity for all items. Styled table with consistent colors.useAuth().logout() and redirects to /login. Uses React Router's <Link> component..jsx files in src/. This is how Tailwind knows which classes to include in the final CSS bundle.localhost:5173.http://localhost:5000START BACKEND.bat or cd backend-project && npm start). Use the browser or a tool like Postman/Thunder Client.| Method | Endpoint | What It Does | Auth? | Body / Params |
|---|---|---|---|---|
| POST | /api/auth/register | Create new user account, bcrypt hashes password | No | { username, password } |
| POST | /api/auth/login | Login, returns JWT token valid 8 hours | No | { username, password } |
| GET | /api/stockin | Get all stock-in records (optional search) | Bearer | ?search=cement |
| GET | /api/stockin/:id | Get one stock-in record by ID | Bearer | — |
| POST | /api/stockin | Add new stock-in, auto-calculates TotalQuantityIn | Bearer | { ItemName, Description, QuantityIn, SupplierName, StockInDate } |
| PUT | /api/stockin/:id | Update existing stock-in record | Bearer | Same as POST body |
| DELETE | /api/stockin/:id | Delete stock-in (blocked if stock-out records exist) | Bearer | — |
| GET | /api/stockout | Get all stock-out records (optional search) | Bearer | ?search=kevin |
| GET | /api/stockout/:id | Get one stock-out record by ID | Bearer | — |
| POST | /api/stockout | Issue stock out, validates remaining stock first | Bearer | { QuantityOut, StockOutDate, stockInId } |
| PUT | /api/stockout/:id | Update stock-out record | Bearer | Same as POST body |
| DELETE | /api/stockout/:id | Delete stock-out record | Bearer | — |
| GET | /api/reports/stock-status | Daily report: all items with total in, out, remaining | Bearer | — |
| GET | /health | Check if server is running — returns { ok: true } |
No | — |
localStorage with key sms_token. Every request to protected endpoints sends it as Authorization: Bearer <token>.
bcrypt.hash(password, 12) creates a salted hash (12 rounds). When logging in, bcrypt.compare(password, hash) verifies it. Even if the database is leaked, passwords cannot be read.
users table has User_Name VARCHAR(100) NOT NULL UNIQUE. If you try to register with an existing username, MySQL throws ER_DUP_ENTRY and the backend returns { error: "Username already exists" }.
sms_token and sms_user in localStorage. On page refresh, AuthContext reads them back — so user stays logged in. Axios interceptor in api/index.js reads sms_token and adds the header automatically to every request.
sms_token after login. Then try accessing /api/stockin without the token — you get 401 Unauthorized./api/auth/loginbcrypt.compare()jwt.sign({ id, username }, JWT_SECRET, { expiresIn: '8h' })localStorage as sms_tokenAuthorization: Bearer <token>middleware/auth.js verifies it with jwt.verify()http://localhost:5173 in the browserhttp://localhost:5000/health in browser → should show {"ok":true}http://localhost:5000/api/stockin without token → should show {"error":"Unauthorized"}http://localhost:5000/health
ensureDbAndTables() function runs at server startup. It:
CREATE DATABASE IF NOT EXISTS SMSCREATE TABLE IF NOT EXISTS users (...)CREATE TABLE IF NOT EXISTS stock_in (...)CREATE TABLE IF NOT EXISTS stock_out (...) with foreign keys to both stock_in and usersawait conn.query(`CREATE DATABASE IF NOT EXISTS \`SMS\``);
ON DELETE CASCADE, meaning if a parent record is deleted, the child records are deleted too.
GET /api/stockin?search=cementAND (s.ItemName LIKE ? OR s.SupplierName LIKE ? OR s.Description LIKE ?)
%cement% pattern matches any record containing the word "cement" anywhere in the field. For Stock Out, search works on ItemName and User_Name.
GET /api/reports/stock-status endpoint in src/routes/reports.js.LEFT JOIN (to include items with zero stock-out)ItemNameSUM(QuantityIn) as TotalQuantityReceivedCOALESCE(SUM(QuantityOut), 0) as TotalQuantityIssuedOut (0 if never issued)RemainingQuantitySELECT si.ItemName, SUM(si.QuantityIn) AS TotalQuantityReceived, COALESCE(SUM(so.QuantityOut), 0) AS TotalQuantityIssuedOut, SUM(si.QuantityIn) - COALESCE(SUM(so.QuantityOut), 0) AS RemainingQuantity FROM stock_in si LEFT JOIN stock_out so ON so.stockInId = si.id GROUP BY si.ItemName ORDER BY si.ItemName ASC
SELECT COALESCE(SUM(QuantityIn),0) FROM stock_in WHERE ItemName=? to get the existing total, then add the new quantity.QuantityOut > remaining, the API returns a 400 error with the exact remaining quantity.cors({ origin: 'http://localhost:5173', credentials: true }).
useEffect(() => { fetchData() }, []))login() function after successful loginreact-router-dom).BrowserRouter wraps the entire app to enable URL-based routingRoutes and Route components map URLs to page components/login → LoginPage (public)/stockin, /stockout, /reports → wrapped in <Protected>Protected component checks useAuth().user — if null, redirects to /login with <Navigate><Link to="/stockin"> for client-side navigation (no full page reload).
dotenv package when the server starts (require('dotenv').config()).DB_HOST — MySQL server hostname (localhost)DB_USER — MySQL username (root)DB_PASSWORD — MySQL passwordDB_NAME — Database name (SMS)DB_PORT — MySQL port (3306)JWT_SECRET — Secret key used to sign JWT tokensPORT — Port the Express server listens on (5000)src/routes/stockOut.js, the POST endpoint:
TotalQuantityInSELECT SUM(QuantityOut) FROM stock_out WHERE stockInId=?remaining = TotalQuantityIn - existingTotalQuantityOut > remaining → returns HTTP 400 with error message: "Insufficient stock. Remaining: X"localhost:5173 (Vite dev server). The Express backend runs on localhost:5000. When the frontend tries to call the backend, the browser blocks it by default.server.js, the CORS middleware is configured to allow requests from http://localhost:5173 specifically:
app.use(cors({ origin: 'http://localhost:5173', credentials: true }));
tailwind.config.js — tells Tailwind to scan ./src/**/*.{js,jsx} files for class namespostcss.config.js — PostCSS plugin that processes Tailwindsrc/index.css — contains the 3 Tailwind directives: @tailwind base, @tailwind components, @tailwind utilities<div className="min-h-screen bg-gradient-to-br from-emerald-900 to-emerald-700">
max-w-7xl, flex, grid, px-4, py-8 handle layout and spacing. Hover states like hover:bg-emerald-900 handle interactions.
backend-project/package.json. The dev script in package.json uses it:
"dev": "nodemon src/server.js", "start": "node src/server.js"
npm start (using plain node) is fine since we are not actively editing code.
START BACKEND.bat OR open terminal in backend-project/ and run npm start. Wait until you see "✅ SMS Backend → http://localhost:5000" in the console. This also auto-creates the SMS database and tables.START FRONTEND.bat OR open terminal in frontend-project/ and run npm run dev. Open http://localhost:5173 in Chrome.sms_token exists.