Redesigned to match sgs

This commit is contained in:
2025-06-28 22:14:42 +03:00
parent ec2cc72a75
commit 20be42eb0c
7 changed files with 549 additions and 484 deletions

1
.gitignore vendored
View File

@@ -35,3 +35,4 @@ yarn-error.log*
# TypeScript
*.tsbuildinfo
node_modules

7
issue.txt Normal file
View File

@@ -0,0 +1,7 @@
Error Handling Consistency: Some error handling is just console.error without user feedback or recovery.
Security: No mention of authentication, authorization, or input validation. If this is a production system, these are critical.
Scalability: State is mostly local; for larger apps, consider a state management library (Redux, Zustand, etc.) or React Context for shared/global state.
Code Comments: Some complex logic could benefit from more inline comments for maintainability.
Modularity: Some files (e.g., pages) are quite large and could be split into smaller, focused components.
Environment Variables: API URLs are hardcoded in some places; best practice is to use environment variables for all endpoints.
Accessibility: No explicit mention of accessibility (a11y) practices (e.g., ARIA labels, keyboard navigation).

749
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
import {
Box,
List,
ListItem,
ListItemButton,
ListItemText,
styled,
@@ -22,34 +21,82 @@ import bgreenLogo from '../../assets/bgreen-logo.png';
const DRAWER_WIDTH = 240;
const LogoContainer = styled(Box)(() => ({
const LogoContainer = styled(Box)(({ theme }) => ({
padding: '20px',
borderBottom: '1px solid rgba(255,255,255,0.1)',
background: 'linear-gradient(180deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%)',
borderBottom: `1px solid ${theme.palette.divider}`,
background: theme.palette.background.paper,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}));
const StyledListItemButton = styled(ListItemButton)(() => ({
margin: '4px 8px',
borderRadius: 4,
const LogoText = styled(Typography)(({ theme }) => ({
color: theme.palette.text.secondary,
fontWeight: 600,
letterSpacing: '0.5px',
}));
const LogoSubText = styled(Typography)(({ theme }) => ({
color: theme.palette.text.disabled,
display: 'block',
marginTop: '-2px',
}));
const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({
borderRadius: '0.6rem',
backgroundColor: theme.palette.background.paper,
margin: '2px 8px',
transition: 'background 0.2s, box-shadow 0.2s',
boxShadow: 'none',
position: 'relative',
overflow: 'visible',
'&.Mui-selected': {
backgroundColor: 'rgba(255,255,255,0.1)',
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.15)',
background: 'linear-gradient(100deg, #028a4a 60%, #28c76f 100%)',
color: theme.palette.common.white,
boxShadow: '0 4px 24px 0 rgba(2,138,74,0.18)',
'&::after': {
content: '""',
position: 'absolute',
zIndex: 0,
top: 0,
left: 0,
right: 0,
bottom: 0,
borderRadius: '0.6rem',
boxShadow: '0 0 24px 8px #28c76f33', // subtle green glow
pointerEvents: 'none',
},
'& .MuiListItemIcon-root': {
color: '#ffffff',
color: theme.palette.common.white,
zIndex: 1,
},
'& .MuiListItemText-primary': {
color: '#ffffff',
fontWeight: 500,
color: theme.palette.common.white,
fontWeight: 600,
zIndex: 1,
},
},
'&:hover': {
backgroundColor: 'rgba(255,255,255,0.05)',
backgroundColor: theme.palette.action.hover,
color: theme.palette.text.primary,
boxShadow: 'none',
'& .MuiListItemIcon-root': {
color: theme.palette.text.primary,
},
'& .MuiListItemText-primary': {
color: theme.palette.text.primary,
},
},
}));
const MenuListContainer = styled(Box)(({ theme }) => ({
background: theme.palette.background.paper,
borderRadius: '0.6rem',
margin: '16px 8px 0 8px',
boxShadow: '0 2px 12px 0 rgba(44,62,80,0.04)',
padding: '4px 0',
display: 'flex',
flexDirection: 'column',
}));
const menuItems = [
@@ -92,49 +139,29 @@ const Sidebar = ({ open, onToggle, isMobile }: SidebarProps) => {
}}
/>
<Box>
<Typography
variant="h6"
sx={{
color: '#ffffff',
fontWeight: 600,
letterSpacing: '0.5px',
}}
>
B'GREEN
</Typography>
<Typography
variant="caption"
sx={{
color: 'rgba(255,255,255,0.7)',
display: 'block',
marginTop: '-2px',
}}
>
Monitor System
</Typography>
<LogoText variant="h6">B'GREEN</LogoText>
<LogoSubText variant="caption">Monitor System</LogoSubText>
</Box>
</Box>
{isMobile && (
<IconButton
onClick={onToggle}
sx={{
color: 'rgba(255,255,255,0.7)',
'&:hover': { color: '#ffffff' }
}}
sx={{ color: theme.palette.text.disabled, '&:hover': { color: theme.palette.text.secondary } }}
>
<ChevronLeftIcon />
</IconButton>
)}
</LogoContainer>
<List sx={{ flexGrow: 1, mt: 2 }}>
<MenuListContainer>
<List sx={{ flexGrow: 1, p: 0 }}>
{menuItems.map((item) => (
<ListItem key={item.text} disablePadding>
<StyledListItemButton
key={item.text}
selected={location.pathname === item.path}
onClick={() => handleNavigation(item.path)}
>
<ListItemIcon sx={{ color: 'rgba(255,255,255,0.7)', minWidth: 40 }}>
<ListItemIcon sx={{ color: theme.palette.text.secondary, minWidth: 40 }}>
{item.icon}
</ListItemIcon>
<ListItemText
@@ -142,14 +169,14 @@ const Sidebar = ({ open, onToggle, isMobile }: SidebarProps) => {
sx={{
'& .MuiListItemText-primary': {
fontSize: '0.9rem',
color: 'rgba(255,255,255,0.7)',
color: theme.palette.text.secondary,
},
}}
/>
</StyledListItemButton>
</ListItem>
))}
</List>
</MenuListContainer>
<Box
sx={{
@@ -185,7 +212,7 @@ const Sidebar = ({ open, onToggle, isMobile }: SidebarProps) => {
display: { xs: 'block' },
'& .MuiDrawer-paper': {
width: DRAWER_WIDTH,
backgroundColor: theme.palette.primary.main,
backgroundColor: theme.palette.background.default,
border: 'none',
height: '100%',
},

View File

@@ -1 +0,0 @@
node_modules/

View File

@@ -47,13 +47,14 @@ import DebugConsole from '../components/DebugConsole';
import MonitoringSystem from './MonitoringSystem';
const StyledCard = styled(Paper)(({ theme }) => ({
borderRadius: theme.spacing(2),
borderRadius: '0.357rem',
padding: theme.spacing(3),
height: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.background.paper,
transition: 'transform 0.2s, box-shadow 0.2s',
boxShadow: theme.shadows[3],
'&:hover': {
transform: 'translateY(-4px)',
boxShadow: theme.shadows[8],
@@ -62,7 +63,8 @@ const StyledCard = styled(Paper)(({ theme }) => ({
const StyledSelect = styled(Select)(({ theme }) => ({
'& .MuiOutlinedInput-notchedOutline': {
borderRadius: theme.spacing(1.5),
borderRadius: '0.357rem',
borderColor: theme.palette.divider,
},
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.primary.main,
@@ -74,15 +76,16 @@ const SectionTitle = styled(Typography)(({ theme }) => ({
alignItems: 'center',
gap: theme.spacing(1),
marginBottom: theme.spacing(3),
color: theme.palette.text.primary,
fontWeight: 600,
color: theme.palette.text.secondary,
fontWeight: 700,
fontFamily: theme.typography.fontFamily,
}));
const IconWrapper = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
color: theme.palette.text.secondary,
color: theme.palette.text.primary,
marginBottom: theme.spacing(1),
}));
@@ -90,19 +93,20 @@ const WeightSlider = styled(Slider)(({ theme }) => ({
'& .MuiSlider-thumb': {
height: 24,
width: 24,
backgroundColor: '#fff',
border: '2px solid currentColor',
backgroundColor: theme.palette.background.paper,
border: `2px solid ${theme.palette.primary.main}`,
'&:focus, &:hover, &.Mui-active, &.Mui-focusVisible': {
boxShadow: 'inherit',
},
},
'& .MuiSlider-track': {
height: 4,
backgroundColor: theme.palette.primary.main,
},
'& .MuiSlider-rail': {
height: 4,
opacity: 0.5,
backgroundColor: theme.palette.mode === 'dark' ? '#bfbfbf' : '#d8d8d8',
backgroundColor: theme.palette.divider,
},
'& .MuiSlider-mark': {
backgroundColor: theme.palette.primary.main,
@@ -118,47 +122,36 @@ const WeightSlider = styled(Slider)(({ theme }) => ({
},
}));
const WeightInput = styled(TextField)(() => ({
const WeightInput = styled(TextField)(({ theme }) => ({
width: 70,
'& input': {
padding: '8px',
textAlign: 'center',
fontFamily: theme.typography.fontFamily,
},
}));
const StatusChip = styled(Chip)(({ theme }) => ({
borderRadius: theme.spacing(1),
fontWeight: 500,
borderRadius: '0.357rem',
fontWeight: 600,
fontFamily: theme.typography.fontFamily,
'&.running': {
backgroundColor: theme.palette.success.light,
color: theme.palette.success.dark,
backgroundColor: theme.palette.success.main,
color: theme.palette.common.white,
},
'&.stopped': {
backgroundColor: theme.palette.error.light,
color: theme.palette.error.dark,
backgroundColor: theme.palette.error.main,
color: theme.palette.common.white,
},
}));
interface Weights {
energy: number;
balance: number;
overload: number;
allocation: number;
}
interface AlertState {
open: boolean;
message: string;
severity: 'success' | 'error' | 'info' | 'warning';
}
const StatusCard = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: theme.spacing(1),
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius * 2,
borderRadius: '0.357rem',
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[1],
minWidth: 280,
@@ -174,12 +167,9 @@ const StatusIndicator = styled('div')(({ theme }) => ({
height: 3,
backgroundColor: 'transparent',
'&.loading': {
background: `linear-gradient(90deg,
${theme.palette.primary.main},
${theme.palette.primary.light},
${theme.palette.primary.main})`,
background: `linear-gradient(90deg, ${theme.palette.primary.main}, ${theme.palette.primary.light}, ${theme.palette.primary.main})`,
backgroundSize: '200% 100%',
animation: '$shimmer 2s infinite',
animation: 'shimmer 2s infinite',
},
'&.success': {
backgroundColor: theme.palette.success.main,

View File

@@ -4,54 +4,94 @@ export const theme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#0B1A33', // Deep navy blue from BLC
light: '#1e3a6b',
dark: '#060d19',
main: '#028a4a', // B'GREEN primary green
light: '#28c76f', // Success green
dark: '#026c39',
},
secondary: {
main: '#FF5722', // Orange accent from icons
light: '#ff784e',
dark: '#c41c00',
main: '#FF1744', // Material-UI Red[500] as secondary
light: '#ff616f',
dark: '#b2102f',
},
background: {
default: '#f5f5f5',
paper: '#ffffff',
default: '#f8f8f8', // Body background
paper: '#fff',
},
text: {
primary: '#0B1A33',
secondary: '#546e7a',
primary: '#6e6b7b', // Body text
secondary: '#5e5873', // Headings
disabled: '#b9b9c3', // Muted text
},
success: {
main: '#28c76f',
},
warning: {
main: '#ffb400',
},
error: {
main: '#FF1744',
},
info: {
main: '#00cfe8',
},
divider: '#ebe9f1', // Border color
},
typography: {
fontFamily: '"Segoe UI", "Roboto", "Helvetica", sans-serif',
fontFamily: 'Montserrat, Helvetica, Arial, serif',
fontSize: 14, // 1rem
h1: {
fontSize: '2.5rem',
fontSize: '2rem', // 28px
fontWeight: 700,
color: '#5e5873',
},
h2: {
fontSize: '1.714rem', // 24px
fontWeight: 600,
color: '#0B1A33',
color: '#5e5873',
},
h3: {
fontSize: '1.5rem', // 21px
fontWeight: 600,
color: '#5e5873',
},
h4: {
fontSize: '1.286rem', // 18px
fontWeight: 500,
color: '#5e5873',
},
h5: {
fontSize: '1.07rem', // 15px
fontWeight: 500,
color: '#0B1A33',
color: '#5e5873',
},
body1: {
fontSize: '1rem',
color: '#6e6b7b',
},
body2: {
fontSize: '0.9rem',
color: '#6e6b7b',
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 4,
borderRadius: '0.357rem',
textTransform: 'none',
padding: '8px 24px',
fontWeight: 600,
transition: 'all 0.3s ease-in-out',
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: '0 4px 12px rgba(11, 26, 51, 0.15)',
boxShadow: '0 4px 12px rgba(2, 138, 74, 0.15)',
},
},
contained: {
background: '#0B1A33',
color: '#ffffff',
background: '#028a4a',
color: '#fff',
'&:hover': {
background: '#1e3a6b',
background: '#026c39',
},
},
},
@@ -59,18 +99,18 @@ export const theme = createTheme({
MuiCard: {
styleOverrides: {
root: {
borderRadius: 8,
boxShadow: '0 2px 12px rgba(11, 26, 51, 0.08)',
border: '1px solid rgba(11, 26, 51, 0.1)',
borderRadius: '0.357rem',
boxShadow: '0 2px 12px rgba(2, 138, 74, 0.08)',
border: '1px solid #ebe9f1',
},
},
},
MuiPaper: {
styleOverrides: {
root: {
borderRadius: 8,
boxShadow: '0 2px 12px rgba(11, 26, 51, 0.08)',
border: '1px solid rgba(11, 26, 51, 0.1)',
borderRadius: '0.357rem',
boxShadow: '0 2px 12px rgba(2, 138, 74, 0.08)',
border: '1px solid #ebe9f1',
},
},
},
@@ -79,13 +119,13 @@ export const theme = createTheme({
root: {
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'rgba(11, 26, 51, 0.2)',
borderColor: '#ebe9f1',
},
'&:hover fieldset': {
borderColor: 'rgba(11, 26, 51, 0.3)',
borderColor: '#028a4a',
},
'&.Mui-focused fieldset': {
borderColor: '#0B1A33',
borderColor: '#028a4a',
},
},
},