forked from BLC/AyposWeb
Redesigned to match sgs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ yarn-error.log*
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
node_modules
|
||||
|
||||
7
issue.txt
Normal file
7
issue.txt
Normal 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
749
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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%',
|
||||
},
|
||||
|
||||
1
src/pages/.gitignore
vendored
1
src/pages/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -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,
|
||||
|
||||
96
src/theme.ts
96
src/theme.ts
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user