feat: implement dark mode and add custom color palette (theme)

This commit is contained in:
2026-07-09 14:27:43 +02:00
parent bbca5a36cc
commit 580db6afe9
15 changed files with 923 additions and 181 deletions
+7 -1
View File
@@ -2,6 +2,8 @@ import { createRouter, RouterProvider } from "@tanstack/react-router";
import "./App.css"; import "./App.css";
import { routeTree } from "./routeTree.gen"; import { routeTree } from "./routeTree.gen";
import { NotFound } from "./components/NotFound.tsx"; import { NotFound } from "./components/NotFound.tsx";
import { CssVarsProvider } from "@mui/joy";
import { theme } from "./theme.ts";
const router = createRouter({ routeTree, defaultNotFoundComponent: NotFound }); const router = createRouter({ routeTree, defaultNotFoundComponent: NotFound });
@@ -12,7 +14,11 @@ declare module "@tanstack/react-router" {
} }
function App() { function App() {
return <RouterProvider router={router} />; return (
<CssVarsProvider theme={theme} defaultMode={"system"}>
<RouterProvider router={router} />
</CssVarsProvider>
);
} }
export default App; export default App;
+41 -7
View File
@@ -68,14 +68,34 @@ export const LoginCard = () => {
}); });
return ( return (
<div className="flex min-h-screen w-full items-center justify-center bg-[radial-gradient(1200px_circle_at_20%_10%,#e6f2ff_0%,#f7f9fc_40%,#f1f5fb_100%)] px-6 py-10"> <div
className="flex min-h-screen w-full items-center justify-center px-6 py-10"
style={{
background:
"radial-gradient(1200px circle at 20% 10%, var(--joy-palette-primary-100) 0%, var(--joy-palette-background-body) 40%, var(--joy-palette-background-level1) 100%)",
}}
>
<div className="mx-auto flex w-full max-w-4xl items-center justify-center"> <div className="mx-auto flex w-full max-w-4xl items-center justify-center">
<div className="w-full max-w-md rounded-3xl border border-white/70 bg-white/80 p-8 shadow-[0_24px_60px_rgba(12,38,78,0.18)] backdrop-blur"> <div
className="w-full max-w-md rounded-3xl p-8 backdrop-blur"
style={{
border: "1px solid var(--joy-palette-divider)",
backgroundColor: "var(--joy-palette-background-surface)",
boxShadow:
"0 24px 60px color-mix(in srgb, var(--joy-palette-primary-800) 18%, transparent)",
}}
>
<div className="mb-8 space-y-2"> <div className="mb-8 space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.3em] text-[#0b6bcb]"> <p
className="text-xs font-semibold uppercase tracking-[0.3em]"
style={{ color: "var(--joy-palette-primary-solidBg)" }}
>
Stockhome Stockhome
</p> </p>
<h1 className="text-3xl font-semibold text-slate-900"> <h1
className="text-3xl font-semibold"
style={{ color: "var(--joy-palette-text-primary)" }}
>
{t("login")} {t("login")}
</h1> </h1>
</div> </div>
@@ -101,7 +121,11 @@ export const LoginCard = () => {
placeholder={t("username")} placeholder={t("username")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -114,7 +138,11 @@ export const LoginCard = () => {
placeholder={t("password")} placeholder={t("password")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -122,7 +150,13 @@ export const LoginCard = () => {
type="submit" type="submit"
loading={isPending} loading={isPending}
size="lg" size="lg"
className="w-full rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="w-full rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("login")} {t("login")}
</Button> </Button>
+4 -1
View File
@@ -9,7 +9,10 @@ export const NotFound = () => {
<Typography color={"primary"} level={"h1"}> <Typography color={"primary"} level={"h1"}>
{t("not-found-header")} {t("not-found-header")}
</Typography> </Typography>
<Typography className={"text-slate-900"} level={"body-lg"}> <Typography
sx={{ color: "var(--joy-palette-text-primary)" }}
level={"body-lg"}
>
{t("not-found-body")} {t("not-found-body")}
</Typography> </Typography>
</div> </div>
+64 -5
View File
@@ -7,20 +7,23 @@ import StorageIcon from "@mui/icons-material/Storage";
import SettingsIcon from "@mui/icons-material/Settings"; import SettingsIcon from "@mui/icons-material/Settings";
import ExitToAppIcon from "@mui/icons-material/ExitToApp"; import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import TranslateIcon from "@mui/icons-material/Translate"; import TranslateIcon from "@mui/icons-material/Translate";
import Brightness4Icon from "@mui/icons-material/Brightness4";
import MenuIcon from "@mui/icons-material/Menu"; import MenuIcon from "@mui/icons-material/Menu";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { useMatchRoute, useNavigate } from "@tanstack/react-router"; import { useMatchRoute, useNavigate } from "@tanstack/react-router";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { changeTranslation } from "../utils/uxFncs"; import { changeTranslation } from "../utils/uxFncs";
import { useColorScheme } from "@mui/joy/styles";
export const Sidebar = () => { export const Sidebar = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const matchRoute = useMatchRoute(); const matchRoute = useMatchRoute();
const { mode, setMode } = useColorScheme();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const btnClass = const btnClass =
"h-11 w-full justify-start! rounded-2xl px-4 text-left text-sm font-semibold text-slate-700 transition hover:bg-white/80 hover:text-[#0b6bcb] [&_.MuiButton-startDecorator]:mr-3! [&_.MuiButton-startDecorator]:ml-0!"; "h-11 w-full justify-start! rounded-2xl px-4 text-left text-sm font-semibold transition [&_.MuiButton-startDecorator]:mr-3! [&_.MuiButton-startDecorator]:ml-0!";
const variant = (to: string) => const variant = (to: string) =>
!!matchRoute({ to, fuzzy: false }) ? "soft" : "plain"; !!matchRoute({ to, fuzzy: false }) ? "soft" : "plain";
@@ -31,18 +34,30 @@ export const Sidebar = () => {
}; };
return ( return (
<aside className="flex w-full flex-col gap-6 border-b border-white/80 bg-linear-to-b from-[#f7fbff] via-[#f2f6fb] to-[#eef3f9] px-4 py-5 shadow-[0_20px_60px_rgba(11,107,203,0.08)] sm:px-5 lg:h-full lg:min-h-screen lg:max-w-70 lg:border-b-0 lg:border-r lg:px-6 lg:py-8"> <aside
className="flex w-full flex-col gap-6 px-4 py-5 sm:px-5 lg:h-full lg:min-h-screen lg:max-w-70 lg:border-b-0 lg:border-r lg:px-6 lg:py-8"
style={{
borderBottom: "1px solid var(--joy-palette-divider)",
borderRightColor: "var(--joy-palette-divider)",
background:
"linear-gradient(to bottom, var(--joy-palette-primary-50), var(--joy-palette-background-level1), var(--joy-palette-background-level2))",
boxShadow:
"0 20px 60px color-mix(in srgb, var(--joy-palette-primary-solidBg) 8%, transparent)",
}}
>
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Typography <Typography
level="h2" level="h2"
className="text-[22px] font-semibold text-[#0b6bcb]" className="text-[22px] font-semibold"
sx={{ color: "var(--joy-palette-primary-solidBg)" }}
> >
{t("app-title")} {t("app-title")}
</Typography> </Typography>
<Typography <Typography
level="body-lg" level="body-lg"
className="text-sm font-medium text-slate-500" className="text-sm font-medium"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
> >
{Cookies.get("app-name") ? Cookies.get("app-name") : ""} {Cookies.get("app-name") ? Cookies.get("app-name") : ""}
</Typography> </Typography>
@@ -83,6 +98,13 @@ export const Sidebar = () => {
variant={variant("/app/inventory")} variant={variant("/app/inventory")}
startDecorator={<InventoryIcon />} startDecorator={<InventoryIcon />}
className={btnClass} className={btnClass}
sx={{
color: "var(--joy-palette-text-secondary)",
"&:hover": {
bgcolor: "var(--joy-palette-background-surface)",
color: "var(--joy-palette-primary-solidBg)",
},
}}
> >
{t("inventory")} {t("inventory")}
</Button> </Button>
@@ -91,6 +113,13 @@ export const Sidebar = () => {
variant={variant("/app/add-product")} variant={variant("/app/add-product")}
startDecorator={<AddBoxIcon />} startDecorator={<AddBoxIcon />}
className={btnClass} className={btnClass}
sx={{
color: "var(--joy-palette-text-secondary)",
"&:hover": {
bgcolor: "var(--joy-palette-background-surface)",
color: "var(--joy-palette-primary-solidBg)",
},
}}
> >
{t("add")} {t("add")}
</Button> </Button>
@@ -99,6 +128,13 @@ export const Sidebar = () => {
variant={variant("/app/storages")} variant={variant("/app/storages")}
startDecorator={<StorageIcon />} startDecorator={<StorageIcon />}
className={btnClass} className={btnClass}
sx={{
color: "var(--joy-palette-text-secondary)",
"&:hover": {
bgcolor: "var(--joy-palette-background-surface)",
color: "var(--joy-palette-primary-solidBg)",
},
}}
> >
{t("storages")} {t("storages")}
</Button> </Button>
@@ -107,6 +143,13 @@ export const Sidebar = () => {
variant={variant("/app/app-settings")} variant={variant("/app/app-settings")}
startDecorator={<SettingsIcon />} startDecorator={<SettingsIcon />}
className={btnClass} className={btnClass}
sx={{
color: "var(--joy-palette-text-secondary)",
"&:hover": {
bgcolor: "var(--joy-palette-background-surface)",
color: "var(--joy-palette-primary-solidBg)",
},
}}
> >
{t("settings")} {t("settings")}
</Button> </Button>
@@ -132,9 +175,25 @@ export const Sidebar = () => {
> >
{t("change-translation")} {t("change-translation")}
</Button> </Button>
<Button
onClick={() => setMode(mode === "dark" ? "light" : "dark")}
color="neutral"
startDecorator={<Brightness4Icon />}
className={btnClass}
>
{mode === "dark" ? "Light Mode" : "Dark Mode"}
</Button>
</div> </div>
<div className="flex items-center gap-3 rounded-2xl border border-white/70 bg-white/80 px-4 py-3 text-xs font-semibold uppercase tracking-[0.2em] text-[#0b6bcb] shadow-[0_12px_30px_rgba(12,38,78,0.12)]"> <div
className="flex items-center gap-3 rounded-2xl px-4 py-3 text-xs font-semibold uppercase tracking-[0.2em]"
style={{
border: "1px solid var(--joy-palette-divider)",
backgroundColor: "var(--joy-palette-background-surface)",
color: "var(--joy-palette-primary-solidBg)",
boxShadow: "0 12px 30px var(--joy-palette-divider)",
}}
>
<img <img
src="/favicon.png" src="/favicon.png"
alt="Stockhome" alt="Stockhome"
+21 -5
View File
@@ -50,7 +50,7 @@ export const StorageRow = ({ storage, onError }: StorageRowProps) => {
(values.description ?? "") !== (storage.description ?? ""); (values.description ?? "") !== (storage.description ?? "");
return ( return (
<tr key={storage.uuid} className="border-t border-slate-200 align-top"> <tr key={storage.uuid} className="align-top">
<td className="px-6 py-5"> <td className="px-6 py-5">
<form.Field name="name"> <form.Field name="name">
{(field) => ( {(field) => (
@@ -60,7 +60,12 @@ export const StorageRow = ({ storage, onError }: StorageRowProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="sm" size="sm"
variant="outlined" variant="outlined"
className="rounded-xl bg-slate-50/80 text-slate-700 shadow-none ring-1 ring-inset ring-slate-200 focus-within:ring-2 focus-within:ring-slate-300" className="rounded-xl shadow-none"
sx={{
bgcolor: "var(--joy-palette-background-level1)",
color: "var(--joy-palette-text-secondary)",
"--Input-focusedHighlight": "var(--joy-palette-neutral-300)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -74,15 +79,26 @@ export const StorageRow = ({ storage, onError }: StorageRowProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="sm" size="sm"
variant="outlined" variant="outlined"
className="rounded-xl bg-slate-50/80 text-slate-700 shadow-none ring-1 ring-inset ring-slate-200 focus-within:ring-2 focus-within:ring-slate-300" className="rounded-xl shadow-none"
sx={{
bgcolor: "var(--joy-palette-background-level1)",
color: "var(--joy-palette-text-secondary)",
"--Input-focusedHighlight": "var(--joy-palette-neutral-300)",
}}
/> />
)} )}
</form.Field> </form.Field>
</td> </td>
<td className="px-6 py-5 text-sm text-slate-500"> <td
className="px-6 py-5 text-sm"
style={{ color: "var(--joy-palette-text-tertiary)" }}
>
{formatDate(storage.created_at)} {formatDate(storage.created_at)}
</td> </td>
<td className="px-6 py-5 text-sm text-slate-500"> <td
className="px-6 py-5 text-sm"
style={{ color: "var(--joy-palette-text-tertiary)" }}
>
{formatDate(storage.updated_at)} {formatDate(storage.updated_at)}
</td> </td>
<td className="px-6 py-5 text-right"> <td className="px-6 py-5 text-right">
@@ -51,11 +51,20 @@ export const AddStorageModal = (props: AddStorageModalProps) => {
return ( return (
<> <>
<Modal open={props.isOpen} onClose={() => props.setOpen(false)}> <Modal open={props.isOpen} onClose={() => props.setOpen(false)}>
<ModalDialog className="rounded-3xl border border-white/70 bg-white/90 p-6 shadow-[0_30px_70px_rgba(12,38,78,0.2)] backdrop-blur"> <ModalDialog
<DialogTitle className="text-slate-900"> className="rounded-3xl p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 30px 70px color-mix(in srgb, var(--joy-palette-primary-800) 20%, transparent)",
}}
>
<DialogTitle sx={{ color: "var(--joy-palette-text-primary)" }}>
{t("new-storage-title")} {t("new-storage-title")}
</DialogTitle> </DialogTitle>
<DialogContent className="text-slate-500"> <DialogContent sx={{ color: "var(--joy-palette-text-tertiary)" }}>
{t("new-storage-content")} {t("new-storage-content")}
</DialogContent> </DialogContent>
<form <form
@@ -73,7 +82,11 @@ export const AddStorageModal = (props: AddStorageModalProps) => {
placeholder={t("storage-name")} placeholder={t("storage-name")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -85,14 +98,24 @@ export const AddStorageModal = (props: AddStorageModalProps) => {
placeholder={t("description")} placeholder={t("description")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
<Button <Button
type="submit" type="submit"
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("submit")} {t("submit")}
</Button> </Button>
@@ -1,11 +1,4 @@
import { import { Button, DialogTitle, Input, Modal, ModalDialog, Stack, } from "@mui/joy";
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";
@@ -66,7 +59,6 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
}); });
}, },
onSuccess: () => { onSuccess: () => {
// Sets the success alert in the settings page via component props
props.alert({ props.alert({
isAlert: true, isAlert: true,
type: "success", type: "success",
@@ -74,7 +66,6 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
text: t("SU005"), text: t("SU005"),
}); });
// Sets the success alert locally (in the modal itself)
setAlert({ setAlert({
isAlert: true, isAlert: true,
type: "success", type: "success",
@@ -88,8 +79,17 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
return ( return (
<> <>
<Modal open={props.isOpen} onClose={() => props.setOpen(false)}> <Modal open={props.isOpen} onClose={() => props.setOpen(false)}>
<ModalDialog className="rounded-3xl border border-white/70 bg-white/90 p-6 shadow-[0_30px_70px_rgba(12,38,78,0.2)] backdrop-blur"> <ModalDialog
<DialogTitle className="text-slate-900"> className="rounded-3xl p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 30px 70px color-mix(in srgb, var(--joy-palette-primary-800) 20%, transparent)",
}}
>
<DialogTitle sx={{ color: "var(--joy-palette-text-primary)" }}>
{t("new-password-title")} {t("new-password-title")}
</DialogTitle> </DialogTitle>
<form <form
@@ -108,7 +108,11 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
placeholder={t("current-password")} placeholder={t("current-password")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -121,7 +125,11 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
placeholder={t("new-password")} placeholder={t("new-password")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -134,14 +142,24 @@ export const ChangePasswordModal = (props: ChangePasswordProps) => {
placeholder={t("new-password-rep")} placeholder={t("new-password-rep")}
variant="outlined" variant="outlined"
size="lg" size="lg"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
<Button <Button
type="submit" type="submit"
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("change")} {t("change")}
</Button> </Button>
+104 -22
View File
@@ -90,10 +90,16 @@ export const AddProduct = () => {
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="h2" className="text-slate-900"> <Typography
level="h2"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("add-product")} {t("add-product")}
</Typography> </Typography>
<Typography level="body-lg" className="text-slate-500"> <Typography
level="body-lg"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("add-product-subtitle")} {t("add-product-subtitle")}
</Typography> </Typography>
</div> </div>
@@ -106,7 +112,16 @@ export const AddProduct = () => {
</Chip> </Chip>
</div> </div>
</div> </div>
<Box 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"> <Box
className="mt-6 rounded-3xl p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 24px 60px color-mix(in srgb, var(--joy-palette-primary-800) 12%, transparent)",
}}
>
<form <form
className="space-y-6" className="space-y-6"
onSubmit={(e) => { onSubmit={(e) => {
@@ -117,7 +132,10 @@ export const AddProduct = () => {
<div className="grid gap-5 lg:grid-cols-[1.2fr_1fr]"> <div className="grid 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"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("product-name")} {t("product-name")}
</Typography> </Typography>
<form.Field name="name"> <form.Field name="name">
@@ -130,13 +148,20 @@ export const AddProduct = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("description")} {t("description")}
</Typography> </Typography>
<form.Field name="description"> <form.Field name="description">
@@ -148,14 +173,21 @@ export const AddProduct = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("expiry-date")} {t("expiry-date")}
</Typography> </Typography>
<form.Field name="expiry_date"> <form.Field name="expiry_date">
@@ -167,13 +199,19 @@ export const AddProduct = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("bottling-date")} {t("bottling-date")}
</Typography> </Typography>
<form.Field name="bottling_date"> <form.Field name="bottling_date">
@@ -185,7 +223,10 @@ export const AddProduct = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -193,14 +234,28 @@ export const AddProduct = () => {
</div> </div>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded-2xl border border-white/70 bg-linear-to-br from-[#f7fbff] via-[#f2f6fb] to-[#eef3f9] p-5 shadow-[0_16px_40px_rgba(12,38,78,0.08)]"> <div
<Typography level="title-md" className="text-slate-900"> className="rounded-2xl p-5"
style={{
border: "1px solid var(--joy-palette-divider)",
background:
"linear-gradient(to bottom right, var(--joy-palette-primary-50), var(--joy-palette-background-level1), var(--joy-palette-background-level2))",
boxShadow: "0 16px 40px var(--joy-palette-divider)",
}}
>
<Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("inventory")} {t("inventory")}
</Typography> </Typography>
<Divider className="my-3" /> <Divider className="my-3" />
<div className="grid gap-4"> <div className="grid gap-4">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("amount")} {t("amount")}
</Typography> </Typography>
<form.Field name="amount"> <form.Field name="amount">
@@ -220,13 +275,19 @@ export const AddProduct = () => {
); );
}} }}
onBlur={field.handleBlur} onBlur={field.handleBlur}
className="rounded-2xl bg-white/80" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("price")} {t("price")}
</Typography> </Typography>
<form.Field name="price"> <form.Field name="price">
@@ -238,16 +299,25 @@ export const AddProduct = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{Cookies.get("currency")} {Cookies.get("currency")}
</Typography> </Typography>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("storage-place")} {t("storage-place")}
</Typography> </Typography>
<form.Field name="storage_location_uuid"> <form.Field name="storage_location_uuid">
@@ -260,7 +330,10 @@ export const AddProduct = () => {
} }
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
> >
{storages?.map((storage) => ( {storages?.map((storage) => (
<Option key={storage.uuid} value={storage.uuid}> <Option key={storage.uuid} value={storage.uuid}>
@@ -276,7 +349,10 @@ export const AddProduct = () => {
</div> </div>
</div> </div>
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center">
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("product-details")} {t("product-details")}
</Typography> </Typography>
<div className="grow"></div> <div className="grow"></div>
@@ -284,7 +360,13 @@ export const AddProduct = () => {
type="submit" type="submit"
loading={isPending} loading={isPending}
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("save")} {t("save")}
</Button> </Button>
+32 -13
View File
@@ -124,9 +124,20 @@ export const InventoryPage = () => {
<Sheet <Sheet
variant="outlined" variant="outlined"
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)]" className="mt-6 flex min-h-0 w-full max-w-full flex-col overflow-hidden rounded-2xl shadow-sm sm:h-[calc(100vh-260px)]"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
}}
> >
<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 px-6 py-4"
style={{
borderBottom: "1px solid var(--joy-palette-divider)",
color: "var(--joy-palette-text-secondary)",
}}
>
<Typography level="body-lg" fontWeight="bold"> <Typography level="body-lg" fontWeight="bold">
{t("inventory")} {t("inventory")}
</Typography> </Typography>
@@ -147,24 +158,26 @@ export const InventoryPage = () => {
stripe="odd" stripe="odd"
variant="plain" variant="plain"
hoverRow hoverRow
className="w-full text-slate-700" className="w-full"
sx={{ sx={{
tableLayout: "fixed", tableLayout: "fixed",
color: "var(--joy-palette-text-secondary)",
"--TableCell-headBackground": "--TableCell-headBackground":
"var(--joy-palette-background-surface)", "var(--joy-palette-background-level1)",
"& thead": { "& thead": {
position: "sticky", position: "sticky",
top: 0, top: 0,
zIndex: 3, zIndex: 3,
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
}, },
"& thead tr": { "& thead tr": {
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
}, },
"& thead th": { "& thead th": {
zIndex: 2, zIndex: 2,
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
backgroundImage: "none", backgroundImage: "none",
color: "var(--joy-palette-text-secondary)",
}, },
"& thead th:nth-child(1)": { "& thead th:nth-child(1)": {
width: "44px", width: "44px",
@@ -198,10 +211,13 @@ export const InventoryPage = () => {
minWidth: "100px", minWidth: "100px",
}, },
"& tr > *:nth-child(n+4)": { textAlign: "left" }, "& tr > *:nth-child(n+4)": { textAlign: "left" },
"& tbody tr": {
borderTop: "1px solid var(--joy-palette-divider)",
},
}} }}
> >
<thead> <thead>
<tr className="text-slate-600"> <tr>
<th className="px-2 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}
@@ -229,7 +245,6 @@ export const InventoryPage = () => {
return ( return (
<tr <tr
key={row.id} key={row.id}
className="border-t border-slate-200"
onClick={(event) => handleClick(event, row.id)} onClick={(event) => handleClick(event, row.id)}
role="checkbox" role="checkbox"
aria-checked={isItemSelected} aria-checked={isItemSelected}
@@ -254,13 +269,15 @@ export const InventoryPage = () => {
<div className="min-w-0"> <div className="min-w-0">
<Typography <Typography
level="title-md" level="title-md"
className="text-slate-900 truncate" className="truncate"
sx={{ color: "var(--joy-palette-text-primary)" }}
> >
{row.name} {row.name}
</Typography> </Typography>
<Typography <Typography
level="body-sm" level="body-sm"
className="text-slate-500 truncate" className="truncate"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
> >
{row.description} {row.description}
</Typography> </Typography>
@@ -273,7 +290,8 @@ export const InventoryPage = () => {
</Typography> </Typography>
<Typography <Typography
level="body-sm" level="body-sm"
className="truncate text-slate-400" className="truncate"
sx={{ color: "var(--joy-palette-neutral-400)" }}
> >
{Cookies.get("currency")} {Cookies.get("currency")}
</Typography> </Typography>
@@ -294,7 +312,8 @@ export const InventoryPage = () => {
</Typography> </Typography>
<Typography <Typography
level="body-sm" level="body-sm"
className="truncate text-slate-500" className="truncate"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
> >
{row.locationDetail} {row.locationDetail}
</Typography> </Typography>
+103 -38
View File
@@ -1,27 +1,12 @@
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 { Box, Button, Chip, CircularProgress, Divider, Input, Option, Select, Typography, } from "@mui/joy";
Box,
Button,
Chip,
CircularProgress,
Divider,
Input,
Option,
Select,
Typography,
} from "@mui/joy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { getProductDetails, mutateProduct } from "../utils/api/products.ts"; import { getProductDetails, mutateProduct } from "../utils/api/products.ts";
import { toInputDate } from "../utils/uxFncs"; import { toInputDate } from "../utils/uxFncs";
import type { import type { AlertInterface, productDetailsInterface, ProductFormValues, Storage, } from "../misc/interfaces";
AlertInterface,
productDetailsInterface,
ProductFormValues,
Storage,
} 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 ElectricBoltIcon from "@mui/icons-material/ElectricBolt"; import ElectricBoltIcon from "@mui/icons-material/ElectricBolt";
@@ -140,7 +125,11 @@ export const ProductQuickView = () => {
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="h2" fontWeight="1000" className="text-slate-900"> <Typography
level="h2"
fontWeight="1000"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
<ElectricBoltIcon color="primary" /> <ElectricBoltIcon color="primary" />
{t("product-details-quick")} {t("product-details-quick")}
<ElectricBoltIcon color="primary" /> <ElectricBoltIcon color="primary" />
@@ -157,7 +146,16 @@ export const ProductQuickView = () => {
{productDetailsLoading && <CircularProgress size="sm" />} {productDetailsLoading && <CircularProgress size="sm" />}
</div> </div>
{isSuccess && ( {isSuccess && (
<Box 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"> <Box
className="mt-6 rounded-3xl p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 24px 60px color-mix(in srgb, var(--joy-palette-primary-800) 12%, transparent)",
}}
>
<form <form
className="space-y-6" className="space-y-6"
onSubmit={(e) => { onSubmit={(e) => {
@@ -168,7 +166,10 @@ export const ProductQuickView = () => {
<div className="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"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("product-name")} {t("product-name")}
</Typography> </Typography>
<form.Field name="name"> <form.Field name="name">
@@ -180,13 +181,20 @@ export const ProductQuickView = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("description")} {t("description")}
</Typography> </Typography>
<form.Field name="description"> <form.Field name="description">
@@ -198,14 +206,21 @@ export const ProductQuickView = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("expiry-date")} {t("expiry-date")}
</Typography> </Typography>
<form.Field name="expiry_date"> <form.Field name="expiry_date">
@@ -217,13 +232,19 @@ export const ProductQuickView = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("bottling-date")} {t("bottling-date")}
</Typography> </Typography>
<form.Field name="bottling_date"> <form.Field name="bottling_date">
@@ -235,7 +256,10 @@ export const ProductQuickView = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -243,14 +267,28 @@ export const ProductQuickView = () => {
</div> </div>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded-2xl border border-white/70 bg-linear-to-br from-[#f7fbff] via-[#f2f6fb] to-[#eef3f9] p-5 shadow-[0_16px_40px_rgba(12,38,78,0.08)]"> <div
<Typography level="title-lg" className="text-slate-900"> className="rounded-2xl p-5"
style={{
border: "1px solid var(--joy-palette-divider)",
background:
"linear-gradient(to bottom right, var(--joy-palette-primary-50), var(--joy-palette-background-level1), var(--joy-palette-background-level2))",
boxShadow: "0 16px 40px var(--joy-palette-divider)",
}}
>
<Typography
level="title-lg"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("inventory")} {t("inventory")}
</Typography> </Typography>
<Divider className="my-3" /> <Divider className="my-3" />
<div className="grid gap-4"> <div className="grid gap-4">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("amount")} {t("amount")}
</Typography> </Typography>
<form.Field name="amount"> <form.Field name="amount">
@@ -270,13 +308,19 @@ export const ProductQuickView = () => {
); );
}} }}
onBlur={field.handleBlur} onBlur={field.handleBlur}
className="rounded-2xl bg-white/80" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("price")} {t("price")}
</Typography> </Typography>
<form.Field name="price"> <form.Field name="price">
@@ -288,16 +332,25 @@ export const ProductQuickView = () => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{Cookies.get("currency")} {Cookies.get("currency")}
</Typography> </Typography>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("storage-place")} {t("storage-place")}
</Typography> </Typography>
<form.Field name="storage_location_uuid"> <form.Field name="storage_location_uuid">
@@ -309,7 +362,10 @@ export const ProductQuickView = () => {
} }
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
> >
{storages?.map((storage) => ( {storages?.map((storage) => (
<Option key={storage.uuid} value={storage.uuid}> <Option key={storage.uuid} value={storage.uuid}>
@@ -325,14 +381,23 @@ export const ProductQuickView = () => {
</div> </div>
</div> </div>
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("product-details")} {t("product-details")}
</Typography> </Typography>
<Button <Button
type="submit" type="submit"
loading={isPending} loading={isPending}
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("save")} {t("save")}
</Button> </Button>
+73 -16
View File
@@ -91,10 +91,16 @@ export const Settings = () => {
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="h2" className="text-slate-900"> <Typography
level="h2"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("settings")} {t("settings")}
</Typography> </Typography>
<Typography level="body-lg" className="text-slate-500"> <Typography
level="body-lg"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("settings-sub")} {t("settings-sub")}
</Typography> </Typography>
</div> </div>
@@ -108,7 +114,16 @@ export const Settings = () => {
</div> </div>
</div> </div>
<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 p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 24px 60px rgba(var(--joy-palette-primary-900, 12 38 78) / 0.12)",
}}
>
{alert.isAlert && ( {alert.isAlert && (
<MyAlert type={alert.type} header={alert.header} text={alert.text} /> <MyAlert type={alert.type} header={alert.header} text={alert.text} />
)} )}
@@ -124,13 +139,19 @@ export const Settings = () => {
form.handleSubmit(); form.handleSubmit();
}} }}
> >
<div className="grid gap-6 lg:grid-cols-[1.1fr_0.9fr]"> <div className="grid gap-6 lg:grid-cols-[1.1fr\_0.9fr]">
<div className="space-y-5"> <div className="space-y-5">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("app-name")} {t("app-name")}
</Typography> </Typography>
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("app-name-sub")} {t("app-name-sub")}
</Typography> </Typography>
<form.Field name="app-name"> <form.Field name="app-name">
@@ -141,16 +162,26 @@ export const Settings = () => {
size="lg" size="lg"
variant="outlined" variant="outlined"
placeholder="Stockhome" placeholder="Stockhome"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("currency")} {t("currency")}
</Typography> </Typography>
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("currency-sub")} {t("currency-sub")}
</Typography> </Typography>
<form.Field name="currency"> <form.Field name="currency">
@@ -161,18 +192,36 @@ export const Settings = () => {
size="lg" size="lg"
variant="outlined" variant="outlined"
placeholder="EUR" placeholder="EUR"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
</div> </div>
<div className="rounded-2xl border border-white/70 bg-linear-to-br from-[#f7fbff] via-[#f2f6fb] to-[#eef3f9] p-5 shadow-[0_16px_40px_rgba(12,38,78,0.08)]"> <div
<Typography level="title-lg" className="text-slate-900"> className="rounded-2xl p-5"
style={{
border: "1px solid var(--joy-palette-divider)",
background:
"linear-gradient(to bottom right, var(--joy-palette-primary-50), var(--joy-palette-background-level1), var(--joy-palette-background-level2))",
boxShadow: "0 16px 40px var(--joy-palette-divider)",
}}
>
<Typography
level="title-lg"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("quick-tips")} {t("quick-tips")}
</Typography> </Typography>
<Divider className="my-3" /> <Divider className="my-3" />
<div className="space-y-3 text-md text-slate-600"> <div
className="space-y-3 text-md"
style={{ color: "var(--joy-palette-text-secondary)" }}
>
<p>{t("quick-tips-1")}</p> <p>{t("quick-tips-1")}</p>
<p>{t("quick-tips-2")}</p> <p>{t("quick-tips-2")}</p>
</div> </div>
@@ -183,15 +232,23 @@ export const Settings = () => {
type="submit" type="submit"
size="lg" size="lg"
color="primary" color="primary"
className="rounded-2xl text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("save")} {t("save")}
</Button> </Button>
<Button <Button
onClick={() => setModal(true)} onClick={() => setModal(true)}
size="lg" size="lg"
color="warning" color="primary"
className="rounded-2xl text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("change-password")} {t("change-password")}
</Button> </Button>
+32 -10
View File
@@ -50,10 +50,16 @@ export const Storages = () => {
<> <>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="min-w-65 space-y-2"> <div className="min-w-65 space-y-2">
<Typography level="h2" className="text-slate-900"> <Typography
level="h2"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("storages")} {t("storages")}
</Typography> </Typography>
<Typography level="body-lg" className="text-slate-500"> <Typography
level="body-lg"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("storage-delete-info")} {t("storage-delete-info")}
</Typography> </Typography>
</div> </div>
@@ -74,7 +80,12 @@ export const Storages = () => {
<Sheet <Sheet
variant="outlined" variant="outlined"
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)]" className="mt-6 flex min-h-0 w-full max-w-full flex-col overflow-hidden rounded-2xl shadow-sm sm:h-[calc(100vh-260px)]"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
}}
> >
<AddStorageModal isOpen={modal} setOpen={setModal} /> <AddStorageModal isOpen={modal} setOpen={setModal} />
{isLoading ? ( {isLoading ? (
@@ -83,7 +94,13 @@ export const Storages = () => {
</div> </div>
) : ( ) : (
<> <>
<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 px-6 py-4"
style={{
borderBottom: "1px solid var(--joy-palette-divider)",
color: "var(--joy-palette-text-secondary)",
}}
>
<Typography level="body-lg" fontWeight="bold"> <Typography level="body-lg" fontWeight="bold">
{t("storages")} {t("storages")}
</Typography> </Typography>
@@ -94,24 +111,26 @@ export const Storages = () => {
stripe="odd" stripe="odd"
variant="plain" variant="plain"
hoverRow hoverRow
className="w-full text-slate-700" className="w-full"
sx={{ sx={{
tableLayout: "fixed", tableLayout: "fixed",
color: "var(--joy-palette-text-secondary)",
"--TableCell-headBackground": "--TableCell-headBackground":
"var(--joy-palette-background-surface)", "var(--joy-palette-background-level1)",
"& thead": { "& thead": {
position: "sticky", position: "sticky",
top: 0, top: 0,
zIndex: 3, zIndex: 3,
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
}, },
"& thead tr": { "& thead tr": {
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
}, },
"& thead th": { "& thead th": {
zIndex: 2, zIndex: 2,
backgroundColor: "rgb(248 250 252)", backgroundColor: "var(--joy-palette-background-level1)",
backgroundImage: "none", backgroundImage: "none",
color: "var(--joy-palette-text-secondary)",
}, },
"& thead th:nth-child(1)": { "& thead th:nth-child(1)": {
width: "26%", width: "26%",
@@ -134,10 +153,13 @@ export const Storages = () => {
minWidth: "80px", minWidth: "80px",
}, },
"& tr > *:nth-child(n+3)": { textAlign: "left" }, "& tr > *:nth-child(n+3)": { textAlign: "left" },
"& tbody tr": {
borderTop: "1px solid var(--joy-palette-divider)",
},
}} }}
> >
<thead> <thead>
<tr className="text-slate-600"> <tr>
<th className="px-3 py-4">{t("storage-name")}</th> <th className="px-3 py-4">{t("storage-name")}</th>
<th className="px-3 py-4">{t("description")}</th> <th className="px-3 py-4">{t("description")}</th>
<th className="px-3 py-4">{t("created-at")}</th> <th className="px-3 py-4">{t("created-at")}</th>
+109 -39
View File
@@ -1,27 +1,12 @@
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 { Box, Button, Chip, CircularProgress, Divider, Input, Option, Select, Typography, } from "@mui/joy";
Box,
Button,
Chip,
CircularProgress,
Divider,
Input,
Option,
Select,
Typography,
} from "@mui/joy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { getProductDetails, mutateProduct } from "../utils/api/products.ts"; import { getProductDetails, mutateProduct } from "../utils/api/products.ts";
import { toInputDate } from "../utils/uxFncs"; import { toInputDate } from "../utils/uxFncs";
import type { import type { AlertInterface, productDetailsInterface, ProductFormValues, Storage, } from "../misc/interfaces";
AlertInterface,
productDetailsInterface,
ProductFormValues,
Storage,
} from "../misc/interfaces";
import type { ApiError } from "../utils/api/apiError"; 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";
@@ -160,7 +145,10 @@ export const ViewProduct = (props: ViewProductProps) => {
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="h2" className="text-slate-900"> <Typography
level="h2"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("product-details")} {t("product-details")}
</Typography> </Typography>
</div> </div>
@@ -175,7 +163,16 @@ export const ViewProduct = (props: ViewProductProps) => {
{productDetailsLoading && <CircularProgress size="sm" />} {productDetailsLoading && <CircularProgress size="sm" />}
</div> </div>
{isSuccess && ( {isSuccess && (
<Box 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"> <Box
className="mt-6 rounded-3xl p-6 backdrop-blur"
sx={{
border: "1px solid",
borderColor: "divider",
bgcolor: "background.surface",
boxShadow:
"0 24px 60px color-mix(in srgb, var(--joy-palette-primary-800) 12%, transparent)",
}}
>
<form <form
className="space-y-6" className="space-y-6"
onSubmit={(e) => { onSubmit={(e) => {
@@ -186,7 +183,10 @@ export const ViewProduct = (props: ViewProductProps) => {
<div className="grid gap-5 lg:grid-cols-[1.2fr_1fr]"> <div className="grid 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"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("product-name")} {t("product-name")}
</Typography> </Typography>
<form.Field name="name"> <form.Field name="name">
@@ -198,13 +198,20 @@ export const ViewProduct = (props: ViewProductProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("description")} {t("description")}
</Typography> </Typography>
<form.Field name="description"> <form.Field name="description">
@@ -216,14 +223,21 @@ export const ViewProduct = (props: ViewProductProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
boxShadow: "0 10px 24px var(--joy-palette-divider)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("expiry-date")} {t("expiry-date")}
</Typography> </Typography>
<form.Field name="expiry_date"> <form.Field name="expiry_date">
@@ -235,13 +249,19 @@ export const ViewProduct = (props: ViewProductProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("bottling-date")} {t("bottling-date")}
</Typography> </Typography>
<form.Field name="bottling_date"> <form.Field name="bottling_date">
@@ -253,7 +273,10 @@ export const ViewProduct = (props: ViewProductProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
@@ -261,14 +284,28 @@ export const ViewProduct = (props: ViewProductProps) => {
</div> </div>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded-2xl border border-white/70 bg-linear-to-br from-[#f7fbff] via-[#f2f6fb] to-[#eef3f9] p-5 shadow-[0_16px_40px_rgba(12,38,78,0.08)]"> <div
<Typography level="title-lg" className="text-slate-900"> className="rounded-2xl p-5"
style={{
border: "1px solid var(--joy-palette-divider)",
background:
"linear-gradient(to bottom right, var(--joy-palette-primary-50), var(--joy-palette-background-level1), var(--joy-palette-background-level2))",
boxShadow: "0 16px 40px var(--joy-palette-divider)",
}}
>
<Typography
level="title-lg"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("inventory")} {t("inventory")}
</Typography> </Typography>
<Divider className="my-3" /> <Divider className="my-3" />
<div className="grid gap-4"> <div className="grid gap-4">
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("amount")} {t("amount")}
</Typography> </Typography>
<form.Field name="amount"> <form.Field name="amount">
@@ -288,13 +325,19 @@ export const ViewProduct = (props: ViewProductProps) => {
); );
}} }}
onBlur={field.handleBlur} onBlur={field.handleBlur}
className="rounded-2xl bg-white/80" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("price")} {t("price")}
</Typography> </Typography>
<form.Field name="price"> <form.Field name="price">
@@ -306,16 +349,25 @@ export const ViewProduct = (props: ViewProductProps) => {
onBlur={field.handleBlur} onBlur={field.handleBlur}
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
/> />
)} )}
</form.Field> </form.Field>
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{Cookies.get("currency")} {Cookies.get("currency")}
</Typography> </Typography>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Typography level="title-md" className="text-slate-900"> <Typography
level="title-md"
sx={{ color: "var(--joy-palette-text-primary)" }}
>
{t("storage-place")} {t("storage-place")}
</Typography> </Typography>
<form.Field name="storage_location_uuid"> <form.Field name="storage_location_uuid">
@@ -327,7 +379,10 @@ export const ViewProduct = (props: ViewProductProps) => {
} }
size="lg" size="lg"
variant="outlined" variant="outlined"
className="rounded-2xl bg-white/90" className="rounded-2xl"
sx={{
bgcolor: "var(--joy-palette-background-surface)",
}}
> >
{storages?.map((storage) => ( {storages?.map((storage) => (
<Option key={storage.uuid} value={storage.uuid}> <Option key={storage.uuid} value={storage.uuid}>
@@ -344,7 +399,10 @@ export const ViewProduct = (props: ViewProductProps) => {
</div> </div>
<div> <div>
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center">
<Typography level="body-sm" className="text-slate-500"> <Typography
level="body-sm"
sx={{ color: "var(--joy-palette-text-tertiary)" }}
>
{t("product-details")} {t("product-details")}
</Typography> </Typography>
<div className="grow"></div> <div className="grow"></div>
@@ -353,7 +411,13 @@ export const ViewProduct = (props: ViewProductProps) => {
loading={isPending} loading={isPending}
onClick={() => downloadQRcode()} onClick={() => downloadQRcode()}
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("download-qr-code")} {t("download-qr-code")}
</Button> </Button>
@@ -361,7 +425,13 @@ export const ViewProduct = (props: ViewProductProps) => {
type="submit" type="submit"
loading={isPending} loading={isPending}
size="lg" size="lg"
className="rounded-2xl bg-[#0b6bcb] text-white shadow-[0_16px_36px_rgba(11,107,203,0.35)] transition hover:-translate-y-0.5 hover:bg-[#095aa7]" color="primary"
variant="solid"
className="rounded-2xl transition hover:-translate-y-0.5"
sx={{
boxShadow:
"0 16px 36px color-mix(in srgb, var(--joy-palette-primary-solidBg) 35%, transparent)",
}}
> >
{t("save")} {t("save")}
</Button> </Button>
+5 -2
View File
@@ -1,4 +1,4 @@
import { Outlet, createFileRoute } from "@tanstack/react-router"; import { createFileRoute, Outlet } from "@tanstack/react-router";
import { Sidebar } from "../../components/Sidebar"; import { Sidebar } from "../../components/Sidebar";
export const Route = createFileRoute("/app/_hiddenLayout")({ export const Route = createFileRoute("/app/_hiddenLayout")({
@@ -7,7 +7,10 @@ export const Route = createFileRoute("/app/_hiddenLayout")({
function AppLayout() { function AppLayout() {
return ( return (
<div className="flex min-h-screen w-full flex-col bg-[#f7f9fc] lg:flex-row"> <div
className="flex min-h-screen w-full flex-col lg:flex-row"
style={{ backgroundColor: "var(--joy-palette-background-body)" }}
>
<Sidebar /> <Sidebar />
<main className="flex-1 px-4 py-5 sm:px-6 lg:px-8 lg:py-6"> <main className="flex-1 px-4 py-5 sm:px-6 lg:px-8 lg:py-6">
<Outlet /> <Outlet />
+265
View File
@@ -0,0 +1,265 @@
import { extendTheme } from "@mui/joy/styles";
// I generated this theme with AI (Claude Opus 4.6)
export const theme = extendTheme({
cssVarPrefix: "joy",
colorSchemes: {
light: {
palette: {
primary: {
50: "#eef7ff",
100: "#dcedff",
200: "#b3d9ff",
300: "#7fbdff",
400: "#469cf5",
500: "#0b6bcb",
600: "#0958a8",
700: "#08478a",
800: "#0a3a6e",
900: "#0c2f59",
solidBg: "#0b6bcb",
solidHoverBg: "#095aa7",
solidActiveBg: "#08478a",
outlinedBorder: "#7fbdff",
outlinedColor: "#08478a",
softBg: "#dcedff", // Color in sidebar for selected view
softColor: "#08478a",
},
success: {
50: "#ecfdf5",
100: "#d1faee",
200: "#a6f4dc",
300: "#6ee7c9",
400: "#34d0ac",
500: "#0d9488",
600: "#0b7c73",
700: "#0a655e",
800: "#0b514c",
900: "#0a423f",
solidBg: "#0d9488",
solidHoverBg: "#0b7c73",
softBg: "#d1faee",
softColor: "#0a655e",
},
warning: {
50: "#fffaeb",
100: "#fef0c7",
200: "#fedf89",
300: "#fdc74a",
400: "#fbaf24",
500: "#f59e0b",
600: "#d67e06",
700: "#b25f08",
800: "#8f4a0e",
900: "#763d10",
solidBg: "#f59e0b",
solidHoverBg: "#d67e06",
softBg: "#fef0c7",
softColor: "#8f4a0e",
},
danger: {
50: "#fff1f2",
100: "#ffe1e4",
200: "#ffc7cd",
300: "#ff9ba7",
400: "#fb6b7d",
500: "#e11d48",
600: "#be123c",
700: "#9b1038",
800: "#7f1230",
900: "#6a122b",
solidBg: "#e11d48",
solidHoverBg: "#be123c",
softBg: "#ffe1e4",
softColor: "#9b1038",
},
neutral: {
50: "#f8fafc",
100: "#f1f5f9",
200: "#e2e8f0",
300: "#cbd5e1",
400: "#94a3b8",
500: "#64748b",
600: "#475569",
700: "#334155",
800: "#1e293b",
900: "#0f172a",
},
background: {
body: "#f7f9fc",
surface: "#ffffff",
level1: "#f1f5f9",
level2: "#e2e8f0",
level3: "#cbd5e1",
},
text: {
primary: "#0f172a",
secondary: "#475569",
tertiary: "#64748b",
},
divider: "rgba(15, 23, 42, 0.08)",
},
},
dark: {
palette: {
primary: {
50: "#0c2f59",
100: "#0a3a6e",
200: "#08478a",
300: "#0958a8",
400: "#0b6bcb",
500: "#3b8ce0",
600: "#6ba9ea",
700: "#9cc6f2",
800: "#c7defa",
900: "#eef7ff",
solidBg: "#3b8ce0",
solidHoverBg: "#6ba9ea",
solidActiveBg: "#0958a8",
outlinedBorder: "#0958a8",
outlinedColor: "#9cc6f2",
softBg: "rgba(59, 140, 224, 0.16)", // Color in sidebar for selected view
softColor: "#9cc6f2",
},
success: {
50: "#0a423f",
100: "#0b514c",
200: "#0a655e",
300: "#0b7c73",
400: "#0d9488",
500: "#2dc8b1",
600: "#6ee7c9",
700: "#a6f4dc",
800: "#d1faee",
900: "#ecfdf5",
solidBg: "#0d9488",
solidHoverBg: "#2dc8b1",
softBg: "rgba(13, 148, 136, 0.18)",
softColor: "#6ee7c9",
},
warning: {
50: "#763d10",
100: "#8f4a0e",
200: "#b25f08",
300: "#d67e06",
400: "#f59e0b",
500: "#fbaf24",
600: "#fdc74a",
700: "#fedf89",
800: "#fef0c7",
900: "#fffaeb",
solidBg: "#f59e0b",
solidHoverBg: "#fbaf24",
softBg: "rgba(245, 158, 11, 0.16)",
softColor: "#fdc74a",
},
danger: {
50: "#6a122b",
100: "#7f1230",
200: "#9b1038",
300: "#be123c",
400: "#e11d48",
500: "#f04463",
600: "#fb6b7d",
700: "#ff9ba7",
800: "#ffc7cd",
900: "#fff1f2",
solidBg: "#e11d48",
solidHoverBg: "#f04463",
softBg: "rgba(225, 29, 72, 0.18)",
softColor: "#fb6b7d",
},
neutral: {
50: "#0b1220",
100: "#0f172a",
200: "#1e293b",
300: "#334155",
400: "#475569",
500: "#64748b",
600: "#94a3b8",
700: "#cbd5e1",
800: "#e2e8f0",
900: "#f1f5f9",
},
background: {
body: "#0b1220",
surface: "#111a2e",
level1: "#16213a",
level2: "#1e293b",
level3: "#334155",
},
text: {
primary: "#f1f5f9",
secondary: "#cbd5e1",
tertiary: "#94a3b8",
},
divider: "rgba(226, 232, 240, 0.08)",
},
},
},
fontFamily: {
body: "'Inter', 'Segoe UI', system-ui, sans-serif",
display: "'Inter', 'Segoe UI', system-ui, sans-serif",
},
radius: {
xs: "6px",
sm: "10px",
md: "14px",
lg: "20px",
xl: "28px",
},
components: {
JoyInput: {
styleOverrides: {
input: ({ theme }) => ({
color: theme.vars.palette.text.primary,
"&::placeholder": {
color: theme.vars.palette.text.tertiary,
opacity: 1,
},
}),
},
},
JoySelect: {
styleOverrides: {
button: ({ theme }) => ({
color: theme.vars.palette.text.primary,
}),
listbox: ({ theme }) => ({
color: theme.vars.palette.text.primary,
backgroundColor: theme.vars.palette.background.surface,
}),
},
},
JoySheet: {
styleOverrides: {
root: ({ theme }) => ({
backgroundColor: theme.vars.palette.background.surface,
borderColor: theme.vars.palette.divider,
}),
},
},
JoyOption: {
styleOverrides: {
root: ({ theme }) => ({
color: theme.vars.palette.text.primary,
backgroundColor: theme.vars.palette.background.surface,
"&:hover": {
backgroundColor: theme.vars.palette.background.level1,
},
'&[aria-selected="true"]': {
backgroundColor: theme.vars.palette.background.level2,
},
}),
},
},
JoyButton: {
styleOverrides: {
root: {
transition: "all 0.15s ease",
},
},
},
},
});