ui changes

This commit is contained in:
2025-10-03 08:39:32 -04:00
parent 1341b325ee
commit 9bc7a5e6b8
18 changed files with 629 additions and 419 deletions

View 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;

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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>
);
};