Files
dndmaster/frontend/src/pages/games.jsx
T
Marces Zastrow da5071fa34 +
Stuff Design
2025-01-09 10:51:54 +01:00

266 lines
10 KiB
React

import { useState, useEffect, useContext } from 'react';
import { Link, useParams } from 'react-router-dom';
import axios from 'axios';
import { UserContext } from '../context/UserContext';
import { Box, Typography, Grid2, Card, CardContent, CardMedia, Button, IconButton, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import PaidIcon from '@mui/icons-material/Paid';
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
import PetsIcon from '@mui/icons-material/Pets';
import WcIcon from '@mui/icons-material/Wc';
import WorkIcon from '@mui/icons-material/Work';
import defaultCharacterImage from '../assets/default-character.png';
import defaultItemImage from '../assets/default-item.png';
import './games.css';
const GamesPage = () => {
const { userId } = useContext(UserContext);
const { gameId } = useParams();
const [character, setCharacter] = useState(null);
const [inventory, setInventory] = useState([]);
const [isEditOpen, setIsEditOpen] = useState(false);
const [newDescription, setNewDescription] = useState('');
const fetchCharacter = async () => {
try {
console.log(`Fetching character for gameId: ${gameId}, userId: ${userId}`); // Debug output
const response = await axios.get(`http://localhost:5000/games/${gameId}/${userId}/character`);
console.log('Character data:', response.data); // Debug output
setCharacter(response.data);
} catch (error) {
console.error('Error fetching character:', error);
}
};
const fetchInventory = async (charId) => {
try {
const response = await axios.get(`http://localhost:5000/games/${gameId}/${charId}/items`);
setInventory(response.data ? [response.data].flat() : []);
} catch (error) {
console.error('Error fetching inventory:', error);
}
};
useEffect(() => {
if (userId) {
fetchCharacter();
}
}, [userId, gameId]);
useEffect(() => {
if (character?.CharID) {
fetchInventory(character.CharID);
}
}, [character]);
useEffect(() => {
const interval = setInterval(() => {
if (character?.CharID) {
fetchCharacter();
fetchInventory(character.CharID);
}
}, 5000);
return () => clearInterval(interval);
}, [character]);
const handleEditOpen = () => {
setNewDescription(character.description);
setIsEditOpen(true);
};
const handleEditClose = () => {
setIsEditOpen(false);
};
const handleSaveDescription = async () => {
try {
const response = await axios.put(`http://localhost:5000/games/${gameId}/${userId}/character`, { description: newDescription });
setCharacter({ ...character, description: newDescription });
setIsEditOpen(false);
} catch (error) {
console.error('Error updating description:', error);
}
};
if (!character) {
return (
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', p: 4 }}>
<Typography variant="h4" sx={{ mb: 4 }}>No Character Found</Typography>
<Button
variant="contained"
sx={{ backgroundColor: '#764ACB', '&:hover': { backgroundColor: '#5e3aa2' } }}
component={Link}
to="/create-character"
>
Create New Character
</Button>
</Box>
);
}
return (
<Box sx={{ p: 3, color: '#fff' }}>
<Grid2 container spacing={3}>
{/* Character Image and Details */}
<Grid2 item xs={12} md={4}>
<Box sx={{ border: '1px solid #444', borderRadius: '3px', p: 2, backgroundColor: '#2e2e3f' }}>
<Card sx={{ width: '100%', backgroundColor: '#1e1e2f', color: '#fff' }}>
<CardMedia
component="img"
height="300"
image={character.Img || defaultCharacterImage}
alt={character.CharName}
sx={{ borderRadius: '3px' }}
/>
<CardContent>
<Typography variant="h4" sx={{ mb: 4, color: '#fff' }}>{character.CharName}</Typography>
<Typography variant="h5"><PaidIcon sx={{color: 'gold'}}/> <strong>Gold:</strong> {character.Gold}</Typography>
<Typography variant="h5"><CalendarTodayIcon sx={{}}/> <strong>Age:</strong> {character.Age}</Typography>
<Typography variant="h5"><PetsIcon sx={{}}/> <strong>Race:</strong> {character.Race}</Typography>
<Typography variant="h5"><WcIcon sx={{}}/> <strong>Sex:</strong> {character.Sex}</Typography>
<Typography variant="h5"><WorkIcon sx={{}}/> <strong>Job:</strong> {character.Job}</Typography>
</CardContent>
</Card>
</Box>
</Grid2>
{/* Health and Mana Bars */}
<Grid2 item xs={12} md={8}>
<Box sx={{ display: 'flex', gap: 2, mb: 4, width: '885px' }}>
<Box sx={{ flex: 1, backgroundColor: '#2e2e3f', borderRadius: '8px', p: 2, border: '1px solid #444' }}>
<Typography variant="body1" sx={{ display: 'flex', justifyContent: 'space-between', color: '#fff' }}>
<span>Health</span>
<span>{character.currentHealth}/{character.maxHealth}</span>
</Typography>
<Box className="bar-bg">
<Box
className="bar-fg bar-fg-red"
sx={{ width: `${(character.currentHealth / character.maxHealth) * 100}%` }}
></Box>
</Box>
</Box>
<Box sx={{ flex: 1, backgroundColor: '#2e2e3f', borderRadius: '8px', p: 2, border: '1px solid #444' }}>
<Typography variant="body1" sx={{ display: 'flex', justifyContent: 'space-between', color: '#fff' }}>
<span>Mana</span>
<span>{character.currentMana}/{character.maxMana}</span>
</Typography>
<Box className="bar-bg">
<Box
className="bar-fg bar-fg-blue"
sx={{ width: `${(character.currentMana / character.maxMana) * 100}%` }}
></Box>
</Box>
</Box>
</Box>
{/* Character Info Section */}
<Box sx={{ mb: 4 }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Typography variant="h5" sx={{ color: '#fff', mr: 1 }}>Description</Typography>
<IconButton onClick={handleEditOpen} sx={{ marginLeft: '10px', color: '#fff', backgroundColor: '#764ACB', borderRadius: '4px', p: 1, width: '50px' }}>
<EditIcon />
</IconButton>
</Box>
<Box sx={{ maxHeight: '200px', maxWidth: '850px', overflowY: 'auto', backgroundColor: '#2e2e3f', p: 2, borderRadius: '8px', border: '1px solid #444' }}>
<Typography variant="body1" sx={{ color: '#fff' }}>{character.description}</Typography>
</Box>
</Box>
{/* Inventory */}
<Box sx={{ backgroundColor: '#2e2e3f', borderRadius: '8px', p: 2, border: '1px solid #444', width: "850px" }}>
<Typography variant="h5" sx={{ mb: 2, color: '#fff' }}>Inventory</Typography>
<Grid2
container
spacing={2}
wrap="wrap"
sx={{
maxHeight: '340px', // Height for 2 rows (128px image + ~40px text) * 2 + spacing
overflowY: 'auto',
'&::-webkit-scrollbar': {
width: '8px'
},
'&::-webkit-scrollbar-track': {
background: '#1e1e2f'
},
'&::-webkit-scrollbar-thumb': {
background: '#444',
borderRadius: '4px'
}
}}
>
{inventory.map((item, index) => (
<Grid2 item xs={12} sm={4} md={2} key={index}>
<Card sx={{ backgroundColor: '#1e1e2f', color: '#fff' }}>
<CardMedia
component="img"
height="128px"
image={item.Img || defaultItemImage}
alt={item.ItemName}
sx={{ borderRadius: '3px' }}
/>
<CardContent>
<Typography variant="h6" color="#fff">{item.ItemName}</Typography>
<Typography variant="body2" color="#fff">Value: {item.GoldValue}</Typography>
</CardContent>
</Card>
</Grid2>
))}
</Grid2>
</Box>
</Grid2>
</Grid2>
{/* Edit Description Dialog */}
<Dialog
open={isEditOpen}
onClose={handleEditClose}
PaperProps={{
sx: {
backgroundColor: '#1e1e2f',
color: '#fff',
'& .MuiDialogTitle-root': {
color: '#fff'
}
}
}}
>
<DialogTitle>Edit Description</DialogTitle>
<DialogContent>
<DialogContentText sx={{ color: '#bbb' }}>
---------------------------------------------------------------------
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="description"
label="Description"
type="text"
fullWidth
variant="standard"
multiline
rows={6}
value={newDescription}
onChange={(e) => setNewDescription(e.target.value)}
sx={{
'& .MuiInputBase-root': {
color: '#fff'
},
'& .MuiInputLabel-root': {
color: '#ccc'
}
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleEditClose}>Cancel</Button>
<Button onClick={handleSaveDescription}>Save</Button>
</DialogActions>
</Dialog>
</Box>
);
};
export default GamesPage;