fixed themes and ui added new features

This commit is contained in:
2025-10-04 08:39:54 -04:00
parent 0b1a6cdea4
commit 834e77a93e
7 changed files with 373 additions and 24 deletions

View File

@@ -6,12 +6,14 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
// UserSettings moved to NavBar
import ConfirmDialog from './ConfirmDialog';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import DeleteIcon from '@mui/icons-material/Delete';
const ServerSettings = () => {
const { guildId } = useParams();
const navigate = useNavigate();
const location = useLocation();
const [settings, setSettings] = useState({ pingCommand: false });
// settings state removed (not used) to avoid lint warnings
const [isBotInServer, setIsBotInServer] = useState(false);
const [clientId, setClientId] = useState(null);
const [server, setServer] = useState(null);
@@ -23,6 +25,8 @@ const ServerSettings = () => {
roleId: '',
});
const [commandsList, setCommandsList] = useState([]);
const [invites, setInvites] = useState([]);
const [inviteForm, setInviteForm] = useState({ channelId: '', maxAge: 0, maxUses: 0, temporary: false });
const [commandsExpanded, setCommandsExpanded] = useState(false);
const [welcomeLeaveSettings, setWelcomeLeaveSettings] = useState({
welcome: {
@@ -54,11 +58,8 @@ const ServerSettings = () => {
}
}
// Fetch settings
axios.get(`http://localhost:3002/api/servers/${guildId}/settings`)
.then(response => {
setSettings(response.data);
});
// Fetch settings (not used directly in this component)
axios.get(`http://localhost:3002/api/servers/${guildId}/settings`).catch(() => {});
// Check if bot is in server
axios.get(`http://localhost:3002/api/servers/${guildId}/bot-status`)
@@ -107,6 +108,11 @@ const ServerSettings = () => {
})
.catch(() => setCommandsList([]));
// Fetch invites
axios.get(`http://localhost:3002/api/servers/${guildId}/invites`)
.then(resp => setInvites(resp.data || []))
.catch(() => setInvites([]));
// Open commands accordion if navigated from Help back button
if (location.state && location.state.openCommands) {
setCommandsExpanded(true);
@@ -188,16 +194,6 @@ const ServerSettings = () => {
return 'custom';
}
const togglePingCommand = () => {
const newSettings = { ...settings, pingCommand: !settings.pingCommand };
axios.post(`http://localhost:3002/api/servers/${guildId}/settings`, newSettings)
.then(response => {
if (response.data.success) {
setSettings(newSettings);
}
});
};
const handleInviteBot = () => {
if (!clientId) return;
const permissions = 8; // Administrator
@@ -255,7 +251,7 @@ const ServerSettings = () => {
<AccordionDetails>
{!isBotInServer && <Typography>Invite the bot to enable commands.</Typography>}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, marginTop: '10px' }}>
{commandsList.map(cmd => (
{commandsList && [...commandsList].sort((a,b) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'})).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>
@@ -288,6 +284,87 @@ const ServerSettings = () => {
</Box>
</AccordionDetails>
</Accordion>
{/* Invite creation and list */}
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">Invites</Typography>
</AccordionSummary>
<AccordionDetails>
{!isBotInServer && <Typography>Invite features require the bot to be in the server.</Typography>}
<Box sx={{ display: 'flex', gap: 2, flexDirection: { xs: 'column', sm: 'row' }, marginTop: 1 }}>
<Box sx={{ width: { xs: '100%', sm: '40%' } }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Channel (optional)</Typography>
<FormControl fullWidth>
<Select value={inviteForm.channelId} onChange={(e) => setInviteForm(f => ({ ...f, channelId: e.target.value }))} displayEmpty>
<MenuItem value="">(Any channel)</MenuItem>
{channels.map(ch => (<MenuItem key={ch.id} value={ch.id}>{ch.name}</MenuItem>))}
</Select>
</FormControl>
</Box>
<Box sx={{ width: { xs: '100%', sm: '20%' } }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Expiry</Typography>
<FormControl fullWidth>
<Select value={inviteForm.maxAge} onChange={(e) => setInviteForm(f => ({ ...f, maxAge: Number(e.target.value) }))}>
<MenuItem value={0}>Never expire</MenuItem>
<MenuItem value={3600}>1 hour</MenuItem>
<MenuItem value={86400}>1 day</MenuItem>
<MenuItem value={604800}>7 days</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ width: { xs: '100%', sm: '20%' } }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Max Uses</Typography>
<FormControl fullWidth>
<Select value={inviteForm.maxUses} onChange={(e) => setInviteForm(f => ({ ...f, maxUses: Number(e.target.value) }))}>
<MenuItem value={0}>Unlimited</MenuItem>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={5}>5</MenuItem>
<MenuItem value={10}>10</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: { xs: '100%', sm: '20%' } }}>
<Box sx={{ width: '100%' }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Temporary</Typography>
<FormControlLabel control={<Switch checked={inviteForm.temporary} onChange={(e) => setInviteForm(f => ({ ...f, temporary: e.target.checked }))} />} label="" />
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-end' }}>
<Button variant="contained" onClick={async () => {
try {
const resp = await axios.post(`http://localhost:3002/api/servers/${guildId}/invites`, inviteForm);
if (resp.data && resp.data.success) {
setInvites(prev => [...prev, resp.data.invite]);
}
} catch (err) {
console.error('Error creating invite:', err);
}
}} disabled={!isBotInServer}>Create Invite</Button>
</Box>
</Box>
<Box sx={{ marginTop: 2 }}>
{invites.length === 0 && <Typography>No invites created by the bot.</Typography>}
{invites.map(inv => (
<Box key={inv.code} sx={{ border: '1px solid #eee', borderRadius: 1, padding: 1, marginTop: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box>
<Typography>{inv.url}</Typography>
<Typography variant="caption">Created: {new Date(inv.createdAt).toLocaleString()} Uses: {inv.uses || 0} MaxUses: {inv.maxUses || 0} MaxAge(s): {inv.maxAge || 0} Temporary: {inv.temporary ? 'Yes' : 'No'}</Typography>
</Box>
<Box>
<Button startIcon={<ContentCopyIcon />} onClick={() => { navigator.clipboard.writeText(inv.url); }}>Copy</Button>
<Button startIcon={<DeleteIcon />} color="error" onClick={async () => {
try {
await axios.delete(`http://localhost:3002/api/servers/${guildId}/invites/${inv.code}`);
setInvites(prev => prev.filter(i => i.code !== inv.code));
} catch (err) { console.error('Error deleting invite:', err); }
}}>Delete</Button>
</Box>
</Box>
))}
</Box>
</AccordionDetails>
</Accordion>
{/* Help moved to dedicated Help page */}
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>