refactor: add custom alert component and remove all other Alert standard components in all components

This commit is contained in:
2026-07-09 11:23:41 +02:00
parent 925cb73c47
commit ef905fb30b
13 changed files with 213 additions and 222 deletions
+17 -11
View File
@@ -1,11 +1,12 @@
import { useForm } from "@tanstack/react-form"; 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 { useMutation } from "@tanstack/react-query";
import { signInUser } from "../utils/api/auth"; import { signInUser } from "../utils/api/auth";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { useState } from "react"; import { useEffect, useState } from "react";
import type { AlertInterface } from "../misc/interfaces"; import type { AlertInterface } from "../misc/interfaces";
import { MyAlert } from "./MyAlert.tsx";
export const LoginCard = () => { export const LoginCard = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -17,6 +18,15 @@ export const LoginCard = () => {
text: "", text: "",
}); });
useEffect(() => {
setAlert({
isAlert: true,
type: "primary",
header: t("success"),
text: t("logout-success-text"),
});
}, []);
const form = useForm({ const form = useForm({
defaultValues: { defaultValues: {
username: "", username: "",
@@ -77,15 +87,11 @@ export const LoginCard = () => {
}} }}
> >
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
variant="soft" type={alert.type}
color={alert.type} header={alert.header}
className="rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
<form.Field name="username"> <form.Field name="username">
{(field) => ( {(field) => (
+21
View File
@@ -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 (
<Alert
variant="soft"
color={props.type}
className="rounded-2xl drop-shadow-sm"
>
{props.header}
<br />
{props.text}
</Alert>
);
};
+6 -3
View File
@@ -50,7 +50,7 @@ export const Sidebar = () => {
<Button <Button
variant="soft" variant="soft"
size="sm" size="sm"
className="lg:hidden transition-transform duration-200 ease-out" className="transition-transform duration-200 ease-out"
onClick={() => setIsOpen((open) => !open)} onClick={() => setIsOpen((open) => !open)}
startDecorator={ startDecorator={
<span <span
@@ -61,7 +61,10 @@ export const Sidebar = () => {
{isOpen ? <CloseIcon /> : <MenuIcon />} {isOpen ? <CloseIcon /> : <MenuIcon />}
</span> </span>
} }
sx={{ display: { lg: "none" } }} sx={{
display: "inline-flex",
"@media (min-width: 1024px)": { display: "none" },
}}
> >
{isOpen ? t("close") : t("menu")} {isOpen ? t("close") : t("menu")}
</Button> </Button>
@@ -110,7 +113,7 @@ export const Sidebar = () => {
<Button <Button
onClick={() => { onClick={() => {
Cookies.remove("token"); Cookies.remove("token");
handleNavigate("/login"); handleNavigate("/login?logout=true");
}} }}
color="danger" color="danger"
startDecorator={<ExitToAppIcon />} startDecorator={<ExitToAppIcon />}
@@ -1,19 +1,11 @@
import { import { Button, DialogContent, DialogTitle, Input, Modal, ModalDialog, Stack, } from "@mui/joy";
Alert,
Button,
DialogContent,
DialogTitle,
Input,
Modal,
ModalDialog,
Stack,
} from "@mui/joy";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import type { AlertInterface, NewStorage } from "../../misc/interfaces"; import type { AlertInterface, NewStorage } from "../../misc/interfaces";
import { mutateNewStorage } from "../../utils/api/storages"; import { mutateNewStorage } from "../../utils/api/storages";
import { useState } from "react"; import { useState } from "react";
import { MyAlert } from "../MyAlert.tsx";
interface AddStorageModalProps { interface AddStorageModalProps {
isOpen: boolean; isOpen: boolean;
@@ -107,15 +99,11 @@ export const AddStorageModal = (props: AddStorageModalProps) => {
</Stack> </Stack>
</form> </form>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
variant="soft" type={alert.type}
color={alert.type} header={alert.header}
className="mt-4 rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
</ModalDialog> </ModalDialog>
</Modal> </Modal>
@@ -1,4 +1,11 @@
import { Alert, Button, DialogTitle, Input, Modal, ModalDialog, Stack, } from "@mui/joy"; import {
Button,
DialogTitle,
Input,
Modal,
ModalDialog,
Stack,
} from "@mui/joy";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -6,6 +13,7 @@ import type { AlertInterface, ChangePasswordIntf } from "../../misc/interfaces";
import { mutatePassword } from "../../utils/api/auth"; import { mutatePassword } from "../../utils/api/auth";
import { useState } from "react"; import { useState } from "react";
import { USER_ERROR_CODE } from "@stockhome/shared"; import { USER_ERROR_CODE } from "@stockhome/shared";
import { MyAlert } from "../MyAlert.tsx";
interface ChangePasswordProps { interface ChangePasswordProps {
isOpen: boolean; isOpen: boolean;
@@ -140,15 +148,11 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
</Stack> </Stack>
</form> </form>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
variant="soft" type={alert.type}
color={alert.type} header={alert.header}
className="mt-4 rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
</ModalDialog> </ModalDialog>
</Modal> </Modal>
+12 -26
View File
@@ -1,6 +1,5 @@
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { import {
Alert,
Box, Box,
Button, Button,
Chip, Chip,
@@ -22,10 +21,10 @@ import type {
} from "../misc/interfaces"; } from "../misc/interfaces";
import type { ApiError } from "../utils/api/apiError"; import type { ApiError } from "../utils/api/apiError";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { MyAlert } from "../components/MyAlert.tsx";
export const AddProduct = () => { export const AddProduct = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [success, setSuccess] = useState(false);
const [alert, setAlert] = useState<AlertInterface>({ const [alert, setAlert] = useState<AlertInterface>({
isAlert: false, isAlert: false,
type: "neutral", type: "neutral",
@@ -69,7 +68,6 @@ export const AddProduct = () => {
storage_location_uuid: "", storage_location_uuid: "",
}, },
onSubmit: async ({ value }) => { onSubmit: async ({ value }) => {
setSuccess(false);
mutate(value); mutate(value);
}, },
}); });
@@ -77,7 +75,12 @@ export const AddProduct = () => {
const { mutate, isPending } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn: (values: ProductFormValues) => createProduct(values), mutationFn: (values: ProductFormValues) => createProduct(values),
onSuccess: () => { onSuccess: () => {
setSuccess(true); setAlert({
isAlert: true,
type: "success",
header: "",
text: "",
});
}, },
onError: showError, onError: showError,
}); });
@@ -287,28 +290,11 @@ export const AddProduct = () => {
</Button> </Button>
</div> </div>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
color={alert.type} type={alert.type}
variant="soft" header={alert.header}
className="rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)}
{success && (
<Alert
color="success"
variant="soft"
className="rounded-2xl border border-emerald-200/70 bg-emerald-50/80 text-emerald-700 shadow-[0_14px_30px_rgba(16,185,129,0.18)]"
>
<div className="flex w-full items-center justify-between">
<Typography level="body-sm" className="text-emerald-700">
{t("success")}
</Typography>
</div>
</Alert>
)} )}
</form> </form>
</Box> </Box>
+86 -56
View File
@@ -1,15 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { import { Avatar, Button, Checkbox, Chip, CircularProgress, Sheet, Table, Typography, } from "@mui/joy";
Alert,
Avatar,
Button,
Checkbox,
Chip,
CircularProgress,
Sheet,
Table,
Typography,
} from "@mui/joy";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
@@ -19,6 +9,8 @@ import { formatDate } from "../utils/uxFncs";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import type { AlertInterface, ProductRow } from "../misc/interfaces"; import type { AlertInterface, ProductRow } from "../misc/interfaces";
import type { ApiError } from "../utils/api/apiError"; import type { ApiError } from "../utils/api/apiError";
import CategoryIcon from "@mui/icons-material/category";
import { MyAlert } from "../components/MyAlert.tsx";
export const InventoryPage = () => { export const InventoryPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -127,20 +119,12 @@ export const InventoryPage = () => {
{productsIsLoading && <CircularProgress size="sm" />} {productsIsLoading && <CircularProgress size="sm" />}
</div> </div>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert type={alert.type} header={alert.header} text={alert.text} />
variant="soft"
color={alert.type}
className="mt-4 rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]"
>
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
<Sheet <Sheet
variant="outlined" variant="outlined"
className="mt-6 flex min-h-0 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm sm:h-[calc(100vh-260px)]" className="mt-6 flex min-h-0 w-full max-w-full flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm sm:h-[calc(100vh-260px)]"
> >
<div className="flex items-center justify-between border-b border-slate-200 px-6 py-4 text-slate-700"> <div className="flex items-center justify-between border-b border-slate-200 px-6 py-4 text-slate-700">
<Typography level="body-lg" fontWeight="bold"> <Typography level="body-lg" fontWeight="bold">
@@ -163,8 +147,9 @@ export const InventoryPage = () => {
stripe="odd" stripe="odd"
variant="plain" variant="plain"
hoverRow hoverRow
className="min-w-240 text-slate-700" className="w-full text-slate-700"
sx={{ sx={{
tableLayout: "fixed",
"--TableCell-headBackground": "--TableCell-headBackground":
"var(--joy-palette-background-surface)", "var(--joy-palette-background-surface)",
"& thead": { "& thead": {
@@ -176,23 +161,48 @@ export const InventoryPage = () => {
"& thead tr": { "& thead tr": {
backgroundColor: "rgb(248 250 252)", backgroundColor: "rgb(248 250 252)",
}, },
"& thead th:nth-child(2)": {
width: "40%",
},
"& thead th:nth-child(3)": {
width: "14%",
},
"& thead th": { "& thead th": {
zIndex: 2, zIndex: 2,
backgroundColor: "rgb(248 250 252)", backgroundColor: "rgb(248 250 252)",
backgroundImage: "none", 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" }, "& tr > *:nth-child(n+4)": { textAlign: "left" },
}} }}
> >
<thead> <thead>
<tr className="text-slate-600"> <tr className="text-slate-600">
<th className="px-4 py-4"> <th className="px-2 py-4">
<Checkbox <Checkbox
checked={rows.length > 0 && selected.length === rows.length} checked={rows.length > 0 && selected.length === rows.length}
indeterminate={ indeterminate={
@@ -203,13 +213,13 @@ export const InventoryPage = () => {
sx={{ verticalAlign: "sub" }} sx={{ verticalAlign: "sub" }}
/> />
</th> </th>
<th className="px-6 py-4">{t("product-name")}</th> <th className="px-3 py-4">{t("product-name")}</th>
<th className="px-6 py-4">{t("price")}</th> <th className="px-3 py-4">{t("price")}</th>
<th className="px-6 py-4">{t("stock")}</th> <th className="px-3 py-4">{t("stock")}</th>
<th className="px-6 py-4">{t("storage-place")}</th> <th className="px-3 py-4">{t("storage-place")}</th>
<th className="px-6 py-4">{t("expiry-date")}</th> <th className="px-3 py-4">{t("expiry-date")}</th>
<th className="px-6 py-4">{t("bottling-date")}</th> <th className="px-3 py-4">{t("bottling-date")}</th>
<th className="px-6 py-4 text-right">{t("actions")}</th> <th className="px-3 py-4 text-right">{t("actions")}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -225,61 +235,81 @@ export const InventoryPage = () => {
aria-checked={isItemSelected} aria-checked={isItemSelected}
tabIndex={-1} tabIndex={-1}
> >
<th scope="row" className="px-4 py-5"> <th scope="row" className="px-2 py-5">
<Checkbox <Checkbox
checked={isItemSelected} checked={isItemSelected}
slotProps={{ input: { "aria-labelledby": labelId } }} slotProps={{ input: { "aria-labelledby": labelId } }}
sx={{ verticalAlign: "top" }} sx={{ verticalAlign: "top" }}
/> />
</th> </th>
<th id={labelId} scope="row" className="px-6 py-5"> <th
<div className="flex items-center gap-4"> id={labelId}
<Avatar size="lg" variant="soft" src={row.imageUrl} /> scope="row"
className="px-3 py-5 overflow-hidden"
>
<div className="flex min-w-0 items-center gap-3">
<Avatar size="lg" variant="soft" className="shrink-0">
<CategoryIcon />
</Avatar>
<div className="min-w-0"> <div className="min-w-0">
<Typography <Typography
level="title-md" level="title-md"
className="text-slate-900" className="text-slate-900 truncate"
> >
{row.name} {row.name}
</Typography> </Typography>
<Typography <Typography
level="body-sm" level="body-sm"
className="text-slate-500" className="text-slate-500 truncate"
> >
{row.description} {row.description}
</Typography> </Typography>
</div> </div>
</div> </div>
</th> </th>
<td className="px-6 py-5"> <td className="px-3 py-5 overflow-hidden">
<Typography level="title-md">{row.price}</Typography> <Typography level="title-md" className="truncate">
<Typography level="body-sm" className="text-slate-400"> {row.price}
</Typography>
<Typography
level="body-sm"
className="truncate text-slate-400"
>
{Cookies.get("currency")} {Cookies.get("currency")}
</Typography> </Typography>
</td> </td>
<td className="px-6 py-5"> <td className="px-3 py-5 overflow-hidden">
<Chip <Chip
variant="soft" variant="soft"
color="neutral" color="neutral"
size="lg" size="lg"
className="px-3" className="max-w-full px-3"
> >
{row.stock} <span className="truncate">{row.stock}</span>
</Chip> </Chip>
</td> </td>
<td className="px-6 py-5"> <td className="px-3 py-5 overflow-hidden">
<Typography level="title-md">{row.location}</Typography> <Typography level="title-md" className="truncate">
<Typography level="body-sm" className="text-slate-500"> {row.location}
</Typography>
<Typography
level="body-sm"
className="truncate text-slate-500"
>
{row.locationDetail} {row.locationDetail}
</Typography> </Typography>
</td> </td>
<td className="px-6 py-5"> <td className="px-3 py-5 overflow-hidden">
<Typography level="title-md">{row.expiryDate}</Typography> <Typography level="title-md" className="truncate">
{row.expiryDate}
</Typography>
</td> </td>
<td className="px-6 py-5"> <td className="px-3 py-5 overflow-hidden">
<Typography level="title-md">{row.refillDate}</Typography> <Typography level="title-md" className="truncate">
{row.refillDate}
</Typography>
</td> </td>
<td className="px-6 py-5 text-right"> <td className="px-3 py-5 text-right">
<Button <Button
onClick={() => onClick={() =>
navigate({ navigate({
+7 -24
View File
@@ -1,7 +1,6 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { getStorages } from "../utils/api/storages.ts"; import { getStorages } from "../utils/api/storages.ts";
import { import {
Alert,
Box, Box,
Button, Button,
Chip, Chip,
@@ -26,6 +25,7 @@ import type {
import type { ApiError } from "../utils/api/apiError"; import type { ApiError } from "../utils/api/apiError";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import ElectricBoltIcon from "@mui/icons-material/ElectricBolt"; import ElectricBoltIcon from "@mui/icons-material/ElectricBolt";
import { MyAlert } from "../components/MyAlert.tsx";
export const ProductQuickView = () => { export const ProductQuickView = () => {
const uuid = window.location.search.split("uuid=")[1]; const uuid = window.location.search.split("uuid=")[1];
@@ -165,7 +165,7 @@ export const ProductQuickView = () => {
form.handleSubmit(); form.handleSubmit();
}} }}
> >
<div className="grid gap-5 lg:grid-cols-[1.2fr_1fr]"> <div className="gap-5 lg:grid-cols-[1.2fr_1fr]">
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography level="title-md" className="text-slate-900">
@@ -338,28 +338,11 @@ export const ProductQuickView = () => {
</Button> </Button>
</div> </div>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
color={alert.type} type={alert.type}
variant="soft" header={alert.header}
className="rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)}
{success && (
<Alert
color="success"
variant="soft"
className="rounded-2xl border border-emerald-200/70 bg-emerald-50/80 text-emerald-700 shadow-[0_14px_30px_rgba(16,185,129,0.18)]"
>
<div className="flex w-full items-center justify-between">
<Typography level="body-sm" className="text-emerald-700">
{t("success")}
</Typography>
</div>
</Alert>
)} )}
</form> </form>
</Box> </Box>
+3 -19
View File
@@ -1,13 +1,4 @@
import { import { Button, Chip, CircularProgress, Divider, Input, Sheet, Typography, } from "@mui/joy";
Alert,
Button,
Chip,
CircularProgress,
Divider,
Input,
Sheet,
Typography,
} from "@mui/joy";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
@@ -17,6 +8,7 @@ import type { ApiError } from "../utils/api/apiError";
import { fetchSettings, mutateSettings } from "../utils/api/settings"; import { fetchSettings, mutateSettings } from "../utils/api/settings";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ChangePasswordModal } from "../components/modals/ChangePasswordModal"; import { ChangePasswordModal } from "../components/modals/ChangePasswordModal";
import { MyAlert } from "../components/MyAlert.tsx";
export const Settings = () => { export const Settings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -118,15 +110,7 @@ export const Settings = () => {
<Sheet className="mt-6 rounded-3xl border border-white/70 bg-white/80 p-6 shadow-[0_24px_60px_rgba(12,38,78,0.12)] backdrop-blur"> <Sheet className="mt-6 rounded-3xl border border-white/70 bg-white/80 p-6 shadow-[0_24px_60px_rgba(12,38,78,0.12)] backdrop-blur">
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert type={alert.type} header={alert.header} text={alert.text} />
variant="soft"
color={alert.type}
className="mb-6 rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]"
>
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
{settingsPending ? ( {settingsPending ? (
<div className="flex items-center justify-center py-16"> <div className="flex items-center justify-center py-16">
+32 -31
View File
@@ -1,13 +1,6 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { getStorages } from "../utils/api/storages"; import { getStorages } from "../utils/api/storages";
import { import { Button, CircularProgress, Sheet, Table, Typography } from "@mui/joy";
Alert,
Button,
CircularProgress,
Sheet,
Table,
Typography,
} from "@mui/joy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import type { AlertInterface, Storage } from "../misc/interfaces"; import type { AlertInterface, Storage } from "../misc/interfaces";
import type { ApiError } from "../utils/api/apiError"; import type { ApiError } from "../utils/api/apiError";
@@ -15,6 +8,7 @@ import { StorageRow } from "../components/StorageRow";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { AddStorageModal } from "../components/modals/AddStorageModal"; import { AddStorageModal } from "../components/modals/AddStorageModal";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import { MyAlert } from "../components/MyAlert.tsx";
export const Storages = () => { export const Storages = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -74,21 +68,13 @@ export const Storages = () => {
</Button> </Button>
</div> </div>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert type={alert.type} header={alert.header} text={alert.text} />
variant="soft"
color={alert.type}
className="rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]"
>
{alert.header}
<br />
{alert.text}
</Alert>
)} )}
</div> </div>
<Sheet <Sheet
variant="outlined" variant="outlined"
className="mt-6 flex min-h-0 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm sm:h-[calc(100vh-260px)]" className="mt-6 flex min-h-0 w-full max-w-full flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm sm:h-[calc(100vh-260px)]"
> >
<AddStorageModal isOpen={modal} setOpen={setModal} /> <AddStorageModal isOpen={modal} setOpen={setModal} />
{isLoading ? ( {isLoading ? (
@@ -108,8 +94,9 @@ export const Storages = () => {
stripe="odd" stripe="odd"
variant="plain" variant="plain"
hoverRow hoverRow
className="min-w-240 text-slate-700" className="w-full text-slate-700"
sx={{ sx={{
tableLayout: "fixed",
"--TableCell-headBackground": "--TableCell-headBackground":
"var(--joy-palette-background-surface)", "var(--joy-palette-background-surface)",
"& thead": { "& thead": {
@@ -121,27 +108,41 @@ export const Storages = () => {
"& thead tr": { "& thead tr": {
backgroundColor: "rgb(248 250 252)", backgroundColor: "rgb(248 250 252)",
}, },
"& thead th:nth-child(2)": {
width: "40%",
},
"& thead th:nth-child(3)": {
width: "14%",
},
"& thead th": { "& thead th": {
zIndex: 2, zIndex: 2,
backgroundColor: "rgb(248 250 252)", backgroundColor: "rgb(248 250 252)",
backgroundImage: "none", 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" },
}} }}
> >
<thead> <thead>
<tr className="text-slate-600"> <tr className="text-slate-600">
<th className="px-6 py-4">{t("storage-name")}</th> <th className="px-3 py-4">{t("storage-name")}</th>
<th className="px-6 py-4">{t("description")}</th> <th className="px-3 py-4">{t("description")}</th>
<th className="px-6 py-4">{t("created-at")}</th> <th className="px-3 py-4">{t("created-at")}</th>
<th className="px-6 py-4">{t("updated-at")}</th> <th className="px-3 py-4">{t("updated-at")}</th>
<th className="px-6 py-4 text-right"></th> <th className="px-3 py-4 text-right"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
+6 -23
View File
@@ -1,7 +1,6 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { getStorages } from "../utils/api/storages.ts"; import { getStorages } from "../utils/api/storages.ts";
import { import {
Alert,
Box, Box,
Button, Button,
Chip, Chip,
@@ -27,6 +26,7 @@ import type { ApiError } from "../utils/api/apiError";
import QrCodeIcon from "@mui/icons-material/QrCode"; import QrCodeIcon from "@mui/icons-material/QrCode";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import QRCode from "qrcode"; import QRCode from "qrcode";
import { MyAlert } from "../components/MyAlert.tsx";
interface ViewProductProps { interface ViewProductProps {
uuid: string; uuid: string;
@@ -369,28 +369,11 @@ export const ViewProduct = (props: ViewProductProps) => {
</div> </div>
{alert.isAlert && ( {alert.isAlert && (
<Alert <MyAlert
color={alert.type} type={alert.type}
variant="soft" header={alert.header}
className="rounded-2xl border border-rose-200/70 bg-rose-50/80 text-rose-700 shadow-[0_12px_30px_rgba(220,38,38,0.12)]" text={alert.text}
> />
{alert.header}
<br />
{alert.text}
</Alert>
)}
{success && (
<Alert
color="success"
variant="soft"
className="rounded-2xl border border-emerald-200/70 bg-emerald-50/80 text-emerald-700 shadow-[0_14px_30px_rgba(16,185,129,0.18)]"
>
<div className="flex w-full items-center justify-between">
<Typography level="body-sm" className="text-emerald-700">
{t("success")}
</Typography>
</div>
</Alert>
)} )}
</form> </form>
</Box> </Box>
@@ -57,6 +57,7 @@
"error": "Fehler", "error": "Fehler",
"success": "Erfolg", "success": "Erfolg",
"submit": "Absenden", "submit": "Absenden",
"logout-success-text": "Logout erfolgreich!",
"SU005": "Das Passwort wurde erfolgreich geändert!", "SU005": "Das Passwort wurde erfolgreich geändert!",
"SE001": "Die Einstellungen wurden erfolgreich geändert!", "SE001": "Die Einstellungen wurden erfolgreich geändert!",
"EG001": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.", "EG001": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.",
@@ -57,6 +57,7 @@
"error": "Error", "error": "Error",
"success": "Success", "success": "Success",
"submit": "Submit", "submit": "Submit",
"logout-success-text": "Logout successful!",
"SU005": "Your password is updated successfully!", "SU005": "Your password is updated successfully!",
"SE001": "The settings are updated successfully!", "SE001": "The settings are updated successfully!",
"EG001": "An unexpected error occurred. Please try again later.", "EG001": "An unexpected error occurred. Please try again later.",