+
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import './App.css';
|
||||
import Header from './components/header';
|
||||
@@ -10,6 +10,7 @@ import JoinGame from './pages/joinGame';
|
||||
import StartGame from './pages/startGame';
|
||||
import Profile from './pages/profile';
|
||||
import Games from './pages/games.jsx';
|
||||
import CreateCharacter from './pages/createCharacter.jsx';
|
||||
import { UserProvider } from './context/UserContext.jsx';
|
||||
|
||||
function App() {
|
||||
@@ -23,13 +24,13 @@ function App() {
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<meta name="P&P-Master" content="WoW. A description."/>
|
||||
<meta name="P&P-Master" content="WoW. A description." />
|
||||
<Router>
|
||||
<div>
|
||||
<Helmet>
|
||||
<Helmet>
|
||||
<title>P&P-Master</title>
|
||||
</Helmet>
|
||||
{popupMessage && <div className="popup">{popupMessage}</div>}
|
||||
{popupMessage && <div className="popup">{popupMessage}</div>}
|
||||
<Header isLoggedIn={isLoggedIn} />
|
||||
|
||||
<Routes>
|
||||
@@ -64,6 +65,10 @@ function App() {
|
||||
path='/games/:gameId'
|
||||
element={<Games isLoggedIn={isLoggedIn} />}
|
||||
/>
|
||||
<Route
|
||||
path='/create-Character'
|
||||
element={<CreateCharacter isLoggedIn={isLoggedIn} />}
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
</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}
|
||||
to="/create-character"
|
||||
to={`/create-character?gameId=${gameId}`}
|
||||
>
|
||||
Create New Character
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user