Skip to content

Commit 1d29904

Browse files
committed
feat: Add Team API for cross-device persistence
- GET /api/team - get user's battle team - POST /api/team - set entire team (max 3) - PATCH /api/team/heal - heal all team members - PATCH /api/team/:pokemonId - update HP after battle - DELETE /api/team/:pokemonId - remove from team - Added team API methods to client.js - Created D1 migration for team table - Migration executed on remote DB All 180 tests passing
1 parent 2c3ec24 commit 1d29904

3 files changed

Lines changed: 156 additions & 0 deletions

File tree

src/api/client.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,38 @@ class PokemonAPI {
7575
}
7676
// =========================
7777

78+
// ===== TEAM API =====
79+
async getTeam() {
80+
return this.request('/api/team');
81+
}
82+
83+
async setTeam(teamData) {
84+
return this.request('/api/team', {
85+
method: 'POST',
86+
body: JSON.stringify(teamData),
87+
});
88+
}
89+
90+
async healTeam() {
91+
return this.request('/api/team/heal', {
92+
method: 'PATCH',
93+
});
94+
}
95+
96+
async updateTeamMemberHP(pokemonId, currentHP) {
97+
return this.request(`/api/team/${pokemonId}`, {
98+
method: 'PATCH',
99+
body: JSON.stringify({ currentHP }),
100+
});
101+
}
102+
103+
async removeFromTeam(pokemonId) {
104+
return this.request(`/api/team/${pokemonId}`, {
105+
method: 'DELETE',
106+
});
107+
}
108+
// ====================
109+
78110
// ===== POKEMON CREATOR =====
79111
async generatePokemon(data) {
80112
return this.request('/api/pokemon/generate', {

worker/index.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,113 @@ app.delete('/api/caught/:id', async (c) => {
263263
}
264264
});
265265

266+
// ===== TEAM API - Cross-device persistence =====
267+
// Get user's battle team
268+
app.get('/api/team', async (c) => {
269+
try {
270+
const { results } = await c.env.DB.prepare(
271+
'SELECT * FROM team ORDER BY position ASC'
272+
).all();
273+
return c.json(results);
274+
} catch (error) {
275+
// If table doesn't exist yet, return empty array
276+
if (error.message.includes('no such table')) {
277+
return c.json([]);
278+
}
279+
return c.json({ error: error.message }, 500);
280+
}
281+
});
282+
283+
// Set user's battle team (replaces entire team)
284+
app.post('/api/team', async (c) => {
285+
try {
286+
const teamData = await c.req.json();
287+
288+
// Clear existing team
289+
await c.env.DB.prepare('DELETE FROM team').run();
290+
291+
// Insert new team members
292+
for (let i = 0; i < teamData.length && i < 3; i++) {
293+
const member = teamData[i];
294+
const id = uuidv4();
295+
296+
await c.env.DB.prepare(
297+
`INSERT INTO team (id, pokemon_id, name, type, power_level, rarity, image_url, maxHP, currentHP, position)
298+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
299+
).bind(
300+
id,
301+
member.pokemon_id,
302+
member.name,
303+
member.type,
304+
member.power_level || 0,
305+
member.rarity || 'Common',
306+
member.image_url || '',
307+
member.maxHP || 100,
308+
member.currentHP || 100,
309+
i
310+
).run();
311+
}
312+
313+
// Return updated team
314+
const { results } = await c.env.DB.prepare(
315+
'SELECT * FROM team ORDER BY position ASC'
316+
).all();
317+
318+
return c.json(results);
319+
} catch (error) {
320+
return c.json({ error: error.message }, 500);
321+
}
322+
});
323+
324+
// Heal entire team
325+
app.patch('/api/team/heal', async (c) => {
326+
try {
327+
await c.env.DB.prepare(
328+
'UPDATE team SET currentHP = maxHP'
329+
).run();
330+
331+
const { results } = await c.env.DB.prepare(
332+
'SELECT * FROM team ORDER BY position ASC'
333+
).all();
334+
335+
return c.json(results);
336+
} catch (error) {
337+
return c.json({ error: error.message }, 500);
338+
}
339+
});
340+
341+
// Update single team member HP (after battle)
342+
app.patch('/api/team/:pokemonId', async (c) => {
343+
try {
344+
const pokemonId = c.req.param('pokemonId');
345+
const data = await c.req.json();
346+
347+
await c.env.DB.prepare(
348+
'UPDATE team SET currentHP = ? WHERE pokemon_id = ?'
349+
).bind(data.currentHP, pokemonId).run();
350+
351+
return c.json({ success: true });
352+
} catch (error) {
353+
return c.json({ error: error.message }, 500);
354+
}
355+
});
356+
357+
// Remove from team
358+
app.delete('/api/team/:pokemonId', async (c) => {
359+
try {
360+
const pokemonId = c.req.param('pokemonId');
361+
362+
await c.env.DB.prepare(
363+
'DELETE FROM team WHERE pokemon_id = ?'
364+
).bind(pokemonId).run();
365+
366+
return c.json({ success: true });
367+
} catch (error) {
368+
return c.json({ error: error.message }, 500);
369+
}
370+
});
371+
// ================================================
372+
266373
// Serve static assets for non-API routes
267374
app.all('*', async (c) => {
268375
const asset = c.env.ASSETS;

worker/migrations/0001_team.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- Team table for cross-device team persistence
2+
CREATE TABLE IF NOT EXISTS team (
3+
id TEXT PRIMARY KEY,
4+
pokemon_id TEXT NOT NULL,
5+
name TEXT NOT NULL,
6+
type TEXT NOT NULL,
7+
power_level INTEGER DEFAULT 0,
8+
rarity TEXT DEFAULT 'Common',
9+
image_url TEXT,
10+
maxHP INTEGER DEFAULT 100,
11+
currentHP INTEGER DEFAULT 100,
12+
position INTEGER DEFAULT 0,
13+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
14+
);
15+
16+
-- Index for position-based ordering
17+
CREATE INDEX IF NOT EXISTS idx_team_position ON team(position);

0 commit comments

Comments
 (0)