e2512637b3
jhkj
326 lines
12 KiB
React
326 lines
12 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 HeartIcon from '@mui/icons-material/Favorite';
|
|
import WaterDropIcon from '@mui/icons-material/WaterDrop';
|
|
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
|
|
|
|
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: '#9865f7'
|
|
}
|
|
}}
|
|
component={Link}
|
|
to="/create-character"
|
|
>
|
|
Create New Character
|
|
</Button>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box sx={{ p: 3, background: 'rgba(30, 30, 47, 0.9)', borderRadius: '8px', marginTop: '40px' }}>
|
|
<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: '400px', backgroundColor: '#1e1e2f', color: '#fff', height: '635px' }}>
|
|
<CardMedia
|
|
component="img"
|
|
height="300"
|
|
image={character.Img || defaultCharacterImage}
|
|
alt={character.CharName}
|
|
sx={{ borderRadius: '3px', objectFit: 'cover', margin: '0 auto' }}
|
|
/>
|
|
<CardContent sx={{padding: '4px'}}>
|
|
<Typography variant="h4" sx={{ mb: 4, color: '#fff', textAlign: 'center'}}>{character.CharName}</Typography>
|
|
<Grid2 container spacing={2} sx={{ padding: '4px'}}>
|
|
<CardContent sx={{}}>
|
|
<Typography variant='h6'><KeyboardDoubleArrowUpIcon sx={{ color: '#fff' }} /> <strong>Level:</strong> {character.Level}</Typography>
|
|
</CardContent>
|
|
<CardContent sx={{}}>
|
|
<Typography variant="h6"><PaidIcon sx={{ color: 'gold' }} /> <strong>Gold:</strong> {character.Gold}</Typography>
|
|
</CardContent>
|
|
</Grid2>
|
|
</CardContent>
|
|
|
|
<Grid2 container spacing={2} sx={{ padding: '8px', borderTop: '1px solid #444' }}>
|
|
<CardContent sx={{borderRight: '3px solid #444'}}>
|
|
<Typography variant="h6"><CalendarTodayIcon sx={{ color: '#C0C0C0' }} /> <strong>Age:</strong> {character.Age}</Typography>
|
|
<Typography variant="h6"><PetsIcon sx={{ color: '#8B4513' }} /> <strong>Race:</strong> {character.Race}</Typography>
|
|
<Typography variant="h6"><WcIcon className="gender-icon" sx={{ fontSize: 'inherit' }} /> <strong>Sex:</strong> {character.Sex}</Typography>
|
|
<Typography variant="h6"><WorkIcon sx={{ color: '#CD7F32' }} /> <strong>Job:</strong> {character.Job}</Typography>
|
|
</CardContent>
|
|
<CardContent>
|
|
<Typography variant="h6"><strong>Strength:</strong> {character.Strength}</Typography>
|
|
<Typography variant="h6"><strong>Dexterity:</strong> {character.Dexterity}</Typography>
|
|
<Typography variant="h6"><strong>Agility:</strong> {character.Agility}</Typography>
|
|
<Typography variant="h6"><strong>Endurance:</strong> {character.Endurance}</Typography>
|
|
</CardContent>
|
|
</Grid2>
|
|
</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' }}>
|
|
<HeartIcon sx={{ color: "red" }} />
|
|
<span>Health</span>
|
|
<span>{character.currentHealth}/{character.maxHealth}</span>
|
|
</Typography>
|
|
<Box className="bar-bg-health">
|
|
<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' }}>
|
|
<WaterDropIcon sx={{ color: 'blue' }} />
|
|
<span>Mana</span>
|
|
<span>{character.currentMana}/{character.maxMana}</span>
|
|
</Typography>
|
|
<Box className="bar-bg-mana">
|
|
<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',
|
|
'&:hover': {
|
|
backgroundColor: '#9865f7'
|
|
}
|
|
}}
|
|
>
|
|
<EditIcon />
|
|
</IconButton>
|
|
</Box>
|
|
<Box sx={{ height: '115px', 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={{
|
|
height: '250px',
|
|
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' }}>
|
|
<Box sx={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: '8px'
|
|
}}>
|
|
<CardMedia
|
|
component="img"
|
|
height="128px"
|
|
width="128px"
|
|
image={item.Img || defaultItemImage}
|
|
alt={item.ItemName}
|
|
className={`rarity-${item.Rarity} rarity-image`}
|
|
sx={{
|
|
objectFit: 'contain',
|
|
width: '128px'
|
|
}}
|
|
/>
|
|
</Box>
|
|
<CardContent>
|
|
<Typography
|
|
variant="h6"
|
|
className={`rarity-name-${item.Rarity}`}
|
|
>
|
|
{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;
|