From ef905fb30bec27754a574beaccef4a1b5e3964c1 Mon Sep 17 00:00:00 2001 From: Theis Gaedigk Date: Thu, 9 Jul 2026 11:23:41 +0200 Subject: [PATCH] refactor: add custom alert component and remove all other Alert standard components in all components --- frontend/src/components/LoginCard.tsx | 28 ++-- frontend/src/components/MyAlert.tsx | 21 +++ frontend/src/components/Sidebar.tsx | 9 +- .../src/components/modals/AddStorageModal.tsx | 26 +--- .../components/modals/ChangePasswordModal.tsx | 24 +-- frontend/src/pages/AddProduct.tsx | 38 ++--- frontend/src/pages/Inventory.tsx | 142 +++++++++++------- frontend/src/pages/ProductQuickView.tsx | 31 +--- frontend/src/pages/Settings.tsx | 22 +-- frontend/src/pages/Storages.tsx | 63 ++++---- frontend/src/pages/ViewProduct.tsx | 29 +--- frontend/src/utils/i18n/locales/de/de.json | 1 + frontend/src/utils/i18n/locales/en/en.json | 1 + 13 files changed, 213 insertions(+), 222 deletions(-) create mode 100644 frontend/src/components/MyAlert.tsx diff --git a/frontend/src/components/LoginCard.tsx b/frontend/src/components/LoginCard.tsx index 83231aa..b5aae61 100644 --- a/frontend/src/components/LoginCard.tsx +++ b/frontend/src/components/LoginCard.tsx @@ -1,11 +1,12 @@ import { useForm } from "@tanstack/react-form"; -import { Alert, Button, Input } from "@mui/joy"; +import { Button, Input } from "@mui/joy"; import { useMutation } from "@tanstack/react-query"; import { signInUser } from "../utils/api/auth"; import { useTranslation } from "react-i18next"; import { useNavigate } from "@tanstack/react-router"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import type { AlertInterface } from "../misc/interfaces"; +import { MyAlert } from "./MyAlert.tsx"; export const LoginCard = () => { const { t } = useTranslation(); @@ -17,6 +18,15 @@ export const LoginCard = () => { text: "", }); + useEffect(() => { + setAlert({ + isAlert: true, + type: "primary", + header: t("success"), + text: t("logout-success-text"), + }); + }, []); + const form = useForm({ defaultValues: { username: "", @@ -77,15 +87,11 @@ export const LoginCard = () => { }} > {alert.isAlert && ( - - {alert.header} -
- {alert.text} -
+ )} {(field) => ( diff --git a/frontend/src/components/MyAlert.tsx b/frontend/src/components/MyAlert.tsx new file mode 100644 index 0000000..738b114 --- /dev/null +++ b/frontend/src/components/MyAlert.tsx @@ -0,0 +1,21 @@ +import { Alert } from "@mui/joy"; + +interface MyAlertProps { + type: "success" | "warning" | "danger" | "neutral" | "primary"; + header: string; + text: string; +} + +export const MyAlert = (props: MyAlertProps) => { + return ( + + {props.header} +
+ {props.text} +
+ ); +}; diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 493054f..b885e1d 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -50,7 +50,7 @@ export const Sidebar = () => { @@ -110,7 +113,7 @@ export const Sidebar = () => { {alert.isAlert && ( - - {alert.header} -
- {alert.text} -
- )} - {success && ( - -
- - {t("success")} - -
-
+ )} diff --git a/frontend/src/pages/Inventory.tsx b/frontend/src/pages/Inventory.tsx index 6d5c6d9..e74a0ca 100644 --- a/frontend/src/pages/Inventory.tsx +++ b/frontend/src/pages/Inventory.tsx @@ -1,15 +1,5 @@ import { useEffect, useState } from "react"; -import { - Alert, - Avatar, - Button, - Checkbox, - Chip, - CircularProgress, - Sheet, - Table, - Typography, -} from "@mui/joy"; +import { Avatar, Button, Checkbox, Chip, CircularProgress, Sheet, Table, Typography, } from "@mui/joy"; import { useNavigate } from "@tanstack/react-router"; import { useTranslation } from "react-i18next"; import AddIcon from "@mui/icons-material/Add"; @@ -19,6 +9,8 @@ import { formatDate } from "../utils/uxFncs"; import Cookies from "js-cookie"; import type { AlertInterface, ProductRow } from "../misc/interfaces"; import type { ApiError } from "../utils/api/apiError"; +import CategoryIcon from "@mui/icons-material/category"; +import { MyAlert } from "../components/MyAlert.tsx"; export const InventoryPage = () => { const { t } = useTranslation(); @@ -127,20 +119,12 @@ export const InventoryPage = () => { {productsIsLoading && } {alert.isAlert && ( - - {alert.header} -
- {alert.text} -
+ )}
@@ -163,8 +147,9 @@ export const InventoryPage = () => { stripe="odd" variant="plain" hoverRow - className="min-w-240 text-slate-700" + className="w-full text-slate-700" sx={{ + tableLayout: "fixed", "--TableCell-headBackground": "var(--joy-palette-background-surface)", "& thead": { @@ -176,23 +161,48 @@ export const InventoryPage = () => { "& thead tr": { backgroundColor: "rgb(248 250 252)", }, - "& thead th:nth-child(2)": { - width: "40%", - }, - "& thead th:nth-child(3)": { - width: "14%", - }, "& thead th": { zIndex: 2, backgroundColor: "rgb(248 250 252)", backgroundImage: "none", }, + "& thead th:nth-child(1)": { + width: "44px", + }, + "& thead th:nth-child(2)": { + width: "22%", + minWidth: "160px", + }, + "& thead th:nth-child(3)": { + width: "9%", + minWidth: "70px", + }, + "& thead th:nth-child(4)": { + width: "12%", + minWidth: "90px", + }, + "& thead th:nth-child(5)": { + width: "17%", + minWidth: "120px", + }, + "& thead th:nth-child(6)": { + width: "13%", + minWidth: "100px", + }, + "& thead th:nth-child(7)": { + width: "13%", + minWidth: "100px", + }, + "& thead th:nth-child(8)": { + width: "14%", + minWidth: "100px", + }, "& tr > *:nth-child(n+4)": { textAlign: "left" }, }} > - + 0 && selected.length === rows.length} indeterminate={ @@ -203,13 +213,13 @@ export const InventoryPage = () => { sx={{ verticalAlign: "sub" }} /> - {t("product-name")} - {t("price")} - {t("stock")} - {t("storage-place")} - {t("expiry-date")} - {t("bottling-date")} - {t("actions")} + {t("product-name")} + {t("price")} + {t("stock")} + {t("storage-place")} + {t("expiry-date")} + {t("bottling-date")} + {t("actions")} @@ -225,61 +235,81 @@ export const InventoryPage = () => { aria-checked={isItemSelected} tabIndex={-1} > - + - -
- + +
+ + +
{row.name} {row.description}
- - {row.price} - + + + {row.price} + + {Cookies.get("currency")} - + - {row.stock} + {row.stock} - - {row.location} - + + + {row.location} + + {row.locationDetail} - - {row.expiryDate} + + + {row.expiryDate} + - - {row.refillDate} + + + {row.refillDate} + - +
{alert.isAlert && ( - - {alert.header} -
- {alert.text} -
- )} - {success && ( - -
- - {t("success")} - -
-
+ )} diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index be562e7..74b6d39 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -1,13 +1,4 @@ -import { - Alert, - Button, - Chip, - CircularProgress, - Divider, - Input, - Sheet, - Typography, -} from "@mui/joy"; +import { Button, Chip, CircularProgress, Divider, Input, Sheet, Typography, } from "@mui/joy"; import { useForm } from "@tanstack/react-form"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import Cookies from "js-cookie"; @@ -17,6 +8,7 @@ import type { ApiError } from "../utils/api/apiError"; import { fetchSettings, mutateSettings } from "../utils/api/settings"; import { useEffect, useState } from "react"; import { ChangePasswordModal } from "../components/modals/ChangePasswordModal"; +import { MyAlert } from "../components/MyAlert.tsx"; export const Settings = () => { const { t } = useTranslation(); @@ -118,15 +110,7 @@ export const Settings = () => { {alert.isAlert && ( - - {alert.header} -
- {alert.text} -
+ )} {settingsPending ? (
diff --git a/frontend/src/pages/Storages.tsx b/frontend/src/pages/Storages.tsx index 6e1fa5b..72113de 100644 --- a/frontend/src/pages/Storages.tsx +++ b/frontend/src/pages/Storages.tsx @@ -1,13 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { getStorages } from "../utils/api/storages"; -import { - Alert, - Button, - CircularProgress, - Sheet, - Table, - Typography, -} from "@mui/joy"; +import { Button, CircularProgress, Sheet, Table, Typography } from "@mui/joy"; import { useTranslation } from "react-i18next"; import type { AlertInterface, Storage } from "../misc/interfaces"; import type { ApiError } from "../utils/api/apiError"; @@ -15,6 +8,7 @@ import { StorageRow } from "../components/StorageRow"; import { useEffect, useState } from "react"; import { AddStorageModal } from "../components/modals/AddStorageModal"; import AddIcon from "@mui/icons-material/Add"; +import { MyAlert } from "../components/MyAlert.tsx"; export const Storages = () => { const { t } = useTranslation(); @@ -74,21 +68,13 @@ export const Storages = () => {
{alert.isAlert && ( - - {alert.header} -
- {alert.text} -
+ )}
{isLoading ? ( @@ -108,8 +94,9 @@ export const Storages = () => { stripe="odd" variant="plain" hoverRow - className="min-w-240 text-slate-700" + className="w-full text-slate-700" sx={{ + tableLayout: "fixed", "--TableCell-headBackground": "var(--joy-palette-background-surface)", "& thead": { @@ -121,27 +108,41 @@ export const Storages = () => { "& thead tr": { backgroundColor: "rgb(248 250 252)", }, - "& thead th:nth-child(2)": { - width: "40%", - }, - "& thead th:nth-child(3)": { - width: "14%", - }, "& thead th": { zIndex: 2, backgroundColor: "rgb(248 250 252)", backgroundImage: "none", }, - "& tr > *:nth-child(n+4)": { textAlign: "left" }, + "& thead th:nth-child(1)": { + width: "26%", + minWidth: "140px", + }, + "& thead th:nth-child(2)": { + width: "34%", + minWidth: "160px", + }, + "& thead th:nth-child(3)": { + width: "16%", + minWidth: "110px", + }, + "& thead th:nth-child(4)": { + width: "16%", + minWidth: "110px", + }, + "& thead th:nth-child(5)": { + width: "8%", + minWidth: "80px", + }, + "& tr > *:nth-child(n+3)": { textAlign: "left" }, }} > - {t("storage-name")} - {t("description")} - {t("created-at")} - {t("updated-at")} - + {t("storage-name")} + {t("description")} + {t("created-at")} + {t("updated-at")} + diff --git a/frontend/src/pages/ViewProduct.tsx b/frontend/src/pages/ViewProduct.tsx index b804117..c3c6e80 100644 --- a/frontend/src/pages/ViewProduct.tsx +++ b/frontend/src/pages/ViewProduct.tsx @@ -1,7 +1,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { getStorages } from "../utils/api/storages.ts"; import { - Alert, Box, Button, Chip, @@ -27,6 +26,7 @@ import type { ApiError } from "../utils/api/apiError"; import QrCodeIcon from "@mui/icons-material/QrCode"; import Cookies from "js-cookie"; import QRCode from "qrcode"; +import { MyAlert } from "../components/MyAlert.tsx"; interface ViewProductProps { uuid: string; @@ -369,28 +369,11 @@ export const ViewProduct = (props: ViewProductProps) => { {alert.isAlert && ( - - {alert.header} -
- {alert.text} -
- )} - {success && ( - -
- - {t("success")} - -
-
+ )} diff --git a/frontend/src/utils/i18n/locales/de/de.json b/frontend/src/utils/i18n/locales/de/de.json index 6722956..15bbdf8 100644 --- a/frontend/src/utils/i18n/locales/de/de.json +++ b/frontend/src/utils/i18n/locales/de/de.json @@ -57,6 +57,7 @@ "error": "Fehler", "success": "Erfolg", "submit": "Absenden", + "logout-success-text": "Logout erfolgreich!", "SU005": "Das Passwort wurde erfolgreich geändert!", "SE001": "Die Einstellungen wurden erfolgreich geändert!", "EG001": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.", diff --git a/frontend/src/utils/i18n/locales/en/en.json b/frontend/src/utils/i18n/locales/en/en.json index cda7e7a..99349d1 100644 --- a/frontend/src/utils/i18n/locales/en/en.json +++ b/frontend/src/utils/i18n/locales/en/en.json @@ -57,6 +57,7 @@ "error": "Error", "success": "Success", "submit": "Submit", + "logout-success-text": "Logout successful!", "SU005": "Your password is updated successfully!", "SE001": "The settings are updated successfully!", "EG001": "An unexpected error occurred. Please try again later.",