9c45795e80
Added the character creation page.
424 lines
15 KiB
React
424 lines
15 KiB
React
import React, { useState, useContext } from 'react';
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
import { UserContext } from '../context/UserContext';
|
|
import axios from 'axios';
|
|
import { Box, TextField, Button, Typography, Select, MenuItem, FormControl, InputLabel, Grid2, Card, CardContent, CardMedia, Menu } from '@mui/material';
|
|
|
|
// Import the same icons as in games.jsx
|
|
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';
|
|
|
|
const CreateCharacter = () => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const { userId } = useContext(UserContext);
|
|
const gameId = new URLSearchParams(location.search).get('gameId');
|
|
|
|
const [formData, setFormData] = useState({
|
|
charName: '',
|
|
race: '',
|
|
sex: '',
|
|
age: '',
|
|
job: '',
|
|
description: '',
|
|
maxHealth: 100,
|
|
maxMana: 100,
|
|
strength: 10,
|
|
dexterity: 10,
|
|
agility: 10,
|
|
endurance: 10
|
|
});
|
|
|
|
const [selectedImage, setSelectedImage] = useState(null);
|
|
const [imagePreview, setImagePreview] = useState(null);
|
|
|
|
const handleChange = (e) => {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
};
|
|
|
|
const handleImageChange = (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
setSelectedImage(file);
|
|
setImagePreview(URL.createObjectURL(file));
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
const formDataToSend = new FormData();
|
|
|
|
// Add all form data
|
|
Object.keys(formData).forEach(key => {
|
|
formDataToSend.append(key, formData[key]);
|
|
});
|
|
|
|
// Add image if selected
|
|
if (selectedImage) {
|
|
formDataToSend.append('image', selectedImage);
|
|
}
|
|
|
|
// Add gameId and playerId
|
|
formDataToSend.append('gameId', gameId);
|
|
formDataToSend.append('playerId', userId);
|
|
|
|
try {
|
|
const response = await axios.post(
|
|
'http://localhost:5000/games/character/create',
|
|
formDataToSend,
|
|
{
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data'
|
|
}
|
|
}
|
|
);
|
|
|
|
if (response.status === 201) {
|
|
// Redirect to the game page after successful character creation
|
|
navigate(`/games/${gameId}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error creating character:', error);
|
|
// You might want to show an error message to the user here
|
|
}
|
|
};
|
|
|
|
const inputStyles = {
|
|
'& .MuiOutlinedInput-root': {
|
|
'& fieldset': { borderColor: '#444' },
|
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
|
'&.Mui-focused fieldset': { borderColor: '#764ACB' },
|
|
},
|
|
'& .MuiInputLabel-root': { color: '#fff' },
|
|
'& .MuiInputBase-input': { color: '#fff' }
|
|
};
|
|
|
|
return (
|
|
<Box sx={{ p: 3, background: 'rgba(30, 30, 47, 0.9)', borderRadius: '8px', marginTop: '40px' }}>
|
|
<form onSubmit={handleSubmit}>
|
|
<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' }}>
|
|
{imagePreview ? (
|
|
<Box
|
|
component="label"
|
|
htmlFor="image-upload"
|
|
sx={{
|
|
cursor: 'pointer',
|
|
position: 'relative',
|
|
'&:hover::after': {
|
|
content: '"Change Image"',
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
color: '#fff',
|
|
fontSize: '1.2rem',
|
|
borderRadius: '3px'
|
|
}
|
|
}}
|
|
>
|
|
<input
|
|
accept="image/*"
|
|
type="file"
|
|
id="image-upload"
|
|
style={{ display: 'none' }}
|
|
onChange={handleImageChange}
|
|
/>
|
|
<CardMedia
|
|
component="img"
|
|
height="300"
|
|
image={imagePreview}
|
|
alt="Character Preview"
|
|
sx={{ borderRadius: '3px', objectFit: 'cover', margin: '0 auto' }}
|
|
/>
|
|
</Box>
|
|
) : (
|
|
<Box
|
|
sx={{
|
|
height: 300,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: '#2e2e3f',
|
|
borderRadius: '3px',
|
|
cursor: 'pointer'
|
|
}}
|
|
component="label"
|
|
htmlFor="image-upload"
|
|
>
|
|
<input
|
|
accept="image/*"
|
|
type="file"
|
|
id="image-upload"
|
|
style={{ display: 'none' }}
|
|
onChange={handleImageChange}
|
|
/>
|
|
<Button
|
|
variant="contained"
|
|
component="span"
|
|
sx={{
|
|
backgroundColor: '#764ACB',
|
|
'&:hover': { backgroundColor: '#9865f7' }
|
|
}}
|
|
>
|
|
Upload Character Image
|
|
</Button>
|
|
</Box>
|
|
)}
|
|
<CardContent sx={{
|
|
padding: '4px',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center'
|
|
}}>
|
|
<TextField
|
|
label="Character Name"
|
|
name="charName"
|
|
value={formData.charName}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{
|
|
mb: 4,
|
|
width: '315px',
|
|
'& .MuiOutlinedInput-root': {
|
|
'& fieldset': { borderColor: '#444' },
|
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
|
},
|
|
'& .MuiInputLabel-root': { color: '#fff' },
|
|
'& .MuiInputBase-input': { color: '#fff', textAlign: 'center' }
|
|
}}
|
|
/>
|
|
|
|
<Grid2 container spacing={2} sx={{ maxWidth: '600px', justifyContent: 'center' }}>
|
|
<Grid2 item xs={5}>
|
|
<TextField
|
|
fullWidth
|
|
label="Age"
|
|
name="age"
|
|
type="number"
|
|
value={formData.age}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{ ...inputStyles, width: '150px' }}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 item xs={5}>
|
|
<TextField
|
|
fullWidth
|
|
label="Race"
|
|
name="race"
|
|
value={formData.race}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{ ...inputStyles, width: '150px' }}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 item xs={5}>
|
|
<FormControl fullWidth>
|
|
<InputLabel sx={{ color: '#fff' }}>Sex</InputLabel>
|
|
<Select
|
|
name="sex"
|
|
value={formData.sex}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{
|
|
color: '#fff',
|
|
'& .MuiOutlinedInput-notchedOutline': { borderColor: '#444' },
|
|
'&:hover .MuiOutlinedInput-notchedOutline': { borderColor: '#764ACB' },
|
|
width: '150px'
|
|
}}
|
|
>
|
|
<MenuItem value="Male">Male</MenuItem>
|
|
<MenuItem value="Female">Female</MenuItem>
|
|
<MenuItem value="Other">Other</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
</Grid2>
|
|
<Grid2 item xs={5}>
|
|
<TextField
|
|
fullWidth
|
|
label="Job/Class"
|
|
name="job"
|
|
value={formData.job}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{ ...inputStyles, width: '150px' }}
|
|
/>
|
|
</Grid2>
|
|
</Grid2>
|
|
</CardContent>
|
|
</Card>
|
|
</Box>
|
|
</Grid2 >
|
|
|
|
<Grid2 item xs={12} md={8}>
|
|
{/* Health/Mana Bars - Keep existing */}
|
|
<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>Max Health</span>
|
|
</Typography>
|
|
<TextField
|
|
fullWidth
|
|
name="maxHealth"
|
|
type="number"
|
|
value={formData.maxHealth}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{
|
|
'& .MuiOutlinedInput-root': {
|
|
'& fieldset': { borderColor: '#444' },
|
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
|
},
|
|
'& .MuiInputBase-input': { color: '#fff' }
|
|
}}
|
|
/>
|
|
</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>Max Mana</span>
|
|
</Typography>
|
|
<TextField
|
|
fullWidth
|
|
name="maxMana"
|
|
type="number"
|
|
value={formData.maxMana}
|
|
onChange={handleChange}
|
|
required
|
|
sx={{
|
|
'& .MuiOutlinedInput-root': {
|
|
'& fieldset': { borderColor: '#444' },
|
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
|
},
|
|
'& .MuiInputBase-input': { color: '#fff' }
|
|
}}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Description */}
|
|
<Box sx={{ mb: 4 }}>
|
|
<Typography variant="h5" sx={{ color: '#fff', mb: 2 }}>Description</Typography>
|
|
<Box sx={{ backgroundColor: '#2e2e3f', p: 2, borderRadius: '8px', border: '1px solid #444' }}>
|
|
<TextField
|
|
fullWidth
|
|
multiline
|
|
rows={4}
|
|
name="description"
|
|
value={formData.description}
|
|
onChange={handleChange}
|
|
sx={{
|
|
'& .MuiOutlinedInput-root': {
|
|
'& fieldset': { borderColor: '#444' },
|
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
|
},
|
|
'& .MuiInputBase-input': { color: '#fff' }
|
|
}}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Stats Grid */}
|
|
<Box sx={{ mb: 4 }}>
|
|
<Typography variant="h5" sx={{ color: '#fff', mb: 2 }}>Character Stats</Typography>
|
|
<Box sx={{
|
|
backgroundColor: '#2e2e3f',
|
|
p: 2,
|
|
borderRadius: '8px',
|
|
border: '1px solid #444',
|
|
maxWidth: '800px',
|
|
margin: '0 auto'
|
|
}}>
|
|
<Grid2 container spacing={3} justifyContent="center">
|
|
|
|
<Grid2 item xs={10} md={2}>
|
|
<TextField
|
|
fullWidth
|
|
label="Strength"
|
|
name="strength"
|
|
type="number"
|
|
value={formData.strength}
|
|
onChange={handleChange}
|
|
sx={{ ...inputStyles }}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 item xs={10} md={2}>
|
|
<TextField
|
|
fullWidth
|
|
label="Dexterity"
|
|
name="dexterity"
|
|
type="number"
|
|
value={formData.dexterity}
|
|
onChange={handleChange}
|
|
sx={{ ...inputStyles }}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 item xs={10} md={2}>
|
|
<TextField
|
|
fullWidth
|
|
label="Agility"
|
|
name="agility"
|
|
type="number"
|
|
value={formData.agility}
|
|
onChange={handleChange}
|
|
sx={{ ...inputStyles }}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 item xs={10} md={2}>
|
|
<TextField
|
|
fullWidth
|
|
label="Endurance"
|
|
name="endurance"
|
|
type="number"
|
|
value={formData.endurance}
|
|
onChange={handleChange}
|
|
sx={{ ...inputStyles }}
|
|
/>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Submit Button */}
|
|
<Button
|
|
type="submit"
|
|
variant="contained"
|
|
fullWidth
|
|
sx={{
|
|
backgroundColor: '#764ACB',
|
|
'&:hover': { backgroundColor: '#9865f7' },
|
|
mt: 3
|
|
}}
|
|
>
|
|
Create Character
|
|
</Button>
|
|
</Grid2>
|
|
</Grid2 >
|
|
</form >
|
|
</Box >
|
|
);
|
|
};
|
|
|
|
export default CreateCharacter;
|