Files
ECS-FullStack/frontend/src/components/Dashboard.js
2025-10-04 08:08:45 -04:00

219 lines
8.0 KiB
JavaScript

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 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(() => {
const urlParams = new URLSearchParams(window.location.search);
const userParam = urlParams.get('user');
const guildsParam = urlParams.get('guilds');
if (userParam && guildsParam) {
const parsedUser = JSON.parse(decodeURIComponent(userParam));
const parsedGuilds = JSON.parse(decodeURIComponent(guildsParam));
localStorage.setItem('user', JSON.stringify(parsedUser));
localStorage.setItem('guilds', JSON.stringify(parsedGuilds));
setUser(parsedUser);
setGuilds(parsedGuilds);
// Clean the URL
window.history.replaceState({}, document.title, "/dashboard");
} else {
const storedUser = localStorage.getItem('user');
const storedGuilds = localStorage.getItem('guilds');
if (storedUser && storedGuilds) {
setUser(JSON.parse(storedUser));
setGuilds(JSON.parse(storedGuilds));
}
}
}, [setUser]);
useEffect(() => {
const fetchBotStatus = async () => {
const statusPromises = guilds.map(async (guild) => {
try {
const response = await axios.get(`http://localhost:3002/api/servers/${guild.id}/bot-status`);
return { guildId: guild.id, isBotInServer: response.data.isBotInServer };
} catch (error) {
console.error(`Error fetching bot status for guild ${guild.id}:`, error);
return { guildId: guild.id, isBotInServer: false };
}
});
const results = await Promise.all(statusPromises);
const newBotStatus = results.reduce((acc, curr) => {
acc[curr.guildId] = curr.isBotInServer;
return acc;
}, {});
setBotStatus(newBotStatus);
};
if (guilds.length > 0) {
fetchBotStatus();
}
}, [guilds]);
useLayoutEffect(() => {
const scrollPosition = sessionStorage.getItem('scrollPosition');
if (scrollPosition) {
window.scrollTo(0, parseInt(scrollPosition));
sessionStorage.removeItem('scrollPosition');
}
}, []);
const handleCardClick = (guild) => {
sessionStorage.setItem('scrollPosition', window.scrollY);
navigate(`/server/${guild.id}`, { state: { guild } });
};
const handleInviteBot = async (e, guild) => {
e.stopPropagation();
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;
}
setSnackbarOpen(false);
};
return (
<div style={{ padding: '20px' }}>
{/* UserSettings moved to NavBar */}
<Typography variant="h4" gutterBottom>
Dashboard
</Typography>
{user && (
<Typography variant="h5" gutterBottom>
Welcome, {user.username}
</Typography>
)}
<Typography variant="h6" gutterBottom>
Your Admin Servers:
</Typography>
<Grid container spacing={3}>
{guilds.map(guild => (
<Grid item xs={12} sm={6} md={4} lg={3} key={guild.id}>
<Card
onClick={() => handleCardClick(guild)}
sx={{
cursor: 'pointer',
borderRadius: '20px',
boxShadow: '0 8px 16px 0 rgba(0,0,0,0.2)',
transition: 'transform 0.3s',
height: '250px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
'&:hover': {
transform: 'scale(1.05)'
}
}}
>
<CardMedia
component="img"
sx={{ height: '60%', objectFit: 'cover' }}
image={guild.icon ? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png` : 'https://cdn.discordapp.com/embed/avatars/0.png'}
alt={guild.name}
/>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, flexDirection: { xs: 'column', sm: 'row' } }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: '100%', justifyContent: { xs: 'center', sm: 'flex-start' } }}>
<Box
title={guild.name}
sx={{
px: 2,
py: 0.5,
borderRadius: '999px',
fontWeight: 'bold',
bgcolor: 'rgba(0,0,0,0.06)',
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
textAlign: { xs: 'center', sm: 'left' }
}}
>
{guild.name}
</Box>
</Box>
{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>
</Grid>
))}
</Grid>
<Snackbar open={snackbarOpen} autoHideDuration={6000} onClose={handleSnackbarClose}>
<Alert onClose={handleSnackbarClose} severity="info" sx={{ width: '100%' }}>
{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>
);
};
export default Dashboard;