fixed themes and ui added new features
This commit is contained in:
@@ -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 />}>
|
||||
|
||||
Reference in New Issue
Block a user