updated navbar and ui

This commit is contained in:
2025-10-04 08:08:45 -04:00
parent f63fca3f1b
commit 0b1a6cdea4
7 changed files with 118 additions and 40 deletions

View File

@@ -73,13 +73,34 @@ app.get('/api/servers/:guildId/settings', (req, res) => {
app.post('/api/servers/:guildId/settings', (req, res) => {
const { guildId } = req.params;
const { pingCommand } = req.body;
const newSettings = req.body || {};
const db = readDb();
db[guildId] = { pingCommand };
if (!db[guildId]) db[guildId] = {};
// Merge incoming settings with existing settings to avoid overwriting unrelated keys
db[guildId] = { ...db[guildId], ...newSettings };
writeDb(db);
res.json({ success: true });
});
// Toggle a single command for a guild (preserves other toggles)
app.post('/api/servers/:guildId/commands/:cmdName/toggle', (req, res) => {
const { guildId, cmdName } = req.params;
const { enabled } = req.body; // boolean
const protectedCommands = ['help', 'manage-commands'];
if (protectedCommands.includes(cmdName)) {
return res.status(403).json({ success: false, message: 'This command is locked and cannot be toggled.' });
}
const db = readDb();
if (!db[guildId]) db[guildId] = {};
if (!db[guildId].commandToggles) db[guildId].commandToggles = {};
if (typeof enabled === 'boolean') {
db[guildId].commandToggles[cmdName] = enabled;
writeDb(db);
return res.json({ success: true, cmdName, enabled });
}
return res.status(400).json({ success: false, message: 'Missing or invalid "enabled" boolean in request body' });
});
app.get('/api/servers/:guildId/bot-status', (req, res) => {
const { guildId } = req.params;
const guild = bot.client.guilds.cache.get(guildId);

View File

@@ -33,6 +33,13 @@
- [x] Further fine-tune card UI
- [x] Add Commands section to server settings page
- [x] Refine 'Back to Dashboard' button
- [x] Remove 'Open Help Page' button from individual command controls (moved to dedicated Commands List page)
- [x] Rename Help nav/button to 'Commands List' and update page title
- [x] Restructure Commands list UI to show per-command toggles and cleaner layout
- [x] Ensure frontend persists selections and doesn't overwrite other settings
- [x] Improve NavBar layout and styling for clarity and compactness
- [x] Implement single-hamburger NavBar (hamburger toggles to X; buttons hidden when collapsed)
- [x] Commands List button added above Commands accordion in Server Settings
- [x] Place 'Invite' button beside the server title on dashboard/server cards
- Acceptance criteria: the invite button appears horizontally adjacent to the server title (to the right), remains visible and usable on tablet and desktop layouts, is keyboard-focusable, and has an accessible aria-label (e.g. "Invite bot to SERVER_NAME").
- [x] Show the server name in a rounded "bubble" and render it bold
@@ -108,6 +115,10 @@
- [x] Ensure the `/help` command is locked (protected) and cannot be disabled via `/manage-commands`.
- [x] Add a Help tab in the frontend Server Settings that lists all bot commands and their descriptions per-server.
- [x] Move Help to a dedicated page within the server dashboard and add a top NavBar (collapsible) with Dashboard, Discord!, Contact, and Help (when on a server) buttons. Ensure Help page has a back arrow to return to the Commands section.
- [x] Move Help to a dedicated page within the server dashboard and add a top NavBar (collapsible) with Dashboard, Discord!, and Commands List (when on a server) buttons. Ensure Help page has a back arrow to return to the Commands section.
- [x] Remove Contact page from the frontend and App routes (no longer needed).
- [x] Redesign NavBar for cleaner layout and prettier appearance.
- [x] Redesign NavBar for cleaner layout and prettier appearance. (Title updated to 'ECS - EHDCHADSWORTH')
- [x] Added `/help` slash command that lists commands and their descriptions and shows per-server enable/disable status.
- [x] **Autorole**
- [x] Add "Autorole" section to server settings.

View File

@@ -8,7 +8,6 @@ import Dashboard from './components/Dashboard';
import ServerSettings from './components/ServerSettings';
import NavBar from './components/NavBar';
import HelpPage from './components/HelpPage';
import ContactPage from './components/ContactPage';
import DiscordPage from './components/DiscordPage';
function App() {
@@ -23,7 +22,6 @@ function App() {
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/server/:guildId" element={<ServerSettings />} />
<Route path="/server/:guildId/help" element={<HelpPage />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="/discord" element={<DiscordPage />} />
</Routes>
</Router>

View File

@@ -2,7 +2,6 @@ import React, { useState, useEffect, useLayoutEffect, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { Grid, Card, CardContent, Typography, Box, CardMedia, IconButton, Snackbar, Alert } from '@mui/material';
import { UserContext } from '../contexts/UserContext';
import UserSettings from './UserSettings';
import PersonAddIcon from '@mui/icons-material/PersonAdd';
import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
import axios from 'axios';
@@ -119,9 +118,7 @@ const Dashboard = () => {
return (
<div style={{ padding: '20px' }}>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<UserSettings />
</Box>
{/* UserSettings moved to NavBar */}
<Typography variant="h4" gutterBottom>
Dashboard
</Typography>

View File

@@ -24,8 +24,8 @@ const HelpPage = () => {
return (
<div style={{ padding: 20 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<IconButton onClick={handleBack}><ArrowBackIcon /></IconButton>
<Typography variant="h5">Help - Commands for this Server</Typography>
<IconButton onClick={handleBack}><ArrowBackIcon /></IconButton>
<Typography variant="h5">Commands List</Typography>
</Box>
<Box sx={{ marginTop: 2 }}>
{commands.length === 0 && <Typography>No commands available.</Typography>}

View File

@@ -1,31 +1,58 @@
import React, { useState } from 'react';
import { AppBar, Toolbar, Button, Box, IconButton, Collapse } from '@mui/material';
import { AppBar, Toolbar, IconButton, Typography, Box, Menu, MenuItem } from '@mui/material';
import UserSettings from './UserSettings';
import MenuIcon from '@mui/icons-material/Menu';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import { useNavigate, useLocation, useParams } from 'react-router-dom';
import CloseIcon from '@mui/icons-material/Close';
import { useNavigate, useLocation } from 'react-router-dom';
const NavBar = () => {
const [open, setOpen] = useState(true);
const navigate = useNavigate();
const location = useLocation();
// Pull guildId from URL if present
const [open, setOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const guildIdMatch = location.pathname.match(/\/server\/(\d+)/);
const guildId = guildIdMatch ? guildIdMatch[1] : null;
const toggleOpen = (e) => {
if (!open) {
setAnchorEl(e.currentTarget);
setOpen(true);
} else {
setAnchorEl(null);
setOpen(false);
}
};
const closeMenu = () => { setAnchorEl(null); setOpen(false); };
return (
<AppBar position="static" color="transparent" elevation={0} sx={{ mb: 2 }}>
<Toolbar>
<IconButton onClick={() => setOpen(prev => !prev)} aria-label="toggle menu"><MenuIcon /></IconButton>
<Collapse in={open} orientation="horizontal">
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Button onClick={() => navigate('/dashboard')} color="inherit">Dashboard</Button>
<Button onClick={() => navigate('/discord')} color="inherit">Discord!</Button>
<Button onClick={() => navigate('/contact')} color="inherit">Contact</Button>
{guildId && (
<Button startIcon={<HelpOutlineIcon />} onClick={() => navigate(`/server/${guildId}/help`)} color="inherit">Help</Button>
)}
</Box>
</Collapse>
<AppBar position="static" color="transparent" elevation={0} sx={{ mb: 2, borderBottom: '1px solid rgba(0,0,0,0.06)' }}>
<Toolbar sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', px: { xs: 2, sm: 4 } }}>
<Box>
<IconButton onClick={toggleOpen} aria-label="menu" size="large" sx={{ bgcolor: open ? 'primary.main' : 'transparent', color: open ? 'white' : 'text.primary' }}>
{open ? <CloseIcon /> : <MenuIcon />}
</IconButton>
</Box>
<Typography variant="h6" component="div" sx={{ fontWeight: 800 }} onClick={() => { navigate('/dashboard'); closeMenu(); }}>
ECS - EHDCHADSWORTH
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<UserSettings />
</Box>
<Menu
anchorEl={anchorEl}
open={open}
onClose={closeMenu}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
PaperProps={{ sx: { width: { xs: 'calc(100% - 32px)', sm: 300 }, left: { xs: 16, sm: 'auto' } } }}
>
<MenuItem onClick={() => { navigate('/dashboard'); closeMenu(); }}>Dashboard</MenuItem>
<MenuItem onClick={() => { window.open('https://discord.gg/EAqNqWjJMQ', '_blank'); closeMenu(); }}>Join The Discord!</MenuItem>
{guildId && <MenuItem onClick={() => { navigate(`/server/${guildId}/help`); closeMenu(); }}>Commands List</MenuItem>}
</Menu>
</Toolbar>
</AppBar>
);

View File

@@ -4,7 +4,7 @@ import axios from 'axios';
import { Button, Typography, Box, IconButton, Switch, Select, MenuItem, FormControl, FormControlLabel, Radio, RadioGroup, TextField, Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import UserSettings from './UserSettings';
// UserSettings moved to NavBar
import ConfirmDialog from './ConfirmDialog';
const ServerSettings = () => {
@@ -243,24 +243,48 @@ const ServerSettings = () => {
</Button>
)}
</Box>
<UserSettings />
{/* UserSettings moved to NavBar */}
</Box>
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }} expanded={commandsExpanded} onChange={() => setCommandsExpanded(prev => !prev)}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">Commands</Typography>
</AccordionSummary>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', padding: 1 }}>
<Button variant="text" onClick={() => navigate(`/server/${guildId}/help`)} disabled={!isBotInServer}>Commands List</Button>
</Box>
<AccordionDetails>
{!isBotInServer && <Typography>Invite the bot to enable commands.</Typography>}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: '10px' }}>
<Typography>Ping Command</Typography>
<Box>
<Button variant="contained" onClick={togglePingCommand} disabled={!isBotInServer}>
{settings.pingCommand ? 'Disable' : 'Enable'}
</Button>
<Button sx={{ ml: 1 }} variant="outlined" onClick={() => navigate(`/server/${guildId}/help`)}>
Open Help Page
</Button>
</Box>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, marginTop: '10px' }}>
{commandsList.map(cmd => (
<Box key={cmd.name} sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: 1, border: '1px solid #eee', borderRadius: 1 }}>
<Box>
<Typography sx={{ fontWeight: 'bold' }}>{cmd.name}</Typography>
<Typography variant="body2">{cmd.description}</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{['help', 'manage-commands'].includes(cmd.name) ? (
<FormControlLabel
control={<Switch checked={true} disabled />}
label="Locked"
/>
) : (
<FormControlLabel
control={<Switch checked={cmd.enabled} onChange={async (e) => {
const newVal = e.target.checked;
// optimistic update
setCommandsList(prev => prev.map(c => c.name === cmd.name ? { ...c, enabled: newVal } : c));
try {
await axios.post(`http://localhost:3002/api/servers/${guildId}/commands/${cmd.name}/toggle`, { enabled: newVal });
} catch (err) {
// revert on error
setCommandsList(prev => prev.map(c => c.name === cmd.name ? { ...c, enabled: cmd.enabled } : c));
}
}} disabled={!isBotInServer} label={cmd.enabled ? 'Enabled' : 'Disabled'} />}
/>
)}
</Box>
</Box>
))}
</Box>
</AccordionDetails>
</Accordion>