Files
dndmaster/frontend/src/pages/createCharacter.jsx
T
Marces Zastrow 9c45795e80 Character Creator
Added the character creation page.
2025-01-10 12:05:05 +01:00

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;