Merge pull request #951 from getmaxun/sdk-tab
feat(ui): redesign main menu layout and navigation
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Tabs from '@mui/material/Tabs';
|
import Tabs from '@mui/material/Tabs';
|
||||||
import Tab from '@mui/material/Tab';
|
import Tab from '@mui/material/Tab';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { Paper, Button, useTheme, Modal, Typography, Stack, Divider } from "@mui/material";
|
import { Paper, Button, useTheme, Modal, Typography, Stack, Divider } from "@mui/material";
|
||||||
import { AutoAwesome, VpnKey, Usb, CloudQueue, Description, Favorite, SlowMotionVideo, PlayArrow } from "@mui/icons-material";
|
import { AutoAwesome, VpnKey, Usb, CloudQueue, Description, Favorite, SlowMotionVideo, PlayArrow, ArrowForwardIos, Star } from "@mui/icons-material";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useGlobalInfoStore } from "../../context/globalInfo";
|
|
||||||
|
|
||||||
interface MainMenuProps {
|
interface MainMenuProps {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -18,12 +17,42 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { notify } = useGlobalInfoStore();
|
|
||||||
|
|
||||||
const [sponsorModalOpen, setSponsorModalOpen] = useState(false);
|
const [sponsorModalOpen, setSponsorModalOpen] = useState(false);
|
||||||
const [docModalOpen, setDocModalOpen] = useState(false);
|
const [docModalOpen, setDocModalOpen] = useState(false);
|
||||||
|
const [starCount, setStarCount] = useState<number | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const ossDiscountCode = "MAXUNOSS8";
|
useEffect(() => {
|
||||||
|
const fetchStarCount = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://api.github.com/repos/getmaxun/maxun', {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/vnd.github.v3+json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setStarCount(data.stargazers_count);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch GitHub star count');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching GitHub star count:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchStarCount();
|
||||||
|
|
||||||
|
// Optional: Refresh star count every 5 minutes
|
||||||
|
const intervalId = setInterval(fetchStarCount, 5 * 60 * 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
|
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
|
||||||
navigate(`/${newValue}`);
|
navigate(`/${newValue}`);
|
||||||
@@ -37,24 +66,15 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyDiscountCode = () => {
|
|
||||||
navigator.clipboard.writeText(ossDiscountCode).then(() => {
|
|
||||||
notify("success", "Discount code copied to clipboard!");
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Failed to copy text: ', err);
|
|
||||||
notify("error", "Failed to copy discount code.");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultcolor = theme.palette.mode === 'light' ? 'black' : 'white';
|
const defaultcolor = theme.palette.mode === 'light' ? 'black' : 'white';
|
||||||
|
|
||||||
const buttonStyles = {
|
const buttonStyles = {
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
fontSize: '17px',
|
fontSize: '15px',
|
||||||
letterSpacing: '0.02857em',
|
letterSpacing: '0.02857em',
|
||||||
padding: '20px 20px 20px 22px',
|
padding: '20px 20px 0px 22px',
|
||||||
minHeight: '48px',
|
minHeight: '60px',
|
||||||
minWidth: '100%',
|
minWidth: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -66,6 +86,23 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const starButtonStyles = {
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
textAlign: 'left',
|
||||||
|
fontSize: '15px',
|
||||||
|
padding: '12px 20px 12px 22px',
|
||||||
|
minHeight: '48px',
|
||||||
|
minWidth: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
textTransform: 'none',
|
||||||
|
color: theme.palette.mode === 'light' ? '#6C6C6C' : 'inherit',
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : 'rgba(255, 255, 255, 0.04)',
|
||||||
|
'&:hover': {
|
||||||
|
color: theme.palette.mode === 'light' ? '#6C6C6C' : 'inherit',
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#f0f0f0' : 'rgba(255, 255, 255, 0.08)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -74,50 +111,67 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
width: '230px',
|
width: '230px',
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
paddingTop: '0.5rem',
|
|
||||||
color: defaultcolor,
|
color: defaultcolor,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
square
|
square
|
||||||
>
|
>
|
||||||
<Box sx={{ width: '100%', paddingBottom: '1rem' }}>
|
<Box sx={{
|
||||||
|
width: '100%',
|
||||||
|
paddingBottom: '1rem',
|
||||||
|
flexGrow: 1,
|
||||||
|
overflowY: 'auto'
|
||||||
|
}}>
|
||||||
<Tabs
|
<Tabs
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
textColor="primary"
|
textColor="primary"
|
||||||
indicatorColor="primary"
|
indicatorColor="primary"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
sx={{ alignItems: 'flex-start', '& .MuiTabs-indicator': { display: 'none' }}}
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
'& .MuiTabs-indicator': { display: 'none' },
|
||||||
|
paddingTop: '0.5rem'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Tab
|
<Tab
|
||||||
value="robots"
|
value="robots"
|
||||||
label={t('mainmenu.recordings')}
|
label={t('mainmenu.recordings')}
|
||||||
icon={<AutoAwesome />}
|
icon={<AutoAwesome sx={{ fontSize: 20 }} />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
disableRipple={true}
|
disableRipple={true}
|
||||||
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }}
|
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: '16px' }}
|
||||||
onClick={handleRobotsClick} />
|
onClick={handleRobotsClick} />
|
||||||
<Tab value="runs"
|
<Tab value="runs"
|
||||||
label={t('mainmenu.runs')}
|
label={t('mainmenu.runs')}
|
||||||
icon={<PlayArrow />}
|
icon={<PlayArrow sx={{ fontSize: 20 }} />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
disableRipple={true}
|
disableRipple={true}
|
||||||
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
|
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: '16px' }} />
|
||||||
<Tab value="proxy"
|
<Tab value="proxy"
|
||||||
label={t('mainmenu.proxy')}
|
label={t('mainmenu.proxy')}
|
||||||
icon={<Usb />}
|
icon={<Usb sx={{ fontSize: 20 }} />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
disableRipple={true}
|
disableRipple={true}
|
||||||
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
|
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: '16px' }} />
|
||||||
<Tab value="apikey"
|
<Tab value="apikey"
|
||||||
label={t('mainmenu.apikey')}
|
label={t('mainmenu.apikey')}
|
||||||
icon={<VpnKey />}
|
icon={<VpnKey sx={{ fontSize: 20 }}/>}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
disableRipple={true}
|
disableRipple={true}
|
||||||
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
|
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: '16px' }} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Divider sx={{ borderColor: theme.palette.mode === 'dark' ? "#080808ff" : "" }} />
|
<Divider sx={{ borderColor: theme.palette.mode === 'dark' ? "#080808ff" : "" }} />
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', textAlign: 'left' }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', textAlign: 'left' }}>
|
||||||
|
<Button
|
||||||
|
href='https://docs.maxun.dev/sdk/sdk-overview'
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
sx={buttonStyles} startIcon={<ArrowForwardIos sx={{ fontSize: 20 }} />}>
|
||||||
|
SDK
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setDocModalOpen(true)}
|
onClick={() => setDocModalOpen(true)}
|
||||||
sx={buttonStyles}
|
sx={buttonStyles}
|
||||||
@@ -155,15 +209,59 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
href='https://app.maxun.dev/'
|
href='https://app.maxun.dev/'
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
sx={buttonStyles} startIcon={<CloudQueue sx={{ fontSize: 20 }} />}>
|
sx={buttonStyles} startIcon={<CloudQueue sx={{ fontSize: 16 }} />}>
|
||||||
Join Maxun Cloud
|
Join Maxun Cloud
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setSponsorModalOpen(true)} sx={buttonStyles} startIcon={<Favorite sx={{ fontSize: 20 }} />}>
|
<Button onClick={() => setSponsorModalOpen(true)} sx={buttonStyles} startIcon={<Favorite sx={{ fontSize: 16 }} />}>
|
||||||
Sponsor Us
|
Sponsor Us
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
href="https://github.com/getmaxun/maxun"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
sx={starButtonStyles}
|
||||||
|
startIcon={
|
||||||
|
<Star
|
||||||
|
sx={{
|
||||||
|
fontSize: 16,
|
||||||
|
color: theme.palette.mode === 'light' ? '#ffb400' : '#ffd740'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<span style={{ fontSize: "0.85rem"}}>Star On GitHub</span>
|
||||||
|
{isLoading ? (
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.mode === 'light' ? '#666' : '#aaa',
|
||||||
|
fontSize: '0.75rem'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
...
|
||||||
|
</Typography>
|
||||||
|
) : starCount !== null ? (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#f0f0f0' : 'rgba(255, 255, 255, 0.1)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '2px 8px',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
color: theme.palette.mode === 'light' ? '#666' : '#ccc',
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{starCount.toLocaleString()}
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
</Button>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Modal open={sponsorModalOpen} onClose={() => setSponsorModalOpen(false)}>
|
<Modal open={sponsorModalOpen} onClose={() => setSponsorModalOpen(false)}>
|
||||||
<Box sx={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', bgcolor: 'rgba(13, 13, 13, 1)', borderRadius: 2, p: 4, width: 600 }}>
|
<Box sx={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', bgcolor: 'rgba(13, 13, 13, 1)', borderRadius: 2, p: 4, width: 600 }}>
|
||||||
<Typography variant="h6" marginBottom={4}>
|
<Typography variant="h6" marginBottom={4}>
|
||||||
@@ -183,4 +281,4 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
|
|||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -27,7 +27,6 @@ import {
|
|||||||
GitHub,
|
GitHub,
|
||||||
Update,
|
Update,
|
||||||
Close,
|
Close,
|
||||||
Description,
|
|
||||||
LightMode,
|
LightMode,
|
||||||
DarkMode,
|
DarkMode,
|
||||||
Translate
|
Translate
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { createContext, useContext, useState, useEffect } from 'react';
|
|||||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
|
||||||
|
|
||||||
const lightTheme = createTheme({
|
const lightTheme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
@@ -75,7 +76,8 @@ const lightTheme = createTheme({
|
|||||||
MuiTab: {
|
MuiTab: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
textTransform: "none",
|
minHeight: 60,
|
||||||
|
textTransform: "none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -212,6 +214,7 @@ const darkTheme = createTheme({
|
|||||||
MuiTab: {
|
MuiTab: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
|
minHeight: 60,
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
|
|||||||
Reference in New Issue
Block a user