updated navbar and ui
This commit is contained in:
@@ -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);
|
||||
|
||||
11
checklist.md
11
checklist.md
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -25,7 +25,7 @@ const HelpPage = () => {
|
||||
<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>
|
||||
<Typography variant="h5">Commands List</Typography>
|
||||
</Box>
|
||||
<Box sx={{ marginTop: 2 }}>
|
||||
{commands.length === 0 && <Typography>No commands available.</Typography>}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
<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>
|
||||
</Collapse>
|
||||
|
||||
<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>
|
||||
);
|
||||
|
||||
@@ -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 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>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user