+
This commit is contained in:
@@ -10,6 +10,7 @@ import JoinGame from './pages/joinGame';
|
|||||||
import StartGame from './pages/startGame';
|
import StartGame from './pages/startGame';
|
||||||
import Profile from './pages/profile';
|
import Profile from './pages/profile';
|
||||||
import Games from './pages/games.jsx';
|
import Games from './pages/games.jsx';
|
||||||
|
import CreateCharacter from './pages/createCharacter.jsx';
|
||||||
import { UserProvider } from './context/UserContext.jsx';
|
import { UserProvider } from './context/UserContext.jsx';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -64,6 +65,10 @@ function App() {
|
|||||||
path='/games/:gameId'
|
path='/games/:gameId'
|
||||||
element={<Games isLoggedIn={isLoggedIn} />}
|
element={<Games isLoggedIn={isLoggedIn} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path='/create-Character'
|
||||||
|
element={<CreateCharacter isLoggedIn={isLoggedIn} />}
|
||||||
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -0,0 +1,419 @@
|
|||||||
|
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
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Append all form data
|
||||||
|
Object.keys(formData).forEach(key => {
|
||||||
|
formDataToSend.append(key, formData[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Append image if exists
|
||||||
|
if (selectedImage) {
|
||||||
|
formDataToSend.append('image', selectedImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post('http://localhost:5000/games/character/create',
|
||||||
|
formDataToSend,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
navigate(`/games/${gameId}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating character:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
p: 3,
|
||||||
|
background: 'rgba(30, 30, 47, 0.9)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
marginTop: '40px',
|
||||||
|
maxWidth: '800px',
|
||||||
|
margin: '40px auto'
|
||||||
|
}}>
|
||||||
|
<Typography variant="h4" sx={{ mb: 4, color: '#fff', textAlign: 'center' }}>
|
||||||
|
Create New Character
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<Grid2 container spacing={2}>
|
||||||
|
{/* Basic Info */}
|
||||||
|
<Grid2 item xs={12}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Character Name"
|
||||||
|
name="charName"
|
||||||
|
value={formData.charName}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': {
|
||||||
|
borderColor: '#444',
|
||||||
|
},
|
||||||
|
'&:hover fieldset': {
|
||||||
|
borderColor: '#764ACB',
|
||||||
|
},
|
||||||
|
'&.Mui-focused fieldset': {
|
||||||
|
borderColor: '#764ACB',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
'& .MuiInputBase-input': {
|
||||||
|
color: '#fff',
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/* Image Upload */}
|
||||||
|
<Grid2 item xs={12}>
|
||||||
|
<input
|
||||||
|
accept="image/*"
|
||||||
|
type="file"
|
||||||
|
id="image-upload"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleImageChange}
|
||||||
|
/>
|
||||||
|
<label htmlFor="image-upload">
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#764ACB',
|
||||||
|
'&:hover': { backgroundColor: '#9865f7' },
|
||||||
|
mb: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Upload Character Image
|
||||||
|
</Button>
|
||||||
|
</label>
|
||||||
|
{imagePreview && (
|
||||||
|
<Box sx={{ mt: 2, mb: 2 }}>
|
||||||
|
<img
|
||||||
|
src={imagePreview}
|
||||||
|
alt="Preview"
|
||||||
|
style={{
|
||||||
|
maxWidth: '200px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
border: '1px solid #444'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/* Character Details */}
|
||||||
|
<Grid2 container item spacing={2}>
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<FormControl fullWidth sx={{ mb: 2 }}>
|
||||||
|
<InputLabel sx={{ color: '#fff' }}>Race</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="race"
|
||||||
|
value={formData.race}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
color: '#fff',
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: '#444',
|
||||||
|
},
|
||||||
|
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: '#764ACB',
|
||||||
|
},
|
||||||
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: '#764ACB',
|
||||||
|
},
|
||||||
|
'& .MuiSelect-icon': {
|
||||||
|
color: '#fff',
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value="Human">Human</MenuItem>
|
||||||
|
<MenuItem value="Elf">Elf</MenuItem>
|
||||||
|
<MenuItem value="Dwarf">Dwarf</MenuItem>
|
||||||
|
<MenuItem value="Orc">Orc</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel sx={{ color: '#fff' }}>Sex</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="sex"
|
||||||
|
value={formData.sex}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{ color: '#fff', '& fieldset': { borderColor: '#444' } }}
|
||||||
|
>
|
||||||
|
<MenuItem value="Male">Male</MenuItem>
|
||||||
|
<MenuItem value="Female">Female</MenuItem>
|
||||||
|
<MenuItem value="Other">Other</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Age"
|
||||||
|
name="age"
|
||||||
|
type="number"
|
||||||
|
value={formData.age}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Job/Class"
|
||||||
|
name="job"
|
||||||
|
value={formData.job}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Max Health"
|
||||||
|
name="maxHealth"
|
||||||
|
type="number"
|
||||||
|
value={formData.maxHealth}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Max Mana"
|
||||||
|
name="maxMana"
|
||||||
|
type="number"
|
||||||
|
value={formData.maxMana}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Strength"
|
||||||
|
name="strength"
|
||||||
|
type="number"
|
||||||
|
value={formData.strength}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Dexterity"
|
||||||
|
name="dexterity"
|
||||||
|
type="number"
|
||||||
|
value={formData.dexterity}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Agility"
|
||||||
|
name="agility"
|
||||||
|
type="number"
|
||||||
|
value={formData.agility}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Grid2 item xs={12} md={6}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Endurance"
|
||||||
|
name="endurance"
|
||||||
|
type="number"
|
||||||
|
value={formData.endurance}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<Grid2 item xs={12}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Description"
|
||||||
|
name="description"
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
value={formData.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': { borderColor: '#444' },
|
||||||
|
'&:hover fieldset': { borderColor: '#764ACB' },
|
||||||
|
},
|
||||||
|
'& .MuiInputLabel-root': { color: '#fff' },
|
||||||
|
'& .MuiInputBase-input': { color: '#fff' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
fullWidth
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#764ACB',
|
||||||
|
'&:hover': { backgroundColor: '#9865f7' },
|
||||||
|
mt: 3
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Character
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateCharacter;
|
||||||
@@ -100,7 +100,7 @@ const GamesPage = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
component={Link}
|
component={Link}
|
||||||
to="/create-character"
|
to={`/create-character?gameId=${gameId}`}
|
||||||
>
|
>
|
||||||
Create New Character
|
Create New Character
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user