ui changes
This commit is contained in:
28
frontend/src/components/ConfirmDialog.js
Normal file
28
frontend/src/components/ConfirmDialog.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Button } from '@mui/material';
|
||||
|
||||
const ConfirmDialog = ({ open, onClose, onConfirm, title, message }) => {
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{message}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} color="primary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={onConfirm} color="primary" autoFocus>
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmDialog;
|
||||
@@ -4,14 +4,19 @@ import { Grid, Card, CardContent, Typography, Box, CardMedia, IconButton, Snackb
|
||||
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';
|
||||
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
|
||||
const Dashboard = () => {
|
||||
const { user, setUser } = useContext(UserContext);
|
||||
const [guilds, setGuilds] = useState([]);
|
||||
const [botStatus, setBotStatus] = useState({});
|
||||
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [selectedGuild, setSelectedGuild] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -77,18 +82,34 @@ const Dashboard = () => {
|
||||
|
||||
const handleInviteBot = async (e, guild) => {
|
||||
e.stopPropagation();
|
||||
if (botStatus[guild.id]) {
|
||||
setSnackbarMessage('Bot already added to this server.');
|
||||
setSnackbarOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = '1423377662055026840'; // Hardcoded client ID from user request
|
||||
const permissions = 8; // Administrator
|
||||
const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&permissions=${permissions}&scope=bot%20applications.commands&guild_id=${guild.id}&disable_guild_select=true`;
|
||||
window.open(inviteUrl, '_blank', 'noopener,noreferrer');
|
||||
};
|
||||
|
||||
const handleLeaveBot = (e, guild) => {
|
||||
e.stopPropagation();
|
||||
setSelectedGuild(guild);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleConfirmLeave = async () => {
|
||||
if (!selectedGuild) return;
|
||||
try {
|
||||
await axios.post(`http://localhost:3002/api/servers/${selectedGuild.id}/leave`);
|
||||
setBotStatus(prevStatus => ({ ...prevStatus, [selectedGuild.id]: false }));
|
||||
setSnackbarMessage('Bot has left the server.');
|
||||
setSnackbarOpen(true);
|
||||
} catch (error) {
|
||||
console.error('Error leaving server:', error);
|
||||
setSnackbarMessage('Failed to make the bot leave the server.');
|
||||
setSnackbarOpen(true);
|
||||
}
|
||||
setDialogOpen(false);
|
||||
setSelectedGuild(null);
|
||||
};
|
||||
|
||||
const handleSnackbarClose = (event, reason) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
@@ -158,14 +179,23 @@ const Dashboard = () => {
|
||||
{guild.name}
|
||||
</Box>
|
||||
</Box>
|
||||
<IconButton
|
||||
aria-label={`Invite bot to ${guild.name}`}
|
||||
size="small"
|
||||
onClick={(e) => handleInviteBot(e, guild)}
|
||||
disabled={botStatus[guild.id]}
|
||||
>
|
||||
<PersonAddIcon />
|
||||
</IconButton>
|
||||
{botStatus[guild.id] ? (
|
||||
<IconButton
|
||||
aria-label={`Make bot leave ${guild.name}`}
|
||||
size="small"
|
||||
onClick={(e) => handleLeaveBot(e, guild)}
|
||||
>
|
||||
<RemoveCircleOutlineIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton
|
||||
aria-label={`Invite bot to ${guild.name}`}
|
||||
size="small"
|
||||
onClick={(e) => handleInviteBot(e, guild)}
|
||||
>
|
||||
<PersonAddIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -177,6 +207,13 @@ const Dashboard = () => {
|
||||
{snackbarMessage}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<ConfirmDialog
|
||||
open={dialogOpen}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
onConfirm={handleConfirmLeave}
|
||||
title="Confirm Leave"
|
||||
message={`Are you sure you want the bot to leave ${selectedGuild?.name}?`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Container, Paper, Typography, Box } from '@mui/material';
|
||||
|
||||
const Login = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -16,10 +17,27 @@ const Login = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Login</h2>
|
||||
<button onClick={handleLogin}>Login with Discord</button>
|
||||
</div>
|
||||
<Container component="main" maxWidth="xs" sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>
|
||||
<Paper elevation={3} sx={{ padding: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', borderRadius: '20px', boxShadow: '0 8px 16px 0 rgba(0,0,0,0.2)' }}>
|
||||
<Typography component="h1" variant="h4" sx={{ marginBottom: 2, fontWeight: 'bold' }}>
|
||||
Welcome!
|
||||
</Typography>
|
||||
<Typography component="p" variant="h6" sx={{ marginBottom: 4, textAlign: 'center' }}>
|
||||
Login with your Discord account to continue
|
||||
</Typography>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleLogin}
|
||||
sx={{ borderRadius: '10px', padding: '10px 0', textTransform: 'none', fontSize: '1.2rem' }}
|
||||
>
|
||||
Login with Discord
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { Button, Typography, Card, CardContent, Box, IconButton } from '@mui/material';
|
||||
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';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
|
||||
const ServerSettings = () => {
|
||||
const { guildId } = useParams();
|
||||
@@ -13,6 +15,25 @@ const ServerSettings = () => {
|
||||
const [isBotInServer, setIsBotInServer] = useState(false);
|
||||
const [clientId, setClientId] = useState(null);
|
||||
const [server, setServer] = useState(null);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [channels, setChannels] = useState([]);
|
||||
const [welcomeLeaveSettings, setWelcomeLeaveSettings] = useState({
|
||||
welcome: {
|
||||
enabled: false,
|
||||
channel: '',
|
||||
message: 'Welcome to the server, {user}!',
|
||||
customMessage: '',
|
||||
},
|
||||
leave: {
|
||||
enabled: false,
|
||||
channel: '',
|
||||
message: '{user} has left the server.',
|
||||
customMessage: '',
|
||||
},
|
||||
});
|
||||
|
||||
const defaultWelcomeMessages = ["Welcome to the server, {user}!", "Hey {user}, welcome!", "{user} has joined the party!"];
|
||||
const defaultLeaveMessages = ["{user} has left the server.", "Goodbye, {user}.", "We'll miss you, {user}."];
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state && location.state.guild) {
|
||||
@@ -43,9 +64,78 @@ const ServerSettings = () => {
|
||||
.then(response => {
|
||||
setClientId(response.data.clientId);
|
||||
});
|
||||
|
||||
// Fetch channels
|
||||
axios.get(`http://localhost:3002/api/servers/${guildId}/channels`)
|
||||
.then(response => {
|
||||
setChannels(response.data);
|
||||
});
|
||||
|
||||
// Fetch welcome/leave settings
|
||||
axios.get(`http://localhost:3002/api/servers/${guildId}/welcome-leave-settings`)
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
setWelcomeLeaveSettings(response.data);
|
||||
}
|
||||
});
|
||||
|
||||
}, [guildId, location.state]);
|
||||
|
||||
const handleSettingUpdate = (newSettings) => {
|
||||
axios.post(`http://localhost:3002/api/servers/${guildId}/welcome-leave-settings`, newSettings)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
setWelcomeLeaveSettings(newSettings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleToggleChange = (type) => (event) => {
|
||||
const newSettings = { ...welcomeLeaveSettings };
|
||||
newSettings[type].enabled = event.target.checked;
|
||||
handleSettingUpdate(newSettings);
|
||||
};
|
||||
|
||||
const handleChannelChange = (type) => (event) => {
|
||||
const newSettings = { ...welcomeLeaveSettings };
|
||||
newSettings[type].channel = event.target.value;
|
||||
handleSettingUpdate(newSettings);
|
||||
};
|
||||
|
||||
const handleMessageOptionChange = (type) => (event) => {
|
||||
const newSettings = { ...welcomeLeaveSettings };
|
||||
if (event.target.value !== 'custom') {
|
||||
newSettings[type].message = event.target.value;
|
||||
handleSettingUpdate(newSettings);
|
||||
} else {
|
||||
const tempSettings = { ...welcomeLeaveSettings };
|
||||
// Set message to custom message to get the radio button to select custom
|
||||
tempSettings[type].message = tempSettings[type].customMessage;
|
||||
setWelcomeLeaveSettings(tempSettings);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCustomMessageChange = (type) => (event) => {
|
||||
const newSettings = { ...welcomeLeaveSettings };
|
||||
newSettings[type].customMessage = event.target.value;
|
||||
setWelcomeLeaveSettings(newSettings);
|
||||
};
|
||||
|
||||
const handleApplyCustomMessage = (type) => () => {
|
||||
const newSettings = { ...welcomeLeaveSettings };
|
||||
newSettings[type].message = newSettings[type].customMessage;
|
||||
handleSettingUpdate(newSettings);
|
||||
};
|
||||
|
||||
const getMessageValue = (type) => {
|
||||
const currentMessage = welcomeLeaveSettings[type].message;
|
||||
const defaultMessages = type === 'welcome' ? defaultWelcomeMessages : defaultLeaveMessages;
|
||||
if (defaultMessages.includes(currentMessage)) {
|
||||
return currentMessage;
|
||||
}
|
||||
return 'custom';
|
||||
}
|
||||
|
||||
const togglePingCommand = () => {
|
||||
const newSettings = { ...settings, pingCommand: !settings.pingCommand };
|
||||
axios.post(`http://localhost:3002/api/servers/${guildId}/settings`, newSettings)
|
||||
@@ -63,6 +153,20 @@ const ServerSettings = () => {
|
||||
window.open(url, '_blank');
|
||||
};
|
||||
|
||||
const handleLeaveBot = () => {
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleConfirmLeave = async () => {
|
||||
try {
|
||||
await axios.post(`http://localhost:3002/api/servers/${guildId}/leave`);
|
||||
setIsBotInServer(false);
|
||||
} catch (error) {
|
||||
console.error('Error leaving server:', error);
|
||||
}
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
navigate('/dashboard');
|
||||
}
|
||||
@@ -78,7 +182,9 @@ const ServerSettings = () => {
|
||||
{server ? `Server Settings for ${server.name}` : 'Loading...'}
|
||||
</Typography>
|
||||
{isBotInServer ? (
|
||||
<Typography>The bot is already in this server.</Typography>
|
||||
<Button variant="contained" size="small" color="error" onClick={handleLeaveBot}>
|
||||
Leave Server
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="contained" size="small" onClick={handleInviteBot} disabled={!clientId}>
|
||||
Invite Bot
|
||||
@@ -87,23 +193,125 @@ const ServerSettings = () => {
|
||||
</Box>
|
||||
<UserSettings />
|
||||
</Box>
|
||||
<Card sx={{ borderRadius: '20px', boxShadow: '0 8px 16px 0 rgba(0,0,0,0.2)', marginTop: '20px' }}>
|
||||
<CardContent>
|
||||
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">Commands</Typography>
|
||||
</AccordionSummary>
|
||||
<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>
|
||||
<Button variant="contained" onClick={togglePingCommand}>
|
||||
<Button variant="contained" onClick={togglePingCommand} disabled={!isBotInServer}>
|
||||
{settings.pingCommand ? 'Disable' : 'Enable'}
|
||||
</Button>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card sx={{ borderRadius: '20px', boxShadow: '0 8px 16px 0 rgba(0,0,0,0.2)', marginTop: '20px' }}>
|
||||
<CardContent>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">Welcome/Leave</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{!isBotInServer && <Typography>Invite the bot to enable this feature.</Typography>}
|
||||
<Box sx={{ marginTop: '10px' }}>
|
||||
<Typography variant="subtitle1">Welcome Messages</Typography>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={welcomeLeaveSettings.welcome.enabled} onChange={handleToggleChange('welcome')} disabled={!isBotInServer} />}
|
||||
label="Enable Welcome Messages"
|
||||
/>
|
||||
<FormControl fullWidth sx={{ marginTop: '10px' }} disabled={!isBotInServer || !welcomeLeaveSettings.welcome.enabled}>
|
||||
<Select
|
||||
value={welcomeLeaveSettings.welcome.channel}
|
||||
onChange={handleChannelChange('welcome')}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="" disabled>Select a channel</MenuItem>
|
||||
{channels.map(channel => (
|
||||
<MenuItem key={channel.id} value={channel.id}>{channel.name}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl component="fieldset" sx={{ marginTop: '10px' }} disabled={!isBotInServer || !welcomeLeaveSettings.welcome.enabled}>
|
||||
<RadioGroup
|
||||
value={getMessageValue('welcome')}
|
||||
onChange={handleMessageOptionChange('welcome')}
|
||||
>
|
||||
{defaultWelcomeMessages.map(msg => (
|
||||
<FormControlLabel key={msg} value={msg} control={<Radio />} label={msg} />
|
||||
))}
|
||||
<FormControlLabel value="custom" control={<Radio />} label="Custom" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', marginTop: '10px' }} >
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Your custom message"
|
||||
value={welcomeLeaveSettings.welcome.customMessage}
|
||||
onChange={handleCustomMessageChange('welcome')}
|
||||
disabled={!isBotInServer || !welcomeLeaveSettings.welcome.enabled || getMessageValue('welcome') !== 'custom'}
|
||||
/>
|
||||
<Button onClick={handleApplyCustomMessage('welcome')} disabled={!isBotInServer || !welcomeLeaveSettings.welcome.enabled || getMessageValue('welcome') !== 'custom'}>Apply</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ marginTop: '20px' }}>
|
||||
<Typography variant="subtitle1">Leave Messages</Typography>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={welcomeLeaveSettings.leave.enabled} onChange={handleToggleChange('leave')} disabled={!isBotInServer} />}
|
||||
label="Enable Leave Messages"
|
||||
/>
|
||||
<FormControl fullWidth sx={{ marginTop: '10px' }} disabled={!isBotInServer || !welcomeLeaveSettings.leave.enabled}>
|
||||
<Select
|
||||
value={welcomeLeaveSettings.leave.channel}
|
||||
onChange={handleChannelChange('leave')}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="" disabled>Select a channel</MenuItem>
|
||||
{channels.map(channel => (
|
||||
<MenuItem key={channel.id} value={channel.id}>{channel.name}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl component="fieldset" sx={{ marginTop: '10px' }} disabled={!isBotInServer || !welcomeLeaveSettings.leave.enabled}>
|
||||
<RadioGroup
|
||||
value={getMessageValue('leave')}
|
||||
onChange={handleMessageOptionChange('leave')}
|
||||
>
|
||||
{defaultLeaveMessages.map(msg => (
|
||||
<FormControlLabel key={msg} value={msg} control={<Radio />} label={msg} />
|
||||
))}
|
||||
<FormControlLabel value="custom" control={<Radio />} label="Custom" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', marginTop: '10px' }} >
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Your custom message"
|
||||
value={welcomeLeaveSettings.leave.customMessage}
|
||||
onChange={handleCustomMessageChange('leave')}
|
||||
disabled={!isBotInServer || !welcomeLeaveSettings.leave.enabled || getMessageValue('leave') !== 'custom'}
|
||||
/>
|
||||
<Button onClick={handleApplyCustomMessage('leave')} disabled={!isBotInServer || !welcomeLeaveSettings.leave.enabled || getMessageValue('leave') !== 'custom'}>Apply</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion sx={{ marginTop: '20px', opacity: isBotInServer ? 1 : 0.5 }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">Admin Commands</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography>Coming soon...</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<ConfirmDialog
|
||||
open={dialogOpen}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
onConfirm={handleConfirmLeave}
|
||||
title="Confirm Leave"
|
||||
message={`Are you sure you want the bot to leave ${server?.name}?`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user