mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
feat: enhance player model with organizationId and update related API handling
This commit is contained in:
@@ -22,10 +22,11 @@ const SEED_PLAYERS = [
|
|||||||
isNpc: 0,
|
isNpc: 0,
|
||||||
isAdmin: 0,
|
isAdmin: 0,
|
||||||
tags: JSON.stringify(['#Bürger', '#Händler']),
|
tags: JSON.stringify(['#Bürger', '#Händler']),
|
||||||
stats: JSON.stringify({ playtimeHours: 482, level: 45, role: 'Bürger', organizationId: 'org-3' }),
|
stats: JSON.stringify({ playtimeHours: 482, level: 45, role: 'Bürger' }),
|
||||||
minecraftStats: null,
|
minecraftStats: null,
|
||||||
inventory: JSON.stringify([]),
|
inventory: JSON.stringify([]),
|
||||||
storyMarkdown: '# Der Bauplan von V\n\n> "Stein erinnert sich..."'
|
storyMarkdown: '# Der Bauplan von V\n\n> "Stein erinnert sich..."',
|
||||||
|
organizationId: 'org-3'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
uuid: '8984c0b5-d912-4462-b189-c864fba4a1af',
|
uuid: '8984c0b5-d912-4462-b189-c864fba4a1af',
|
||||||
@@ -34,10 +35,11 @@ const SEED_PLAYERS = [
|
|||||||
isNpc: 0,
|
isNpc: 0,
|
||||||
isAdmin: 1, // DrKButz is admin for testing
|
isAdmin: 1, // DrKButz is admin for testing
|
||||||
tags: JSON.stringify(['#Bauunternehmer']),
|
tags: JSON.stringify(['#Bauunternehmer']),
|
||||||
stats: JSON.stringify({ playtimeHours: 120, level: 12, role: 'Unternehmer', organizationId: 'org-4' }),
|
stats: JSON.stringify({ playtimeHours: 120, level: 12, role: 'Unternehmer' }),
|
||||||
minecraftStats: null,
|
minecraftStats: null,
|
||||||
inventory: JSON.stringify([]),
|
inventory: JSON.stringify([]),
|
||||||
storyMarkdown: '# Forschungslogbuch:\n\nSpezialisiert auf...'
|
storyMarkdown: '# Forschungslogbuch:\n\nSpezialisiert auf...',
|
||||||
|
organizationId: 'org-4'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -122,7 +124,8 @@ function setupTables() {
|
|||||||
minecraftStats JSON,
|
minecraftStats JSON,
|
||||||
inventory JSON,
|
inventory JSON,
|
||||||
storyMarkdown TEXT,
|
storyMarkdown TEXT,
|
||||||
discordId VARCHAR(255)
|
discordId VARCHAR(255),
|
||||||
|
organizationId VARCHAR(50)
|
||||||
)`,
|
)`,
|
||||||
`CREATE TABLE IF NOT EXISTS orgs (
|
`CREATE TABLE IF NOT EXISTS orgs (
|
||||||
id VARCHAR(50) PRIMARY KEY,
|
id VARCHAR(50) PRIMARY KEY,
|
||||||
@@ -184,8 +187,8 @@ function seedData() {
|
|||||||
if (!err && rows[0].count === 0) {
|
if (!err && rows[0].count === 0) {
|
||||||
console.log("Seeding Players...");
|
console.log("Seeding Players...");
|
||||||
SEED_PLAYERS.forEach(p => {
|
SEED_PLAYERS.forEach(p => {
|
||||||
pool.query("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
pool.query("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
[p.uuid, p.username, p.isOnline, p.isNpc, p.isAdmin, p.tags, p.stats, p.inventory, p.storyMarkdown, null]);
|
[p.uuid, p.username, p.isOnline, p.isNpc, p.isAdmin, p.tags, p.stats, p.minecraftStats, p.inventory, p.storyMarkdown, null, p.organizationId]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,11 +153,11 @@ app.get('/api/players', (req, res) => {
|
|||||||
const parsed = rows.map(r => ({
|
const parsed = rows.map(r => ({
|
||||||
...r,
|
...r,
|
||||||
tags: JSON.parse(r.tags || '[]'),
|
tags: JSON.parse(r.tags || '[]'),
|
||||||
stats: JSON.parse(r.minecraftStats || '{}'),
|
stats: JSON.parse(r.stats || '{}'),
|
||||||
//stats: JSON.parse(r.stats || '{}'),
|
minecraftStats: r.minecraftStats ? JSON.parse(r.minecraftStats) : undefined,
|
||||||
//minecraftStats: r.minecraftStats ? JSON.parse(r.minecraftStats) : undefined,
|
|
||||||
inventory: JSON.parse(r.inventory || '[]'),
|
inventory: JSON.parse(r.inventory || '[]'),
|
||||||
isOnline: !!r.isOnline
|
isOnline: !!r.isOnline,
|
||||||
|
organizationId: r.organizationId || undefined
|
||||||
}));
|
}));
|
||||||
res.json(parsed);
|
res.json(parsed);
|
||||||
});
|
});
|
||||||
@@ -189,60 +189,11 @@ app.put('/api/players/:uuid', (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updates = req.body;
|
const updates = req.body;
|
||||||
const allowedFields = ['storyMarkdown', 'tags'];
|
const allowedFields = ['storyMarkdown', 'tags', 'organizationId'];
|
||||||
const updateFields = [];
|
const updateFields = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
|
|
||||||
// Handle organizationId specially - it goes into stats JSON
|
// Handle fields
|
||||||
if (updates.organizationId !== undefined) {
|
|
||||||
// First get current player data
|
|
||||||
db.get("SELECT stats FROM players WHERE uuid = ?", [req.params.uuid], (err, row) => {
|
|
||||||
if (err) return res.status(500).json({error: err.message});
|
|
||||||
if (!row) return res.status(404).json({error: 'Spieler nicht gefunden'});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentStats = JSON.parse(row.stats || '{}');
|
|
||||||
const updatedStats = { ...currentStats, organizationId: updates.organizationId };
|
|
||||||
|
|
||||||
updateFields.push('stats = ?');
|
|
||||||
values.push(JSON.stringify(updatedStats));
|
|
||||||
} catch (e) {
|
|
||||||
return res.status(500).json({error: 'Fehler beim Verarbeiten der Stats'});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue with other fields
|
|
||||||
for (const field of allowedFields) {
|
|
||||||
if (updates[field] !== undefined) {
|
|
||||||
if (field === 'tags') {
|
|
||||||
// Tags are stored as JSON
|
|
||||||
updateFields.push(`${field} = ?`);
|
|
||||||
values.push(JSON.stringify(updates[field]));
|
|
||||||
} else {
|
|
||||||
updateFields.push(`${field} = ?`);
|
|
||||||
values.push(updates[field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateFields.length === 0) {
|
|
||||||
return res.status(400).json({error: 'Keine gültigen Felder zum Aktualisieren'});
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = `UPDATE players SET ${updateFields.join(', ')} WHERE uuid = ?`;
|
|
||||||
values.push(req.params.uuid);
|
|
||||||
|
|
||||||
db.run(query, values, function(err) {
|
|
||||||
if (err) {
|
|
||||||
console.error('Error updating player:', err);
|
|
||||||
return res.status(500).json({error: 'Fehler beim Aktualisieren'});
|
|
||||||
}
|
|
||||||
res.json({success: true, message: 'Spieler erfolgreich aktualisiert'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return; // Exit early since we're handling this asynchronously
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other fields normally
|
|
||||||
for (const field of allowedFields) {
|
for (const field of allowedFields) {
|
||||||
if (updates[field] !== undefined) {
|
if (updates[field] !== undefined) {
|
||||||
if (field === 'tags') {
|
if (field === 'tags') {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const PlayerProfile: React.FC = () => {
|
|||||||
|
|
||||||
// Is this the logged-in user's profile?
|
// Is this the logged-in user's profile?
|
||||||
const isOwner = currentUser?.linkedPlayerUuid === player?.uuid;
|
const isOwner = currentUser?.linkedPlayerUuid === player?.uuid;
|
||||||
const playerOrg = player ? dbService.getOrg(player.stats?.organizationId || '') : null;
|
const playerOrg = player ? dbService.getOrg(player.organizationId || '') : null;
|
||||||
|
|
||||||
// Check if player is already linked to anyone in our mock/real DB logic
|
// Check if player is already linked to anyone in our mock/real DB logic
|
||||||
// Since the Player object doesn't expose 'discordId' publicly in types yet (it's hidden in DB),
|
// Since the Player object doesn't expose 'discordId' publicly in types yet (it's hidden in DB),
|
||||||
@@ -44,12 +44,15 @@ const PlayerProfile: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(playerData);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [id, navigate]);
|
}, [id, navigate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!player) return;
|
if (!player) return;
|
||||||
|
|
||||||
|
console.log(player);
|
||||||
|
|
||||||
// Load owned projects
|
// Load owned projects
|
||||||
const allProjects = dbService.getProjects();
|
const allProjects = dbService.getProjects();
|
||||||
const playerProjects = allProjects.filter(p => p.owner === player.username);
|
const playerProjects = allProjects.filter(p => p.owner === player.username);
|
||||||
@@ -105,7 +108,7 @@ const PlayerProfile: React.FC = () => {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Update local state
|
// Update local state
|
||||||
setPlayer(prev => ({ ...prev, stats: { ...prev.stats, organizationId: orgId || undefined } }));
|
setPlayer(prev => ({ ...prev, organizationId: orgId || undefined }));
|
||||||
} else {
|
} else {
|
||||||
alert('Fehler beim Aktualisieren der Zugehörigkeit');
|
alert('Fehler beim Aktualisieren der Zugehörigkeit');
|
||||||
}
|
}
|
||||||
@@ -135,6 +138,7 @@ const PlayerProfile: React.FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(player);
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto animate-in slide-in-from-right-4 duration-300">
|
<div className="max-w-4xl mx-auto animate-in slide-in-from-right-4 duration-300">
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
@@ -201,8 +205,8 @@ const PlayerProfile: React.FC = () => {
|
|||||||
<div className="flex items-center gap-2 text-textMuted">
|
<div className="flex items-center gap-2 text-textMuted">
|
||||||
<Icons.Terminal className="w-4 h-4" />
|
<Icons.Terminal className="w-4 h-4" />
|
||||||
<span className="font-mono text-textMain">
|
<span className="font-mono text-textMain">
|
||||||
{player.minecraftStats?.statistics?.general?.["minecraft:play_time"]
|
{player.stats?.statistics?.general?.["minecraft:play_time"]
|
||||||
? `${Math.round((player.minecraftStats.statistics.general["minecraft:play_time"] || 0) / 20 / 3600)}h`
|
? `${Math.round((player.stats.statistics.general["minecraft:play_time"] || 0) / 20 / 3600)}h`
|
||||||
: `${player.stats?.playtimeHours || 0}h`
|
: `${player.stats?.playtimeHours || 0}h`
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
@@ -210,27 +214,27 @@ const PlayerProfile: React.FC = () => {
|
|||||||
<div className="flex items-center gap-2 text-textMuted">
|
<div className="flex items-center gap-2 text-textMuted">
|
||||||
<Icons.Box className="w-4 h-4" />
|
<Icons.Box className="w-4 h-4" />
|
||||||
<span className="font-mono text-textMain">
|
<span className="font-mono text-textMain">
|
||||||
Lvl {player.minecraftStats?.char?.xpLevel || player.stats?.level || 1}
|
Lvl {player.stats?.char?.xpLevel || player.stats?.level || 1}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{player.minecraftStats && player.minecraftStats.char && player.minecraftStats.statistics && (
|
{player.stats && player.stats.char && player.stats.statistics && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-2 text-textMuted">
|
<div className="flex items-center gap-2 text-textMuted">
|
||||||
<Icons.Shield className="w-4 h-4" />
|
<Icons.Shield className="w-4 h-4" />
|
||||||
<span className="font-mono text-textMain">
|
<span className="font-mono text-textMain">
|
||||||
{player.minecraftStats.char.health}/{player.minecraftStats.char.maxHealth} HP
|
{player.stats.char.health}/{player.stats.char.maxHealth} HP
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-textMuted">
|
<div className="flex items-center gap-2 text-textMuted">
|
||||||
<Icons.Coins className="w-4 h-4" />
|
<Icons.Coins className="w-4 h-4" />
|
||||||
<span className="font-mono text-textMain">
|
<span className="font-mono text-textMain">
|
||||||
{player.minecraftStats.char.foodLevel}/20 Food
|
{player.stats.char.foodLevel}/20 Food
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-textMuted">
|
<div className="flex items-center gap-2 text-textMuted">
|
||||||
<Icons.Hammer className="w-4 h-4" />
|
<Icons.Hammer className="w-4 h-4" />
|
||||||
<span className="font-mono text-textMain">
|
<span className="font-mono text-textMain">
|
||||||
{player.minecraftStats.statistics.general?.["minecraft:mob_kills"] || 0} Kills
|
{player.stats.statistics.general?.["minecraft:mob_kills"] || 0} Kills
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
2
types.ts
2
types.ts
@@ -35,7 +35,6 @@ export interface PlayerStats {
|
|||||||
playtimeHours: number;
|
playtimeHours: number;
|
||||||
level: number;
|
level: number;
|
||||||
role: string;
|
role: string;
|
||||||
organizationId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Player {
|
export interface Player {
|
||||||
@@ -48,6 +47,7 @@ export interface Player {
|
|||||||
storyMarkdown: string;
|
storyMarkdown: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
|
organizationId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CityStats {
|
export interface CityStats {
|
||||||
|
|||||||
Reference in New Issue
Block a user