This commit is contained in:
Marces Zastrow
2025-01-17 13:27:12 +01:00
parent f65c71619e
commit 0e888ca258
6 changed files with 1109 additions and 72 deletions
+6 -1
View File
@@ -13,6 +13,7 @@ import Games from './pages/games.jsx';
import GameMasterPage from './pages/gameMasterPage.jsx';
import CreateItem from './pages/createItem.jsx';
import CreateCharacter from './pages/createCharacter.jsx';
import CreateNpc from './pages/createNpc.jsx';
import { UserProvider } from './context/UserContext.jsx';
function App() {
@@ -79,7 +80,11 @@ function App() {
path='/create-item'
element={<CreateItem isLoggedIn={isLoggedIn} />}
/>
<Route
path='/create-npc'
element={<CreateNpc isLoggedIn={isLoggedIn} />}
/>
</Routes>
</div>
</Router>
+12
View File
@@ -89,6 +89,18 @@ const CreateItem = () => {
return (
<Box sx={{ p: 3, background: 'rgba(30, 30, 47, 0.9)', borderRadius: '8px', marginTop: '40px' }}>
<Button
onClick={() => navigate(`/games/${gameId}/master`)}
variant="contained"
sx={{
backgroundColor: '#764ACB',
'&:hover': { backgroundColor: '#9865f7' },
mb: 3
}}
>
Back to Game
</Button>
<form onSubmit={handleSubmit}>
<Grid2 container spacing={3}>
{/* Left Column - Image and Basic Info */}
+417
View File
@@ -0,0 +1,417 @@
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 } from '@mui/material';
// Import the same icons as 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 SecurityIcon from '@mui/icons-material/Security';
import defaultCharacterImage from '../assets/default-character.png';
const CreateNpc = () => {
const navigate = useNavigate();
const location = useLocation();
const { userId } = useContext(UserContext);
const gameId = new URLSearchParams(location.search).get('gameId');
const [formData, setFormData] = useState({
Name: '',
Race: '',
Sex: '',
Age: '',
Job: '',
Description: '',
MaxHealth: 100,
MaxMana: 100,
Strength: 10,
Dexterity: 10,
Agility: 10,
Endurance: 10,
Allied: 0, // 0 = Allied, 1 = Neutral, 2 = Enemy
Level: 1,
GameID: gameId
});
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();
Object.keys(formData).forEach(key => {
formDataToSend.append(key, formData[key]);
});
if (selectedImage) {
formDataToSend.append('image', selectedImage);
}
try {
const response = await axios.post(
'http://localhost:5000/games/npc/create',
formDataToSend,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
);
if (response.status === 201) {
navigate(`/games/${gameId}/master`);
}
} catch (error) {
console.error('Error creating NPC:', error);
}
};
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}>
{/* NPC Image and Basic Info */}
<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' }}>
{/* Image Upload Section - Similar to CreateCharacter */}
{imagePreview ? (
<Box component="label" htmlFor="image-upload" sx={{ cursor: 'pointer', position: 'relative' }}>
<input
accept="image/*"
type="file"
id="image-upload"
style={{ display: 'none' }}
onChange={handleImageChange}
/>
<CardMedia
component="img"
height="300"
image={imagePreview}
alt="NPC Preview"
sx={{ borderRadius: '3px', objectFit: 'contain', margin: '0 auto', borderBottom: '1px solid #444' }}
/>
</Box>
) : (
<Box
component="label"
htmlFor="image-upload"
sx={{
height: 300,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#2e2e3f',
borderRadius: '3px',
cursor: 'pointer'
}}
>
<input
accept="image/*"
type="file"
id="image-upload"
style={{ display: 'none' }}
onChange={handleImageChange}
/>
<Button
variant="contained"
component="span"
sx={{
backgroundColor: '#764ACB',
'&:hover': { backgroundColor: '#9865f7' }
}}
>
Bild Hochladen
</Button>
</Box>
)}
<CardContent sx={{ p: 3 }}>
<TextField
fullWidth
label="Name des NPCs"
name="Name"
value={formData.Name}
onChange={handleChange}
required
sx={{ ...inputStyles, mb: 2, width: '347px'}}
/>
<Grid2 container spacing={2}>
<Grid2 item xs={6}>
<TextField
fullWidth
label="Alter"
name="Age"
type="number"
value={formData.Age}
onChange={handleChange}
required
sx={{ ...inputStyles, width: '165px' }}
/>
</Grid2>
<Grid2 item xs={6}>
<TextField
fullWidth
label="Rasse"
name="Race"
value={formData.Race}
onChange={handleChange}
required
sx={{ ...inputStyles, width: '165px' }}
/>
</Grid2>
</Grid2>
<Grid2 container spacing={2} sx={{ mt: 1 }}>
<Grid2 item xs={6}>
<FormControl sx={{ width: '165px' }}>
<InputLabel sx={{ color: '#fff' }}>Geschlecht</InputLabel>
<Select
name="Sex"
value={formData.Sex}
onChange={handleChange}
required
sx={{
color: '#fff',
'& .MuiOutlinedInput-notchedOutline': { borderColor: '#444' },
'&:hover .MuiOutlinedInput-notchedOutline': { borderColor: '#764ACB' },
}}
>
<MenuItem value="Male">Männlich</MenuItem>
<MenuItem value="Female">Weiblich</MenuItem>
<MenuItem value="Other">Divers</MenuItem>
</Select>
</FormControl>
</Grid2>
<Grid2 item xs={6}>
<FormControl sx={{ width: '165px' }}>
<InputLabel sx={{ color: '#fff' }}>Status</InputLabel>
<Select
name="Allied"
value={formData.Allied}
onChange={handleChange}
required
sx={{
color: '#fff',
'& .MuiOutlinedInput-notchedOutline': { borderColor: '#444' },
'&:hover .MuiOutlinedInput-notchedOutline': { borderColor: '#764ACB' },
}}
>
<MenuItem value={0}>Verbündet</MenuItem>
<MenuItem value={1}>Neutral</MenuItem>
<MenuItem value={2}>Feindlich</MenuItem>
</Select>
</FormControl>
</Grid2>
</Grid2>
<Grid2 container spacing={2} sx={{ mt: 1 }}>
<Grid2 item xs={6}>
<TextField
fullWidth
label="Beruf/Klasse"
name="Job"
value={formData.Job}
onChange={handleChange}
required
sx={{ ...inputStyles, width: '165px' }}
/>
</Grid2>
<Grid2 item xs={6}>
<TextField
fullWidth
label="Level"
name="Level"
type="number"
value={formData.Level}
onChange={handleChange}
required
sx={{ ...inputStyles, width: '165px' }}
/>
</Grid2>
</Grid2>
</CardContent>
</Card>
</Box>
</Grid2>
{/* Right Column - Stats and Description */}
<Grid2 item xs={12} md={8}>
{/* Health/Mana Bars */}
<Box sx={{ display: 'flex', gap: 2, mb: 4, width: '810px' }}>
<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>Maximale Gesundheit</span>
</Typography>
<TextField
fullWidth
name="MaxHealth"
type="number"
value={formData.MaxHealth}
onChange={handleChange}
required
sx={inputStyles}
/>
</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>Maximales Mana</span>
</Typography>
<TextField
fullWidth
name="MaxMana"
type="number"
value={formData.MaxMana}
onChange={handleChange}
required
sx={inputStyles}
/>
</Box>
</Box>
{/* Description */}
<Box sx={{ mb: 4, width: '810px' }}>
<Typography variant="h5" sx={{ color: '#fff', mb: 2 }}>Beschreibung</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={inputStyles}
/>
</Box>
</Box>
{/* Stats */}
<Box sx={{ mb: 4, width: '810px' }}>
<Typography variant="h5" sx={{ color: '#fff', mb: 2 }}>Attribute</Typography>
<Box sx={{
backgroundColor: '#2e2e3f',
p: 2,
borderRadius: '8px',
border: '1px solid #444'
}}>
<Grid2 container spacing={2} justifyContent="space-between">
<Grid2 item>
<TextField
label="Stärke"
name="Strength"
type="number"
value={formData.Strength}
onChange={handleChange}
sx={{ ...inputStyles, width: '180px' }}
/>
</Grid2>
<Grid2 item>
<TextField
label="Geschicklichkeit"
name="Dexterity"
type="number"
value={formData.Dexterity}
onChange={handleChange}
sx={{ ...inputStyles, width: '180px' }}
/>
</Grid2>
<Grid2 item>
<TextField
label="Beweglichkeit"
name="Agility"
type="number"
value={formData.Agility}
onChange={handleChange}
sx={{ ...inputStyles, width: '180px' }}
/>
</Grid2>
<Grid2 item>
<TextField
label="Ausdauer"
name="Endurance"
type="number"
value={formData.Endurance}
onChange={handleChange}
sx={{ ...inputStyles, width: '180px' }}
/>
</Grid2>
</Grid2>
</Box>
</Box>
{/* Button Container */}
<Box sx={{
display: 'flex',
justifyContent: 'space-between',
gap: 2,
width: '800px'
}}>
<Button
onClick={() => navigate(`/games/${gameId}/master`)}
variant="contained"
sx={{
backgroundColor: '#764ACB',
'&:hover': { backgroundColor: '#9865f7' },
width: '200px'
}}
>
Zurück ohne Speichern
</Button>
<Button
type="submit"
variant="contained"
sx={{
backgroundColor: '#764ACB',
'&:hover': { backgroundColor: '#9865f7' },
width: '580px'
}}
>
NPC Erstellen
</Button>
</Box>
</Grid2>
</Grid2>
</form>
</Box>
);
};
export default CreateNpc;
+482 -59
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useContext } from 'react';
import { Link, useParams } from 'react-router-dom';
import { UserContext } from '../context/UserContext';
import axios from 'axios';
import { Box, Typography, Grid2, Card, CardContent, CardMedia, Button, IconButton } from '@mui/material';
import { Box, Typography, Grid2, Card, CardContent, CardMedia, Button, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, TextField, FormControl, InputLabel, Select, MenuItem, Tooltip } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import defaultCharacterImage from '../assets/default-character.png';
@@ -14,6 +14,12 @@ const GameMasterPage = () => {
const [playerCharacters, setPlayerCharacters] = useState([]);
const [npcs, setNpcs] = useState([]);
const [items, setItems] = useState([]);
const [editModalOpen, setEditModalOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const [editType, setEditType] = useState(''); // 'item', 'npc', or 'character'
const [isEditing, setIsEditing] = useState(false);
const [allOwners, setAllOwners] = useState([]);
const [formData, setFormData] = useState(null);
// Fix useEffect data fetching
useEffect(() => {
@@ -39,10 +45,400 @@ const GameMasterPage = () => {
};
fetchData();
const interval = setInterval(fetchData, 5000);
return () => clearInterval(interval);
let interval;
if (!isEditing && !editModalOpen) { // Add editModalOpen check
interval = setInterval(fetchData, 10000);
}
return () => {
if (interval) clearInterval(interval);
};
}, [gameId, isEditing, editModalOpen]); // Add editModalOpen dependency
useEffect(() => {
const fetchOwners = async () => {
try {
// Only fetch player characters
const pcsResponse = await axios.get(`http://localhost:5000/games/${gameId}/playerchars`);
const pcs = pcsResponse.data.map(pc => ({
id: pc.CharID,
name: pc.CharName,
type: 'Player Character'
}));
setAllOwners([
{ id: null, name: 'Unassigned', type: 'None' },
...pcs
]);
} catch (error) {
console.error('Error fetching potential owners:', error);
}
};
fetchOwners();
}, [gameId]);
const handleItemClick = (item) => {
setSelectedItem(item);
setFormData({...item});
setEditType('item');
setEditModalOpen(true);
setIsEditing(true);
};
const handleNpcClick = (npc) => {
setSelectedItem(npc);
setFormData({...npc});
setEditType('npc');
setEditModalOpen(true);
setIsEditing(true);
};
const handleCharacterClick = (character) => {
setSelectedItem(character);
setFormData({...character});
setEditType('character');
setEditModalOpen(true);
setIsEditing(true);
};
const handleUpdate = async () => {
try {
let response;
if (editType === 'item') {
response = await axios.put(`http://localhost:5000/games/item/${selectedItem.ItemID}`, formData);
} else if (editType === 'npc') {
response = await axios.put(`http://localhost:5000/games/npc/${selectedItem.NPCID}`, formData);
} else if (editType === 'character') {
response = await axios.put(`http://localhost:5000/games/character/${selectedItem.CharID}`, formData);
}
if (response.status === 200) {
setEditModalOpen(false);
setIsEditing(false);
setFormData(null);
fetchData();
}
} catch (error) {
console.error('Error updating:', error);
}
};
const handleModalClose = () => {
setEditModalOpen(false);
setIsEditing(false);
setFormData(null);
};
// First, define the common input styles at component level
const commonStyles = {
dialog: {
'& .MuiPaper-root': {
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
borderRadius: '8px',
minWidth: '500px'
}
},
input: {
'& .MuiOutlinedInput-root': {
color: '#fff',
'& fieldset': { borderColor: '#444' },
'&:hover fieldset': { borderColor: '#764ACB' },
'&.Mui-focused fieldset': { borderColor: '#764ACB' },
},
'& .MuiInputLabel-root': {
color: '#fff',
'&.Mui-focused': {
color: '#764ACB'
}
},
'& .MuiInputBase-input': { color: '#fff' }
},
select: {
color: '#fff',
'.MuiInputLabel-root': {
color: '#fff',
'&.Mui-focused': {
color: '#764ACB'
}
},
'.MuiSelect-select': {
color: '#fff',
backgroundColor: '#1e1e2f'
},
'.MuiOutlinedInput-root': {
color: '#fff',
backgroundColor: '#1e1e2f',
'& fieldset': { borderColor: '#444' },
'&:hover fieldset': { borderColor: '#764ACB' },
'&.Mui-focused fieldset': { borderColor: '#764ACB' }
},
'.MuiSelect-icon': { color: '#fff' }
},
menuItem: {
backgroundColor: '#1e1e2f',
color: '#fff',
'&:hover': {
backgroundColor: '#2e2e3f'
},
'&.Mui-selected': {
backgroundColor: '#764ACB',
'&:hover': {
backgroundColor: '#9865f7'
}
}
}
};
// Update the EditModal component
const EditModal = () => {
const nonEditableFields = [
'GameID', 'CharID', 'ItemID', 'NPCID', 'Img', 'img',
'GameId', 'NpcID', 'itemID', 'PlayerID', 'OwnerName'
];
const renderField = (key) => {
if (key === 'Allied') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel>Status</InputLabel>
<Select
value={formData[key] || 0}
label="Status"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
>
<MenuItem value={0}>Allied</MenuItem>
<MenuItem value={1}>Neutral</MenuItem>
<MenuItem value={2}>Enemy</MenuItem>
</Select>
</FormControl>
);
}
if (key === 'Sex') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel>Sex</InputLabel>
<Select
value={formData[key] || ''}
label="Sex"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
>
<MenuItem value="Male">Male</MenuItem>
<MenuItem value="Female">Female</MenuItem>
<MenuItem value="Other">Other</MenuItem>
</Select>
</FormControl>
);
}
if (key === 'Rarity') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel>Rarity</InputLabel>
<Select
value={formData[key] || 1}
label="Rarity"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
>
<MenuItem value={1}>Poor</MenuItem>
<MenuItem value={2}>Common</MenuItem>
<MenuItem value={3}>Uncommon</MenuItem>
<MenuItem value={4}>Rare</MenuItem>
<MenuItem value={5}>Epic</MenuItem>
<MenuItem value={6}>Legendary</MenuItem>
<MenuItem value={7}>Artifact</MenuItem>
</Select>
</FormControl>
);
}
if (key === 'Type') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel>Type</InputLabel>
<Select
value={formData[key] || ''}
label="Type"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
>
<MenuItem value="Weapon">Weapon</MenuItem>
<MenuItem value="Armor">Armor</MenuItem>
<MenuItem value="Consumable">Consumable</MenuItem>
<MenuItem value="Quest">Quest Item</MenuItem>
<MenuItem value="Other">Other</MenuItem>
</Select>
</FormControl>
);
}
if (key === 'Art') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel>Art</InputLabel>
<Select
value={formData[key] || ''}
label="Art"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
>
<MenuItem value="Sword">Sword</MenuItem>
<MenuItem value="Axe">Axe</MenuItem>
<MenuItem value="Bow">Bow</MenuItem>
<MenuItem value="Shield">Shield</MenuItem>
<MenuItem value="Staff">Staff</MenuItem>
<MenuItem value="Potion">Potion</MenuItem>
<MenuItem value="Other">Other</MenuItem>
</Select>
</FormControl>
);
}
if (key === 'OwnerID') {
return (
<FormControl fullWidth key={key} sx={{ mb: 2, ...commonStyles.select }}>
<InputLabel sx={{ color: '#fff' }}>Owner</InputLabel>
<Select
value={formData[key] || ''}
label="Owner"
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
sx={{
color: '#fff',
'& .MuiOutlinedInput-notchedOutline': { borderColor: '#444' },
'&:hover .MuiOutlinedInput-notchedOutline': { borderColor: '#764ACB' },
}}
>
{allOwners.map((owner) => (
<MenuItem
key={owner.id}
value={owner.id || ''}
sx={{
...commonStyles.menuItem,
display: 'flex',
justifyContent: 'space-between'
}}
>
<span>{owner.name}</span>
{owner.type !== 'None' && (
<Typography
variant="caption"
sx={{
ml: 2,
color: '#764ACB'
}}
>
{owner.type}
</Typography>
)}
</MenuItem>
))}
</Select>
</FormControl>
);
}
return (
<TextField
key={key}
fullWidth
label={key}
value={formData[key] || ''}
onChange={(e) => setFormData({
...formData,
[key]: e.target.value
})}
sx={{ ...commonStyles.input, mb: 2 }}
/>
);
};
return (
<Dialog
open={editModalOpen}
onClose={handleModalClose}
sx={commonStyles.dialog}
>
<DialogTitle sx={{
borderBottom: '1px solid #444',
color: '#fff',
backgroundColor: '#2e2e3f',
padding: '16px 24px'
}}>
{editType === 'item' ? 'Edit Item' : editType === 'npc' ? 'Edit NPC' : 'Edit Character'}
</DialogTitle>
<DialogContent sx={{
p: 3,
backgroundColor: '#1e1e2f'
}}>
{formData && (
<Box sx={{ p: 2 }}>
{Object.keys(formData)
.filter(key => !nonEditableFields.includes(key))
.map(key => renderField(key))}
</Box>
)}
</DialogContent>
<DialogActions sx={{
borderTop: '1px solid #444',
p: 2,
backgroundColor: '#2e2e3f',
'& .MuiButton-root': {
textTransform: 'none',
fontSize: '1rem',
padding: '6px 16px'
}
}}>
<Button
onClick={handleModalClose}
variant="outlined"
sx={{
color: '#fff',
borderColor: '#444',
'&:hover': {
borderColor: '#764ACB',
backgroundColor: 'rgba(118, 74, 203, 0.1)'
}
}}
>
Cancel
</Button>
<Button
onClick={handleUpdate}
variant="contained"
sx={{
backgroundColor: '#764ACB',
color: '#fff',
'&:hover': {
backgroundColor: '#9865f7'
}
}}
>
Save Changes
</Button>
</DialogActions>
</Dialog>
);
};
const Section = ({ title, items, createPath, createText }) => (
<Box sx={{
mb: 4,
@@ -70,49 +466,59 @@ const GameMasterPage = () => {
<Grid2 container spacing={2}>
{items.map((item, index) => (
<Grid2 item xs={12} sm={6} md={3} key={index}>
<Card sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
height: '95%', // Reduced height
border: '1px solid #444',
}}>
<Box sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '8px'
}}>
<CardMedia
component="img"
height="135" // Reduced from 140
image={item.Img || defaultItemImage}
alt={item.ItemName}
className={`rarity-${item.Rarity} rarity-image`}
sx={{
objectFit: 'contain',
width: '128px'
}}
/>
</Box>
<CardContent>
<Typography
variant="h6"
component="div"
className={`rarity-name-${item.Rarity}`}
>
{item.ItemName}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Type: {item.Type}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Value: {item.GoldValue}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Owner: {item.OwnerName || 'Unassigned'}
</Typography>
</CardContent>
</Card>
<Tooltip title={item.Abilities || 'No description available'} arrow>
<Card
onClick={() => handleItemClick(item)}
sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
height: '95%',
cursor: 'pointer',
transition: 'border-color 0.2s',
'&:hover': {
borderColor: '#764ACB'
}
}}
>
<Box sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '8px'
}}>
<CardMedia
component="img"
height="135" // Reduced from 140
image={item.img || defaultItemImage} // Changed from item.img to item.Img
alt={item.ItemName}
className={`rarity-${item.Rarity} rarity-image`}
sx={{
objectFit: 'contain',
width: '128px'
}}
/>
</Box>
<CardContent>
<Typography
variant="h6"
component="div"
className={`rarity-name-${item.Rarity}`}
>
{item.ItemName}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Type: {item.Type}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Value: {item.GoldValue}
</Typography>
<Typography variant="body2" sx={{ color: '#bbb' }}>
Owner: {item.OwnerName || 'Unassigned'}
</Typography>
</CardContent>
</Card>
</Tooltip>
</Grid2>
))}
</Grid2>
@@ -147,12 +553,20 @@ const GameMasterPage = () => {
<Grid2 container spacing={2}>
{playerCharacters.map((character, index) => (
<Grid2 item xs={12} sm={6} md={3} key={index}>
<Card sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
height: '100%'
}}>
<Card
onClick={() => handleCharacterClick(character)}
sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
height: '100%',
cursor: 'pointer',
transition: 'border-color 0.2s',
'&:hover': {
borderColor: '#764ACB'
}
}}
>
<Box sx={{
display: 'flex',
justifyContent: 'center',
@@ -215,12 +629,20 @@ const GameMasterPage = () => {
<Grid2 container spacing={2}>
{npcs.map((npc, index) => (
<Grid2 item xs={12} sm={6} md={3} key={index}>
<Card sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
height: '100%'
}}>
<Card
onClick={() => handleNpcClick(npc)}
sx={{
backgroundColor: '#1e1e2f',
color: '#fff',
border: '1px solid #444',
height: '100%',
cursor: 'pointer',
transition: 'border-color 0.2s',
'&:hover': {
borderColor: '#764ACB'
}
}}
>
<Box sx={{
display: 'flex',
justifyContent: 'center',
@@ -231,7 +653,7 @@ const GameMasterPage = () => {
component="img"
height="140"
image={npc.Img || defaultCharacterImage}
alt={npc.CharName}
alt={npc.Name || 'NPC'}
sx={{
objectFit: 'contain',
width: '140px',
@@ -276,6 +698,7 @@ const GameMasterPage = () => {
createPath="/create-item"
createText="Create Item"
/>
<EditModal />
</Box>
);
};