From ebd569b0c001e6644d7cbc56d28e756b842d16dd Mon Sep 17 00:00:00 2001 From: dekzter Date: Mon, 17 Mar 2025 16:04:25 -0400 Subject: [PATCH] lots of ui updates --- docker/nginx.conf | 6 +- frontend/src/WebSocket.jsx | 1 - frontend/src/api.js | 2 +- frontend/src/components/Sidebar.jsx | 7 +- frontend/src/components/forms/Channel.jsx | 1 - frontend/src/components/forms/M3UProfiles.jsx | 15 +- .../src/components/tables/ChannelsTable.jsx | 30 +--- frontend/src/components/tables/EPGsTable.jsx | 6 - frontend/src/components/tables/M3UsTable.jsx | 27 +--- .../components/tables/StreamProfilesTable.jsx | 15 +- .../src/components/tables/StreamsTable.jsx | 36 ++--- .../src/components/tables/UserAgentsTable.jsx | 17 +-- frontend/src/helpers/table.jsx | 21 ++- frontend/src/pages/Channels.jsx | 28 +--- frontend/src/pages/Stats.jsx | 97 ++++++------- frontend/src/store/channels.jsx | 137 +++++++++++++++--- frontend/src/theme.jsx | 92 ------------ 17 files changed, 217 insertions(+), 321 deletions(-) delete mode 100644 frontend/src/theme.jsx diff --git a/docker/nginx.conf b/docker/nginx.conf index 3683224b..26098d95 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -8,7 +8,11 @@ server { } location /assets/ { - root /app/static; # Base directory for static files + root /app/static; + } + + location /static/ { + root /app; } # admin disabled when not in dev mode diff --git a/frontend/src/WebSocket.jsx b/frontend/src/WebSocket.jsx index edf45d7c..d361409b 100644 --- a/frontend/src/WebSocket.jsx +++ b/frontend/src/WebSocket.jsx @@ -62,7 +62,6 @@ export const WebsocketProvider = ({ children }) => { break; case 'channel_stats': - console.log(JSON.parse(event.data.stats)); setChannelStats(JSON.parse(event.data.stats)); break; diff --git a/frontend/src/api.js b/frontend/src/api.js index 5b318d68..8a3cd97e 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -849,8 +849,8 @@ export default class API { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${await API.getAuthToken()}`, - body: JSON.stringify({ client_id: clientId }), }, + body: JSON.stringify({ client_id: clientId }), }); const retval = await response.json(); diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx index f66d6c1b..157381f0 100644 --- a/frontend/src/components/Sidebar.jsx +++ b/frontend/src/components/Sidebar.jsx @@ -7,6 +7,8 @@ import { SlidersHorizontal, LayoutGrid, Settings as LucideSettings, + Copy, + ChartLine, } from 'lucide-react'; import { Avatar, @@ -23,7 +25,6 @@ import logo from '../images/logo.png'; import useChannelsStore from '../store/channels'; import './sidebar.css'; import useSettingsStore from '../store/settings'; -import { ContentCopy } from '@mui/icons-material'; const NavLink = ({ item, isActive, collapsed }) => { return ( @@ -79,7 +80,7 @@ const Sidebar = ({ collapsed, toggleDrawer, drawerWidth, miniDrawerWidth }) => { path: '/stream-profiles', }, { label: 'TV Guide', icon: , path: '/guide' }, - { label: 'Stats', icon: , path: '/stats' }, + { label: 'Stats', icon: , path: '/stats' }, { label: 'Settings', icon: , @@ -196,7 +197,7 @@ const Sidebar = ({ collapsed, toggleDrawer, drawerWidth, miniDrawerWidth }) => { color="gray.9" onClick={copyPublicIP} > - + } /> diff --git a/frontend/src/components/forms/Channel.jsx b/frontend/src/components/forms/Channel.jsx index af56d041..89b6ac86 100644 --- a/frontend/src/components/forms/Channel.jsx +++ b/frontend/src/components/forms/Channel.jsx @@ -4,7 +4,6 @@ import * as Yup from 'yup'; import useChannelsStore from '../../store/channels'; import API from '../../api'; import useStreamProfilesStore from '../../store/streamProfiles'; -import { Add as AddIcon, Remove as RemoveIcon } from '@mui/icons-material'; import useStreamsStore from '../../store/streams'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import ChannelGroupForm from './ChannelGroup'; diff --git a/frontend/src/components/forms/M3UProfiles.jsx b/frontend/src/components/forms/M3UProfiles.jsx index 7579db4f..3da62902 100644 --- a/frontend/src/components/forms/M3UProfiles.jsx +++ b/frontend/src/components/forms/M3UProfiles.jsx @@ -1,7 +1,6 @@ import React, { useState, useMemo } from 'react'; import API from '../../api'; import M3UProfile from './M3UProfile'; -import { Delete as DeleteIcon, Edit as EditIcon } from '@mui/icons-material'; import usePlaylistsStore from '../../store/playlists'; import { Card, @@ -13,6 +12,7 @@ import { ActionIcon, Text, } from '@mantine/core'; +import { SquareMinus, SquarePen } from 'lucide-react'; const M3UProfiles = ({ playlist = null, isOpen, onClose }) => { const profiles = usePlaylistsStore((state) => state.profiles[playlist.id]); @@ -69,14 +69,19 @@ const M3UProfiles = ({ playlist = null, isOpen, onClose }) => { onChange={() => toggleActive(item)} color="primary" /> - editProfile(item)} color="yellow.5"> - + editProfile(item)} + color="yellow.5" + variant="transparent" + > + deleteProfile(item.id)} - color="error" + color="red.9" + variant="transparent" > - + diff --git a/frontend/src/components/tables/ChannelsTable.jsx b/frontend/src/components/tables/ChannelsTable.jsx index d22b90ed..6c457792 100644 --- a/frontend/src/components/tables/ChannelsTable.jsx +++ b/frontend/src/components/tables/ChannelsTable.jsx @@ -2,15 +2,6 @@ import { useEffect, useMemo, useRef, useState, useCallback } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import useChannelsStore from '../../store/channels'; import { notifications } from '@mantine/notifications'; -import { - Add as AddIcon, - LiveTv as LiveTvIcon, - ContentCopy, - IndeterminateCheckBox, - CompareArrows, - Code, - AddBox, -} from '@mui/icons-material'; import API from '../../api'; import ChannelForm from '../forms/Channel'; import { TableHelper } from '../../helpers'; @@ -29,6 +20,7 @@ import { Binary, ArrowDown01, SquarePlus, + Copy, } from 'lucide-react'; import ghostImage from '../../images/ghost.svg'; import { @@ -641,7 +633,7 @@ const ChannelsTable = ({}) => { variant="transparent" color="gray.5" > - + @@ -671,7 +663,7 @@ const ChannelsTable = ({}) => { variant="transparent" color="gray.5" > - + @@ -702,7 +694,7 @@ const ChannelsTable = ({}) => { variant="transparent" color="gray.5" > - + @@ -712,25 +704,13 @@ const ChannelsTable = ({}) => { {/* Paper container: contains top toolbar and table (or ghost state) */} - + {/* Top toolbar with Remove, Assign, Auto-match, and Add buttons */} diff --git a/frontend/src/components/tables/EPGsTable.jsx b/frontend/src/components/tables/EPGsTable.jsx index 8f5dafe7..c3a9a17f 100644 --- a/frontend/src/components/tables/EPGsTable.jsx +++ b/frontend/src/components/tables/EPGsTable.jsx @@ -1,12 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import API from '../../api'; -import { - Delete as DeleteIcon, - Edit as EditIcon, - Add as AddIcon, - Refresh as RefreshIcon, -} from '@mui/icons-material'; import useEPGsStore from '../../store/epgs'; import EPGForm from '../forms/EPG'; import { TableHelper } from '../../helpers'; diff --git a/frontend/src/components/tables/M3UsTable.jsx b/frontend/src/components/tables/M3UsTable.jsx index 4059aa18..fd8b0902 100644 --- a/frontend/src/components/tables/M3UsTable.jsx +++ b/frontend/src/components/tables/M3UsTable.jsx @@ -1,25 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import API from '../../api'; -import { - Delete as DeleteIcon, - Edit as EditIcon, - Add as AddIcon, - SwapVert as SwapVertIcon, - Check as CheckIcon, - Close as CloseIcon, -} from '@mui/icons-material'; -import { - LiveTv as LiveTvIcon, - ContentCopy, - Tv as TvIcon, - Clear as ClearIcon, - IndeterminateCheckBox, - CompareArrows, - Code, - AddBox, - Hd as HdIcon, -} from '@mui/icons-material'; import usePlaylistsStore from '../../store/playlists'; import M3UForm from '../forms/M3U'; import { TableHelper } from '../../helpers'; @@ -46,6 +27,8 @@ import { TvMinimalPlay, SquarePen, RefreshCcw, + Check, + X, } from 'lucide-react'; import { IconArrowDown, @@ -103,11 +86,7 @@ const Example = () => { }, Cell: ({ cell }) => ( - {cell.getValue() ? ( - - ) : ( - - )} + {cell.getValue() ? : } ), }, diff --git a/frontend/src/components/tables/StreamProfilesTable.jsx b/frontend/src/components/tables/StreamProfilesTable.jsx index 2390e219..ed710be5 100644 --- a/frontend/src/components/tables/StreamProfilesTable.jsx +++ b/frontend/src/components/tables/StreamProfilesTable.jsx @@ -1,13 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import API from '../../api'; -import { - Delete as DeleteIcon, - Edit as EditIcon, - Add as AddIcon, - Check as CheckIcon, - Close as CloseIcon, -} from '@mui/icons-material'; import StreamProfileForm from '../forms/StreamProfile'; import useStreamProfilesStore from '../../store/streamProfiles'; import { TableHelper } from '../../helpers'; @@ -24,7 +17,7 @@ import { useMantineTheme, } from '@mantine/core'; import { IconSquarePlus } from '@tabler/icons-react'; -import { SquareMinus, SquarePen } from 'lucide-react'; +import { SquareMinus, SquarePen, Check, X } from 'lucide-react'; const StreamProfiles = () => { const [profile, setProfile] = useState(null); @@ -62,11 +55,7 @@ const StreamProfiles = () => { }, Cell: ({ cell }) => ( - {cell.getValue() ? ( - - ) : ( - - )} + {cell.getValue() ? : } ), Filter: ({ column }) => ( diff --git a/frontend/src/components/tables/StreamsTable.jsx b/frontend/src/components/tables/StreamsTable.jsx index fb0084d7..8672aba0 100644 --- a/frontend/src/components/tables/StreamsTable.jsx +++ b/frontend/src/components/tables/StreamsTable.jsx @@ -1,22 +1,17 @@ import { useEffect, useMemo, useCallback, useState, useRef } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import API from '../../api'; -import { useTheme } from '@mui/material/styles'; -import { - Delete as DeleteIcon, - Edit as EditIcon, - Add as AddIcon, - MoreVert as MoreVertIcon, - PlaylistAdd as PlaylistAddIcon, - IndeterminateCheckBox, - AddBox, -} from '@mui/icons-material'; import { TableHelper } from '../../helpers'; import StreamForm from '../forms/Stream'; import usePlaylistsStore from '../../store/playlists'; import useChannelsStore from '../../store/channels'; import { useDebounce } from '../../utils'; -import { SquarePlus, ListPlus, SquareMinus } from 'lucide-react'; +import { + SquarePlus, + ListPlus, + SquareMinus, + EllipsisVertical, +} from 'lucide-react'; import { TextInput, ActionIcon, @@ -49,8 +44,6 @@ import { import { useNavigate } from 'react-router-dom'; const StreamsTable = ({}) => { - const theme = useTheme(); - /** * useState */ @@ -538,7 +531,7 @@ const StreamsTable = ({}) => { variant="transparent" size="sm" > - + @@ -620,25 +613,13 @@ const StreamsTable = ({}) => { - + {/* Top toolbar with Remove, Assign, Auto-match, and Add buttons */} @@ -647,6 +628,7 @@ const StreamsTable = ({}) => { variant="default" size="xs" onClick={deleteStreams} + disabled={rowSelection.length === 0} > Remove diff --git a/frontend/src/components/tables/UserAgentsTable.jsx b/frontend/src/components/tables/UserAgentsTable.jsx index 29e8453e..81e052e3 100644 --- a/frontend/src/components/tables/UserAgentsTable.jsx +++ b/frontend/src/components/tables/UserAgentsTable.jsx @@ -1,13 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import API from '../../api'; -import { - Delete as DeleteIcon, - Edit as EditIcon, - Add as AddIcon, - Check as CheckIcon, - Close as CloseIcon, -} from '@mui/icons-material'; +import { Check as CheckIcon, Close as CloseIcon } from '@mui/icons-material'; import useUserAgentsStore from '../../store/userAgents'; import UserAgentForm from '../forms/UserAgent'; import { TableHelper } from '../../helpers'; @@ -24,14 +18,7 @@ import { Box, Button, } from '@mantine/core'; -import { - IconArrowDown, - IconArrowUp, - IconDeviceDesktopSearch, - IconSelector, - IconSortAscendingNumbers, - IconSquarePlus, -} from '@tabler/icons-react'; +import { IconSquarePlus } from '@tabler/icons-react'; import { SquareMinus, SquarePen } from 'lucide-react'; const UserAgentsTable = () => { diff --git a/frontend/src/helpers/table.jsx b/frontend/src/helpers/table.jsx index d0cfcc18..1db0061c 100644 --- a/frontend/src/helpers/table.jsx +++ b/frontend/src/helpers/table.jsx @@ -26,13 +26,17 @@ export default { size: 'xs', }, mantineTableBodyRowProps: ({ isDetailPanel, row }) => { - if (isDetailPanel && row.getIsSelected()) { - return { - style: { - backgroundColor: '#163632', - }, - }; - } + return { + style: { + ...(isDetailPanel && { + border: 'none', + }), + ...(isDetailPanel && + row.getIsSelected() && { + backgroundColor: '#163632', + }), + }, + }; }, mantineTableBodyCellProps: { style: { @@ -48,6 +52,9 @@ export default { style: { paddingLeft: 10, paddingRight: 10, + paddingTop: 2, + paddingBottom: 2, + fontWeight: 'normal', color: '#CFCFCF', backgroundColor: '#383A3F', borderColor: '#444', diff --git a/frontend/src/pages/Channels.jsx b/frontend/src/pages/Channels.jsx index c3913d76..c5a23fbe 100644 --- a/frontend/src/pages/Channels.jsx +++ b/frontend/src/pages/Channels.jsx @@ -7,34 +7,10 @@ const ChannelsPage = () => { return ( - - - + - - - + ); diff --git a/frontend/src/pages/Stats.jsx b/frontend/src/pages/Stats.jsx index 73814089..e6fcbc3d 100644 --- a/frontend/src/pages/Stats.jsx +++ b/frontend/src/pages/Stats.jsx @@ -1,5 +1,5 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react'; -import { ActionIcon, Box, Center, Grid } from '@mantine/core'; +import { ActionIcon, Box, Center, Grid, Text } from '@mantine/core'; import { MantineReactTable, useMantineReactTable } from 'mantine-react-table'; import { TableHelper } from '../helpers'; import API from '../api'; @@ -18,7 +18,7 @@ import { } from 'lucide-react'; const ChannelsPage = () => { - const { channels, stats: channelStats } = useChannelsStore(); + const { channels, channelsByUUID, stats: channelStats } = useChannelsStore(); const [activeChannels, setActiveChannels] = useState([]); const [clients, setClients] = useState([]); @@ -106,8 +106,8 @@ const ChannelsPage = () => { await API.stopChannel(id); }; - const stopClient = async (id) => { - await API.stopClient(id); + const stopClient = async (channelId, clientId) => { + await API.stopClient(channelId, clientId); }; const channelsTable = useMantineReactTable({ @@ -165,6 +165,12 @@ const ChannelsPage = () => { header: 'Channel', accessorKey: 'channel.name', size: 100, + mantineTableBodyCellProps: { + style: { + whiteSpace: 'nowrap', + maxWidth: 100, + }, + }, }, { header: 'User-Agent', @@ -210,19 +216,21 @@ const ChannelsPage = () => { ), + mantineTableContainerProps: { + style: { + height: '100%', + overflowY: 'auto', + }, + }, }); useEffect(() => { const stats = channelStats.channels.map((ch) => ({ ...ch, - ...Object.values(channels).filter( - (channel) => channel.uuid === channelStats.channels[0].channel_id - )[0], + ...channels[channelsByUUID[ch.channel_id]], })); setActiveChannels(stats); - console.log(stats); - const clientStats = stats.reduce((acc, ch) => { return acc.concat( ch.clients.map((client) => ({ @@ -232,66 +240,47 @@ const ChannelsPage = () => { ); }, []); setClients(clientStats); - console.log(clientStats); }, [channelStats]); - // const fetchData = useCallback(async () => { - // const response = await API.getChannelStats(); - // const channelStats = response.channels.map((ch) => ({ - // ...ch, - // ...Object.values(channels).filter( - // (channel) => channel.uuid === response.channels[0].channel_id - // )[0], - // })); - // setActiveChannels(channelStats); - - // console.log(channelStats); - - // const clientStats = channelStats.reduce((acc, ch) => { - // return acc.concat( - // ch.clients.map((client) => ({ - // ...client, - // channel: ch, - // })) - // ); - // }, []); - // setClients(clientStats); - // console.log(clientStats); - // }, [channels]); - - // useEffect(() => { - // fetchData(); - // }, [fetchData]); - return ( - + Channels + + - + Clients + + diff --git a/frontend/src/store/channels.jsx b/frontend/src/store/channels.jsx index 47d36e8f..377e5063 100644 --- a/frontend/src/store/channels.jsx +++ b/frontend/src/store/channels.jsx @@ -1,11 +1,15 @@ import { create } from 'zustand'; import api from '../api'; +import { notifications } from '@mantine/notifications'; -const useChannelsStore = create((set) => ({ +const useChannelsStore = create((set, get) => ({ channels: [], + channelsByUUID: {}, channelGroups: [], channelsPageSelection: [], - stats: [], + stats: {}, + activeChannels: {}, + activeClients: {}, isLoading: false, error: null, @@ -13,11 +17,15 @@ const useChannelsStore = create((set) => ({ set({ isLoading: true, error: null }); try { const channels = await api.getChannels(); + const channelsByUUID = {}; + const channelsByID = channels.reduce((acc, channel) => { + acc[channel.id] = channel; + channelsByUUID[channel.uuid] = channel.id; + return acc; + }, {}); set({ - channels: channels.reduce((acc, channel) => { - acc[channel.id] = channel; - return acc; - }, {}), + channels: channelsByID, + channelsByUUID, isLoading: false, }); } catch (error) { @@ -43,35 +51,59 @@ const useChannelsStore = create((set) => ({ ...state.channels, [newChannel.id]: newChannel, }, - })), - - addChannels: (newChannels) => - set((state) => ({ - channels: { - ...state.channels, - ...newChannels.reduce((acc, channel) => { - acc[channel.id] = channel; - return acc; - }, {}), + channelsByUUID: { + ...state.channelsByUUID, + [newChannel.uuid]: newChannel.id, }, })), + addChannels: (newChannels) => { + const channelsByUUID = {}; + const channelsByID = newChannels.reduce((acc, channel) => { + acc[channel.id] = channel; + channelsByUUID[channel.uuid] = channel.id; + return acc; + }, {}); + return set((state) => ({ + channels: { + ...state.channels, + ...channelsByID, + }, + channelsByUUID: { + ...state.channelsByUUID, + ...channelsByUUID, + }, + })); + }, + updateChannel: (channel) => set((state) => ({ channels: { ...state.channels, [channel.id]: channel, }, + channelsByUUID: { + ...state.channelsByUUID, + [channel.uuid]: channel.id, + }, })), removeChannels: (channelIds) => set((state) => { const updatedChannels = { ...state.channels }; + const channelsByUUID = { ...state.channelsByUUID }; for (const id of channelIds) { delete updatedChannels[id]; + + for (const uuid in channelsByUUID) { + if (channelsByUUID[uuid] == id) { + delete channelsByUUID[uuid]; + break; + } + } } - return { channels: updatedChannels }; + return { channels: updatedChannels, channelsByUUID }; }), addChannelGroup: (newChannelGroup) => @@ -89,10 +121,75 @@ const useChannelsStore = create((set) => ({ setChannelsPageSelection: (channelsPageSelection) => set((state) => ({ channelsPageSelection })), - setChannelStats: (stats) => - set((state) => ({ + setChannelStats: (stats) => { + const { + channels, + stats: currentStats, + activeChannels: oldChannels, + activeClients: oldClients, + channelsByUUID, + } = get(); + + const newClients = {}; + const newChannels = stats.channels.reduce((acc, ch) => { + acc[ch.channel_id] = ch; + + if (currentStats.channels) { + if (oldChannels[ch.channel_id] === undefined) { + notifications.show({ + title: 'New channel streaming', + message: channels[channelsByUUID[ch.channel_id]].name, + color: 'blue.5', + }); + } + } + + ch.clients.map((client) => { + newClients[client.client_id] = client; + // This check prevents the notifications if streams are active on page load + if (currentStats.channels) { + if (oldClients[client.client_id] === undefined) { + notifications.show({ + title: 'New client started streaming', + message: `Client streaming from ${client.ip_address}`, + color: 'blue.5', + }); + } + } + }); + + return acc; + }, {}); + + // This check prevents the notifications if streams are active on page load + if (currentStats.channels) { + for (const uuid in oldChannels) { + if (newChannels[uuid] === undefined) { + notifications.show({ + title: 'Channel streaming stopped', + message: channels[channelsByUUID[uuid]].name, + color: 'blue.5', + }); + } + } + + for (const clientId in oldClients) { + if (newClients[clientId] === undefined) { + notifications.show({ + title: 'Client stopped streaming', + message: `Client stopped streaming from ${oldClients[clientId].ip_address}`, + color: 'blue.5', + }); + } + } + } + + return set((state) => ({ stats, - })), + activeChannels: newChannels, + activeClients: newClients, + })); + }, })); export default useChannelsStore; diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx deleted file mode 100644 index 2fdb34bb..00000000 --- a/frontend/src/theme.jsx +++ /dev/null @@ -1,92 +0,0 @@ -// src/theme.js -import { createTheme } from '@mui/material/styles'; - -const theme = createTheme({ - palette: { - mode: 'dark', - background: { - default: '#18181b', // Global background color (Tailwind zinc-900) - paper: '#27272a', // Paper background (Tailwind zinc-800) - }, - primary: { - main: '#4A90E2', - contrastText: '#FFFFFF', - }, - secondary: { - main: '#F5A623', - contrastText: '#FFFFFF', - }, - text: { - primary: '#FFFFFF', - secondary: '#d4d4d8', // Updated secondary text color (Tailwind zinc-300) - }, - // Custom colors for components (chip buttons, borders, etc.) - custom: { - // For chip buttons: - greenMain: '#90C43E', - greenHoverBg: 'rgba(144,196,62,0.1)', - - indigoMain: '#4F39F6', - indigoHoverBg: 'rgba(79,57,246,0.1)', - - greyBorder: '#707070', - greyHoverBg: 'rgba(112,112,112,0.1)', - greyText: '#a0a0a0', - - // Common border colors: - borderDefault: '#3f3f46', // Tailwind zinc-700 - borderHover: '#5f5f66', // Approximate Tailwind zinc-600 - - // For the "Add" button: - successBorder: '#00a63e', - successBg: '#0d542b', - successBgHover: '#0a4020', - successIcon: '#05DF72', - }, - }, - typography: { - // Set Inter as the global font - fontFamily: 'Inter, sans-serif', - h1: { fontSize: '2.5rem', fontWeight: 700 }, - h2: { fontSize: '2rem', fontWeight: 700 }, - body1: { fontSize: '1rem' }, - }, - spacing: 8, - components: { - MuiButton: { - styleOverrides: { - root: { - borderRadius: 4, - textTransform: 'none', - fontWeight: 500, - }, - }, - }, - MuiDrawer: { - styleOverrides: { - paper: { - backgroundColor: '#27272a', // Use the same paper color - color: '#FFFFFF', - }, - }, - }, - MuiAppBar: { - styleOverrides: { - root: { - backgroundColor: '#18181b', - }, - }, - }, - }, - custom: { - sidebar: { - activeBackground: 'rgba(21, 69, 62, 0.67)', - activeBorder: '#14917e', - hoverBackground: '#27272a', - hoverBorder: '#3f3f46', - fontFamily: 'Inter, sans-serif', - }, - }, -}); - -export default theme;