From 6be34c93aadcc9374c1a40937e48499efc874924 Mon Sep 17 00:00:00 2001 From: Theis Gaedigk Date: Wed, 8 Jul 2026 14:56:43 +0200 Subject: [PATCH] refactor: edit tailwindcss classes and change code formatting, therefore added .prettierrc file fix: frontend bug where the passwords won't be checked when the password is changed --- .prettierrc | 7 + backend/Dockerfile | 13 +- backend/babel.config.json | 2 +- backend/package.json | 2 +- .../routes/app/database/products.database.ts | 236 +++++---- .../routes/app/database/storage.database.ts | 125 ++--- backend/routes/app/database/users.database.ts | 181 +++---- backend/routes/app/products.route.ts | 490 +++++++++--------- backend/routes/app/storage.route.ts | 315 +++++------ backend/routes/app/users.route.ts | 434 ++++++++-------- backend/server.ts | 177 +++---- backend/services/helperFuncs.ts | 22 +- backend/services/tokenService.ts | 42 +- backend/tsconfig.json | 10 +- backend/vite.config.ts | 44 +- frontend/src/components/LoginCard.tsx | 2 +- frontend/src/components/Sidebar.tsx | 2 +- frontend/src/components/StorageRow.tsx | 4 +- .../src/components/modals/AddStorageModal.tsx | 12 +- .../components/modals/ChangePasswordModal.tsx | 40 +- frontend/src/pages/AddProduct.tsx | 5 +- frontend/src/pages/Inventory.tsx | 13 +- frontend/src/pages/ProductQuickView.tsx | 20 +- frontend/src/pages/Settings.tsx | 21 +- frontend/src/pages/Storages.tsx | 9 +- frontend/src/pages/ViewProduct.tsx | 66 +-- frontend/src/utils/i18n/locales/de/de.json | 2 + frontend/src/utils/i18n/locales/en/en.json | 2 + frontend/tsconfig.app.json | 5 +- package.json | 9 +- shared/package.json | 18 +- shared/src/error/errorcodes.enum.ts | 57 +- shared/tsconfig.base.json | 9 + shared/tsconfig.cjs.json | 11 + shared/tsconfig.esm.json | 12 + shared/tsconfig.json | 6 +- 36 files changed, 1280 insertions(+), 1145 deletions(-) create mode 100644 .prettierrc create mode 100644 shared/tsconfig.base.json create mode 100644 shared/tsconfig.cjs.json create mode 100644 shared/tsconfig.esm.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..472b176 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "printWidth": 80, + "tabWidth": 2 +} \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index d2a076c..a78dca1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -2,34 +2,45 @@ FROM node:22-alpine AS builder WORKDIR /app +# Workspace manifests (npm workspaces need these present) COPY package.json package-lock.json ./ COPY backend/package.json backend/ COPY frontend/package.json frontend/ COPY shared/package.json shared/ +# Install all deps for the monorepo (includes devDeps for building) RUN npm ci +# Copy sources required to build shared + backend COPY shared/ shared/ COPY backend/ backend/ +# Build shared (now produces dist/esm + dist/cjs + dist/types) and then backend RUN npm run build --workspace=shared RUN npm run build --workspace=backend + FROM node:22-alpine AS runner ENV NODE_ENV=production WORKDIR /app +# Workspace manifests again (so npm can install prod deps for backend + shared) COPY package.json package-lock.json ./ COPY backend/package.json backend/ COPY frontend/package.json frontend/ COPY shared/package.json shared/ +# Install prod deps only RUN npm ci --omit=dev +# Copy runtime artifacts COPY --from=builder /app/backend/dist backend/dist -COPY --from=builder /app/backend/database.scheme.sql backend/ +COPY --from=builder /app/backend/database.scheme.sql backend/database.scheme.sql + +# Copy the full shared dist output (esm/cjs/types) COPY --from=builder /app/shared/dist shared/dist +COPY shared/package.json shared/package.json EXPOSE 8004 WORKDIR /app/backend diff --git a/backend/babel.config.json b/backend/babel.config.json index c3ea17c..6e8dc4f 100644 --- a/backend/babel.config.json +++ b/backend/babel.config.json @@ -11,4 +11,4 @@ ], "@babel/preset-typescript" ] -} \ No newline at end of file +} diff --git a/backend/package.json b/backend/package.json index 80e8bbb..5fb4020 100644 --- a/backend/package.json +++ b/backend/package.json @@ -27,4 +27,4 @@ "typescript": "~6.0.2", "tsx": "^4.23.0" } -} \ No newline at end of file +} diff --git a/backend/routes/app/database/products.database.ts b/backend/routes/app/database/products.database.ts index b724c33..7468a2d 100644 --- a/backend/routes/app/database/products.database.ts +++ b/backend/routes/app/database/products.database.ts @@ -1,48 +1,61 @@ -import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise"; +import mysql, { + type ResultSetHeader, + type RowDataPacket, +} from "mysql2/promise"; import dotenv from "dotenv"; -import {returnErrorCode} from "../../../services/helperFuncs.js"; -import {PRODUCT_ERROR_CODE} from "@stockhome/shared"; +import { returnErrorCode } from "../../../services/helperFuncs.js"; +import { PRODUCT_ERROR_CODE } from "@stockhome/shared"; dotenv.config(); const pool = mysql.createPool({ - host: process.env.DB_HOST!, - user: process.env.DB_USER!, - password: process.env.DB_PASSWORD!, - database: process.env.DB_NAME!, + host: process.env.DB_HOST!, + user: process.env.DB_USER!, + password: process.env.DB_PASSWORD!, + database: process.env.DB_NAME!, }); export const newProduct = async ( - name: string, - description: string, - price: string, - amount: string, - storage_location: string, - expiry_date: string, - bottling_date: string, -): Promise => { - const newPrice = price !== "" ? price : null; + name: string, + description: string, + price: string, + amount: string, + storage_location: string, + expiry_date: string, + bottling_date: string, +): Promise< + boolean | { success: boolean; code: string; data: null; message: string } +> => { + const newPrice = price !== "" ? price : null; - const [result] = await pool.query( - "INSERT INTO products (name, description, price, amount, storage_location, expiry_date, bottling_date) VALUES (?, ?, ?, ?, UUID_TO_BIN(?), ?, ?)", - [name, description, newPrice, amount, storage_location, expiry_date, bottling_date], - ); + const [result] = await pool.query( + "INSERT INTO products (name, description, price, amount, storage_location, expiry_date, bottling_date) VALUES (?, ?, ?, ?, UUID_TO_BIN(?), ?, ?)", + [ + name, + description, + newPrice, + amount, + storage_location, + expiry_date, + bottling_date, + ], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SP001", - data: null, - message: "New Product successfully created.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SP001", + data: null, + message: "New Product successfully created.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_CREATED); + return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_CREATED); }; export const productDetails = async (uuid: string) => { - const [result] = await pool.query( - `SELECT BIN_TO_UUID(p.uuid) AS uuid, + const [result] = await pool.query( + `SELECT BIN_TO_UUID(p.uuid) AS uuid, p.name, p.description, p.price, @@ -55,23 +68,23 @@ export const productDetails = async (uuid: string) => { FROM products p JOIN storage_locations s ON p.storage_location = s.uuid WHERE p.uuid = UUID_TO_BIN(?)`, - [uuid], - ); + [uuid], + ); - if (result.length > 0) { - return { - success: true, - code: "SP003", - data: result[0], - message: "Product details successfully loaded.", - }; - } + if (result.length > 0) { + return { + success: true, + code: "SP003", + data: result[0], + message: "Product details successfully loaded.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND); + return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND); }; export const allProducts = async () => { - const [result] = await pool.query(` + const [result] = await pool.query(` SELECT BIN_TO_UUID(p.uuid) AS uuid, p.name, p.description, @@ -89,52 +102,52 @@ export const allProducts = async () => { WHERE p.deleted = 0 `); - if (result.length > 0) { - return { - success: true, - code: "SP002", - data: result, - message: "All products successfully loaded.", - }; - } + if (result.length > 0) { + return { + success: true, + code: "SP002", + data: result, + message: "All products successfully loaded.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND); + return returnErrorCode(PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND); }; export const setAmount = async (itemUUID: string, amount: number) => { - const [result] = await pool.query( - `UPDATE products + const [result] = await pool.query( + `UPDATE products SET amount = ? WHERE uuid = UUID_TO_BIN(?)`, - [amount, itemUUID], - ); + [amount, itemUUID], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SP004", - data: null, - message: "Amount successfully updated.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SP004", + data: null, + message: "Amount successfully updated.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_AMOUNT_NOT_UPDATED); + return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_AMOUNT_NOT_UPDATED); }; export const updateItem = async ( - itemUUID: string, - newValues: { - name: string; - description: string; - price: string; - amount: string; - storage_location_uuid: string; - expiry_date: string; - bottling_date: string; - }, + itemUUID: string, + newValues: { + name: string; + description: string; + price: string; + amount: string; + storage_location_uuid: string; + expiry_date: string; + bottling_date: string; + }, ) => { - const [result] = await pool.query( - `UPDATE products + const [result] = await pool.query( + `UPDATE products SET name = ?, description = ?, price = ?, @@ -143,47 +156,46 @@ export const updateItem = async ( expiry_date = ?, bottling_date = ? WHERE uuid = UUID_TO_BIN(?);`, - [ - newValues.name, - newValues.description, - newValues.price, - newValues.amount, - newValues.storage_location_uuid, - newValues.expiry_date, - newValues.bottling_date, - itemUUID, - ], - ); + [ + newValues.name, + newValues.description, + newValues.price, + newValues.amount, + newValues.storage_location_uuid, + newValues.expiry_date, + newValues.bottling_date, + itemUUID, + ], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SP005", - data: null, - message: "Item updated successfully.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SP005", + data: null, + message: "Item updated successfully.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_UPDATED); + return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_UPDATED); }; export const deleteProduct = async (uuid: string) => { - const [result] = await pool.query( - `UPDATE products + const [result] = await pool.query( + `UPDATE products SET deleted = 1 WHERE uuid = UUID_TO_BIN(?);`, - [uuid], - ); + [uuid], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SP006", - data: null, - message: "Product deleted successfully.", - }; - ; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SP006", + data: null, + message: "Product deleted successfully.", + }; + } - return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED); -}; \ No newline at end of file + return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED); +}; diff --git a/backend/routes/app/database/storage.database.ts b/backend/routes/app/database/storage.database.ts index 952e33b..ec03d62 100644 --- a/backend/routes/app/database/storage.database.ts +++ b/backend/routes/app/database/storage.database.ts @@ -1,87 +1,90 @@ -import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise"; +import mysql, { + type ResultSetHeader, + type RowDataPacket, +} from "mysql2/promise"; import dotenv from "dotenv"; -import {STORAGE_ERROR_CODE} from "@stockhome/shared"; -import {returnErrorCode} from "../../../services/helperFuncs.js"; +import { STORAGE_ERROR_CODE } from "@stockhome/shared"; +import { returnErrorCode } from "../../../services/helperFuncs.js"; dotenv.config(); const pool = mysql.createPool({ - host: process.env.DB_HOST!, - user: process.env.DB_USER!, - password: process.env.DB_PASSWORD!, - database: process.env.DB_NAME!, + host: process.env.DB_HOST!, + user: process.env.DB_USER!, + password: process.env.DB_PASSWORD!, + database: process.env.DB_NAME!, }); export const allStorages = async () => { - const [result] = await pool.query( - "SELECT BIN_TO_UUID(uuid) AS uuid, name, description, created_at, updated_at FROM storage_locations;", - ); + const [result] = await pool.query( + "SELECT BIN_TO_UUID(uuid) AS uuid, name, description, created_at, updated_at FROM storage_locations;", + ); - if (result.length > 0) { - return { - success: true, - code: "SS001", - data: result, - message: "Successfully fetched all storage locations.", - }; - } + if (result.length > 0) { + return { + success: true, + code: "SS001", + data: result, + message: "Successfully fetched all storage locations.", + }; + } - return returnErrorCode(STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND); + return returnErrorCode(STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND); }; export const newStorage = async (name: string, description: string) => { - const [result] = await pool.query( - "INSERT INTO storage_locations (name, description) VALUES (?, ?)", - [name, description], - ); + const [result] = await pool.query( + "INSERT INTO storage_locations (name, description) VALUES (?, ?)", + [name, description], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SS002", - data: null, - message: "Successfully created new storage location.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SS002", + data: null, + message: "Successfully created new storage location.", + }; + } - return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_CREATED); + return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_CREATED); }; export const updateStorage = async ( - uuid: string, - values: { name: string; description: string }, + uuid: string, + values: { name: string; description: string }, ) => { - const [result] = await pool.query( - "UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);", - [values.name, values.description, uuid], - ); + const [result] = await pool.query( + "UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);", + [values.name, values.description, uuid], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SS003", - data: null, - message: "Successfully updated storage location.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SS003", + data: null, + message: "Successfully updated storage location.", + }; + } - return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_UPDATED); + return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_UPDATED); }; export const deleteStorage = async (uuid: string) => { - const [result] = await pool.query( - "DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);", - [uuid], - ); + const [result] = await pool.query( + "DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);", + [uuid], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SS004", - data: null, - message: "Successfully deleted storage location.", - }; - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SS004", + data: null, + message: "Successfully deleted storage location.", + }; + } - return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_DELETED); -}; \ No newline at end of file + return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_DELETED); +}; diff --git a/backend/routes/app/database/users.database.ts b/backend/routes/app/database/users.database.ts index abd1c77..db3c82b 100644 --- a/backend/routes/app/database/users.database.ts +++ b/backend/routes/app/database/users.database.ts @@ -1,132 +1,135 @@ -import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise"; +import mysql, { + type ResultSetHeader, + type RowDataPacket, +} from "mysql2/promise"; import dotenv from "dotenv"; -import {GENERAL_ERROR_CODE, USER_ERROR_CODE} from "@stockhome/shared"; -import {returnErrorCode} from "../../../services/helperFuncs.js"; +import { GENERAL_ERROR_CODE, USER_ERROR_CODE } from "@stockhome/shared"; +import { returnErrorCode } from "../../../services/helperFuncs.js"; import type { AuthTokenPayload } from "../../../services/tokenService"; dotenv.config(); const pool = mysql.createPool({ - host: process.env.DB_HOST!, - user: process.env.DB_USER!, - password: process.env.DB_PASSWORD!, - database: process.env.DB_NAME!, + host: process.env.DB_HOST!, + user: process.env.DB_USER!, + password: process.env.DB_PASSWORD!, + database: process.env.DB_NAME!, }); export const findUser = async (username: string, password: string) => { - const [result] = await pool.query( - "SELECT BIN_TO_UUID(uuid) AS uuid, username, first_name, last_name, email, is_admin, is_active, last_login FROM users WHERE username = ? AND password = ?;", - [username, password], - ); + const [result] = await pool.query( + "SELECT BIN_TO_UUID(uuid) AS uuid, username, first_name, last_name, email, is_admin, is_active, last_login FROM users WHERE username = ? AND password = ?;", + [username, password], + ); - const userRow = result[0]; + const userRow = result[0]; - if (!userRow) { - return returnErrorCode(USER_ERROR_CODE.WRONG_USERNAME_PASSWORD); - } + if (!userRow) { + return returnErrorCode(USER_ERROR_CODE.WRONG_USERNAME_PASSWORD); + } - // Cast DB row to AuthTokenPayload for token generation / typing - const user = userRow as unknown as AuthTokenPayload; + // Cast DB row to AuthTokenPayload for token generation / typing + const user = userRow as unknown as AuthTokenPayload; - if (!user.is_active) { - return returnErrorCode(USER_ERROR_CODE.USER_IS_DEACTIVATED); - } + if (!user.is_active) { + return returnErrorCode(USER_ERROR_CODE.USER_IS_DEACTIVATED); + } - return { - success: true, - code: "SU001", - data: user, - message: "Successfully found user.", - }; + return { + success: true, + code: "SU001", + data: user, + message: "Successfully found user.", + }; }; export const loginUser = async (username: string) => { - const [result] = await pool.query( - "UPDATE users SET last_login = NOW() WHERE username = ?;", - [username], - ); + const [result] = await pool.query( + "UPDATE users SET last_login = NOW() WHERE username = ?;", + [username], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SU002", - data: null, - message: "Successfully logged in user.", - }; - } else { - return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SU002", + data: null, + message: "Successfully logged in user.", + }; + } else { + return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); + } }; export const updateSettings = async (payload: { - "app-name": string; - currency: string; + "app-name": string; + currency: string; }) => { - const appName = payload["app-name"]; - const currency = payload.currency; + const appName = payload["app-name"]; + const currency = payload.currency; - const [result] = await pool.query( - `UPDATE app_settings + const [result] = await pool.query( + `UPDATE app_settings SET value = CASE name WHEN "app-name" THEN ? WHEN "currency" THEN ? ELSE value END WHERE name IN ("app-name", "currency");`, - [appName, currency], - ); + [appName, currency], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SU003", - data: null, - message: "Successfully updated settings.", - }; - } else { - return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); - } + if (result.affectedRows > 0) { + return { + success: true, + code: "SU003", + data: null, + message: "Successfully updated settings.", + }; + } else { + return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); + } }; export const getSettings = async () => { - const [result] = await pool.query( - `SELECT * + const [result] = await pool.query( + `SELECT * FROM app_settings;`, - ); + ); - if (result.length > 0) { - return { - success: true, - code: "SU004", - data: result, - message: "Successfully fetched settings.", - }; - } else { - return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); - } + if (result.length > 0) { + return { + success: true, + code: "SU004", + data: result, + message: "Successfully fetched settings.", + }; + } else { + return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); + } }; export const changePassword = async ( - username: string, - currentPasswordUser: string, - newPassword: string, + username: string, + currentPasswordUser: string, + newPassword: string, ) => { - const [result] = await pool.query( - `UPDATE users + const [result] = await pool.query( + `UPDATE users SET password = ? WHERE username = ? AND password = ?;`, - [newPassword, username, currentPasswordUser], - ); + [newPassword, username, currentPasswordUser], + ); - if (result.affectedRows > 0) { - return { - success: true, - code: "SU005", - data: null, - message: "Successfully fetched settings.", - }; - } else { - return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR); - } -}; \ No newline at end of file + if (result.affectedRows > 0) { + return { + success: true, + code: "SU005", + data: null, + message: "Successfully fetched settings.", + }; + } else { + return returnErrorCode(USER_ERROR_CODE.PASSWORD_CHANGE_FAILED); + } +}; diff --git a/backend/routes/app/products.route.ts b/backend/routes/app/products.route.ts index 1548a8b..b45b576 100644 --- a/backend/routes/app/products.route.ts +++ b/backend/routes/app/products.route.ts @@ -1,284 +1,284 @@ import express from "express"; import dotenv from "dotenv"; -import {authenticate} from "../../services/tokenService"; +import { authenticate } from "../../services/tokenService"; import { - allProducts, - deleteProduct, - newProduct, - productDetails, - setAmount, - updateItem, + allProducts, + deleteProduct, + newProduct, + productDetails, + setAmount, + updateItem, } from "./database/products.database"; -import {GENERAL_ERROR_CODE, PRODUCT_ERROR_CODE} from "@stockhome/shared"; +import { GENERAL_ERROR_CODE, PRODUCT_ERROR_CODE } from "@stockhome/shared"; dotenv.config(); const router = express.Router(); router.post("/new-product", authenticate, async (req, res) => { - const { - name, - description, - price, - amount, - storage_location, - expiry_date, - bottling_date, - } = req.body; + const { + name, + description, + price, + amount, + storage_location, + expiry_date, + bottling_date, + } = req.body; - if (!name) { - return res.status(400).json({ - success: false, - code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - const result = await newProduct( - name, - description, - price, - amount, - storage_location, - expiry_date, - bottling_date, - ); - - if (!result || typeof result !== "object") { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SP001") { - return res.status(201).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (!name) { + return res.status(400).json({ + success: false, + code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1], }); + } + + const result = await newProduct( + name, + description, + price, + amount, + storage_location, + expiry_date, + bottling_date, + ); + + if (!result || typeof result !== "object") { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SP001") { + return res.status(201).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.get("/all-products", authenticate, async (req, res) => { - const result = await allProducts(); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SP002") { - return res.status(200).json({ - success: true, - code: result.code, - data: result.data, - message: result.message, - }); - } - - if (result.code === PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND[0]) { - return res.status(404).json({ - success: false, - code: result.code, - data: null, - message: result.message, - }); - } + const result = await allProducts(); + if (!result) { return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], }); + } + + if (result.code === "SP002") { + return res.status(200).json({ + success: true, + code: result.code, + data: result.data, + message: result.message, + }); + } + + if (result.code === PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND[0]) { + return res.status(404).json({ + success: false, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.get("/view", async (req, res) => { - const uuid = req.query.uuid; + const uuid = req.query.uuid; - if (typeof uuid !== "string") { - return res.status(400).json({ - success: false, - code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - const result = await productDetails(uuid); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SP003") { - return res.status(200).json({ - success: true, - code: result.code, - data: result.data, - message: result.message, - }); - } - - if (result.code === PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND[0]) { - return res.status(404).json({ - success: false, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (typeof uuid !== "string") { + return res.status(400).json({ + success: false, + code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], }); + } + + const result = await productDetails(uuid); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SP003") { + return res.status(200).json({ + success: true, + code: result.code, + data: result.data, + message: result.message, + }); + } + + if (result.code === PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND[0]) { + return res.status(404).json({ + success: false, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.put("/mutate/set-amount", authenticate, async (req, res) => { - const amount = req.query.amount; - const itemUUID = req.query.item; + const amount = req.query.amount; + const itemUUID = req.query.item; - if (typeof itemUUID !== "string" || typeof amount !== "number") { - return res.status(400).json({ - success: false, - code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - const result = await setAmount(itemUUID, amount); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SP004") { - return res.status(200).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (typeof itemUUID !== "string" || typeof amount !== "number") { + return res.status(400).json({ + success: false, + code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], }); + } + + const result = await setAmount(itemUUID, amount); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SP004") { + return res.status(200).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/mutate/update-item", authenticate, async (req, res) => { - const itemUUID = req.query.item; - const newValues = req.body; + const itemUUID = req.query.item; + const newValues = req.body; - if (typeof itemUUID !== "string" || !newValues) { - return res.status(400).json({ - success: false, - code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - const result = await updateItem(itemUUID, newValues); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SP005") { - return res.status(200).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (typeof itemUUID !== "string" || !newValues) { + return res.status(400).json({ + success: false, + code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1], }); + } + + const result = await updateItem(itemUUID, newValues); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SP005") { + return res.status(200).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/delete-selection", authenticate, async (req, res) => { - const uuidArray = req.body; + const uuidArray = req.body; - if (!Array.isArray(uuidArray) || uuidArray.length === 0) { - return res.status(400).json({ - success: false, - code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - for (const uuid of uuidArray) { - const response = await deleteProduct(uuid); - - if (!response) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (response.code !== "SP006") { - return res.status(500).json({ - success: false, - code: response.code || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[0], - data: null, - message: response.message || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[1], - }); - } - } - - return res.status(202).json({ - success: true, - code: "SP006", - data: null, - message: "All selected products deleted successfully.", + if (!Array.isArray(uuidArray) || uuidArray.length === 0) { + return res.status(400).json({ + success: false, + code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1], }); + } + + for (const uuid of uuidArray) { + const response = await deleteProduct(uuid); + + if (!response) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (response.code !== "SP006") { + return res.status(500).json({ + success: false, + code: response.code || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[0], + data: null, + message: response.message || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[1], + }); + } + } + + return res.status(202).json({ + success: true, + code: "SP006", + data: null, + message: "All selected products deleted successfully.", + }); }); -export default router; \ No newline at end of file +export default router; diff --git a/backend/routes/app/storage.route.ts b/backend/routes/app/storage.route.ts index 158e5ee..a1f1b26 100644 --- a/backend/routes/app/storage.route.ts +++ b/backend/routes/app/storage.route.ts @@ -1,184 +1,189 @@ import express from "express"; import dotenv from "dotenv"; -import {authenticate} from "../../services/tokenService"; -import {allStorages, deleteStorage, newStorage, updateStorage,} from "./database/storage.database"; -import {GENERAL_ERROR_CODE, STORAGE_ERROR_CODE} from "@stockhome/shared"; +import { authenticate } from "../../services/tokenService"; +import { + allStorages, + deleteStorage, + newStorage, + updateStorage, +} from "./database/storage.database"; +import { GENERAL_ERROR_CODE, STORAGE_ERROR_CODE } from "@stockhome/shared"; dotenv.config(); const router = express.Router(); router.get("/all-storages", authenticate, async (req, res) => { - const result = await allStorages(); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR, - data: null, - message: "Unknown error", - }); - } - - if (result.code === "SS001") { - return res.status(200).json({ - success: true, - code: result.code, - data: result.data, - message: result.message, - }); - } - - if (result.code === STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND[0]) { - return res.status(404).json({ - success: false, - code: result.code, - data: null, - message: result.message, - }); - } + const result = await allStorages(); + if (!result) { return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR, + data: null, + message: "Unknown error", }); + } + + if (result.code === "SS001") { + return res.status(200).json({ + success: true, + code: result.code, + data: result.data, + message: result.message, + }); + } + + if (result.code === STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND[0]) { + return res.status(404).json({ + success: false, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/new-storage", authenticate, async (req, res) => { - const {name, description} = req.body; + const { name, description } = req.body; - let desc = description; + let desc = description; - if (!name) { - return res.status(400).json({ - success: false, - code: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - if (description == "") { - desc = null; - } - - const result = await newStorage(name, desc); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - }); - } - - if (result.code === "SS002") { - return res.status(201).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (!name) { + return res.status(400).json({ + success: false, + code: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[1], }); + } + + if (description == "") { + desc = null; + } + + const result = await newStorage(name, desc); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + }); + } + + if (result.code === "SS002") { + return res.status(201).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/update-storage", authenticate, async (req, res) => { - const storageUUID = req.query.storageUUID; - const values = req.body; + const storageUUID = req.query.storageUUID; + const values = req.body; - if (!storageUUID || !values) { - return res.status(400).json({ - success: false, - code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - if (typeof storageUUID !== "string") { - return res.status(400).json({ - success: false, - code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - const result = await updateStorage(storageUUID, values); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SS003") { - return res.status(201).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (!storageUUID || !values) { + return res.status(400).json({ + success: false, + code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], }); + } + + if (typeof storageUUID !== "string") { + return res.status(400).json({ + success: false, + code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], + }); + } + + const result = await updateStorage(storageUUID, values); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SS003") { + return res.status(201).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.delete("/delete", authenticate, async (req, res) => { - const uuid = req.query.uuid; + const uuid = req.query.uuid; - if (typeof uuid !== "string") { - return res.status(400).json({ - success: false, - code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], - data: null, - message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], - }); - } - - const result = await deleteStorage(uuid); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SS004") { - return res.status(201).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (typeof uuid !== "string") { + return res.status(400).json({ + success: false, + code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0], + data: null, + message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1], }); + } + + const result = await deleteStorage(uuid); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SS004") { + return res.status(201).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); export default router; diff --git a/backend/routes/app/users.route.ts b/backend/routes/app/users.route.ts index bd9a579..282e496 100644 --- a/backend/routes/app/users.route.ts +++ b/backend/routes/app/users.route.ts @@ -1,245 +1,251 @@ import express from "express"; import dotenv from "dotenv"; -import {authenticate, generateToken} from "../../services/tokenService"; -import {changePassword, findUser, getSettings, loginUser, updateSettings,} from "./database/users.database"; -import {GENERAL_ERROR_CODE, USER_ERROR_CODE} from "@stockhome/shared"; +import { authenticate, generateToken } from "../../services/tokenService"; +import { + changePassword, + findUser, + getSettings, + loginUser, + updateSettings, +} from "./database/users.database"; +import { GENERAL_ERROR_CODE, USER_ERROR_CODE } from "@stockhome/shared"; dotenv.config(); const router = express.Router(); router.post("/verify-token", authenticate, async (req, res) => { - res.sendStatus(200); + res.sendStatus(200); }); router.post("/update-app-settings", authenticate, async (req, res) => { - const values = req.body; + const values = req.body; - if (!values || Object.keys(values).length === 0) { - return res.status(400).json({ - success: false, - code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - const result = await updateSettings(values); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SU003") { - return res.status(201).json({ - success: true, - code: result.code, - data: result.data, - message: result.message, - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (!values || Object.keys(values).length === 0) { + return res.status(400).json({ + success: false, + code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], }); + } + + const result = await updateSettings(values); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SU003") { + return res.status(201).json({ + success: true, + code: result.code, + data: result.data, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.get("/settings", authenticate, async (req, res) => { - const result = await getSettings(); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SU004") { - return res.status(200).json({ - success: true, - code: result.code, - data: result.data, - message: result.message, - }); - } + const result = await getSettings(); + if (!result) { return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], }); + } + + if (result.code === "SU004") { + return res.status(200).json({ + success: true, + code: result.code, + data: result.data, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/login", async (req, res) => { - const username = req.body.username; - const password = req.body.password; + const username = req.body.username; + const password = req.body.password; - if (!username || !password) { - return res.status(400).json({ - success: false, - code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - const result = await findUser(username, password); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === USER_ERROR_CODE.WRONG_USERNAME_PASSWORD[0]) { - return res.status(404).json({ - success: false, - code: result.code, - data: null, - message: result.message, - }); - } - - if (result.code === USER_ERROR_CODE.USER_IS_DEACTIVATED[0]) { - return res.status(403).json({ - success: false, - code: result.code, - data: null, - message: result.message, - }); - } - - if (result.code === "SU001") { - if (!result.data) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - const token = await generateToken(result.data); - - if (!token) { - return res.status(500).json({ - success: false, - code: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[0], - data: null, - message: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[1], - }); - } - - if (!result.data) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - const login = await loginUser(result.data.username); - - if (!login) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (login.code !== "SU002") { - return res.status(500).json({ - success: false, - code: login.code || USER_ERROR_CODE.LOGIN_FAILED[0], - data: null, - message: login.message || USER_ERROR_CODE.LOGIN_FAILED[1], - }); - } - - return res.status(202).json({ - success: true, - code: "SU001", - data: { - token, - }, - message: "User token generated successfully", - }); - } - - return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + if (!username || !password) { + return res.status(400).json({ + success: false, + code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], }); + } + + const result = await findUser(username, password); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === USER_ERROR_CODE.WRONG_USERNAME_PASSWORD[0]) { + return res.status(404).json({ + success: false, + code: result.code, + data: null, + message: result.message, + }); + } + + if (result.code === USER_ERROR_CODE.USER_IS_DEACTIVATED[0]) { + return res.status(403).json({ + success: false, + code: result.code, + data: null, + message: result.message, + }); + } + + if (result.code === "SU001") { + if (!result.data) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + const token = await generateToken(result.data); + + if (!token) { + return res.status(500).json({ + success: false, + code: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[0], + data: null, + message: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[1], + }); + } + + if (!result.data) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + const login = await loginUser(result.data.username); + + if (!login) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (login.code !== "SU002") { + return res.status(500).json({ + success: false, + code: login.code || USER_ERROR_CODE.LOGIN_FAILED[0], + data: null, + message: login.message || USER_ERROR_CODE.LOGIN_FAILED[1], + }); + } + + return res.status(202).json({ + success: true, + code: "SU001", + data: { + token, + }, + message: "User token generated successfully", + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); router.post("/change-password", authenticate, async (req, res) => { - const currentPassword = req.body.currentPassword; - const newPassword = req.body.newPassword; - - if (!req.user) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - const username = req.user.username; - - if (!currentPassword || !newPassword) { - return res.status(400).json({ - success: false, - code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], - data: null, - message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], - }); - } - - const result = await changePassword(username, currentPassword, newPassword); - - if (!result) { - return res.status(500).json({ - success: false, - code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], - }); - } - - if (result.code === "SU005") { - return res.status(202).json({ - success: true, - code: result.code, - data: null, - message: result.message, - }); - } + const currentPassword = req.body.currentPassword; + const newPassword = req.body.newPassword; + if (!req.user) { return res.status(500).json({ - success: false, - code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], - data: null, - message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], }); + } + + const username = req.user.username; + + if (!currentPassword || !newPassword) { + return res.status(400).json({ + success: false, + code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0], + data: null, + message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1], + }); + } + + const result = await changePassword(username, currentPassword, newPassword); + + if (!result) { + return res.status(500).json({ + success: false, + code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); + } + + if (result.code === "SU005") { + return res.status(202).json({ + success: true, + code: result.code, + data: null, + message: result.message, + }); + } + + return res.status(500).json({ + success: false, + code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0], + data: null, + message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1], + }); }); -export default router; \ No newline at end of file +export default router; diff --git a/backend/server.ts b/backend/server.ts index a1aa29b..e06589e 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -1,10 +1,9 @@ -import type {NextFunction, Request, Response} from "express"; +import type { NextFunction, Request, Response } from "express"; import express from "express"; import cors from "cors"; import dotenv from "dotenv"; -import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2"; -import {readFile} from "fs/promises"; -// frontend routes +import mysql, { type ResultSetHeader, type RowDataPacket } from "mysql2"; +import { readFile } from "fs/promises"; // frontend routes import userRouter from "./routes/app/users.route"; import productRouter from "./routes/app/products.route"; import storageRouter from "./routes/app/storage.route"; @@ -18,16 +17,16 @@ const port = 8004; app.use(cors()); app.use(express.json()); -app.use(express.urlencoded({extended: true})); +app.use(express.urlencoded({ extended: true })); const pool = mysql - .createPool({ - host: process.env.DB_HOST!, - user: process.env.DB_USER!, - password: process.env.DB_PASSWORD!, - database: process.env.DB_NAME!, - }) - .promise(); + .createPool({ + host: process.env.DB_HOST!, + user: process.env.DB_USER!, + password: process.env.DB_PASSWORD!, + database: process.env.DB_NAME!, + }) + .promise(); app.use("/users", userRouter); @@ -36,113 +35,117 @@ app.use("/products", productRouter); app.use("/storage", storageRouter); app.listen(port, () => { - runStartup(port); + runStartup(port); }); // Startup code const runStartup = async (port: number) => { - let firstStartupValue: string | null = null; - try { - const [firstResponse] = await pool.query( - `SELECT value + let firstStartupValue: string | null = null; + try { + const [firstResponse] = await pool.query( + `SELECT value FROM app_settings WHERE name = "first-startup";`, - ); - firstStartupValue = firstResponse[0]?.value ?? null; - } catch (err: unknown) { - if (err instanceof Error && (err as NodeJS.ErrnoException & { code?: string }).code !== "ER_NO_SUCH_TABLE") { - throw err; - } + ); + firstStartupValue = firstResponse[0]?.value ?? null; + } catch (err: unknown) { + if ( + err instanceof Error && + (err as NodeJS.ErrnoException & { code?: string }).code !== + "ER_NO_SUCH_TABLE" + ) { + throw err; + } + } + + if (firstStartupValue !== "false") { + const schemaPath = path.join(__dirname, "database.scheme.sql"); + const schemaSql = await readFile(schemaPath, "utf8"); + const statements = schemaSql + .split(";") + .map((statement: string) => statement.trim()) + .filter((statement: string) => statement.length > 0); + + for (const statement of statements) { + await pool.query(statement); } - if (firstStartupValue !== "false") { - const schemaPath = path.join(__dirname, "database.scheme.sql"); - const schemaSql = await readFile(schemaPath, "utf8"); - const statements = schemaSql - .split(";") - .map((statement: string) => statement.trim()) - .filter((statement: string) => statement.length > 0); - - for (const statement of statements) { - await pool.query(statement); - } - - const [result] = await pool.query( - `SELECT value + const [result] = await pool.query( + `SELECT value FROM app_settings WHERE name = "first-startup";`, - ); + ); - if (result[0]?.value === "true") { - const insertResult = await insertFirstData(); + if (result[0]?.value === "true") { + const insertResult = await insertFirstData(); - if (insertResult.affectedRows > 0) { - console.log("Successfully created admin user!"); - console.log("Username: admin"); - console.log("Password: admin"); + if (insertResult.affectedRows > 0) { + console.log("Successfully created admin user!"); + console.log("Username: admin"); + console.log("Password: admin"); - const [scndResponse] = await pool.query( - `UPDATE app_settings + const [scndResponse] = await pool.query( + `UPDATE app_settings SET value = "false" WHERE name = "first-startup";`, - ); + ); - if (scndResponse.affectedRows > 0) { - console.log("Database settet up successfully!"); - } else { - console.error("There was an error while setting up the database!"); - } - } else { - console.error("Error while creating admin user."); - } + if (scndResponse.affectedRows > 0) { + console.log("Database settet up successfully!"); + } else { + console.error("There was an error while setting up the database!"); } + } else { + console.error("Error while creating admin user."); + } } + } - console.log("Everything is settet up successfully!"); - console.log(`Backend is running on http://localhost:${port}`); + console.log("Everything is settet up successfully!"); + console.log(`Backend is running on http://localhost:${port}`); }; const insertFirstData = async (): Promise => { - const [insertResult] = await pool.query( - `INSERT INTO users (username, first_name, last_name, email, password, is_admin) + const [insertResult] = await pool.query( + `INSERT INTO users (username, first_name, last_name, email, password, is_admin) VALUES ("admin", "admin", "admin", "admin@example.com", "admin", 1)`, - ); + ); - await pool.query( - `INSERT INTO storage_locations (name, description) + await pool.query( + `INSERT INTO storage_locations (name, description) VALUES (?, ?);`, - ["Default Storage", "Initial storage location"], - ); + ["Default Storage", "Initial storage location"], + ); - const [storageRows] = await pool.query( - `SELECT uuid + const [storageRows] = await pool.query( + `SELECT uuid FROM storage_locations WHERE name = ? LIMIT 1;`, - ["Default Storage"], - ); + ["Default Storage"], + ); - const storageUuid: string | null = storageRows[0]?.uuid ?? null; - if (storageUuid) { - await pool.query( - `INSERT INTO products (name, description, price, amount, storage_location, picture) + const storageUuid: string | null = storageRows[0]?.uuid ?? null; + if (storageUuid) { + await pool.query( + `INSERT INTO products (name, description, price, amount, storage_location, picture) VALUES (?, ?, ?, ?, ?, ?);`, - [ - "Welcome Product", - "Your first item in Stockhome", - "0.00", - 1, - storageUuid, - null, - ], - ); - } + [ + "Welcome Product", + "Your first item in Stockhome", + "0.00", + 1, + storageUuid, + null, + ], + ); + } - console.log("Welcome product is ready..."); - return insertResult; + console.log("Welcome product is ready..."); + return insertResult; }; // error handling code app.use((err: Error, req: Request, res: Response, next: NextFunction) => { - console.error(err.stack); - res.status(500).send("Something broke!"); -}); \ No newline at end of file + console.error(err.stack); + res.status(500).send("Something broke!"); +}); diff --git a/backend/services/helperFuncs.ts b/backend/services/helperFuncs.ts index 5ecdb57..cb1768f 100644 --- a/backend/services/helperFuncs.ts +++ b/backend/services/helperFuncs.ts @@ -1,14 +1,14 @@ -import type {ErrorCode} from "../misc/types.ts"; +import type { ErrorCode } from "../misc/types.ts"; export const returnErrorCode = (errorKey: ErrorCode) => { - if (!Array.isArray(errorKey) || errorKey.length < 2) { - return false; - } + if (!Array.isArray(errorKey) || errorKey.length < 2) { + return false; + } - return { - success: false, - code: errorKey[0], - data: null, - message: errorKey[1] - } -}; \ No newline at end of file + return { + success: false, + code: errorKey[0], + data: null, + message: errorKey[1], + }; +}; diff --git a/backend/services/tokenService.ts b/backend/services/tokenService.ts index 004f2e7..996b04f 100644 --- a/backend/services/tokenService.ts +++ b/backend/services/tokenService.ts @@ -1,36 +1,40 @@ -import type {NextFunction, Request, Response} from "express"; -import {type JWTPayload, jwtVerify, SignJWT} from "jose"; +import type { NextFunction, Request, Response } from "express"; +import { type JWTPayload, jwtVerify, SignJWT } from "jose"; import env from "dotenv"; env.config(); const secret = new TextEncoder().encode(process.env.SECRET_KEY); export type AuthTokenPayload = JWTPayload & { - username: string; + username: string; }; declare module "express-serve-static-core" { - interface Request { - user?: AuthTokenPayload; - } + interface Request { + user?: AuthTokenPayload; + } } export async function generateToken(payload: AuthTokenPayload) { - return await new SignJWT(payload) - .setProtectedHeader({alg: "HS256"}) - .setIssuedAt() - .setExpirationTime("24h") - .sign(secret); + return await new SignJWT(payload) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime("24h") + .sign(secret); } -export async function authenticate(req: Request, res: Response, next: NextFunction) { - const authHeader = req.headers["authorization"]; - const token = authHeader && authHeader.split(" ")[1]; +export async function authenticate( + req: Request, + res: Response, + next: NextFunction, +) { + const authHeader = req.headers["authorization"]; + const token = authHeader && authHeader.split(" ")[1]; - if (token == null) return res.sendStatus(401); + if (token == null) return res.sendStatus(401); - const {payload} = await jwtVerify(token, secret); - req.user = payload; + const { payload } = await jwtVerify(token, secret); + req.user = payload; - next(); -} \ No newline at end of file + next(); +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 3ba40bd..6babdbc 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -9,17 +9,13 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "ignoreDeprecations": "6.0", - "types": [ - "node" - ], + "types": ["node"], "sourceMap": true, "declaration": true, "strict": true, "skipLibCheck": true }, - "include": [ - "./**/*.ts" - ], + "include": ["./**/*.ts"], "exclude": [ "node_modules", "dist", @@ -33,4 +29,4 @@ "path": "../shared" } ] -} \ No newline at end of file +} diff --git a/backend/vite.config.ts b/backend/vite.config.ts index 11ab574..6fbaa23 100644 --- a/backend/vite.config.ts +++ b/backend/vite.config.ts @@ -1,29 +1,29 @@ -import {defineConfig} from 'vite' -import path from 'path' -import {dirname} from "node:path" -import {fileURLToPath} from "node:url" +import { defineConfig } from "vite"; +import path from "path"; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export default defineConfig({ - root: 'src', - build: { - outDir: '../dist', - emptyOutDir: true, - sourcemap: true, - minify: 'esbuild', - assetsDir: 'assets', - rollupOptions: { - input: path.resolve(__dirname, 'src/index.html'), - output: { - assetFileNames: 'assets/[name]-[hash][extname]', - }, - }, + root: "src", + build: { + outDir: "../dist", + emptyOutDir: true, + sourcemap: true, + minify: "esbuild", + assetsDir: "assets", + rollupOptions: { + input: path.resolve(__dirname, "src/index.html"), + output: { + assetFileNames: "assets/[name]-[hash][extname]", + }, }, - resolve: { - alias: { - '@': path.resolve(__dirname, 'src'), - }, + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), }, -}) \ No newline at end of file + }, +}); diff --git a/frontend/src/components/LoginCard.tsx b/frontend/src/components/LoginCard.tsx index 11da27f..83231aa 100644 --- a/frontend/src/components/LoginCard.tsx +++ b/frontend/src/components/LoginCard.tsx @@ -1,5 +1,5 @@ import { useForm } from "@tanstack/react-form"; -import { Input, Button, Alert } from "@mui/joy"; +import { Alert, Button, Input } from "@mui/joy"; import { useMutation } from "@tanstack/react-query"; import { signInUser } from "../utils/api/auth"; import { useTranslation } from "react-i18next"; diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index ccff9c4..493054f 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -9,7 +9,7 @@ import ExitToAppIcon from "@mui/icons-material/ExitToApp"; import TranslateIcon from "@mui/icons-material/Translate"; import MenuIcon from "@mui/icons-material/Menu"; import CloseIcon from "@mui/icons-material/Close"; -import { useNavigate, useMatchRoute } from "@tanstack/react-router"; +import { useMatchRoute, useNavigate } from "@tanstack/react-router"; import Cookies from "js-cookie"; import { changeTranslation } from "../utils/uxFncs"; diff --git a/frontend/src/components/StorageRow.tsx b/frontend/src/components/StorageRow.tsx index d817b56..ddca9bf 100644 --- a/frontend/src/components/StorageRow.tsx +++ b/frontend/src/components/StorageRow.tsx @@ -1,8 +1,8 @@ -import { useQueryClient, useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { deleteStorage, updateStorage } from "../utils/api/storages"; import { useForm } from "@tanstack/react-form"; import { useStore } from "@tanstack/react-store"; -import { Input, Button } from "@mui/joy"; +import { Button, Input } from "@mui/joy"; import type { Storage } from "../misc/interfaces"; import { formatDate } from "../utils/uxFncs"; import { useTranslation } from "react-i18next"; diff --git a/frontend/src/components/modals/AddStorageModal.tsx b/frontend/src/components/modals/AddStorageModal.tsx index f689865..f7951e8 100644 --- a/frontend/src/components/modals/AddStorageModal.tsx +++ b/frontend/src/components/modals/AddStorageModal.tsx @@ -1,17 +1,17 @@ import { + Alert, + Button, + DialogContent, + DialogTitle, + Input, Modal, ModalDialog, - DialogTitle, - DialogContent, Stack, - Input, - Button, - Alert, } from "@mui/joy"; import { useForm } from "@tanstack/react-form"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; -import type { NewStorage, AlertInterface } from "../../misc/interfaces"; +import type { AlertInterface, NewStorage } from "../../misc/interfaces"; import { mutateNewStorage } from "../../utils/api/storages"; import { useState } from "react"; diff --git a/frontend/src/components/modals/ChangePasswordModal.tsx b/frontend/src/components/modals/ChangePasswordModal.tsx index 9d7b4f6..f76a4af 100644 --- a/frontend/src/components/modals/ChangePasswordModal.tsx +++ b/frontend/src/components/modals/ChangePasswordModal.tsx @@ -1,22 +1,24 @@ import { + Alert, + Button, + DialogTitle, + Input, Modal, ModalDialog, - DialogTitle, Stack, - Input, - Button, - Alert, } from "@mui/joy"; import { useForm } from "@tanstack/react-form"; import { useMutation } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; -import type { ChangePasswordIntf, AlertInterface } from "../../misc/interfaces"; +import type { AlertInterface, ChangePasswordIntf } from "../../misc/interfaces"; import { mutatePassword } from "../../utils/api/auth"; import { useState } from "react"; +import { USER_ERROR_CODE } from "@stockhome/shared"; interface ChangePasswordProps { isOpen: boolean; setOpen: (value: boolean) => void; + alert: (alert: AlertInterface) => void; } export const ChangePasswordModal = (props: ChangePasswordProps) => { @@ -35,7 +37,18 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => { newPasswordRep: "", }, onSubmit: async ({ value }) => { - mutate(value); + console.log(value); + + if (value.newPassword === value.newPasswordRep) { + mutate(value); + } else { + setAlert({ + isAlert: true, + type: "danger", + header: t("error"), + text: t(USER_ERROR_CODE.PASSWORDS_NOT_MATCHED[0]), + }); + } }, }); @@ -50,6 +63,21 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => { }); }, onSuccess: () => { + // Sets the success alert in the settings page via component props + props.alert({ + isAlert: true, + type: "success", + header: t("success"), + text: t("SU005"), + }); + + // Sets the success alert locally (in the modal itself) + setAlert({ + isAlert: true, + type: "success", + header: t("success"), + text: t("SU005"), + }); props.setOpen(false); }, }); diff --git a/frontend/src/pages/AddProduct.tsx b/frontend/src/pages/AddProduct.tsx index 9be100e..c0ae639 100644 --- a/frontend/src/pages/AddProduct.tsx +++ b/frontend/src/pages/AddProduct.tsx @@ -16,8 +16,8 @@ import { useForm } from "@tanstack/react-form"; import { createProduct } from "../utils/api/products"; import { getStorages } from "../utils/api/storages"; import type { - ProductFormValues, AlertInterface, + ProductFormValues, Storage, } from "../misc/interfaces"; import type { ApiError } from "../utils/api/apiError"; @@ -272,10 +272,11 @@ export const AddProduct = () => { -
+
{t("product-details")} +
- +
+
+ + {t("product-details")} + +
+ + +
+ {alert.isAlert && (