Merge pull request #71 from jcreek/67-add-support-for-multiple-pokédex-challenge-types-per-user-new

67 add support for multiple pokedex challenge types per user new
This commit is contained in:
Josh Creek
2026-01-09 18:50:12 +00:00
committed by GitHub
46 changed files with 3642 additions and 2378 deletions
+155
View File
@@ -0,0 +1,155 @@
# Pokémon Data (CSV Format)
This directory contains Pokémon data in CSV format for easy editing and maintenance.
## File Structure
```
/data/pokemon/
├── gen1-kanto.csv # Gen 1 Pokémon (Kanto region, #1-151)
├── gen2-johto.csv # Gen 2 Pokémon (Johto region, #152-251) - Future
├── gen3-hoenn.csv # Gen 3 Pokémon (Hoenn region, #252-386) - Future
├── games.csv # All Pokémon games definitions
└── README.md # This file
```
## CSV Format
### gen{N}-{region}.csv
One CSV file per generation/region containing all Pokémon from that generation.
**Columns:**
- `pokedexNumber` - National Pokédex number (e.g., 1, 25, 151)
- `name` - Pokémon name (e.g., Bulbasaur, Pikachu, Mew)
- `form` - Form name (empty for base form, "Female" for gender differences, "Alolan" for regional forms, etc.)
- `games` - Pipe-separated list of games where this Pokémon can be caught (e.g., "Red|Blue|Yellow")
- `regionalNumber` - Regional Pokédex number for base forms (empty for gender/alternative forms)
**Example (gen1-kanto.csv):**
```csv
pokedexNumber,name,form,games,regionalNumber
1,Bulbasaur,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,1
3,Venusaur,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,3
3,Venusaur,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
25,Pikachu,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,25
151,Mew,,Red|Blue|Yellow,151
```
### games.csv
Defines all Pokémon games.
**Columns:**
- `id` - Unique game identifier (lowercase, hyphenated, e.g., "red", "lg-pikachu")
- `displayName` - Display name for the game (e.g., "Red", "LG: Pikachu")
- `region` - Region/generation the game belongs to (e.g., "Kanto", "Johto")
- `generation` - Generation number (1-9)
**Example:**
```csv
id,displayName,region,generation
red,Red,Kanto,1
blue,Blue,Kanto,1
lg-pikachu,LG: Pikachu,Kanto,7
```
## Editing with Excel/Google Sheets
CSV files are designed to be edited in spreadsheet applications:
1. Open the CSV file in Excel or Google Sheets
2. Use formulas, sorting, and filtering for bulk operations
3. Save as CSV when done
**Example workflows:**
### Adding a new game to existing Pokémon
Find & Replace in the `games` column:
- Find: `Red|Blue`
- Replace: `Red|Blue|NewGame`
### Adding a new Pokémon
Add a new row with all required fields:
```csv
152,Chikorita,,Gold|Silver|Crystal,1
```
### Adding a regional form
Add a new row with the form name:
```csv
19,Rattata,Alolan,Sun|Moon|US|UM,
```
## Generating SQL Migrations
After editing CSV files, generate SQL migrations using npm scripts:
```bash
# Generate migration for Kanto
npm run seed:kanto
# Generate migration for Johto (when ready)
npm run seed:johto
# Custom region
npm run seed:generate <RegionName>
```
Generated migrations will be created in `/supabase/migrations/` with timestamp prefixes.
## Testing Changes
After generating a migration:
```bash
# Reset local database to test
npm run supabase:reset
# Verify the changes loaded correctly
```
## Important Notes
1. **Regional Numbers**: Only base forms (no `form` value) should have `regionalNumber` values. Forms like "Female" or "Alolan" should leave this column empty.
2. **Game Names**: Must exactly match the `displayName` in `games.csv`. Use pipe `|` as separator, no spaces around pipes.
3. **National Dex Numbers**: Must be accurate. Gender forms and regional variants share the same `pokedexNumber` as their base form.
4. **CSV Encoding**: Save files with UTF-8 encoding to support special characters.
5. **Quotes**: Only use quotes around values containing commas. Excel/Google Sheets handles this automatically.
## Common Operations
### Add a new generation (e.g., Gen 2 - Johto)
1. Create `gen2-johto.csv` with the format above
2. Add Johto games to `games.csv`
3. Run `npm run seed:johto`
4. Test with `npm run supabase:reset`
### Add a new game to existing generation
1. Open `games.csv`, add new game row
2. Open the relevant `gen{N}-{region}.csv` file
3. Use Find & Replace to add the new game to the `games` column
4. Regenerate the migration: `npm run seed:{region}`
5. Test with `npm run supabase:reset`
### Add DLC/expansion Pokémon
1. Add new Pokémon rows to the appropriate generation CSV
2. Update the `games` column with the DLC games
3. Regenerate the migration
4. Test
## Database Schema
Generated migrations insert data into two tables:
- **`pokedex_entries`**: Pokémon species data (name, form, games, etc.)
- **`regional_dex_numbers`**: Regional Pokédex numbers (separate table for scalability)
This design allows adding new regions without database schema changes.
+12
View File
@@ -0,0 +1,12 @@
id,displayName,region,generation,releaseYear
red,Red,Kanto,1,1996
blue,Blue,Kanto,1,1996
yellow,Yellow,Kanto,1,1998
lg--pikachu,LG: Pikachu,Kanto,7,2018
lg--eevee,LG: Eevee,Kanto,7,2018
gold,Gold,Johto,2,1999
silver,Silver,Johto,2,1999
crystal,Crystal,Johto,2,2000
heartgold,HeartGold,Johto,4,2009
soulsilver,SoulSilver,Johto,4,2009
legends-arceus,Legends: Arceus,Hisui,8,2022
1 id displayName region generation releaseYear
2 red Red Kanto 1 1996
3 blue Blue Kanto 1 1996
4 yellow Yellow Kanto 1 1998
5 lg--pikachu LG: Pikachu Kanto 7 2018
6 lg--eevee LG: Eevee Kanto 7 2018
7 gold Gold Johto 2 1999
8 silver Silver Johto 2 1999
9 crystal Crystal Johto 2 2000
10 heartgold HeartGold Johto 4 2009
11 soulsilver SoulSilver Johto 4 2009
12 legends-arceus Legends: Arceus Hisui 8 2022
+175
View File
@@ -0,0 +1,175 @@
pokedexNumber,name,form,originGames,regionalDexGames,regionalNumber
1,Bulbasaur,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,1
2,Ivysaur,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,2
3,Venusaur,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,3
3,Venusaur,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
4,Charmander,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,4
5,Charmeleon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,5
6,Charizard,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,6
7,Squirtle,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,7
8,Wartortle,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,8
9,Blastoise,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,9
10,Caterpie,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,10
11,Metapod,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,11
12,Butterfree,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,12
12,Butterfree,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
13,Weedle,,Red|Blue|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,13
14,Kakuna,,Red|Blue|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,14
15,Beedrill,,Red|Blue|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,15
16,Pidgey,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,16
17,Pidgeotto,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,17
18,Pidgeot,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,18
19,Rattata,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,19
19,Rattata,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
20,Raticate,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,20
20,Raticate,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
21,Spearow,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,21
22,Fearow,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,22
23,Ekans,,Red|Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,23
24,Arbok,,Red|Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,24
25,Pikachu,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,25
25,Pikachu,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
26,Raichu,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,26
26,Raichu,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
27,Sandshrew,,Blue|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,27
28,Sandslash,,Blue|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,28
29,Nidoran♀,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,29
30,Nidorina,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,30
31,Nidoqueen,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,31
32,Nidoran♂,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,32
33,Nidorino,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,33
34,Nidoking,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,34
35,Clefairy,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,35
36,Clefable,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,36
37,Vulpix,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,37
38,Ninetales,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,38
39,Jigglypuff,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,39
40,Wigglytuff,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,40
41,Zubat,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,41
41,Zubat,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
42,Golbat,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,42
42,Golbat,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
43,Oddish,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,43
44,Gloom,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,44
44,Gloom,Female,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
45,Vileplume,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,45
45,Vileplume,Female,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
46,Paras,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,46
47,Parasect,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,47
48,Venonat,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,48
49,Venomoth,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,49
50,Diglett,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,50
51,Dugtrio,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,51
52,Meowth,,Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,52
53,Persian,,Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,53
54,Psyduck,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,54
55,Golduck,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,55
56,Mankey,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,56
57,Primeape,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,57
58,Growlithe,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,58
59,Arcanine,,Red|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,59
60,Poliwag,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,60
61,Poliwhirl,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,61
62,Poliwrath,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,62
63,Abra,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,63
64,Kadabra,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,64
64,Kadabra,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
65,Alakazam,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,65
65,Alakazam,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
66,Machop,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,66
67,Machoke,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,67
68,Machamp,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,68
69,Bellsprout,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,69
70,Weepinbell,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,70
71,Victreebel,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,71
72,Tentacool,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,72
73,Tentacruel,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,73
74,Geodude,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,74
75,Graveler,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,75
76,Golem,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,76
77,Ponyta,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,77
78,Rapidash,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,78
79,Slowpoke,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,79
80,Slowbro,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,80
81,Magnemite,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,81
82,Magneton,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,82
83,Farfetchd,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,83
84,Doduo,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,84
84,Doduo,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
85,Dodrio,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,85
85,Dodrio,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
86,Seel,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,86
87,Dewgong,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,87
88,Grimer,,Red|Blue|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,88
89,Muk,,Red|Blue|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,89
90,Shellder,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,90
91,Cloyster,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,91
92,Gastly,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,92
93,Haunter,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,93
94,Gengar,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,94
95,Onix,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,95
96,Drowzee,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,96
97,Hypno,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,97
97,Hypno,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
98,Krabby,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,98
99,Kingler,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,99
100,Voltorb,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,100
101,Electrode,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,101
102,Exeggcute,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,102
103,Exeggutor,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,103
104,Cubone,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,104
105,Marowak,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,105
106,Hitmonlee,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,106
107,Hitmonchan,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,107
108,Lickitung,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,108
109,Koffing,,Red|Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,109
110,Weezing,,Red|Blue|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,110
111,Rhyhorn,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,111
111,Rhyhorn,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
112,Rhydon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,112
112,Rhydon,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
113,Chansey,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,113
114,Tangela,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,114
115,Kangaskhan,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,115
116,Horsea,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,116
117,Seadra,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,117
118,Goldeen,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,118
118,Goldeen,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
119,Seaking,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,119
119,Seaking,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
120,Staryu,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,120
121,Starmie,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,121
122,Mr. Mime,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,122
123,Scyther,,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,123
123,Scyther,Female,Red|Yellow|LG: Pikachu,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
124,Jynx,,Red|Blue|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,124
125,Electabuzz,,Red|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,125
126,Magmar,,Blue|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,126
127,Pinsir,,Blue|Yellow|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,127
128,Tauros,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,128
129,Magikarp,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,129
129,Magikarp,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
130,Gyarados,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,130
130,Gyarados,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
131,Lapras,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,131
132,Ditto,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,132
133,Eevee,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,133
133,Eevee,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,
134,Vaporeon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,134
135,Jolteon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,135
136,Flareon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,136
137,Porygon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,137
138,Omanyte,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,138
139,Omastar,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,139
140,Kabuto,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,140
141,Kabutops,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,141
142,Aerodactyl,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,142
143,Snorlax,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,143
144,Articuno,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,144
145,Zapdos,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,145
146,Moltres,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,146
147,Dratini,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,147
148,Dragonair,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,148
149,Dragonite,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,149
150,Mewtwo,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,150
151,Mew,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,151
1 pokedexNumber name form originGames regionalDexGames regionalNumber
2 1 Bulbasaur Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 1
3 2 Ivysaur Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 2
4 3 Venusaur Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 3
5 3 Venusaur Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
6 4 Charmander Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 4
7 5 Charmeleon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 5
8 6 Charizard Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 6
9 7 Squirtle Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 7
10 8 Wartortle Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 8
11 9 Blastoise Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 9
12 10 Caterpie Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 10
13 11 Metapod Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 11
14 12 Butterfree Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 12
15 12 Butterfree Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
16 13 Weedle Red|Blue|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 13
17 14 Kakuna Red|Blue|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 14
18 15 Beedrill Red|Blue|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 15
19 16 Pidgey Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 16
20 17 Pidgeotto Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 17
21 18 Pidgeot Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 18
22 19 Rattata Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 19
23 19 Rattata Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
24 20 Raticate Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 20
25 20 Raticate Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
26 21 Spearow Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 21
27 22 Fearow Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 22
28 23 Ekans Red|Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 23
29 24 Arbok Red|Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 24
30 25 Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 25
31 25 Pikachu Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
32 26 Raichu Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 26
33 26 Raichu Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
34 27 Sandshrew Blue|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 27
35 28 Sandslash Blue|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 28
36 29 Nidoran♀ Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 29
37 30 Nidorina Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 30
38 31 Nidoqueen Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 31
39 32 Nidoran♂ Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 32
40 33 Nidorino Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 33
41 34 Nidoking Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 34
42 35 Clefairy Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 35
43 36 Clefable Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 36
44 37 Vulpix Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 37
45 38 Ninetales Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 38
46 39 Jigglypuff Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 39
47 40 Wigglytuff Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 40
48 41 Zubat Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 41
49 41 Zubat Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
50 42 Golbat Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 42
51 42 Golbat Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
52 43 Oddish Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 43
53 44 Gloom Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 44
54 44 Gloom Female Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee
55 45 Vileplume Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 45
56 45 Vileplume Female Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee
57 46 Paras Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 46
58 47 Parasect Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 47
59 48 Venonat Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 48
60 49 Venomoth Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 49
61 50 Diglett Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 50
62 51 Dugtrio Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 51
63 52 Meowth Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 52
64 53 Persian Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 53
65 54 Psyduck Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 54
66 55 Golduck Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 55
67 56 Mankey Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 56
68 57 Primeape Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 57
69 58 Growlithe Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 58
70 59 Arcanine Red|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 59
71 60 Poliwag Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 60
72 61 Poliwhirl Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 61
73 62 Poliwrath Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 62
74 63 Abra Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 63
75 64 Kadabra Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 64
76 64 Kadabra Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
77 65 Alakazam Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 65
78 65 Alakazam Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
79 66 Machop Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 66
80 67 Machoke Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 67
81 68 Machamp Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 68
82 69 Bellsprout Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 69
83 70 Weepinbell Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 70
84 71 Victreebel Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 71
85 72 Tentacool Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 72
86 73 Tentacruel Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 73
87 74 Geodude Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 74
88 75 Graveler Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 75
89 76 Golem Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 76
90 77 Ponyta Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 77
91 78 Rapidash Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 78
92 79 Slowpoke Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 79
93 80 Slowbro Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 80
94 81 Magnemite Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 81
95 82 Magneton Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 82
96 83 Farfetch’d Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 83
97 84 Doduo Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 84
98 84 Doduo Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
99 85 Dodrio Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 85
100 85 Dodrio Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
101 86 Seel Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 86
102 87 Dewgong Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 87
103 88 Grimer Red|Blue|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 88
104 89 Muk Red|Blue|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 89
105 90 Shellder Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 90
106 91 Cloyster Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 91
107 92 Gastly Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 92
108 93 Haunter Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 93
109 94 Gengar Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 94
110 95 Onix Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 95
111 96 Drowzee Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 96
112 97 Hypno Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 97
113 97 Hypno Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
114 98 Krabby Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 98
115 99 Kingler Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 99
116 100 Voltorb Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 100
117 101 Electrode Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 101
118 102 Exeggcute Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 102
119 103 Exeggutor Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 103
120 104 Cubone Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 104
121 105 Marowak Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 105
122 106 Hitmonlee Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 106
123 107 Hitmonchan Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 107
124 108 Lickitung Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 108
125 109 Koffing Red|Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 109
126 110 Weezing Red|Blue|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 110
127 111 Rhyhorn Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 111
128 111 Rhyhorn Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
129 112 Rhydon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 112
130 112 Rhydon Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
131 113 Chansey Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 113
132 114 Tangela Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 114
133 115 Kangaskhan Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 115
134 116 Horsea Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 116
135 117 Seadra Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 117
136 118 Goldeen Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 118
137 118 Goldeen Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
138 119 Seaking Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 119
139 119 Seaking Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
140 120 Staryu Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 120
141 121 Starmie Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 121
142 122 Mr. Mime Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 122
143 123 Scyther Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee 123
144 123 Scyther Female Red|Yellow|LG: Pikachu Red|Blue|Yellow|LG: Pikachu|LG: Eevee
145 124 Jynx Red|Blue|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 124
146 125 Electabuzz Red|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 125
147 126 Magmar Blue|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 126
148 127 Pinsir Blue|Yellow|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 127
149 128 Tauros Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 128
150 129 Magikarp Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 129
151 129 Magikarp Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
152 130 Gyarados Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 130
153 130 Gyarados Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
154 131 Lapras Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 131
155 132 Ditto Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 132
156 133 Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 133
157 133 Eevee Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee
158 134 Vaporeon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 134
159 135 Jolteon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 135
160 136 Flareon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 136
161 137 Porygon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 137
162 138 Omanyte Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 138
163 139 Omastar Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 139
164 140 Kabuto Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 140
165 141 Kabutops Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 141
166 142 Aerodactyl Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 142
167 143 Snorlax Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 143
168 144 Articuno Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 144
169 145 Zapdos Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 145
170 146 Moltres Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 146
171 147 Dratini Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 147
172 148 Dragonair Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 148
173 149 Dragonite Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 149
174 150 Mewtwo Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 150
175 151 Mew Red|Blue|Yellow|LG: Pikachu|LG: Eevee Red|Blue|Yellow|LG: Pikachu|LG: Eevee 151
+149
View File
@@ -0,0 +1,149 @@
pokedexNumber,name,form,originGames,regionalDexGames,regionalNumber
152,Chikorita,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,1
153,Bayleef,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,2
154,Meganium,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,3
154,Meganium,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
155,Cyndaquil,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,4
156,Quilava,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,5
157,Typhlosion,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,6
158,Totodile,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,7
159,Croconaw,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,8
160,Feraligatr,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,9
161,Sentret,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,10
162,Furret,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,11
163,Hoothoot,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,12
164,Noctowl,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,13
165,Ledyba,,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,14
165,Ledyba,Female,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
166,Ledian,,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,15
166,Ledian,Female,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
167,Spinarak,,Gold|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,16
168,Ariados,,Gold|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,17
169,Crobat,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,18
170,Chinchou,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,19
171,Lanturn,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,20
172,Pichu,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,21
173,Cleffa,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,22
174,Igglybuff,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,23
175,Togepi,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,24
176,Togetic,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,25
177,Natu,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,26
178,Xatu,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,27
178,Xatu,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
179,Mareep,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,28
180,Flaaffy,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,29
181,Ampharos,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,30
182,Bellossom,,Red|Yellow|LG: Pikachu,Gold|Silver|Crystal|HeartGold|SoulSilver,31
183,Marill,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,32
184,Azumarill,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,33
185,Sudowoodo,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,34
185,Sudowoodo,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
186,Politoed,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,35
186,Politoed,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,
187,Hoppip,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,36
188,Skiploom,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,37
189,Jumpluff,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,38
190,Aipom,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,39
190,Aipom,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
191,Sunkern,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,40
192,Sunflora,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,41
193,Yanma,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,42
194,Wooper,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,43
194,Wooper,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
195,Quagsire,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,44
195,Quagsire,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
196,Espeon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,45
197,Umbreon,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,46
198,Murkrow,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,47
198,Murkrow,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
199,Slowking,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,48
200,Misdreavus,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,49
201,Unown,A,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,50
201,Unown,B,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,C,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,D,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,E,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,F,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,G,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,H,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,I,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,J,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,K,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,L,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,M,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,N,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,O,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,P,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,Q,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,R,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,S,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,T,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,U,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,V,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,W,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,X,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,Y,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,Z,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,!,HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
201,Unown,?,HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
202,Wobbuffet,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,51
202,Wobbuffet,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
203,Girafarig,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,52
203,Girafarig,Female,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
204,Pineco,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,53
205,Forretress,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,54
206,Dunsparce,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,55
207,Gligar,,Gold|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,56
207,Gligar,Female,Gold|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,
208,Steelix,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,57
208,Steelix,Female,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,
209,Snubbull,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,58
210,Granbull,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,59
211,Qwilfish,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,60
212,Scizor,,Red|Yellow|LG: Pikachu,Gold|Silver|Crystal|HeartGold|SoulSilver,61
212,Scizor,Female,Red|Yellow|LG: Pikachu,Gold|Silver|Crystal|HeartGold|SoulSilver,
213,Shuckle,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,62
214,Heracross,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,63
214,Heracross,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
215,Sneasel,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,64
215,Sneasel,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
216,Teddiursa,,Gold|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,65
217,Ursaring,,Gold|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,66
217,Ursaring,Female,Gold|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
218,Slugma,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,67
219,Magcargo,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,68
220,Swinub,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,69
221,Piloswine,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,70
221,Piloswine,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
222,Corsola,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,71
223,Remoraid,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,72
224,Octillery,,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,73
224,Octillery,Female,Gold|Silver|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
225,Delibird,,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,74
226,Mantine,,Gold|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,75
227,Skarmory,,Silver|Crystal|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,76
228,Houndour,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,77
229,Houndoom,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,78
229,Houndoom,Female,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,
230,Kingdra,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,79
231,Phanpy,,Silver|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,80
232,Donphan,,Silver|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,81
232,Donphan,Female,Silver|Crystal|HeartGold,Gold|Silver|Crystal|HeartGold|SoulSilver,
234,Stantler,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,83
235,Smeargle,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,84
236,Tyrogue,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,85
237,Hitmontop,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,86
238,Smoochum,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,87
239,Elekid,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,88
240,Magby,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,89
241,Miltank,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,90
242,Blissey,,Red|Blue|Yellow|LG: Pikachu|LG: Eevee,Gold|Silver|Crystal|HeartGold|SoulSilver,91
243,Raikou,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,92
244,Entei,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,93
245,Suicune,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,94
246,Larvitar,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,95
247,Pupitar,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,96
248,Tyranitar,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,97
249,Lugia,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,98
250,Ho-Oh,,Gold|Silver|Crystal|HeartGold|SoulSilver,Gold|Silver|Crystal|HeartGold|SoulSilver,99
251,Celebi,,Crystal,Crystal|HeartGold|SoulSilver,100
1 pokedexNumber name form originGames regionalDexGames regionalNumber
2 152 Chikorita Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 1
3 153 Bayleef Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 2
4 154 Meganium Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 3
5 154 Meganium Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
6 155 Cyndaquil Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 4
7 156 Quilava Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 5
8 157 Typhlosion Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 6
9 158 Totodile Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 7
10 159 Croconaw Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 8
11 160 Feraligatr Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 9
12 161 Sentret Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 10
13 162 Furret Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 11
14 163 Hoothoot Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 12
15 164 Noctowl Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 13
16 165 Ledyba Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 14
17 165 Ledyba Female Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
18 166 Ledian Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 15
19 166 Ledian Female Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
20 167 Spinarak Gold|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 16
21 168 Ariados Gold|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 17
22 169 Crobat Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 18
23 170 Chinchou Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 19
24 171 Lanturn Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 20
25 172 Pichu Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 21
26 173 Cleffa Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 22
27 174 Igglybuff Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 23
28 175 Togepi Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 24
29 176 Togetic Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 25
30 177 Natu Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 26
31 178 Xatu Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 27
32 178 Xatu Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
33 179 Mareep Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 28
34 180 Flaaffy Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 29
35 181 Ampharos Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 30
36 182 Bellossom Red|Yellow|LG: Pikachu Gold|Silver|Crystal|HeartGold|SoulSilver 31
37 183 Marill Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 32
38 184 Azumarill Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 33
39 185 Sudowoodo Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 34
40 185 Sudowoodo Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
41 186 Politoed Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 35
42 186 Politoed Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver
43 187 Hoppip Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 36
44 188 Skiploom Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 37
45 189 Jumpluff Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 38
46 190 Aipom Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 39
47 190 Aipom Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
48 191 Sunkern Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 40
49 192 Sunflora Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 41
50 193 Yanma Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 42
51 194 Wooper Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 43
52 194 Wooper Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
53 195 Quagsire Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 44
54 195 Quagsire Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
55 196 Espeon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 45
56 197 Umbreon Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 46
57 198 Murkrow Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 47
58 198 Murkrow Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
59 199 Slowking Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 48
60 200 Misdreavus Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 49
61 201 Unown A Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 50
62 201 Unown B Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
63 201 Unown C Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
64 201 Unown D Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
65 201 Unown E Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
66 201 Unown F Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
67 201 Unown G Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
68 201 Unown H Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
69 201 Unown I Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
70 201 Unown J Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
71 201 Unown K Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
72 201 Unown L Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
73 201 Unown M Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
74 201 Unown N Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
75 201 Unown O Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
76 201 Unown P Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
77 201 Unown Q Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
78 201 Unown R Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
79 201 Unown S Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
80 201 Unown T Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
81 201 Unown U Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
82 201 Unown V Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
83 201 Unown W Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
84 201 Unown X Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
85 201 Unown Y Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
86 201 Unown Z Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
87 201 Unown ! HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
88 201 Unown ? HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
89 202 Wobbuffet Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 51
90 202 Wobbuffet Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
91 203 Girafarig Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 52
92 203 Girafarig Female Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
93 204 Pineco Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 53
94 205 Forretress Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 54
95 206 Dunsparce Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 55
96 207 Gligar Gold|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 56
97 207 Gligar Female Gold|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver
98 208 Steelix Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 57
99 208 Steelix Female Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver
100 209 Snubbull Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 58
101 210 Granbull Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 59
102 211 Qwilfish Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 60
103 212 Scizor Red|Yellow|LG: Pikachu Gold|Silver|Crystal|HeartGold|SoulSilver 61
104 212 Scizor Female Red|Yellow|LG: Pikachu Gold|Silver|Crystal|HeartGold|SoulSilver
105 213 Shuckle Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 62
106 214 Heracross Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 63
107 214 Heracross Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
108 215 Sneasel Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 64
109 215 Sneasel Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
110 216 Teddiursa Gold|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 65
111 217 Ursaring Gold|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 66
112 217 Ursaring Female Gold|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
113 218 Slugma Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 67
114 219 Magcargo Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 68
115 220 Swinub Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 69
116 221 Piloswine Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 70
117 221 Piloswine Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
118 222 Corsola Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 71
119 223 Remoraid Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 72
120 224 Octillery Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 73
121 224 Octillery Female Gold|Silver|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
122 225 Delibird Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 74
123 226 Mantine Gold|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 75
124 227 Skarmory Silver|Crystal|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 76
125 228 Houndour Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 77
126 229 Houndoom Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 78
127 229 Houndoom Female Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver
128 230 Kingdra Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 79
129 231 Phanpy Silver|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 80
130 232 Donphan Silver|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver 81
131 232 Donphan Female Silver|Crystal|HeartGold Gold|Silver|Crystal|HeartGold|SoulSilver
132 234 Stantler Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 83
133 235 Smeargle Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 84
134 236 Tyrogue Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 85
135 237 Hitmontop Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 86
136 238 Smoochum Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 87
137 239 Elekid Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 88
138 240 Magby Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 89
139 241 Miltank Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 90
140 242 Blissey Red|Blue|Yellow|LG: Pikachu|LG: Eevee Gold|Silver|Crystal|HeartGold|SoulSilver 91
141 243 Raikou Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 92
142 244 Entei Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 93
143 245 Suicune Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 94
144 246 Larvitar Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 95
145 247 Pupitar Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 96
146 248 Tyranitar Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 97
147 249 Lugia Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 98
148 250 Ho-Oh Gold|Silver|Crystal|HeartGold|SoulSilver Gold|Silver|Crystal|HeartGold|SoulSilver 99
149 251 Celebi Crystal Crystal|HeartGold|SoulSilver 100
+183
View File
@@ -0,0 +1,183 @@
#!/usr/bin/env node
/**
* Generates SQL migration from CSV files
* Usage: node csv-to-migration.js <Region>
* Example: node csv-to-migration.js Kanto
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
* Parse CSV file into array of objects
*/
function parseCSV(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n').filter(line => line.trim());
const headers = lines[0].split(',');
return lines.slice(1).map(line => {
const values = parseCSVLine(line);
const obj = {};
headers.forEach((header, i) => {
obj[header] = values[i] || null;
});
return obj;
});
}
/**
* Parse a single CSV line, handling quoted values
*/
function parseCSVLine(line) {
const values = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
values.push(current);
current = '';
} else {
current += char;
}
}
values.push(current);
return values;
}
/**
* Generate migration SQL from CSV data
*/
function generateMigration(region) {
console.log(`\nGenerating migration for ${region}...`);
// Determine generation number from region
const regionToGen = {
'Kanto': 1, 'Johto': 2, 'Hoenn': 3, 'Sinnoh': 4,
'Unova': 5, 'Kalos': 6, 'Alola': 7, 'Galar': 8,
'Hisui': 8, 'Paldea': 9
};
const gen = regionToGen[region] || 1;
// 1. Load CSV files
const pokemonPath = path.join(__dirname, '..', 'data', 'pokemon', `gen${gen}-${region.toLowerCase()}.csv`);
const gamesPath = path.join(__dirname, '..', 'data', 'pokemon', 'games.csv');
if (!fs.existsSync(pokemonPath)) {
console.error(`Error: Pokemon CSV not found at ${pokemonPath}`);
process.exit(1);
}
const pokemon = parseCSV(pokemonPath);
const games = parseCSV(gamesPath);
// 2. Filter for this region
const regionGames = games.filter(g => g.region === region);
if (regionGames.length === 0) {
console.error(`Error: No games found for region ${region} in games.csv`);
process.exit(1);
}
// 3. Generate SQL
let sql = `-- Seed ${region} region Pokémon (Gen ${gen})\n`;
sql += `-- Auto-generated from CSV files\n\n`;
// Region-game mappings
sql += `-- Insert ${region} region-game mappings\n`;
sql += `INSERT INTO region_game_mappings (region, game) VALUES\n`;
sql += regionGames.map(g => ` ('${region}', '${g.displayName}')`).join(',\n');
sql += `\nON CONFLICT (region, game) DO NOTHING;\n\n`;
// Pokemon entries (without regional dex number)
sql += `-- Insert ${region} Pokémon entries\n`;
sql += `INSERT INTO pokedex_entries (\n`;
sql += ` "pokedexNumber",\n`;
sql += ` pokemon,\n`;
sql += ` form,\n`;
sql += ` "canGigantamax",\n`;
sql += ` "regionToCatchIn",\n`;
sql += ` "gamesToCatchIn"\n`;
sql += `) VALUES\n`;
const rows = pokemon.map(p => {
const form = p.form ? `'${p.form}'` : 'NULL';
// Use regionalDexGames for the database (regional dex availability)
// originGames column is for future origin dex feature
const gamesField = p.regionalDexGames || p.games; // Fallback to old 'games' column for compatibility
const gamesList = gamesField.split('|');
const gamesArray = `ARRAY[${gamesList.map(g => `'${g}'`).join(', ')}]`;
return `(${p.pokedexNumber}, '${p.name}', ${form}, false, '${region}', ${gamesArray})`;
});
sql += rows.join(',\n');
sql += '\nON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;\n\n';
// Regional dex numbers (separate table)
sql += `-- Insert ${region} regional dex numbers\n`;
const dexRows = pokemon
.filter(p => p.regionalNumber) // Only entries with regional dex numbers
.map(p => {
const formCondition = p.form
? `form = '${p.form}'`
: `form IS NULL`;
return ` ((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = ${p.pokedexNumber} AND ${formCondition}), '${region}', ${p.regionalNumber})`;
});
if (dexRows.length > 0) {
sql += `INSERT INTO regional_dex_numbers (\n`;
sql += ` pokedex_entry_id,\n`;
sql += ` region,\n`;
sql += ` dex_number\n`;
sql += `) VALUES\n`;
sql += dexRows.join(',\n');
sql += ';\n\n';
} else {
sql += '-- No regional dex numbers for this region\n\n';
}
// Add metadata
sql += `-- Add metadata\n`;
sql += `INSERT INTO metadata (key, value) VALUES\n`;
sql += ` ('${region.toLowerCase()}_seeded', 'true'),\n`;
sql += ` ('${region.toLowerCase()}_seed_date', NOW()::TEXT),\n`;
sql += ` ('${region.toLowerCase()}_pokemon_count', '${pokemon.filter(p => !p.form).length}');\n`;
// 4. Write file
const timestamp = new Date().toISOString().replace(/[-:T.]/g, '').slice(0, 14);
const filename = `${timestamp}_seed_${region.toLowerCase()}.sql`;
const outputPath = path.join(__dirname, '..', 'supabase', 'migrations', filename);
fs.writeFileSync(outputPath, sql);
console.log(`✓ Generated ${filename}`);
console.log(` - ${pokemon.length} Pokemon entries`);
console.log(` - ${dexRows.length} regional dex numbers`);
console.log(` - ${regionGames.length} games\n`);
return filename;
}
// Run
const region = process.argv[2];
if (!region) {
console.error('Usage: node csv-to-migration.js <Region>');
console.error('Example: node csv-to-migration.js Kanto');
process.exit(1);
}
generateMigration(region);
+18 -14
View File
@@ -31,13 +31,20 @@
// Sanitise the pokemon name by making it all lowercase and replacing any spaces with hyphens and removing other characters
let sanitisedPokemonName = pokemonName.toLowerCase().replace(/[^a-z]/g, '');
// Get the PokeApi id for the pokemon
/**
* Sprite Resolution Strategy:
* 1. For forms with PokeAPI entries (regional forms): Use PokeAPI ID (e.g., 10107.png)
* 2. For forms without PokeAPI entries (Unown, Burmy, etc.): Use {pokedexNumber}-{form} (e.g., 201-a.png)
* 3. For base forms: Use PokeAPI ID matching species_id
*/
let pokeApiId;
if (form && form.length > 0 && form !== 'Female') {
// Sanitise the form by making it all lowercase and replacing spaces with hyphens
let sanitisedForm = form
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll('!', 'exclamation')
.replaceAll('?', 'question')
.replaceAll('2', 'two')
.replaceAll('3', 'three')
.replaceAll('4', 'four');
@@ -59,20 +66,17 @@
break;
}
// If the form is contained in the identifier, use that
if (
pokeApiPokemon.find(
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
)
) {
pokeApiId = pokeApiPokemon.find(
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
)?.id;
// Try to find by PokeAPI identifier (e.g., "meowth-alola" -> 10107)
const pokeApiEntry = pokeApiPokemon.find(
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
);
if (pokeApiEntry) {
// Pattern 1: Use PokeAPI ID (e.g., 10107.png for Meowth-Alola)
pokeApiId = pokeApiEntry.id;
} else {
// If the form is not contained in the identifier, use the species_id
pokeApiId = pokeApiPokemon.find(
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
)?.id;
// Pattern 2: Use {pokedexNumber}-{form} (e.g., 201-a.png for Unown-A)
pokeApiId = `${strippedPokedexNumber}-${sanitisedForm}`;
}
} else {
pokeApiId = pokeApiPokemon.find(
@@ -0,0 +1,51 @@
<script lang="ts">
import type { Pokedex } from '$lib/models/Pokedex';
export let pokedex: Pokedex;
export let onEdit: () => void;
export let onDelete: () => void;
export let onView: () => void;
// Get active type badges
$: typeBadges = [
pokedex.isLivingDex && 'Living',
pokedex.isShinyDex && 'Shiny',
pokedex.isOriginDex && 'Origin',
pokedex.isFormDex && 'Form'
].filter(Boolean);
</script>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
{pokedex.name}
</h2>
{#if pokedex.description}
<p class="text-sm">{pokedex.description}</p>
{/if}
<div class="flex flex-wrap gap-2 my-2">
{#each typeBadges as badge}
<span class="badge badge-primary">{badge}</span>
{/each}
</div>
{#if pokedex.gameScope}
<div class="text-sm text-base-content/70">
<span class="font-semibold">Game:</span>
{pokedex.gameScope}
</div>
{:else}
<div class="text-sm text-base-content/70">
<span class="font-semibold">Scope:</span> All Games
</div>
{/if}
<div class="card-actions justify-end mt-4">
<button class="btn btn-sm btn-ghost" on:click={onEdit}>Edit</button>
<button class="btn btn-sm btn-error btn-outline" on:click={onDelete}>Delete</button>
<button class="btn btn-sm btn-primary" on:click={onView}>View</button>
</div>
</div>
</div>
@@ -10,6 +10,7 @@
export let showForms: boolean;
export let showShiny: boolean;
export let userId: string | null = null;
export let pokedexId: string;
// Create a default catch record if none exists
$: if (!catchRecord) {
@@ -17,6 +18,7 @@
_id: '', // Empty string, not temp ID - will be created by server
userId: userId || '',
pokedexEntryId: pokedexEntry._id,
pokedexId: pokedexId,
haveToEvolve: false,
caught: false,
inHome: false,
@@ -58,17 +60,6 @@
{/if}
<div class="bg-white text-black rounded-lg p-4">
{#if showForms}
<p><strong>Box:</strong> {pokedexEntry.boxPlacementForms.box}</p>
<p><strong>Row:</strong> {pokedexEntry.boxPlacementForms.row}</p>
<p><strong>Column:</strong> {pokedexEntry.boxPlacementForms.column}</p>
<p><strong>Can Gigantamax:</strong> {pokedexEntry.canGigantamax ? 'Yes' : 'No'}</p>
{:else}
<p><strong>Box:</strong> {pokedexEntry.boxPlacement.box}</p>
<p><strong>Row:</strong> {pokedexEntry.boxPlacement.row}</p>
<p><strong>Column:</strong> {pokedexEntry.boxPlacement.column}</p>
{/if}
{#if pokedexEntry.evolutionInformation}
<p><strong>How to evolve: </strong>{pokedexEntry.evolutionInformation}</p>
{:else}
@@ -83,74 +74,77 @@
</div>
</div>
<div class="dex-column catch-record-container bg-white text-black rounded-lg p-4 mb-4 md:mb-0">
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">Caught:</span>
<input
type="checkbox"
bind:checked={catchRecord.caught}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">Needs to evolve:</span>
<input
type="checkbox"
bind:checked={catchRecord.haveToEvolve}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">In home:</span>
<input
type="checkbox"
bind:checked={catchRecord.inHome}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
{#if pokedexEntry.canGigantamax && showForms}
{#if catchRecord}
<div class="dex-column catch-record-container bg-white text-black rounded-lg p-4 mb-4 md:mb-0">
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">Has Gigantamaxed:</span>
<span class="block font-bold mr-2">Caught:</span>
<input
type="checkbox"
bind:checked={catchRecord.hasGigantamaxed}
bind:checked={catchRecord.caught}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
{/if}
<p>
<label
class="block font-bold mb-1"
for={`personalNotesInput-${catchRecord?._id || pokedexEntry._id}`}>Notes:</label
>
<textarea
bind:value={catchRecord.personalNotes}
id={`personalNotesInput-${catchRecord?._id || pokedexEntry._id}`}
class="form-textarea w-full p-2 border rounded"
on:change={updateCatchRecord}
></textarea>
</p>
</div>
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">Needs to evolve:</span>
<input
type="checkbox"
bind:checked={catchRecord.haveToEvolve}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">In home:</span>
<input
type="checkbox"
bind:checked={catchRecord.inHome}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
{#if pokedexEntry.canGigantamax && showForms}
<div class="flex items-center">
<div class="form-control">
<label class="cursor-pointer label">
<span class="block font-bold mr-2">Has Gigantamaxed:</span>
<input
type="checkbox"
bind:checked={catchRecord.hasGigantamaxed}
class="checkbox checkbox-primary border-black"
on:change={updateCatchRecord}
/>
</label>
</div>
</div>
{/if}
<p>
<label
class="block font-bold mb-1"
for={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}>Notes:</label
>
<textarea
bind:value={catchRecord.personalNotes}
id={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}
class="form-textarea w-full p-2 border rounded"
style="min-height: 120px;"
on:change={updateCatchRecord}
></textarea>
</p>
</div>
{/if}
<div class="dex-column additional-details-container">
{#if showOrigins}
@@ -206,7 +200,8 @@
<style>
.dex-column {
width: 300px;
flex: 1;
min-width: 250px;
}
.sprite-container {
@@ -0,0 +1,129 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { Pokedex } from '$lib/models/Pokedex';
export let pokedex: Partial<Pokedex> = {
name: '',
description: '',
isLivingDex: false,
isShinyDex: false,
isOriginDex: false,
isFormDex: false,
gameScope: null
};
export let mode: 'create' | 'edit' = 'create';
export let onSubmit: () => void;
export let onCancel: () => void;
let games: string[] = [];
let loadingGames = true;
// Fetch available games from the database
onMount(async () => {
try {
const response = await fetch('/api/games');
if (response.ok) {
const data = await response.json();
games = data.games || [];
} else {
console.error('Failed to fetch games');
}
} catch (error) {
console.error('Error fetching games:', error);
} finally {
loadingGames = false;
}
});
// Validation
$: hasAtLeastOneType =
pokedex.isLivingDex || pokedex.isShinyDex || pokedex.isOriginDex || pokedex.isFormDex;
$: canSubmit = pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType;
function handleSubmit() {
if (canSubmit) {
onSubmit();
}
}
</script>
<div class="form-control w-full">
<label class="label" for="pokedex-name">
<span class="label-text">Name</span>
</label>
<input
id="pokedex-name"
type="text"
placeholder="My Living Dex"
class="input input-bordered w-full"
bind:value={pokedex.name}
required
/>
</div>
<div class="form-control w-full">
<label class="label" for="pokedex-description">
<span class="label-text">Description (optional)</span>
</label>
<textarea
id="pokedex-description"
class="textarea textarea-bordered h-24"
placeholder="Describe your pokédex..."
bind:value={pokedex.description}
></textarea>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text">Type(s)</span>
<span class="label-text-alt text-error"
>{!hasAtLeastOneType ? 'At least one type required' : ''}</span
>
</label>
<div class="flex flex-col gap-2">
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" bind:checked={pokedex.isLivingDex} />
<span class="label-text">Living Dex (one of each Pokémon)</span>
</label>
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" bind:checked={pokedex.isShinyDex} />
<span class="label-text">Shiny Dex (shiny variants only)</span>
</label>
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" bind:checked={pokedex.isOriginDex} />
<span class="label-text">Origin Dex (caught in native regions)</span>
</label>
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" bind:checked={pokedex.isFormDex} />
<span class="label-text">Form Dex (all forms included)</span>
</label>
</div>
</div>
<div class="form-control w-full">
<label class="label" for="game-scope">
<span class="label-text">Game Scope</span>
</label>
<select
id="game-scope"
class="select select-bordered"
bind:value={pokedex.gameScope}
disabled={loadingGames}
>
<option value={null}>All Games</option>
{#if loadingGames}
<option disabled>Loading games...</option>
{:else}
{#each games as game}
<option value={game}>{game}</option>
{/each}
{/if}
</select>
</div>
<div class="modal-action">
<button class="btn btn-ghost" type="button" on:click={onCancel}>Cancel</button>
<button class="btn btn-primary" type="button" on:click={handleSubmit} disabled={!canSubmit}>
{mode === 'create' ? 'Create' : 'Save'}
</button>
</div>
@@ -0,0 +1,109 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
export let isOpen: boolean;
export let onClose: () => void;
function handleBackdropClick() {
onClose();
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape' && isOpen) {
onClose();
}
}
onMount(() => {
window.addEventListener('keydown', handleKeyDown);
});
onDestroy(() => {
window.removeEventListener('keydown', handleKeyDown);
});
</script>
{#if isOpen}
<div class="modal modal-open" role="dialog" aria-modal="true">
<div class="modal-box-custom">
<button class="close-button" on:click={onClose} aria-label="Close"></button>
<div class="modal-content">
<slot />
</div>
</div>
<div
class="modal-backdrop bg-black/50"
on:click={handleBackdropClick}
on:keydown={() => {}}
></div>
</div>
{/if}
<style>
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.modal-box-custom {
max-width: 72rem;
width: 100%;
max-height: 90vh;
background-color: rgba(220, 38, 38, 0.95);
border: none;
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
outline: none;
position: relative;
border-radius: 1rem;
padding: 0.5rem;
overflow-y: auto;
box-sizing: border-box;
}
.close-button {
position: absolute;
right: 1rem;
top: 1rem;
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.3);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
font-size: 1.5rem;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
z-index: 10;
line-height: 1;
}
.close-button:hover {
background-color: rgba(0, 0, 0, 0.5);
border-color: rgba(255, 255, 255, 0.5);
transform: scale(1.1);
}
.modal-content {
width: 100%;
}
/* Remove margin and background from card inside modal */
.modal-content :global(.dex-entry) {
margin-bottom: 0;
background-color: transparent !important;
box-shadow: none;
}
@media (min-width: 768px) {
.modal-box-custom {
width: 91.666667%;
}
}
</style>
@@ -1,129 +0,0 @@
<script lang="ts">
import Pagination from '$lib/components/Pagination.svelte';
export let viewAsBoxes = false;
export let currentPage = 1;
export let itemsPerPage = 20;
export let totalPages = 0;
export let showForms = true;
export let showOrigins = true;
export let showShiny = false;
export let catchRegion = '';
export let catchGame = '';
export let toggleForms = () => {};
export let toggleOrigins = () => {};
export let toggleShiny = () => {};
export let getData = () => {};
export let toggleViewAsBoxes = () => {};
</script>
<aside
class="menu bg-gray-800 text-white min-h-full w-64 p-4 lg:fixed xs:top-0 lg:left-0 h-full lg:h-5/6"
>
<h2 class="text-2xl font-semibold mb-4">Views</h2>
<ul>
<li class="mb-2">
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleViewAsBoxes}>
{viewAsBoxes ? 'Switch to List View' : 'Switch to Box View'}
</button>
</li>
</ul>
<h2 class="text-2xl font-semibold mb-4">Filters</h2>
<ul>
<li class="mb-2">
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleForms}>
Toggle Forms (Currently {showForms ? 'On' : 'Off'})
</button>
</li>
<li class="mb-2">
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleOrigins}>
Toggle Origins (Currently {showOrigins ? 'On' : 'Off'})
</button>
</li>
<li class="mb-2">
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleShiny}>
Toggle Shiny (Currently {showShiny ? 'On' : 'Off'})
</button>
</li>
</ul>
<div class="mb-4">
<label for="catchRegionSelect">Region to catch in:</label>
<select
id="catchRegionSelect"
class="select select-bordered w-full max-w-xs text-black"
bind:value={catchRegion}
on:change={getData}
>
<option value="">All</option>
<option value="Kanto">Kanto (Red, Blue, Yellow, LG: Pikachu, LG: Eevee)</option>
<option value="Johto">Johto (Gold, Silver, Crystal, HG, SS)</option>
<option value="Hoenn">Hoenn (Ruby, Sapphire, Emerald, OR, AS)</option>
<option value="Sinnoh">Sinnoh (Diamond, Pearl, Platinum, BD, SP)</option>
<option value="Unova">Unova (Black, White, Black2, White2, Dream Radar)</option>
<option value="Kalos">Kalos (X, Y)</option>
<option value="Alola">Alola (Sun, Moon, Moon Demo, US, UM)</option>
<option value="Galar">Galar (Sword, Shield)</option>
<option value="Hisui">Hisui (PLA)</option>
<option value="Paldea">Paldea (Scarlet, Violet)</option>
<option value="Unknown">Unknown (Home, Go)</option>
</select>
</div>
<div class="mb-4">
<label for="catchGameSelect">Game to catch in: {catchGame}</label>
<select
id="catchGameSelect"
class="select select-bordered w-full max-w-xs text-black"
bind:value={catchGame}
on:change={getData}
>
<option value="">All</option>
<option value="Red">Red (Kanto)</option>
<option value="Blue">Blue (Kanto)</option>
<option value="Yellow">Yellow (Kanto)</option>
<option value="LG: Pikachu">LG: Pikachu (Kanto)</option>
<option value="LG: Eevee">LG: Eevee (Kanto)</option>
<option value="Gold">Gold (Johto)</option>
<option value="Silver">Silver (Johto)</option>
<option value="Crystal">Crystal (Johto)</option>
<option value="HG">HG (Johto)</option>
<option value="SS">SS (Johto)</option>
<option value="Ruby">Ruby (Hoenn)</option>
<option value="Sapphire">Sapphire (Hoenn)</option>
<option value="Emerald">Emerald (Hoenn)</option>
<option value="OR">OR (Hoenn)</option>
<option value="AS">AS (Hoenn)</option>
<option value="Diamond">Diamond (Sinnoh)</option>
<option value="Pearl">Pearl (Sinnoh)</option>
<option value="Platinum">Platinum (Sinnoh)</option>
<option value="BD">BD (Sinnoh)</option>
<option value="SP">SP (Sinnoh)</option>
<option value="Black">Black (Unova)</option>
<option value="White">White (Unova)</option>
<option value="Black2">Black2 (Unova)</option>
<option value="White2">White2 (Unova)</option>
<option value="Dream Radar">Dream Radar (Unova)</option>
<option value="X">X (Kalos)</option>
<option value="Y">Y (Kalos)</option>
<option value="Sun">Sun (Alola)</option>
<option value="Moon">Moon (Alola)</option>
<option value="Moon Demo">Moon Demo (Alola)</option>
<option value="US">US (Alola)</option>
<option value="UM">UM (Alola)</option>
<option value="Sword">Sword (Galar)</option>
<option value="Shield">Shield (Galar)</option>
<option value="PLA">PLA (Hisui)</option>
<option value="Scarlet">Scarlet (Paldea)</option>
<option value="Violet">Violet (Paldea)</option>
<option value="Home">Home (Unknown)</option>
<option value="Go">Go (Unknown)</option>
</select>
</div>
{#if !viewAsBoxes}
<h2 class="text-2xl font-semibold mb-4">Paging</h2>
<div>
<Pagination bind:currentPage bind:itemsPerPage bind:totalPages />
</div>
{/if}
</aside>
@@ -1,14 +1,13 @@
<script lang="ts">
import type { CatchRecord } from '$lib/models/CatchRecord';
import type { CombinedData } from '$lib/models/CombinedData';
import type { PokedexEntry } from '$lib/models/PokedexEntry';
import { calculateBoxPlacement } from '$lib/utils/boxPlacement';
import PokemonSprite from '$lib/components/PokemonSprite.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
export let showShiny = false;
export let combinedData: CombinedData[] | null;
export let boxNumbers = Array<number>;
export let currentPlacement = 'boxPlacementForms';
export let boxNumbers: number[] = [];
export let creatingRecords = false;
export let totalRecordsCreated = 0;
export let failedToLoad = false;
@@ -18,6 +17,7 @@
export let markBoxAsInHome = (boxNumber: number) => {};
export let markBoxAsNotInHome = (boxNumber: number) => {};
export let createCatchRecords = () => {};
export let onPokemonClick: (pokemon: CombinedData) => void = () => {};
function cellBackgroundColourClass(catchRecord: CatchRecord | null) {
if (catchRecord?.caught) {
@@ -29,11 +29,12 @@
}
}
function cellBackgroundColourStyle(pokedexEntry: PokedexEntry, catchRecord: CatchRecord | null) {
function cellBackgroundColourStyle(index: number, catchRecord: CatchRecord | null) {
if (catchRecord?.caught || catchRecord?.haveToEvolve) {
return '';
} else {
if (pokedexEntry[currentPlacement].column % 2 === 0) {
const placement = calculateBoxPlacement(index);
if (placement.column % 2 === 0) {
return 'background-color: #ffffff';
} else {
return 'background-color: #f9f9f9;';
@@ -50,29 +51,34 @@
{#each boxNumbers as boxNumber}
<div class="mb-8 md:w-1/2 px-2">
<h2 class="text-xl font-bold mb-4">Box {boxNumber}</h2>
<button class="btn" on:click={markBoxAsNotCaught(boxNumber)}>
<button class="btn" on:click={() => markBoxAsNotCaught(boxNumber)}>
Mark box as not 'caught'
</button>
<button class="btn" on:click={markBoxAsCaught(boxNumber)}>
<button class="btn" on:click={() => markBoxAsCaught(boxNumber)}>
Mark box as 'caught'
</button>
<button class="btn" on:click={markBoxAsNeedsToEvolve(boxNumber)}
<button class="btn" on:click={() => markBoxAsNeedsToEvolve(boxNumber)}
>Mark box as 'needs to evolve'</button
>
<button class="btn" on:click={markBoxAsInHome(boxNumber)}
<button class="btn" on:click={() => markBoxAsInHome(boxNumber)}
>Mark box as 'in Home'</button
>
<button class="btn" on:click={markBoxAsNotInHome(boxNumber)}
<button class="btn" on:click={() => markBoxAsNotInHome(boxNumber)}
>Mark box as not 'in Home'</button
>
<div class="grid grid-cols-6">
{#each combinedData as { pokedexEntry, catchRecord }}
{#if pokedexEntry[currentPlacement].box === boxNumber}
<div
class="pokemon-box {cellBackgroundColourClass(catchRecord)}"
style="grid-column-start: {pokedexEntry[currentPlacement]
.column}; grid-row-start: {pokedexEntry[currentPlacement].row};
{cellBackgroundColourStyle(pokedexEntry, catchRecord)}"
{#each combinedData as { pokedexEntry, catchRecord }, index}
{@const placement = calculateBoxPlacement(index)}
{#if placement.box === boxNumber}
<button
type="button"
class="pokemon-box {cellBackgroundColourClass(
catchRecord
)} hover:scale-105 hover:shadow-lg hover:z-50 transition-all cursor-pointer relative"
style="grid-column-start: {placement.column}; grid-row-start: {placement.row};
{cellBackgroundColourStyle(index, catchRecord)}"
on:click={() => onPokemonClick({ pokedexEntry, catchRecord })}
aria-label="View details for {pokedexEntry.pokemon}"
>
<Tooltip>
<div slot="hover-target">
@@ -103,7 +109,7 @@
</div>
</div>
</Tooltip>
</div>
</button>
{/if}
{/each}
</div>
@@ -1,53 +0,0 @@
<script lang="ts">
import PokedexEntryCatchRecord from './PokedexEntryCatchRecord.svelte';
import type { CombinedData } from '$lib/models/CombinedData';
export let showOrigins = true;
export let showForms = true;
export let showShiny = false;
export let combinedData: CombinedData[] | null;
export let creatingRecords = false;
export let totalRecordsCreated = 0;
export let failedToLoad = false;
export let updateACatch = (event: any) => {};
export let createCatchRecords = () => {};
export let userId: string | null = null;
</script>
<main class="flex-1 p-4 w-full">
<div class="max-w-min mx-auto">
{#if combinedData && combinedData.length > 0}
{#each combinedData as { pokedexEntry, catchRecord }}
<PokedexEntryCatchRecord
{pokedexEntry}
{showOrigins}
{showForms}
{showShiny}
{userId}
bind:catchRecord
on:updateCatch={updateACatch}
/>
{/each}
{:else if failedToLoad}
{#if creatingRecords && totalRecordsCreated > 0}
<p>Processed {totalRecordsCreated} Pokédex entries so far...</p>
<p>Please be patient, this may take some time.</p>
{:else if creatingRecords}
<p>Processing...</p>
<p>Please be patient, this may take some time.</p>
{:else}
<h1>Failed to load</h1>
<p>
If you're seeing this, you probably haven't created your Pokédex data yet. Please do so by
clicking this button.
</p>
<button class="btn" on:click={createCatchRecords}>Create Pokédex data</button>
{/if}
{:else}
<div class="min-w-max mx-auto">
<h1>Loading Pokédex</h1>
<span class="loading loading-spinner loading-xl"></span>
</div>
{/if}
</div>
</main>
+2
View File
@@ -3,6 +3,7 @@ export interface CatchRecord {
_id: string; // Maps to Supabase 'id' field for frontend compatibility
userId: string;
pokedexEntryId: string; // String representation of the foreign key
pokedexId: string; // NEW: Foreign key to pokedexes table
haveToEvolve: boolean;
caught: boolean;
inHome: boolean;
@@ -15,6 +16,7 @@ export interface CatchRecordDB {
id: string;
userId: string;
pokedexEntryId: number; // Numeric foreign key in database
pokedexId: string; // NEW: Foreign key to pokedexes table
haveToEvolve: boolean;
caught: boolean;
inHome: boolean;
+25
View File
@@ -0,0 +1,25 @@
export interface Pokedex {
_id: string;
userId: string;
name: string;
description: string;
isLivingDex: boolean;
isShinyDex: boolean;
isOriginDex: boolean;
isFormDex: boolean;
gameScope: string | null;
}
export interface PokedexDB {
id: string;
userId: string;
name: string;
description: string;
isLivingDex: boolean;
isShinyDex: boolean;
isOriginDex: boolean;
isFormDex: boolean;
gameScope: string | null;
createdAt: string;
updatedAt: string;
}
+2 -8
View File
@@ -2,8 +2,6 @@
export interface PokedexEntry {
_id: string; // Maps to Supabase 'id' field for frontend compatibility
pokedexNumber: number;
boxPlacement: { box: number; row: number; column: number };
boxPlacementForms: { box: number; row: number; column: number };
pokemon: string;
form: string;
canGigantamax: boolean;
@@ -12,6 +10,7 @@ export interface PokedexEntry {
regionToEvolveIn: string;
evolutionInformation: string;
catchInformation: string[];
// Note: Regional dex numbers stored in separate regional_dex_numbers table
}
// Database record type for Supabase
@@ -26,12 +25,7 @@ export interface PokedexEntryDB {
regionToEvolveIn: string | null;
evolutionInformation: string | null;
catchInformation: string[] | null;
boxPlacementFormsBox: number | null;
boxPlacementFormsRow: number | null;
boxPlacementFormsColumn: number | null;
boxPlacementBox: number | null;
boxPlacementRow: number | null;
boxPlacementColumn: number | null;
// Note: Regional dex numbers stored in separate regional_dex_numbers table
createdAt: string;
updatedAt: string;
}
+17 -7
View File
@@ -4,7 +4,8 @@ import type { SupabaseClient } from '@supabase/supabase-js';
class CatchRecordRepository {
constructor(
private supabase: SupabaseClient,
private userId: string
private userId: string,
private pokedexId: string
) {}
// Transform Supabase data to frontend format (minimal transformation)
@@ -13,6 +14,7 @@ class CatchRecordRepository {
_id: record.id,
userId: record.userId,
pokedexEntryId: record.pokedexEntryId.toString(),
pokedexId: record.pokedexId,
haveToEvolve: record.haveToEvolve,
caught: record.caught,
inHome: record.inHome,
@@ -31,6 +33,7 @@ class CatchRecordRepository {
}
dbData.pokedexEntryId = numericId;
}
if (data.pokedexId !== undefined) dbData.pokedexId = data.pokedexId;
if (data.haveToEvolve !== undefined) dbData.haveToEvolve = data.haveToEvolve;
if (data.caught !== undefined) dbData.caught = data.caught;
if (data.inHome !== undefined) dbData.inHome = data.inHome;
@@ -46,6 +49,7 @@ class CatchRecordRepository {
.select('*')
.eq('id', id)
.eq('userId', this.userId)
.eq('pokedexId', this.pokedexId)
.single();
if (error || !data) return null;
@@ -56,7 +60,8 @@ class CatchRecordRepository {
const { data, error } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', this.userId);
.eq('userId', this.userId)
.eq('pokedexId', this.pokedexId);
if (error || !data) return [];
return data.map((record) => this.transformCatchRecord(record));
@@ -66,7 +71,8 @@ class CatchRecordRepository {
const { data, error } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', userId);
.eq('userId', userId)
.eq('pokedexId', this.pokedexId);
if (error || !data) return [];
return data.map((record) => this.transformCatchRecord(record));
@@ -75,6 +81,7 @@ class CatchRecordRepository {
async create(data: Partial<CatchRecord>): Promise<CatchRecord> {
const dbData = {
userId: this.userId,
pokedexId: this.pokedexId,
...this.transformToDatabase(data)
};
@@ -104,6 +111,7 @@ class CatchRecordRepository {
.update(dbData)
.eq('id', id)
.eq('userId', this.userId)
.eq('pokedexId', this.pokedexId)
.select()
.single();
@@ -116,7 +124,8 @@ class CatchRecordRepository {
.from('catch_records')
.delete()
.eq('id', id)
.eq('userId', this.userId);
.eq('userId', this.userId)
.eq('pokedexId', this.pokedexId);
if (error) {
console.error('Error deleting catch record:', error);
@@ -128,8 +137,8 @@ class CatchRecordRepository {
if (data._id) {
return this.update(data._id, data);
} else if (data.pokedexEntryId) {
// Try to find existing record for this user and pokemon
const existing = await this.findByUserAndPokemon(this.userId, data.pokedexEntryId);
// Try to find existing record for this user, pokemon, and pokedex
const existing = await this.findByUserAndPokemon(this.userId, data.pokedexEntryId, this.pokedexId);
if (existing) {
return this.update(existing._id, data);
} else {
@@ -139,7 +148,7 @@ class CatchRecordRepository {
return this.create(data);
}
async findByUserAndPokemon(userId: string, pokedexEntryId: string): Promise<CatchRecord | null> {
async findByUserAndPokemon(userId: string, pokedexEntryId: string, pokedexId: string): Promise<CatchRecord | null> {
const numericId = Number(pokedexEntryId);
if (isNaN(numericId)) {
return null;
@@ -149,6 +158,7 @@ class CatchRecordRepository {
.select('*')
.eq('userId', userId)
.eq('pokedexEntryId', numericId)
.eq('pokedexId', pokedexId)
.single();
if (error || !data) return null;
+30 -48
View File
@@ -6,7 +6,8 @@ import type { SupabaseClient } from '@supabase/supabase-js';
class CombinedDataRepository {
constructor(
private supabase: SupabaseClient,
private userId: string
private userId: string | null,
private pokedexId: string | null
) {}
// Transform Supabase data to match frontend expectations (minimal transformation)
@@ -21,17 +22,8 @@ class CombinedDataRepository {
gamesToCatchIn: entry.gamesToCatchIn || [],
regionToEvolveIn: entry.regionToEvolveIn || '',
evolutionInformation: entry.evolutionInformation || '',
catchInformation: entry.catchInformation || [],
boxPlacementForms: {
box: entry.boxPlacementFormsBox || 0,
row: entry.boxPlacementFormsRow || 0,
column: entry.boxPlacementFormsColumn || 0
},
boxPlacement: {
box: entry.boxPlacementBox || 0,
row: entry.boxPlacementRow || 0,
column: entry.boxPlacementColumn || 0
}
catchInformation: entry.catchInformation || []
// Note: Regional dex numbers are stored in separate regional_dex_numbers table
};
}
@@ -40,6 +32,7 @@ class CombinedDataRepository {
_id: record.id,
userId: record.userId,
pokedexEntryId: record.pokedexEntryId.toString(),
pokedexId: record.pokedexId,
haveToEvolve: record.haveToEvolve,
caught: record.caught,
inHome: record.inHome,
@@ -58,7 +51,8 @@ class CombinedDataRepository {
// Apply filters
if (!enableForms) {
query = query.not('boxPlacementBox', 'is', null);
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
@@ -69,18 +63,9 @@ class CombinedDataRepository {
query = query.contains('gamesToCatchIn', [game]);
}
// Order by box placement
if (enableForms) {
query = query
.order('boxPlacementFormsBox', { ascending: true })
.order('boxPlacementFormsRow', { ascending: true })
.order('boxPlacementFormsColumn', { ascending: true });
} else {
query = query
.order('boxPlacementBox', { ascending: true })
.order('boxPlacementRow', { ascending: true })
.order('boxPlacementColumn', { ascending: true });
}
// Always order by national dex number
// Note: Regional dex ordering would require joining with regional_dex_numbers table
query = query.order('pokedexNumber', { ascending: true });
const { data: entries, error: entriesError } = await query;
@@ -95,12 +80,13 @@ class CombinedDataRepository {
// Get catch records for all entries
let catchRecords: CatchRecordDB[] = [];
if (userId) {
if (userId && this.pokedexId) {
const entryIds = entries.map((entry) => entry.id);
const { data: records, error: recordsError } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokedexEntryId', entryIds);
if (!recordsError && records) {
@@ -109,7 +95,7 @@ class CombinedDataRepository {
}
// Combine the data exactly like master branch
return entries.map((entry) => {
const combinedData = entries.map((entry) => {
const userCatchRecord =
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
@@ -123,6 +109,9 @@ class CombinedDataRepository {
catchRecord: transformedCatchRecord
};
});
// Box placement is calculated dynamically on the frontend based on the current sort order.
return combinedData;
}
async findCombinedData(
@@ -140,7 +129,8 @@ class CombinedDataRepository {
// Apply filters
if (!enableForms) {
query = query.not('boxPlacementBox', 'is', null);
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
@@ -151,18 +141,9 @@ class CombinedDataRepository {
query = query.contains('gamesToCatchIn', [game]);
}
// Order by box placement
if (enableForms) {
query = query
.order('boxPlacementFormsBox', { ascending: true })
.order('boxPlacementFormsRow', { ascending: true })
.order('boxPlacementFormsColumn', { ascending: true });
} else {
query = query
.order('boxPlacementBox', { ascending: true })
.order('boxPlacementRow', { ascending: true })
.order('boxPlacementColumn', { ascending: true });
}
// Always order by national dex number
// Note: Regional dex ordering would require joining with regional_dex_numbers table
query = query.order('pokedexNumber', { ascending: true });
const { data: entries, error: entriesError } = await query;
@@ -177,12 +158,13 @@ class CombinedDataRepository {
// Get catch records for these entries
let catchRecords: CatchRecordDB[] = [];
if (userId) {
if (userId && this.pokedexId) {
const entryIds = entries.map((entry) => entry.id);
const { data: records, error: recordsError } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokedexEntryId', entryIds);
if (!recordsError && records) {
@@ -191,7 +173,7 @@ class CombinedDataRepository {
}
// Combine the data exactly like master branch
return entries.map((entry) => {
const combinedData = entries.map((entry) => {
const userCatchRecord =
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
@@ -205,18 +187,18 @@ class CombinedDataRepository {
catchRecord: transformedCatchRecord
};
});
// Box placement is calculated dynamically on the frontend based on the current sort order.
return combinedData;
}
async countCombinedData(
enableForms: boolean,
region: string,
game: string
): Promise<number> {
async countCombinedData(enableForms: boolean, region: string, game: string): Promise<number> {
let query = this.supabase.from('pokedex_entries').select('id', { count: 'exact', head: true });
// Apply same filters as in findCombinedData
if (!enableForms) {
query = query.not('boxPlacementBox', 'is', null);
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
+3 -31
View File
@@ -16,17 +16,7 @@ class PokedexEntryRepository {
gamesToCatchIn: entry.gamesToCatchIn || [],
regionToEvolveIn: entry.regionToEvolveIn || '',
evolutionInformation: entry.evolutionInformation || '',
catchInformation: entry.catchInformation || [],
boxPlacementForms: {
box: entry.boxPlacementFormsBox || 0,
row: entry.boxPlacementFormsRow || 0,
column: entry.boxPlacementFormsColumn || 0
},
boxPlacement: {
box: entry.boxPlacementBox || 0,
row: entry.boxPlacementRow || 0,
column: entry.boxPlacementColumn || 0
}
catchInformation: entry.catchInformation || []
};
}
@@ -64,16 +54,7 @@ class PokedexEntryRepository {
if (data.evolutionInformation !== undefined)
dbData.evolutionInformation = data.evolutionInformation;
if (data.catchInformation !== undefined) dbData.catchInformation = data.catchInformation;
if (data.boxPlacementForms?.box !== undefined)
dbData.boxPlacementFormsBox = data.boxPlacementForms.box;
if (data.boxPlacementForms?.row !== undefined)
dbData.boxPlacementFormsRow = data.boxPlacementForms.row;
if (data.boxPlacementForms?.column !== undefined)
dbData.boxPlacementFormsColumn = data.boxPlacementForms.column;
if (data.boxPlacement?.box !== undefined) dbData.boxPlacementBox = data.boxPlacement.box;
if (data.boxPlacement?.row !== undefined) dbData.boxPlacementRow = data.boxPlacement.row;
if (data.boxPlacement?.column !== undefined)
dbData.boxPlacementColumn = data.boxPlacement.column;
// Box placement is calculated dynamically - not stored in database
const { data: result, error } = await this.supabase
.from('pokedex_entries')
@@ -98,16 +79,7 @@ class PokedexEntryRepository {
if (data.evolutionInformation !== undefined)
dbData.evolutionInformation = data.evolutionInformation;
if (data.catchInformation !== undefined) dbData.catchInformation = data.catchInformation;
if (data.boxPlacementForms?.box !== undefined)
dbData.boxPlacementFormsBox = data.boxPlacementForms.box;
if (data.boxPlacementForms?.row !== undefined)
dbData.boxPlacementFormsRow = data.boxPlacementForms.row;
if (data.boxPlacementForms?.column !== undefined)
dbData.boxPlacementFormsColumn = data.boxPlacementForms.column;
if (data.boxPlacement?.box !== undefined) dbData.boxPlacementBox = data.boxPlacement.box;
if (data.boxPlacement?.row !== undefined) dbData.boxPlacementRow = data.boxPlacement.row;
if (data.boxPlacement?.column !== undefined)
dbData.boxPlacementColumn = data.boxPlacement.column;
// Box placement is calculated dynamically - not stored in database
const { data: result, error } = await this.supabase
.from('pokedex_entries')
+108
View File
@@ -0,0 +1,108 @@
import type { Pokedex, PokedexDB } from '$lib/models/Pokedex';
import type { SupabaseClient } from '@supabase/supabase-js';
class PokedexRepository {
constructor(
private supabase: SupabaseClient,
private userId: string
) {}
private transform(db: PokedexDB): Pokedex {
return {
_id: db.id,
userId: db.userId,
name: db.name,
description: db.description || '',
isLivingDex: db.isLivingDex,
isShinyDex: db.isShinyDex,
isOriginDex: db.isOriginDex,
isFormDex: db.isFormDex,
gameScope: db.gameScope
};
}
private transformToDatabase(data: Partial<Pokedex>): Partial<PokedexDB> {
const dbData: Partial<PokedexDB> = {};
if (data.name !== undefined) dbData.name = data.name;
if (data.description !== undefined) dbData.description = data.description;
if (data.isLivingDex !== undefined) dbData.isLivingDex = data.isLivingDex;
if (data.isShinyDex !== undefined) dbData.isShinyDex = data.isShinyDex;
if (data.isOriginDex !== undefined) dbData.isOriginDex = data.isOriginDex;
if (data.isFormDex !== undefined) dbData.isFormDex = data.isFormDex;
if (data.gameScope !== undefined) dbData.gameScope = data.gameScope;
return dbData;
}
async findById(id: string): Promise<Pokedex | null> {
const { data, error } = await this.supabase
.from('pokedexes')
.select('*')
.eq('id', id)
.eq('userId', this.userId)
.single();
if (error || !data) return null;
return this.transform(data);
}
async findAll(): Promise<Pokedex[]> {
const { data, error } = await this.supabase
.from('pokedexes')
.select('*')
.eq('userId', this.userId)
.order('createdAt', { ascending: true });
if (error || !data) return [];
return data.map((d) => this.transform(d));
}
async create(data: Partial<Pokedex>): Promise<Pokedex> {
const dbData = {
userId: this.userId,
...this.transformToDatabase(data)
};
const { data: result, error } = await this.supabase
.from('pokedexes')
.insert(dbData)
.select()
.single();
if (error) {
console.error('Error creating pokedex:', error);
throw new Error(`Failed to create pokedex: ${error.message}`);
}
if (!result) {
throw new Error('Failed to create pokedex: No result returned');
}
return this.transform(result);
}
async update(id: string, data: Partial<Pokedex>): Promise<Pokedex | null> {
const dbData = this.transformToDatabase(data);
const { data: result, error } = await this.supabase
.from('pokedexes')
.update(dbData)
.eq('id', id)
.eq('userId', this.userId)
.select()
.single();
if (error || !result) return null;
return this.transform(result);
}
async delete(id: string): Promise<void> {
const { error } = await this.supabase.from('pokedexes').delete().eq('id', id).eq('userId', this.userId);
if (error) {
console.error('Error deleting pokedex:', error);
throw new Error(`Failed to delete pokedex: ${error.message}`);
}
}
}
export default PokedexRepository;
+41
View File
@@ -0,0 +1,41 @@
/**
* Calculate box placement for Pokémon based on their position in a sorted list
*
* Box layout: 6 columns × 5 rows = 30 Pokémon per box
*
* @param index - Zero-based index in the sorted list
* @returns Box placement with box number, row, and column (1-indexed)
*/
export function calculateBoxPlacement(index: number): {
box: number;
row: number;
column: number;
} {
const POKEMON_PER_BOX = 30;
const COLUMNS_PER_BOX = 6;
const ROWS_PER_BOX = 5;
// Calculate which box this Pokémon belongs to (1-indexed)
const box = Math.floor(index / POKEMON_PER_BOX) + 1;
// Calculate position within the box (0-indexed)
const positionInBox = index % POKEMON_PER_BOX;
// Calculate row and column (1-indexed)
const row = Math.floor(positionInBox / COLUMNS_PER_BOX) + 1;
const column = (positionInBox % COLUMNS_PER_BOX) + 1;
return { box, row, column };
}
/**
* Calculate all unique box numbers needed for a list of Pokémon
*
* @param count - Total number of Pokémon
* @returns Array of box numbers (1-indexed)
*/
export function calculateBoxNumbers(count: number): number[] {
const POKEMON_PER_BOX = 30;
const totalBoxes = Math.ceil(count / POKEMON_PER_BOX);
return Array.from({ length: totalBoxes }, (_, i) => i + 1);
}
+141
View File
@@ -0,0 +1,141 @@
/**
* Maps game names to their corresponding regional Pokédex identifier.
* This mapping is used to determine which regional dex number to use when
* filtering and ordering Pokémon for a specific game.
*/
type RegionalDexKey =
| 'kanto'
| 'johto'
| 'hoenn'
| 'sinnoh'
| 'unova_bw'
| 'unova_b2w2'
| 'kalos_central'
| 'kalos_coastal'
| 'kalos_mountain'
| 'alola_sm'
| 'alola_usum'
| 'galar'
| 'galar_isle_of_armor'
| 'galar_crown_tundra'
| 'hisui'
| 'paldea';
const gameToRegionalDexMap: Record<string, RegionalDexKey> = {
// Kanto region
'Red': 'kanto',
'Blue': 'kanto',
'Yellow': 'kanto',
'FireRed': 'kanto',
'LeafGreen': 'kanto',
'LG: Pikachu': 'kanto',
'LG: Eevee': 'kanto',
// Johto region
'Gold': 'johto',
'Silver': 'johto',
'Crystal': 'johto',
'HeartGold': 'johto',
'SoulSilver': 'johto',
// Hoenn region
'Ruby': 'hoenn',
'Sapphire': 'hoenn',
'Emerald': 'hoenn',
'OmegaRuby': 'hoenn',
'AlphaSapphire': 'hoenn',
// Sinnoh region
'Diamond': 'sinnoh',
'Pearl': 'sinnoh',
'Platinum': 'sinnoh',
'BrilliantDiamond': 'sinnoh',
'ShiningPearl': 'sinnoh',
// Unova region
'Black': 'unova_bw',
'White': 'unova_bw',
'Black2': 'unova_b2w2',
'White2': 'unova_b2w2',
// Kalos region - Note: All XY use all three sub-dexes
// We default to Central for simplicity
'X': 'kalos_central',
'Y': 'kalos_central',
// Alola region
'Sun': 'alola_sm',
'Moon': 'alola_sm',
'UltraSun': 'alola_usum',
'UltraMoon': 'alola_usum',
// Galar region
'Sword': 'galar',
'Shield': 'galar',
// Hisui region
'LegendsArceus': 'hisui',
// Paldea region
'Scarlet': 'paldea',
'Violet': 'paldea'
};
/**
* Gets the regional dex key for a given game name.
* Returns undefined if the game doesn't have a regional dex mapping.
*/
export function getRegionalDexKey(gameName: string): RegionalDexKey | undefined {
return gameToRegionalDexMap[gameName];
}
/**
* Checks if a game has a regional dex mapping.
*/
export function hasRegionalDex(gameName: string): boolean {
return gameName in gameToRegionalDexMap;
}
/**
* Gets the database column name for a game's regional dex.
* Maps game name → database column name (e.g., 'Scarlet' → 'paldea_dex_number').
* Returns undefined if the game doesn't have a regional dex mapping.
*/
export function getRegionalDexColumnName(gameName: string): string | undefined {
const regionalKey = getRegionalDexKey(gameName);
if (!regionalKey) return undefined;
return `${regionalKey}_dex_number`;
}
/**
* Gets the frontend field name for a game's regional dex.
* Maps game name → PokedexEntry field name (e.g., 'Scarlet' → 'paldeaDexNumber').
* Returns undefined if the game doesn't have a regional dex mapping.
*/
export function getRegionalDexFieldName(gameName: string): string | undefined {
const regionalKey = getRegionalDexKey(gameName);
if (!regionalKey) return undefined;
// Map regional key to actual PokedexEntry field name (camelCase)
const fieldMap: Record<string, string> = {
'kanto': 'kantoDexNumber',
'johto': 'johtoDexNumber',
'hoenn': 'hoennDexNumber',
'sinnoh': 'sinnohDexNumber',
'unova_bw': 'unovaBwDexNumber',
'unova_b2w2': 'unovaB2w2DexNumber',
'kalos_central': 'kalosCentralDexNumber',
'kalos_coastal': 'kalosCoastalDexNumber',
'kalos_mountain': 'kalosMountainDexNumber',
'alola_sm': 'alolaSmDexNumber',
'alola_usum': 'alolaUsumDexNumber',
'galar': 'galarDexNumber',
'galar_isle_of_armor': 'galarIsleOfArmorDexNumber',
'galar_crown_tundra': 'galarCrownTundraDexNumber',
'hisui': 'hisuiDexNumber',
'paldea': 'paldeaDexNumber'
};
return fieldMap[regionalKey];
}
+1 -1
View File
@@ -149,7 +149,7 @@
>
{#if localUser}
<li>
<a href="/mydex"> My Dex </a>
<a href="/my-pokedexes"> My Pokédexes </a>
</li>
<li>
<a href="/profile"> Profile </a>
-52
View File
@@ -1,52 +0,0 @@
import { json } from '@sveltejs/kit';
import CombinedDataRepository from '$lib/repositories/CombinedDataRepository';
import { getOptionalUserId } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
export const GET = async (event: RequestEvent) => {
const { url } = event;
const page = parseInt(url.searchParams.get('page') || '1', 10);
const limit = parseInt(url.searchParams.get('limit') || '20', 10);
const enableForms = url.searchParams.get('enableForms') === 'true';
const region = url.searchParams.get('region') || '';
const game = url.searchParams.get('game') || '';
try {
// Get userId if authenticated, null if not
const userId = await getOptionalUserId(event);
// If user is authenticated, set their session on the Supabase client
if (userId) {
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
}
const repo = new CombinedDataRepository(event.locals.supabase, userId || '');
const combinedData = await repo.findCombinedData(
userId || '',
page,
limit,
enableForms,
region,
game
);
// Return empty array instead of 404 for better UX
if (combinedData.length === 0) {
return json({ combinedData: [], totalPages: 0 });
}
const totalCount = await repo.countCombinedData(enableForms, region, game);
const totalPages = Math.ceil(totalCount / limit);
return json({ combinedData, totalPages });
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
@@ -1,36 +0,0 @@
import { json } from '@sveltejs/kit';
import CombinedDataRepository from '$lib/repositories/CombinedDataRepository';
import { getOptionalUserId } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
export const GET = async (event: RequestEvent) => {
const { url } = event;
const enableForms = url.searchParams.get('enableForms') === 'true';
const region = url.searchParams.get('region') || '';
const game = url.searchParams.get('game') || '';
try {
// Get userId if authenticated, null if not
const userId = await getOptionalUserId(event);
// If user is authenticated, set their session on the Supabase client
if (userId) {
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
}
const repo = new CombinedDataRepository(event.locals.supabase, userId || '');
const combinedData = await repo.findAllCombinedData(userId || '', enableForms, region, game);
// Return empty array instead of 404 for better UX
return json(combinedData);
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
+32
View File
@@ -0,0 +1,32 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
/**
* GET /api/games
* Returns all games from the games table, sorted by release year descending (newest first)
*/
export const GET: RequestHandler = async ({ locals }) => {
const { supabase } = locals;
try {
// Query games table for all games, sorted by release year descending
const { data, error } = await supabase
.from('games')
.select('displayName, releaseYear')
.order('releaseYear', { ascending: false })
.order('displayName', { ascending: true }); // Secondary sort by name for same year
if (error) {
console.error('Error fetching games:', error);
return json({ error: 'Failed to fetch games' }, { status: 500 });
}
// Extract just the display names
const gameNames = data.map((row) => row.displayName);
return json({ games: gameNames });
} catch (err) {
console.error('Unexpected error fetching games:', err);
return json({ error: 'Internal server error' }, { status: 500 });
}
};
+47
View File
@@ -0,0 +1,47 @@
import { json } from '@sveltejs/kit';
import type { Pokedex } from '$lib/models/Pokedex';
import PokedexRepository from '$lib/repositories/PokedexRepository';
import { requireAuth } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
// GET: List all pokedexes for user
export const GET = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const repo = new PokedexRepository(event.locals.supabase, userId);
const pokedexes = await repo.findAll();
return json(pokedexes);
} catch (err) {
console.error('Error in GET /api/pokedexes:', err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
// POST: Create new pokedex
export const POST = async (event: RequestEvent) => {
try {
console.log('POST /api/pokedexes - Starting request');
const userId = await requireAuth(event);
console.log('POST /api/pokedexes - User authenticated:', userId);
const data: Partial<Pokedex> = await event.request.json();
console.log('POST /api/pokedexes - Request data:', data);
data.userId = userId;
const repo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await repo.create(data);
console.log('POST /api/pokedexes - Pokédex created:', pokedex._id);
return json(pokedex);
} catch (err) {
console.error('Error in POST /api/pokedexes:', err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
+107
View File
@@ -0,0 +1,107 @@
import { json } from '@sveltejs/kit';
import type { Pokedex } from '$lib/models/Pokedex';
import PokedexRepository from '$lib/repositories/PokedexRepository';
import { requireAuth } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
// GET: Get single pokedex by ID
export const GET = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const { id } = event.params;
if (!id) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
const repo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await repo.findById(id);
if (!pokedex) {
// RLS ensures user can only access own pokédexes
// Return 404 whether it doesn't exist or user doesn't own it (don't leak info)
return json({ error: 'Pokedex not found' }, { status: 404 });
}
return json(pokedex);
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
// PUT: Update pokedex
export const PUT = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const { id } = event.params;
const data: Partial<Pokedex> = await event.request.json();
if (!id) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
const repo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await repo.update(id, data);
if (!pokedex) {
return json({ error: 'Pokedex not found' }, { status: 404 });
}
return json(pokedex);
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
// DELETE: Delete pokedex
export const DELETE = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const { id } = event.params;
if (!id) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
const repo = new PokedexRepository(event.locals.supabase, userId);
// Verify pokedex exists and user owns it
const pokedex = await repo.findById(id);
if (!pokedex) {
return json({ error: 'Pokedex not found' }, { status: 404 });
}
await repo.delete(id);
return json({ success: true });
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
@@ -1,22 +1,35 @@
import { json } from '@sveltejs/kit';
import { type CatchRecord } from '$lib/models/CatchRecord';
import CatchRecordRepository from '$lib/repositories/CatchRecordRepository';
import PokedexRepository from '$lib/repositories/PokedexRepository';
import { requireAuth } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
// GET: Get catch records for specific pokédex
export const GET = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const { id: pokedexId } = event.params;
if (!pokedexId) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
// Get the user's session and set it on the Supabase client
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
const repo = new CatchRecordRepository(event.locals.supabase, userId);
const catchData = await repo.findByUserId(userId);
// order by pokedexEntryId property, ascending
// Verify user owns this pokédex (RLS will also check, but explicit verification is better UX)
const pokedexRepo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await pokedexRepo.findById(pokedexId);
if (!pokedex) {
return json({ error: 'Pokedex not found' }, { status: 404 });
}
const repo = new CatchRecordRepository(event.locals.supabase, userId, pokedexId);
const catchData = await repo.findAll();
const sortedData = catchData.sort(
(a, b) => Number(a.pokedexEntryId) - Number(b.pokedexEntryId)
);
@@ -30,24 +43,35 @@ export const GET = async (event: RequestEvent) => {
}
};
// PUT: Upsert single catch record for specific pokédex
export const PUT = async (event: RequestEvent) => {
try {
// Check if we can get a session first
const { session, user } = await event.locals.safeGetSession();
const userId = await requireAuth(event);
const { id: pokedexId } = event.params;
const data: Partial<CatchRecord> = await event.request.json();
// Get the user's session and set it on the Supabase client
if (!pokedexId) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
// Ensure the userId is set to the authenticated user
data.userId = userId;
// Verify user owns this pokédex
const pokedexRepo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await pokedexRepo.findById(pokedexId);
const repo = new CatchRecordRepository(event.locals.supabase, userId);
if (!pokedex) {
return json({ error: 'Pokedex not found' }, { status: 404 });
}
// Ensure the data is scoped to this pokédex and user
data.userId = userId;
data.pokedexId = pokedexId;
const repo = new CatchRecordRepository(event.locals.supabase, userId, pokedexId);
const upsertedRecord = await repo.upsert(data);
return json(upsertedRecord);
} catch (err) {
@@ -59,23 +83,37 @@ export const PUT = async (event: RequestEvent) => {
}
};
// POST: Bulk upsert catch records for specific pokédex
export const POST = async (event: RequestEvent) => {
try {
const userId = await requireAuth(event);
const { id: pokedexId } = event.params;
const records: Partial<CatchRecord>[] = await event.request.json();
// Get the user's session and set it on the Supabase client
if (!pokedexId) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
const { session } = await event.locals.safeGetSession();
if (session) {
await event.locals.supabase.auth.setSession(session);
}
const repo = new CatchRecordRepository(event.locals.supabase, userId);
// Verify user owns this pokédex
const pokedexRepo = new PokedexRepository(event.locals.supabase, userId);
const pokedex = await pokedexRepo.findById(pokedexId);
if (!pokedex) {
return json({ error: 'Pokedex not found' }, { status: 404 });
}
const repo = new CatchRecordRepository(event.locals.supabase, userId, pokedexId);
const insertedRecords = [];
for (const record of records) {
// Ensure the userId is set to the authenticated user
// Ensure the data is scoped to this pokédex and user
record.userId = userId;
record.pokedexId = pokedexId;
const upsertedRecord = await repo.upsert(record);
insertedRecords.push(upsertedRecord);
}
@@ -0,0 +1,72 @@
import { json } from '@sveltejs/kit';
import CombinedDataRepository from '$lib/repositories/CombinedDataRepository';
import PokedexRepository from '$lib/repositories/PokedexRepository';
import { getOptionalUserId } from '$lib/utils/auth';
import type { RequestEvent } from '@sveltejs/kit';
// GET: Get combined data (pokédex entries + catch records) for specific pokédex
export const GET = async (event: RequestEvent) => {
try {
const userId = await getOptionalUserId(event);
const { id: pokedexId } = event.params;
if (!pokedexId) {
return json({ error: 'Pokedex ID is required' }, { status: 400 });
}
// Parse query parameters
const url = new URL(event.request.url);
const page = parseInt(url.searchParams.get('page') || '1');
const limit = parseInt(url.searchParams.get('limit') || '20');
const enableForms = url.searchParams.get('enableForms') === 'true';
const region = url.searchParams.get('region') || '';
const game = url.searchParams.get('game') || '';
// If authenticated, verify user owns this pokédex and get its gameScope
let pokedex;
if (userId) {
const pokedexRepo = new PokedexRepository(event.locals.supabase, userId);
pokedex = await pokedexRepo.findById(pokedexId);
if (!pokedex) {
// User is authenticated but doesn't own this pokédex (or it doesn't exist)
return json({ error: 'Pokedex not found' }, { status: 404 });
}
} else {
// Anonymous users cannot view pokédexes
return json({ error: 'Unauthorized' }, { status: 401 });
}
// Use pokédex's gameScope as default filter if no manual game filter is set
const effectiveGame = game || pokedex.gameScope || '';
const repo = new CombinedDataRepository(event.locals.supabase, userId, pokedexId);
// Get paginated combined data
const combinedData = await repo.findCombinedData(
userId!,
page,
limit,
enableForms,
region,
effectiveGame
);
// Get total count for pagination
const totalCount = await repo.countCombinedData(enableForms, region, effectiveGame);
const totalPages = Math.ceil(totalCount / limit);
return json({
combinedData,
totalPages,
currentPage: page,
totalCount
});
} catch (err) {
console.error(err);
if (err && typeof err === 'object' && 'status' in err) {
throw err;
}
return json({ error: 'Internal Server Error' }, { status: 500 });
}
};
-99
View File
@@ -1,99 +0,0 @@
import { json } from '@sveltejs/kit';
import { createClient } from '@supabase/supabase-js';
import { PUBLIC_SUPABASE_URL } from '$env/static/public';
import { SUPABASE_SERVICE_ROLE_KEY } from '$env/static/private';
import dex from '$lib/helpers/pokedex.json';
import RegionGameMappingJson from '$lib/helpers/region-game-mapping.json';
export const GET = async () => {
// Only allow seeding in development
if (process.env.NODE_ENV === 'production') {
return json({ message: 'Seeding not allowed in production' }, { status: 403 });
}
// Use service role key to bypass RLS for seeding operations
const supabase = createClient(PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
try {
// Seed Pokédex entries
const { count: entriesCount, error: countError } = await supabase
.from('pokedex_entries')
.select('*', { count: 'exact', head: true });
if (countError) {
throw new Error(`Failed to check existing entries: ${countError.message}`);
}
// Only seed if table is empty
if (!entriesCount || entriesCount === 0) {
console.log('Seeding Pokédex entries...');
// Transform data to match database schema
const pokemonData = dex.map((pokemon: any) => ({
pokedexNumber: pokemon.pokedexNumber,
pokemon: pokemon.pokemon,
form: pokemon.form || null,
canGigantamax: pokemon.canGigantamax || false,
regionToCatchIn: pokemon.regionToCatchIn || null,
gamesToCatchIn: pokemon.gamesToCatchIn || null,
regionToEvolveIn: pokemon.regionToEvolveIn || null,
evolutionInformation: pokemon.evolutionInformation || null,
catchInformation: pokemon.catchInformation || null,
boxPlacementFormsBox: pokemon.boxPlacementForms?.box || null,
boxPlacementFormsRow: pokemon.boxPlacementForms?.row || null,
boxPlacementFormsColumn: pokemon.boxPlacementForms?.column || null,
boxPlacementBox: pokemon.boxPlacement?.box || null,
boxPlacementRow: pokemon.boxPlacement?.row || null,
boxPlacementColumn: pokemon.boxPlacement?.column || null
}));
const { error: insertError } = await supabase.from('pokedex_entries').insert(pokemonData);
if (insertError) {
throw new Error(`Failed to seed Pokédex entries: ${insertError.message}`);
}
console.log(`Seeded ${pokemonData.length} Pokédex entries`);
} else {
console.log('Pokédex entries already exist, skipping seeding');
}
// Seed region-game mappings (check if table exists first)
const { count: mappingsCount, error: mappingCountError } = await supabase
.from('region_game_mappings')
.select('*', { count: 'exact', head: true });
if (!mappingCountError && (!mappingsCount || mappingsCount === 0)) {
console.log('Seeding region-game mappings...');
const { error: mappingInsertError } = await supabase
.from('region_game_mappings')
.insert(RegionGameMappingJson);
if (mappingInsertError) {
throw new Error(`Failed to seed region-game mappings: ${mappingInsertError.message}`);
}
console.log(`Seeded ${RegionGameMappingJson.length} region-game mappings`);
} else if (!mappingCountError) {
console.log('Region-game mappings already exist, skipping seeding');
}
return json({
message: 'Seeding completed successfully',
seeded: {
pokemonEntries: !entriesCount || entriesCount === 0,
regionGameMappings: !mappingCountError && (!mappingsCount || mappingsCount === 0)
}
});
} catch (error) {
console.error('Seeding failed:', error);
return json(
{
message: 'Seeding failed',
error: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
);
}
};
+1 -1
View File
@@ -8,7 +8,7 @@ export const GET = async (event) => {
} = event;
const token_hash = url.searchParams.get('token_hash') as string;
const type = url.searchParams.get('type') as EmailOtpType | null;
const next = url.searchParams.get('next') ?? '/';
const next = url.searchParams.get('next') ?? '/welcome';
if (token_hash && type) {
const { error } = await supabase.auth.verifyOtp({ token_hash, type });
+196
View File
@@ -0,0 +1,196 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { user } from '$lib/stores/user';
import type { Pokedex } from '$lib/models/Pokedex';
import PokedexCard from '$lib/components/pokedex/PokedexCard.svelte';
import PokedexForm from '$lib/components/pokedex/PokedexForm.svelte';
let pokedexes: Pokedex[] = [];
let showModal = false;
let editingPokedex: Pokedex | null = null;
let formData: Partial<Pokedex> = {
name: '',
description: '',
isLivingDex: false,
isShinyDex: false,
isOriginDex: false,
isFormDex: false,
gameScope: null
};
$: mode = editingPokedex ? ('edit' as const) : ('create' as const);
onMount(async () => {
if (!$user) {
goto('/signin');
return;
}
await loadPokedexes();
});
async function loadPokedexes() {
const response = await fetch('/api/pokedexes', { credentials: 'include' });
if (response.ok) {
pokedexes = await response.json();
} else {
console.error('Failed to load pokédexes:', response.status, await response.text());
}
}
function openCreateModal() {
editingPokedex = null;
formData = {
name: '',
description: '',
isLivingDex: false,
isShinyDex: false,
isOriginDex: false,
isFormDex: false,
gameScope: null
};
showModal = true;
}
function openEditModal(pokedex: Pokedex) {
editingPokedex = pokedex;
formData = { ...pokedex };
showModal = true;
}
function closeModal() {
showModal = false;
editingPokedex = null;
}
async function handleSubmit() {
try {
if (editingPokedex) {
// Update existing pokédex
const response = await fetch(`/api/pokedexes/${editingPokedex._id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
credentials: 'include'
});
if (!response.ok) {
const errorText = await response.text();
console.error('Failed to update pokédex:', response.status, errorText);
alert(`Failed to update pokédex: ${errorText}`);
return;
}
closeModal();
await loadPokedexes();
return;
} else {
// Create new pokédex
const response = await fetch('/api/pokedexes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
credentials: 'include'
});
if (!response.ok) {
const errorText = await response.text();
console.error('Failed to create pokédex:', response.status, errorText);
alert(`Failed to create pokédex: ${errorText}`);
return;
}
const createdPokedex = (await response.json()) as Pokedex;
// Refresh local state BEFORE deciding whether this is the user's first pokédex.
closeModal();
await loadPokedexes();
// If the user's total pokédex count is now 1, this newly created one is their first.
if (pokedexes.length === 1) {
goto(`/pokedex/${createdPokedex._id}`);
return;
}
}
} catch (error) {
console.error('Error saving pokédex:', error);
alert('An error occurred');
}
}
async function handleDelete(pokedex: Pokedex) {
// TODO: Get catch record count and show in confirmation
const confirmed = confirm(
`Are you sure you want to delete "${pokedex.name}"? This will also delete all associated catch records. This action cannot be undone.`
);
if (!confirmed) return;
try {
const response = await fetch(`/api/pokedexes/${pokedex._id}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) {
const errorText = await response.text();
console.error('Failed to delete pokédex:', response.status, errorText);
alert(`Failed to delete pokédex: ${errorText}`);
return;
}
await loadPokedexes();
} catch (error) {
console.error('Error deleting pokédex:', error);
alert('An error occurred');
}
}
function handleView(pokedex: Pokedex) {
goto(`/pokedex/${pokedex._id}`);
}
</script>
<svelte:head>
<title>My Pokédexes - Living Dex Tracker</title>
</svelte:head>
<div class="container mx-auto p-4">
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold">My Pokédexes</h1>
<button class="btn btn-primary" on:click={openCreateModal}> Create New Pokédex </button>
</div>
{#if pokedexes.length === 0}
<div class="text-center py-12">
<h2 class="text-2xl font-semibold mb-4">You haven't created any pokédexes yet!</h2>
<p class="mb-6">Get started by creating your first pokédex to track your collection.</p>
<button class="btn btn-primary btn-lg" on:click={openCreateModal}>
Create Your First Pokédex
</button>
</div>
{:else}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{#each pokedexes as pokedex (pokedex._id)}
<PokedexCard
{pokedex}
onEdit={() => openEditModal(pokedex)}
onDelete={() => handleDelete(pokedex)}
onView={() => handleView(pokedex)}
/>
{/each}
</div>
{/if}
</div>
<!-- Modal -->
{#if showModal}
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4">
{mode === 'create' ? 'Create New Pokédex' : 'Edit Pokédex'}
</h3>
<PokedexForm bind:pokedex={formData} {mode} onSubmit={handleSubmit} onCancel={closeModal} />
</div>
<div class="modal-backdrop" on:click={closeModal}></div>
</div>
{/if}
-321
View File
@@ -1,321 +0,0 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { user } from '$lib/stores/user.js';
import { type User } from '@supabase/auth-js';
import { type CombinedData } from '$lib/models/CombinedData';
import { browser } from '$app/environment';
import type { PokedexEntry } from '$lib/models/PokedexEntry';
import PokedexSidebar from '$lib/components/pokedex/PokedexSidebar.svelte';
import PokedexViewList from '$lib/components/pokedex/PokedexViewList.svelte';
import PokedexViewBoxes from '$lib/components/pokedex/PokedexViewBoxes.svelte';
import type { PageData } from './$types';
export let data: PageData;
$: ({ supabase, session } = data);
let combinedData = null as CombinedData[] | null;
let currentPage = 1 as number;
let itemsPerPage = 20 as number;
let totalPages = 0 as number;
let creatingRecords = false;
let totalRecordsCreated = 0;
let failedToLoad = false;
let catchRegion = '';
let catchGame = '';
let localUser: User | null;
let showOrigins = true;
let showForms = true;
let showShiny = false;
let drawerOpen = false;
let viewAsBoxes = false;
let currentPlacement = 'boxPlacementForms';
let boxNumbers: number[] = [];
const unsubscribe = user.subscribe((value) => {
localUser = value;
});
onDestroy(unsubscribe);
function toggleOrigins() {
showOrigins = !showOrigins;
}
async function toggleForms() {
showForms = !showForms;
await getData();
}
async function toggleShiny() {
showShiny = !showShiny;
if (showShiny) {
showForms = false;
}
await getData();
}
async function toggleViewAsBoxes() {
viewAsBoxes = !viewAsBoxes;
await getData();
}
async function getData(setCombinedDataToNull = true) {
if (setCombinedDataToNull) {
combinedData = null;
}
let endpoint = viewAsBoxes
? `/api/combined-data/all?enableForms=${showForms}&region=${catchRegion}&game=${catchGame}`
: `/api/combined-data?page=${currentPage}&limit=${itemsPerPage}&enableForms=${showForms}&region=${catchRegion}&game=${catchGame}`;
const response = await fetch(endpoint);
const data = await response.json();
if (data.error) {
failedToLoad = true;
return;
}
combinedData = viewAsBoxes ? data : data.combinedData;
totalPages = data.totalPages || 0;
if (viewAsBoxes) {
boxNumbers = [
...new Set(combinedData.map(({ pokedexEntry }) => pokedexEntry[currentPlacement].box))
].filter(Boolean);
}
}
async function updateACatch(event: any) {
const { catchRecord } = event.detail;
if (!localUser?.id) {
alert('User not signed in');
return;
}
const requestOptions = {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(catchRecord),
credentials: 'include' as RequestCredentials
};
try {
const response = await fetch('/api/catch-records', requestOptions);
if (!response.ok) {
const errorText = await response.text();
console.error('Server response:', response.status, errorText);
alert('Failed to update catch record');
throw new Error('Failed to update catch record');
}
// Refresh the data to reflect changes from server
await getData(false);
} catch (error) {
console.error('Error updating catch record:', error);
}
}
async function updateCatchRecords(
boxNumber: number,
caught: boolean,
needsToEvolve: boolean,
inHome: boolean | null = null
) {
let catchRecordsToUpdate = combinedData
.filter(({ pokedexEntry }) => pokedexEntry[currentPlacement].box === boxNumber)
.map(({ pokedexEntry, catchRecord }) => {
// Create default record if null
const baseRecord = catchRecord || {
_id: '',
userId: localUser?.id || '',
pokedexEntryId: pokedexEntry._id,
haveToEvolve: false,
caught: false,
inHome: false,
hasGigantamaxed: false,
personalNotes: ''
};
let updatedRecord = { ...baseRecord };
if (inHome !== null) {
updatedRecord = {
...updatedRecord,
inHome
};
} else {
updatedRecord = {
...updatedRecord,
caught,
haveToEvolve: needsToEvolve
};
}
return updatedRecord;
});
await fetch('/api/catch-records', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(catchRecordsToUpdate)
}).then(async () => {
await getData(false);
});
}
async function markBoxAsNotCaught(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false);
}
async function markBoxAsCaught(boxNumber: number) {
await updateCatchRecords(boxNumber, true, false);
}
async function markBoxAsNeedsToEvolve(boxNumber: number) {
await updateCatchRecords(boxNumber, false, true);
}
async function markBoxAsInHome(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false, true);
}
async function markBoxAsNotInHome(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false, false);
}
async function getPokedexEntries() {
const response = await fetch('/api/pokedexentries');
if (!response.ok) {
throw new Error('Failed to fetch Pokémon data');
}
return await response.json();
}
async function createCatchRecords() {
// this user doesn't have any catch records, so we need to create them
await getPokedexEntries().then(async (pokedexEntries) => {
// If there are no catch records, make one for each pokedex entry
creatingRecords = true;
const newCatchRecords = pokedexEntries.map((entry: PokedexEntry) => ({
userId: localUser?.id,
pokedexEntryId: entry._id,
haveToEvolve: false,
caught: false,
inHome: false,
hasGigantamaxed: false,
personalNotes: ''
}));
if (newCatchRecords.length === 0) {
alert('No pokedex entries to create catch records for');
return;
}
const batchSize = 500;
for (let i = 0; i < newCatchRecords.length; i += batchSize) {
const batch = newCatchRecords.slice(i, i + batchSize);
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(batch)
};
try {
const response = await fetch('/api/catch-records', requestOptions);
if (!response.ok) {
throw new Error('Failed to create catch records');
}
const createdRecords = await response.json();
totalRecordsCreated += createdRecords.length;
} catch (error) {
console.error('Error creating catch records:', error);
}
}
creatingRecords = false;
failedToLoad = false;
await getData();
});
}
onMount(async () => {
await getData();
});
$: {
if (browser) {
currentPage, itemsPerPage, getData();
}
}
$: currentPlacement = showForms ? 'boxPlacementForms' : 'boxPlacement';
</script>
<svelte:head>
<title>Living Dex Tracker - My Dex</title>
</svelte:head>
{#if !localUser}
<p>Please <a href="/signin" class="underline text-primary hover:text-secondary">sign in</a></p>
{:else}
<div class="drawer lg:drawer-open">
<input id="my-drawer" type="checkbox" class="drawer-toggle" bind:checked={drawerOpen} />
<div class="drawer-content flex flex-col items-center justify-center md:ml-64">
<label
for="my-drawer"
class="btn btn-secondary drawer-button lg:hidden fixed -left-10 top-1/2 -rotate-90"
>
{drawerOpen ? 'Close Filters' : 'Open Filters'}
</label>
{#if viewAsBoxes}
<PokedexViewBoxes
bind:showShiny
bind:combinedData
bind:boxNumbers
bind:currentPlacement
bind:creatingRecords
bind:failedToLoad
{markBoxAsNotCaught}
{markBoxAsCaught}
{markBoxAsNeedsToEvolve}
{markBoxAsInHome}
{markBoxAsNotInHome}
/>
{:else}
<PokedexViewList
bind:showOrigins
bind:showForms
bind:showShiny
bind:combinedData
bind:creatingRecords
bind:totalRecordsCreated
bind:failedToLoad
userId={localUser?.id}
{updateACatch}
{createCatchRecords}
/>
{/if}
</div>
<div class="drawer-side lg:relative">
<label for="my-drawer" class="drawer-overlay lg:hidden"></label>
<PokedexSidebar
bind:viewAsBoxes
bind:currentPage
bind:itemsPerPage
bind:totalPages
bind:showForms
bind:showOrigins
bind:showShiny
bind:catchRegion
bind:catchGame
{getData}
{toggleForms}
{toggleOrigins}
{toggleShiny}
{toggleViewAsBoxes}
/>
</div>
</div>
{/if}
+28
View File
@@ -0,0 +1,28 @@
import { error, redirect } from '@sveltejs/kit';
import PokedexRepository from '$lib/repositories/PokedexRepository';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals, params }) => {
const { safeGetSession, supabase } = locals;
const { session, user } = await safeGetSession();
// Require authentication
if (!session || !user) {
throw redirect(303, '/signin');
}
const { id } = params;
// Fetch pokédex to verify ownership (RLS will also block, but we want a proper 404)
const repo = new PokedexRepository(supabase, user.id);
const pokedex = await repo.findById(id);
if (!pokedex) {
// Either doesn't exist or user doesn't own it
throw error(404, 'Pokédex not found');
}
return {
pokedex
};
};
+470
View File
@@ -0,0 +1,470 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { user } from '$lib/stores/user.js';
import { type User } from '@supabase/auth-js';
import { type CombinedData } from '$lib/models/CombinedData';
import { browser } from '$app/environment';
import type { CatchRecord } from '$lib/models/CatchRecord';
import type { PokedexEntry } from '$lib/models/PokedexEntry';
import { calculateBoxNumbers, calculateBoxPlacement } from '$lib/utils/boxPlacement';
import PokedexViewBoxes from '$lib/components/pokedex/PokedexViewBoxes.svelte';
import PokedexModal from '$lib/components/pokedex/PokedexModal.svelte';
import PokedexEntryCatchRecord from '$lib/components/pokedex/PokedexEntryCatchRecord.svelte';
import type { Pokedex } from '$lib/models/Pokedex';
import type { PageData } from './$types';
export let data: PageData;
// Get pokédex from server load (guarded for transient undefined during navigation/HMR)
let pokedex: Pokedex | undefined;
let pokedexId = '';
$: pokedex = data?.pokedex;
$: pokedexId = pokedex?._id ?? '';
let combinedData = null as CombinedData[] | null;
let currentPage = 1 as number;
// Box view requires the full dataset for correct box numbering/placement.
// If/when a paginated list view is introduced, this can be lowered and paired with UI controls.
let itemsPerPage = 9999 as number;
let totalPages = 0 as number;
let creatingRecords = false;
let totalRecordsCreated = 0;
let failedToLoad = false;
let localUser: User | null;
let boxNumbers: number[] = [];
let showModal = false;
let selectedPokemon: CombinedData | null = null;
// Derive from pokedex config
$: showOrigins = !!pokedex?.isOriginDex;
$: showShiny = !!pokedex?.isShinyDex;
const unsubscribe = user.subscribe((value) => {
localUser = value;
});
onDestroy(unsubscribe);
function openPokemonModal(pokemon: CombinedData) {
selectedPokemon = pokemon;
showModal = true;
}
function closePokemonModal() {
showModal = false;
selectedPokemon = null;
}
async function handleModalCatchUpdate(event: any) {
await updateACatch(event); // Existing handler
// Sync modal with refreshed data
if (selectedPokemon && combinedData) {
const updated = combinedData.find(
(cd) => cd.pokedexEntry._id === selectedPokemon!.pokedexEntry._id
);
if (updated) {
selectedPokemon = updated;
}
}
}
type GetDataOptions = {
page?: number;
perPage?: number;
setCombinedDataToNull?: boolean;
};
async function getData({
page = currentPage,
perPage = itemsPerPage,
setCombinedDataToNull = true
}: GetDataOptions = {}) {
if (!pokedex || !pokedexId) return;
if (setCombinedDataToNull) {
combinedData = null;
}
const effectivePage = Math.max(1, page);
const effectivePerPage = Math.max(1, perPage);
// Use new pokédex-scoped endpoint
const endpoint = `/api/pokedexes/${pokedexId}/combined-data?page=${effectivePage}&limit=${effectivePerPage}&enableForms=${pokedex.isFormDex}`;
const response = await fetch(endpoint);
const fetchedData = await response.json();
if (fetchedData.error) {
failedToLoad = true;
return;
}
combinedData = fetchedData.combinedData;
totalPages = fetchedData.totalPages || 0;
// Always extract box numbers for box view
if (combinedData) {
boxNumbers = calculateBoxNumbers(combinedData.length);
}
}
async function updateACatch(event: any) {
if (!pokedexId) return;
const { catchRecord } = event.detail;
if (!localUser?.id) {
alert('User not signed in');
return;
}
const requestOptions = {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(catchRecord),
credentials: 'include' as RequestCredentials
};
try {
// Use new pokédex-scoped endpoint
const response = await fetch(`/api/pokedexes/${pokedexId}/catch-records`, requestOptions);
if (!response.ok) {
const errorText = await response.text();
console.error('Server response:', response.status, errorText);
alert('Failed to update catch record');
throw new Error('Failed to update catch record');
}
// Refresh the data to reflect changes from server
await getData({ page: currentPage, perPage: itemsPerPage, setCombinedDataToNull: false });
} catch (error) {
console.error('Error updating catch record:', error);
}
}
async function updateCatchRecords(
boxNumber: number,
caught: boolean,
needsToEvolve: boolean,
inHome: boolean | null = null
) {
if (!combinedData) return;
if (!pokedexId) return;
const catchRecordsToUpdate: CatchRecord[] = combinedData
.filter((_, index) => calculateBoxPlacement(index).box === boxNumber)
.map(({ pokedexEntry, catchRecord }) => {
// Create default record if null
const baseRecord: CatchRecord = catchRecord ?? {
_id: '',
userId: localUser?.id || '',
pokedexEntryId: pokedexEntry._id,
pokedexId: pokedexId,
haveToEvolve: false,
caught: false,
inHome: false,
hasGigantamaxed: false,
personalNotes: ''
};
let updatedRecord: CatchRecord = { ...baseRecord };
if (inHome !== null) {
updatedRecord = {
...updatedRecord,
inHome
};
} else {
updatedRecord = {
...updatedRecord,
caught,
haveToEvolve: needsToEvolve
};
}
return updatedRecord;
});
// Use new pokédex-scoped endpoint
try {
const response = await fetch(`/api/pokedexes/${pokedexId}/catch-records`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(catchRecordsToUpdate)
});
if (!response.ok) {
const errorText = await response.text();
console.error('Server response:', response.status, errorText);
alert(
`Failed to update catch records: ${response.status} ${response.statusText}\n${errorText}`
);
return;
}
await getData({ page: currentPage, perPage: itemsPerPage, setCombinedDataToNull: false });
} catch (error) {
console.error('Error updating catch records:', error);
alert(
`Network error updating catch records: ${error instanceof Error ? error.message : String(error)}`
);
}
}
async function markBoxAsNotCaught(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false);
}
async function markBoxAsCaught(boxNumber: number) {
await updateCatchRecords(boxNumber, true, false);
}
async function markBoxAsNeedsToEvolve(boxNumber: number) {
await updateCatchRecords(boxNumber, false, true);
}
async function markBoxAsInHome(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false, true);
}
async function markBoxAsNotInHome(boxNumber: number) {
await updateCatchRecords(boxNumber, false, false, false);
}
async function getPokedexEntries() {
const response = await fetch('/api/pokedexentries');
if (!response.ok) {
throw new Error('Failed to fetch Pokémon data');
}
return await response.json();
}
async function createCatchRecords() {
if (!pokedexId) return;
// this user doesn't have any catch records, so we need to create them
await getPokedexEntries().then(async (pokedexEntries) => {
// If there are no catch records, make one for each pokedex entry
creatingRecords = true;
const newCatchRecords = pokedexEntries.map((entry: PokedexEntry) => ({
userId: localUser?.id,
pokedexEntryId: entry._id,
pokedexId: pokedexId,
haveToEvolve: false,
caught: false,
inHome: false,
hasGigantamaxed: false,
personalNotes: ''
}));
if (newCatchRecords.length === 0) {
alert('No pokedex entries to create catch records for');
return;
}
const batchSize = 500;
for (let i = 0; i < newCatchRecords.length; i += batchSize) {
const batch = newCatchRecords.slice(i, i + batchSize);
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(batch)
};
try {
// Use new pokédex-scoped endpoint
const response = await fetch(`/api/pokedexes/${pokedexId}/catch-records`, requestOptions);
if (!response.ok) {
throw new Error('Failed to create catch records');
}
const createdRecords = await response.json();
totalRecordsCreated += createdRecords.length;
} catch (error) {
console.error('Error creating catch records:', error);
}
}
creatingRecords = false;
failedToLoad = false;
await getData({ page: currentPage, perPage: itemsPerPage });
});
}
// Fetch data whenever pagination controls change (client-side only)
$: if (browser && pokedexId) getData({ page: currentPage, perPage: itemsPerPage });
</script>
<svelte:head>
<title>{pokedex ? `${pokedex.name} - Living Dex Tracker` : 'Pokédex - Living Dex Tracker'}</title>
</svelte:head>
{#if !localUser}
<p>Please <a href="/signin" class="underline text-primary hover:text-secondary">sign in</a></p>
{:else if !pokedex}
<p>Loading...</p>
{:else}
<div class="container mx-auto p-4 max-w-screen-2xl">
<!-- Pokédex Header -->
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
<!-- Left side: Title and metadata -->
<div class="flex-1">
<h1 class="card-title text-3xl mb-3">{pokedex.name}</h1>
<div class="flex flex-wrap items-center gap-3">
<!-- Type badges -->
{#if pokedex.isLivingDex}
<div class="badge badge-primary badge-lg gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
Living
</div>
{/if}
{#if pokedex.isShinyDex}
<div class="badge badge-secondary badge-lg gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
/>
</svg>
Shiny
</div>
{/if}
{#if pokedex.isOriginDex}
<div class="badge badge-accent badge-lg gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
clip-rule="evenodd"
/>
</svg>
Origin
</div>
{/if}
{#if pokedex.isFormDex}
<div class="badge badge-info badge-lg gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"
/>
</svg>
Form
</div>
{/if}
<!-- Game scope -->
{#if pokedex.gameScope}
<div class="divider divider-horizontal mx-0"></div>
<div class="flex items-center gap-2 text-base-content/70">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
<path
fill-rule="evenodd"
d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z"
clip-rule="evenodd"
/>
</svg>
<span class="font-semibold">{pokedex.gameScope}</span>
</div>
{:else}
<div class="divider divider-horizontal mx-0"></div>
<div class="flex items-center gap-2 text-base-content/70">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM4.332 8.027a6.012 6.012 0 011.912-2.706C6.512 5.73 6.974 6 7.5 6A1.5 1.5 0 019 7.5V8a2 2 0 004 0 2 2 0 011.523-1.943A5.977 5.977 0 0116 10c0 .34-.028.675-.083 1H15a2 2 0 00-2 2v2.197A5.973 5.973 0 0110 16v-2a2 2 0 00-2-2 2 2 0 01-2-2 2 2 0 00-1.668-1.973z"
clip-rule="evenodd"
/>
</svg>
<span class="font-semibold">All Games</span>
</div>
{/if}
</div>
{#if pokedex.description}
<p class="text-sm text-base-content/70 mt-3">{pokedex.description}</p>
{/if}
</div>
<!-- Right side: Actions -->
<div class="flex flex-row lg:flex-col gap-2">
<a href="/my-pokedexes" class="btn btn-outline btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"
/>
</svg>
My Pokédexes
</a>
</div>
</div>
</div>
</div>
<!-- Box View -->
<PokedexViewBoxes
{showShiny}
bind:combinedData
bind:boxNumbers
bind:creatingRecords
bind:failedToLoad
{markBoxAsNotCaught}
{markBoxAsCaught}
{markBoxAsNeedsToEvolve}
{markBoxAsInHome}
{markBoxAsNotInHome}
{createCatchRecords}
onPokemonClick={openPokemonModal}
/>
</div>
{#if showModal && selectedPokemon}
<PokedexModal isOpen={showModal} onClose={closePokemonModal}>
<PokedexEntryCatchRecord
pokedexEntry={selectedPokemon.pokedexEntry}
bind:catchRecord={selectedPokemon.catchRecord}
{showOrigins}
showForms={pokedex.isFormDex}
{showShiny}
userId={localUser?.id}
{pokedexId}
on:updateCatch={handleModalCatchUpdate}
/>
</PokedexModal>
{/if}
{/if}
+1 -1
View File
@@ -24,7 +24,7 @@
if (session) {
localUser = session.user;
user.set(localUser);
await goto('/mydex', { replace: true });
await goto('/my-pokedexes');
} else {
localUser = null;
user.set(localUser);
+365
View File
@@ -0,0 +1,365 @@
<script lang="ts">
import { onMount } from 'svelte';
let showSuccess = false;
onMount(() => {
// Trigger success animation after component mounts
setTimeout(() => {
showSuccess = true;
}, 100);
});
</script>
<svelte:head>
<title>Welcome - Living Dex Tracker</title>
<meta
name="description"
content="Welcome to Living Dex Tracker! Check your email to verify your account."
/>
</svelte:head>
<div class="min-h-[calc(100vh-16rem)] bg-base-100 py-8 md:py-16 px-4 sm:px-6 lg:px-8">
<div class="max-w-6xl mx-auto">
<!-- Success Badge -->
{#if showSuccess}
<div class="flex justify-center mb-8 animate-fade-in">
<div class="badge badge-success badge-lg gap-2 p-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span class="text-lg font-semibold">Account Created Successfully!</span>
</div>
</div>
{/if}
<!-- Hero Content -->
<div class="hero">
<div class="hero-content flex-col lg:flex-row-reverse gap-8 lg:gap-12 w-full">
<!-- Main Content -->
<div class="text-center lg:text-left flex-1 space-y-6">
<h1
class="text-4xl md:text-5xl lg:text-6xl font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"
>
Welcome to Living Dex Tracker!
</h1>
<div class="space-y-4 text-base md:text-lg">
<!-- Email Verification Section with Icon -->
<div class="flex items-start gap-4 p-4 bg-info/10 rounded-lg border-l-4 border-info">
<div class="flex-shrink-0 mt-1">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 text-info animate-pulse"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
</div>
<div class="flex-1">
<p class="font-semibold text-info mb-1">Check Your Email</p>
<p class="text-sm opacity-90">
We've sent you a magic link to verify your account. Click the link in your email
to sign in and start tracking your Pokédex progress.
</p>
</div>
</div>
<p class="opacity-80">
After clicking the link, you'll be automatically signed in and can start tracking your
Pokémon catches across all generations!
</p>
<!-- Help Section -->
<div class="pt-4">
<p class="text-sm opacity-70">
<span class="font-semibold">Haven't received the email?</span> Check your spam or junk
folder.
</p>
<p class="text-sm opacity-70 mt-2">
Still having trouble?
<a
href="mailto:support@example.com"
class="link link-primary font-semibold hover:underline"
>
Contact support
</a> for assistance.
</p>
</div>
</div>
</div>
<!-- What's Next Card -->
<div
class="card w-full max-w-md shadow-2xl bg-neutral hover:shadow-3xl transition-shadow duration-300"
>
<div class="card-body">
<h2 class="card-title text-2xl mb-4 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
/>
</svg>
What's Next?
</h2>
<ul class="space-y-4">
<li class="flex items-start gap-3 group">
<div class="flex-shrink-0 mt-1">
<div
class="w-6 h-6 rounded-full bg-success flex items-center justify-center group-hover:scale-110 transition-transform"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 text-success-content"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
</div>
<div class="flex-1">
<p class="font-semibold">Check your email</p>
<p class="text-sm opacity-70">Look for our verification email in your inbox</p>
</div>
</li>
<li class="flex items-start gap-3 group">
<div class="flex-shrink-0 mt-1">
<div
class="w-6 h-6 rounded-full bg-success flex items-center justify-center group-hover:scale-110 transition-transform"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 text-success-content"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
</div>
<div class="flex-1">
<p class="font-semibold">Click the magic link</p>
<p class="text-sm opacity-70">This will sign you in and verify your account</p>
</div>
</li>
<li class="flex items-start gap-3 group">
<div class="flex-shrink-0 mt-1">
<div
class="w-6 h-6 rounded-full bg-success flex items-center justify-center group-hover:scale-110 transition-transform"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 text-success-content"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
</div>
<div class="flex-1">
<p class="font-semibold">Start tracking your Pokémon</p>
<p class="text-sm opacity-70">
Create your first Pokédex and begin your journey!
</p>
</div>
</li>
</ul>
<!-- Additional Info -->
<div class="mt-6 pt-6 border-t border-base-300">
<div class="flex items-center gap-2 text-sm opacity-70">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>The verification link expires in 24 hours</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Additional Features Preview -->
<div class="mt-16 text-center">
<h3 class="text-2xl font-bold mb-6">What You Can Do With Living Dex Tracker</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="card bg-base-200 shadow-md hover:shadow-lg transition-shadow">
<div class="card-body items-center text-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 text-primary mb-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<h4 class="font-semibold">Track Multiple Pokédexes</h4>
<p class="text-sm opacity-70">
Manage different Living Dex challenges across all generations
</p>
</div>
</div>
<div class="card bg-base-200 shadow-md hover:shadow-lg transition-shadow">
<div class="card-body items-center text-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 text-secondary mb-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
<h4 class="font-semibold">Share Your Progress</h4>
<p class="text-sm opacity-70">Connect with friends and share your Pokédex journey</p>
</div>
</div>
<div class="card bg-base-200 shadow-md hover:shadow-lg transition-shadow">
<div class="card-body items-center text-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 text-accent mb-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"
/>
</svg>
<h4 class="font-semibold">Works Offline</h4>
<p class="text-sm opacity-70">Track your catches even without an internet connection</p>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.5s ease-out forwards;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
.animate-fade-in,
.animate-pulse {
animation: none;
}
.group-hover\:scale-110 {
transform: none !important;
}
}
/* Gradient text fallback for older browsers */
@supports not (background-clip: text) {
h1 {
background: none;
color: hsl(var(--p));
}
}
</style>
@@ -9,17 +9,7 @@ CREATE TABLE pokedex_entries (
"regionToEvolveIn" TEXT,
"evolutionInformation" TEXT,
"catchInformation" TEXT[],
-- Box placement for forms view
"boxPlacementFormsBox" INTEGER,
"boxPlacementFormsRow" INTEGER,
"boxPlacementFormsColumn" INTEGER,
-- Box placement for no-forms view
"boxPlacementBox" INTEGER,
"boxPlacementRow" INTEGER,
"boxPlacementColumn" INTEGER,
"createdAt" TIMESTAMPTZ DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ DEFAULT NOW()
);
@@ -65,8 +55,6 @@ CREATE TABLE metadata (
-- Create indexes for better performance
CREATE INDEX idx_pokedex_entries_pokedex_number ON pokedex_entries("pokedexNumber");
CREATE INDEX idx_pokedex_entries_pokemon ON pokedex_entries(pokemon);
CREATE INDEX idx_pokedex_entries_box_placement_forms ON pokedex_entries("boxPlacementFormsBox", "boxPlacementFormsRow", "boxPlacementFormsColumn");
CREATE INDEX idx_pokedex_entries_box_placement ON pokedex_entries("boxPlacementBox", "boxPlacementRow", "boxPlacementColumn");
CREATE INDEX idx_catch_records_user_id ON catch_records("userId");
CREATE INDEX idx_catch_records_pokedex_entry_id ON catch_records("pokedexEntryId");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
-- Create pokedexes table
CREATE TABLE pokedexes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"userId" UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
-- Type flags (combinable)
"isLivingDex" BOOLEAN DEFAULT FALSE,
"isShinyDex" BOOLEAN DEFAULT FALSE,
"isOriginDex" BOOLEAN DEFAULT FALSE,
"isFormDex" BOOLEAN DEFAULT FALSE,
-- Scope (NULL = all games, or specific game)
"gameScope" TEXT,
"createdAt" TIMESTAMPTZ DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ DEFAULT NOW(),
-- User can't have duplicate pokédex names
UNIQUE("userId", name)
);
-- Create index for performance
CREATE INDEX idx_pokedexes_user_id ON pokedexes("userId");
-- Trigger to automatically update updated_at timestamp
CREATE TRIGGER update_pokedexes_updated_at
BEFORE UPDATE ON pokedexes
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Enable RLS
ALTER TABLE pokedexes ENABLE ROW LEVEL SECURITY;
-- RLS POLICIES - CRITICAL: Users can ONLY access their own pokédexes
-- Users can ONLY view their own pokédexes
CREATE POLICY "Users can view own pokedexes" ON pokedexes
FOR SELECT USING (auth.uid() = "userId");
-- Users can ONLY create pokédexes for themselves
CREATE POLICY "Users can create own pokedexes" ON pokedexes
FOR INSERT WITH CHECK (auth.uid() = "userId");
-- Users can ONLY update their own pokédexes
CREATE POLICY "Users can update own pokedexes" ON pokedexes
FOR UPDATE USING (auth.uid() = "userId");
-- Users can ONLY delete their own pokédexes
CREATE POLICY "Users can delete own pokedexes" ON pokedexes
FOR DELETE USING (auth.uid() = "userId");
@@ -0,0 +1,74 @@
-- Truncate existing catch records (no real users, as confirmed)
TRUNCATE TABLE catch_records;
-- Add pokedexId column
ALTER TABLE catch_records
ADD COLUMN "pokedexId" UUID REFERENCES pokedexes(id) ON DELETE CASCADE;
-- Make pokedexId NOT NULL
ALTER TABLE catch_records
ALTER COLUMN "pokedexId" SET NOT NULL;
-- Drop old unique constraint (userId, pokedexEntryId)
ALTER TABLE catch_records
DROP CONSTRAINT IF EXISTS catch_records_userId_pokedexEntryId_key;
-- Add new unique constraint (userId, pokedexEntryId, pokedexId)
ALTER TABLE catch_records
ADD CONSTRAINT catch_records_user_pokemon_pokedex_unique
UNIQUE("userId", "pokedexEntryId", "pokedexId");
-- Add index for performance
CREATE INDEX idx_catch_records_pokedex_id ON catch_records("pokedexId");
-- RLS POLICIES - CRITICAL: Users can ONLY access catch records for their own pokédexes
-- Drop existing RLS policies if they exist
DROP POLICY IF EXISTS "Users can view own catch records" ON catch_records;
DROP POLICY IF EXISTS "Users can insert own catch records" ON catch_records;
DROP POLICY IF EXISTS "Users can update own catch records" ON catch_records;
DROP POLICY IF EXISTS "Users can delete own catch records" ON catch_records;
-- Users can ONLY view catch records for their own pokédexes
CREATE POLICY "Users can view own catch records" ON catch_records
FOR SELECT USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = catch_records."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- Users can ONLY insert catch records for their own pokédexes
CREATE POLICY "Users can insert own catch records" ON catch_records
FOR INSERT WITH CHECK (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = catch_records."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- Users can ONLY update catch records for their own pokédexes
CREATE POLICY "Users can update own catch records" ON catch_records
FOR UPDATE USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = catch_records."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- Users can ONLY delete catch records for their own pokédexes
CREATE POLICY "Users can delete own catch records" ON catch_records
FOR DELETE USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = catch_records."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
@@ -0,0 +1,21 @@
-- Create regional_dex_numbers table for generic regional dex number storage
-- This table replaces region-specific columns (kanto_dex_number, johto_dex_number, etc.)
-- and allows adding new regions without schema changes
CREATE TABLE IF NOT EXISTS regional_dex_numbers (
id SERIAL PRIMARY KEY,
pokedex_entry_id INT NOT NULL REFERENCES pokedex_entries(id) ON DELETE CASCADE,
region TEXT NOT NULL,
dex_number INT NOT NULL,
UNIQUE(pokedex_entry_id, region)
);
-- Create indexes for better query performance
CREATE INDEX IF NOT EXISTS idx_regional_dex_numbers_region ON regional_dex_numbers(region, dex_number);
CREATE INDEX IF NOT EXISTS idx_regional_dex_numbers_entry ON regional_dex_numbers(pokedex_entry_id);
-- RLS policies (read-only reference data, same pattern as pokedex_entries)
ALTER TABLE regional_dex_numbers ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow public read access" ON regional_dex_numbers
FOR SELECT USING (true);
@@ -0,0 +1,40 @@
-- Create games table to store game metadata
CREATE TABLE IF NOT EXISTS games (
id TEXT PRIMARY KEY,
"displayName" TEXT NOT NULL,
region TEXT NOT NULL,
generation INTEGER NOT NULL,
"releaseYear" INTEGER NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE games ENABLE ROW LEVEL SECURITY;
-- Allow anyone to read games (public reference data)
CREATE POLICY "Games are publicly readable"
ON games
FOR SELECT
TO public
USING (true);
-- Insert games data from games.csv
INSERT INTO games (id, "displayName", region, generation, "releaseYear") VALUES
('red', 'Red', 'Kanto', 1, 1996),
('blue', 'Blue', 'Kanto', 1, 1996),
('yellow', 'Yellow', 'Kanto', 1, 1998),
('lg--pikachu', 'LG: Pikachu', 'Kanto', 7, 2018),
('lg--eevee', 'LG: Eevee', 'Kanto', 7, 2018),
('gold', 'Gold', 'Johto', 2, 1999),
('silver', 'Silver', 'Johto', 2, 1999),
('crystal', 'Crystal', 'Johto', 2, 2000),
('heartgold', 'HeartGold', 'Johto', 4, 2009),
('soulsilver', 'SoulSilver', 'Johto', 4, 2009),
('legends-arceus', 'Legends: Arceus', 'Hisui', 8, 2022)
ON CONFLICT (id) DO UPDATE SET
"displayName" = EXCLUDED."displayName",
region = EXCLUDED.region,
generation = EXCLUDED.generation,
"releaseYear" = EXCLUDED."releaseYear",
"updatedAt" = NOW();
@@ -0,0 +1,360 @@
-- Seed Kanto region Pokémon (Gen 1)
-- Auto-generated from CSV files
-- Insert Kanto region-game mappings
INSERT INTO region_game_mappings (region, game) VALUES
('Kanto', 'Red'),
('Kanto', 'Blue'),
('Kanto', 'Yellow'),
('Kanto', 'LG: Pikachu'),
('Kanto', 'LG: Eevee')
ON CONFLICT (region, game) DO NOTHING;
-- Insert Kanto Pokémon entries
INSERT INTO pokedex_entries (
"pokedexNumber",
pokemon,
form,
"canGigantamax",
"regionToCatchIn",
"gamesToCatchIn"
) VALUES
(1, 'Bulbasaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(2, 'Ivysaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(3, 'Venusaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(3, 'Venusaur', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(4, 'Charmander', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(5, 'Charmeleon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(6, 'Charizard', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(7, 'Squirtle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(8, 'Wartortle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(9, 'Blastoise', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(10, 'Caterpie', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(11, 'Metapod', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(12, 'Butterfree', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(12, 'Butterfree', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(13, 'Weedle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(14, 'Kakuna', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(15, 'Beedrill', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(16, 'Pidgey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(17, 'Pidgeotto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(18, 'Pidgeot', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(19, 'Rattata', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(19, 'Rattata', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(20, 'Raticate', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(20, 'Raticate', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(21, 'Spearow', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(22, 'Fearow', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(23, 'Ekans', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(24, 'Arbok', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(25, 'Pikachu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(25, 'Pikachu', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(26, 'Raichu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(26, 'Raichu', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(27, 'Sandshrew', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(28, 'Sandslash', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(29, 'Nidoran♀', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(30, 'Nidorina', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(31, 'Nidoqueen', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(32, 'Nidoran♂', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(33, 'Nidorino', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(34, 'Nidoking', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(35, 'Clefairy', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(36, 'Clefable', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(37, 'Vulpix', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(38, 'Ninetales', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(39, 'Jigglypuff', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(40, 'Wigglytuff', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(41, 'Zubat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(41, 'Zubat', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(42, 'Golbat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(42, 'Golbat', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(43, 'Oddish', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(44, 'Gloom', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(44, 'Gloom', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(45, 'Vileplume', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(45, 'Vileplume', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(46, 'Paras', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(47, 'Parasect', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(48, 'Venonat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(49, 'Venomoth', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(50, 'Diglett', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(51, 'Dugtrio', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(52, 'Meowth', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(53, 'Persian', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(54, 'Psyduck', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(55, 'Golduck', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(56, 'Mankey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(57, 'Primeape', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(58, 'Growlithe', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(59, 'Arcanine', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(60, 'Poliwag', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(61, 'Poliwhirl', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(62, 'Poliwrath', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(63, 'Abra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(64, 'Kadabra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(64, 'Kadabra', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(65, 'Alakazam', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(65, 'Alakazam', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(66, 'Machop', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(67, 'Machoke', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(68, 'Machamp', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(69, 'Bellsprout', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(70, 'Weepinbell', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(71, 'Victreebel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(72, 'Tentacool', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(73, 'Tentacruel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(74, 'Geodude', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(75, 'Graveler', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(76, 'Golem', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(77, 'Ponyta', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(78, 'Rapidash', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(79, 'Slowpoke', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(80, 'Slowbro', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(81, 'Magnemite', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(82, 'Magneton', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(83, 'Farfetchd', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(84, 'Doduo', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(84, 'Doduo', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(85, 'Dodrio', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(85, 'Dodrio', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(86, 'Seel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(87, 'Dewgong', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(88, 'Grimer', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(89, 'Muk', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(90, 'Shellder', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(91, 'Cloyster', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(92, 'Gastly', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(93, 'Haunter', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(94, 'Gengar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(95, 'Onix', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(96, 'Drowzee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(97, 'Hypno', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(97, 'Hypno', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(98, 'Krabby', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(99, 'Kingler', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(100, 'Voltorb', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(101, 'Electrode', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(102, 'Exeggcute', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(103, 'Exeggutor', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(104, 'Cubone', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(105, 'Marowak', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(106, 'Hitmonlee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(107, 'Hitmonchan', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(108, 'Lickitung', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(109, 'Koffing', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(110, 'Weezing', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(111, 'Rhyhorn', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(111, 'Rhyhorn', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(112, 'Rhydon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(112, 'Rhydon', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(113, 'Chansey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(114, 'Tangela', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(115, 'Kangaskhan', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(116, 'Horsea', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(117, 'Seadra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(118, 'Goldeen', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(118, 'Goldeen', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(119, 'Seaking', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(119, 'Seaking', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(120, 'Staryu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(121, 'Starmie', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(122, 'Mr. Mime', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(123, 'Scyther', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(123, 'Scyther', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(124, 'Jynx', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(125, 'Electabuzz', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(126, 'Magmar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(127, 'Pinsir', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(128, 'Tauros', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(129, 'Magikarp', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(129, 'Magikarp', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(130, 'Gyarados', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(130, 'Gyarados', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(131, 'Lapras', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(132, 'Ditto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(133, 'Eevee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(133, 'Eevee', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(134, 'Vaporeon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(135, 'Jolteon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(136, 'Flareon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(137, 'Porygon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(138, 'Omanyte', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(139, 'Omastar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(140, 'Kabuto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(141, 'Kabutops', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(142, 'Aerodactyl', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(143, 'Snorlax', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(144, 'Articuno', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(145, 'Zapdos', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(146, 'Moltres', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(147, 'Dratini', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(148, 'Dragonair', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(149, 'Dragonite', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(150, 'Mewtwo', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(151, 'Mew', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee'])
ON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;
-- Insert Kanto regional dex numbers
INSERT INTO regional_dex_numbers (
pokedex_entry_id,
region,
dex_number
) VALUES
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 1 AND form IS NULL), 'Kanto', 1),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 2 AND form IS NULL), 'Kanto', 2),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 3 AND form IS NULL), 'Kanto', 3),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 4 AND form IS NULL), 'Kanto', 4),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 5 AND form IS NULL), 'Kanto', 5),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 6 AND form IS NULL), 'Kanto', 6),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 7 AND form IS NULL), 'Kanto', 7),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 8 AND form IS NULL), 'Kanto', 8),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 9 AND form IS NULL), 'Kanto', 9),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 10 AND form IS NULL), 'Kanto', 10),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 11 AND form IS NULL), 'Kanto', 11),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 12 AND form IS NULL), 'Kanto', 12),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 13 AND form IS NULL), 'Kanto', 13),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 14 AND form IS NULL), 'Kanto', 14),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 15 AND form IS NULL), 'Kanto', 15),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 16 AND form IS NULL), 'Kanto', 16),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 17 AND form IS NULL), 'Kanto', 17),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 18 AND form IS NULL), 'Kanto', 18),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 19 AND form IS NULL), 'Kanto', 19),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 20 AND form IS NULL), 'Kanto', 20),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 21 AND form IS NULL), 'Kanto', 21),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 22 AND form IS NULL), 'Kanto', 22),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 23 AND form IS NULL), 'Kanto', 23),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 24 AND form IS NULL), 'Kanto', 24),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 25 AND form IS NULL), 'Kanto', 25),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 26 AND form IS NULL), 'Kanto', 26),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 27 AND form IS NULL), 'Kanto', 27),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 28 AND form IS NULL), 'Kanto', 28),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 29 AND form IS NULL), 'Kanto', 29),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 30 AND form IS NULL), 'Kanto', 30),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 31 AND form IS NULL), 'Kanto', 31),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 32 AND form IS NULL), 'Kanto', 32),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 33 AND form IS NULL), 'Kanto', 33),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 34 AND form IS NULL), 'Kanto', 34),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 35 AND form IS NULL), 'Kanto', 35),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 36 AND form IS NULL), 'Kanto', 36),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 37 AND form IS NULL), 'Kanto', 37),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 38 AND form IS NULL), 'Kanto', 38),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 39 AND form IS NULL), 'Kanto', 39),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 40 AND form IS NULL), 'Kanto', 40),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 41 AND form IS NULL), 'Kanto', 41),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 42 AND form IS NULL), 'Kanto', 42),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 43 AND form IS NULL), 'Kanto', 43),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 44 AND form IS NULL), 'Kanto', 44),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 45 AND form IS NULL), 'Kanto', 45),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 46 AND form IS NULL), 'Kanto', 46),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 47 AND form IS NULL), 'Kanto', 47),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 48 AND form IS NULL), 'Kanto', 48),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 49 AND form IS NULL), 'Kanto', 49),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 50 AND form IS NULL), 'Kanto', 50),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 51 AND form IS NULL), 'Kanto', 51),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 52 AND form IS NULL), 'Kanto', 52),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 53 AND form IS NULL), 'Kanto', 53),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 54 AND form IS NULL), 'Kanto', 54),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 55 AND form IS NULL), 'Kanto', 55),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 56 AND form IS NULL), 'Kanto', 56),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 57 AND form IS NULL), 'Kanto', 57),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 58 AND form IS NULL), 'Kanto', 58),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 59 AND form IS NULL), 'Kanto', 59),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 60 AND form IS NULL), 'Kanto', 60),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 61 AND form IS NULL), 'Kanto', 61),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 62 AND form IS NULL), 'Kanto', 62),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 63 AND form IS NULL), 'Kanto', 63),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 64 AND form IS NULL), 'Kanto', 64),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 65 AND form IS NULL), 'Kanto', 65),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 66 AND form IS NULL), 'Kanto', 66),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 67 AND form IS NULL), 'Kanto', 67),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 68 AND form IS NULL), 'Kanto', 68),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 69 AND form IS NULL), 'Kanto', 69),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 70 AND form IS NULL), 'Kanto', 70),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 71 AND form IS NULL), 'Kanto', 71),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 72 AND form IS NULL), 'Kanto', 72),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 73 AND form IS NULL), 'Kanto', 73),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 74 AND form IS NULL), 'Kanto', 74),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 75 AND form IS NULL), 'Kanto', 75),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 76 AND form IS NULL), 'Kanto', 76),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 77 AND form IS NULL), 'Kanto', 77),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 78 AND form IS NULL), 'Kanto', 78),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 79 AND form IS NULL), 'Kanto', 79),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 80 AND form IS NULL), 'Kanto', 80),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 81 AND form IS NULL), 'Kanto', 81),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 82 AND form IS NULL), 'Kanto', 82),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 83 AND form IS NULL), 'Kanto', 83),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 84 AND form IS NULL), 'Kanto', 84),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 85 AND form IS NULL), 'Kanto', 85),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 86 AND form IS NULL), 'Kanto', 86),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 87 AND form IS NULL), 'Kanto', 87),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 88 AND form IS NULL), 'Kanto', 88),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 89 AND form IS NULL), 'Kanto', 89),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 90 AND form IS NULL), 'Kanto', 90),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 91 AND form IS NULL), 'Kanto', 91),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 92 AND form IS NULL), 'Kanto', 92),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 93 AND form IS NULL), 'Kanto', 93),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 94 AND form IS NULL), 'Kanto', 94),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 95 AND form IS NULL), 'Kanto', 95),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 96 AND form IS NULL), 'Kanto', 96),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 97 AND form IS NULL), 'Kanto', 97),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 98 AND form IS NULL), 'Kanto', 98),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 99 AND form IS NULL), 'Kanto', 99),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 100 AND form IS NULL), 'Kanto', 100),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 101 AND form IS NULL), 'Kanto', 101),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 102 AND form IS NULL), 'Kanto', 102),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 103 AND form IS NULL), 'Kanto', 103),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 104 AND form IS NULL), 'Kanto', 104),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 105 AND form IS NULL), 'Kanto', 105),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 106 AND form IS NULL), 'Kanto', 106),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 107 AND form IS NULL), 'Kanto', 107),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 108 AND form IS NULL), 'Kanto', 108),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 109 AND form IS NULL), 'Kanto', 109),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 110 AND form IS NULL), 'Kanto', 110),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 111 AND form IS NULL), 'Kanto', 111),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 112 AND form IS NULL), 'Kanto', 112),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 113 AND form IS NULL), 'Kanto', 113),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 114 AND form IS NULL), 'Kanto', 114),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 115 AND form IS NULL), 'Kanto', 115),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 116 AND form IS NULL), 'Kanto', 116),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 117 AND form IS NULL), 'Kanto', 117),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 118 AND form IS NULL), 'Kanto', 118),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 119 AND form IS NULL), 'Kanto', 119),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 120 AND form IS NULL), 'Kanto', 120),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 121 AND form IS NULL), 'Kanto', 121),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 122 AND form IS NULL), 'Kanto', 122),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 123 AND form IS NULL), 'Kanto', 123),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 124 AND form IS NULL), 'Kanto', 124),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 125 AND form IS NULL), 'Kanto', 125),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 126 AND form IS NULL), 'Kanto', 126),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 127 AND form IS NULL), 'Kanto', 127),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 128 AND form IS NULL), 'Kanto', 128),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 129 AND form IS NULL), 'Kanto', 129),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 130 AND form IS NULL), 'Kanto', 130),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 131 AND form IS NULL), 'Kanto', 131),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 132 AND form IS NULL), 'Kanto', 132),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 133 AND form IS NULL), 'Kanto', 133),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 134 AND form IS NULL), 'Kanto', 134),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 135 AND form IS NULL), 'Kanto', 135),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 136 AND form IS NULL), 'Kanto', 136),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 137 AND form IS NULL), 'Kanto', 137),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 138 AND form IS NULL), 'Kanto', 138),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 139 AND form IS NULL), 'Kanto', 139),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 140 AND form IS NULL), 'Kanto', 140),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 141 AND form IS NULL), 'Kanto', 141),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 142 AND form IS NULL), 'Kanto', 142),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 143 AND form IS NULL), 'Kanto', 143),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 144 AND form IS NULL), 'Kanto', 144),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 145 AND form IS NULL), 'Kanto', 145),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 146 AND form IS NULL), 'Kanto', 146),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 147 AND form IS NULL), 'Kanto', 147),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 148 AND form IS NULL), 'Kanto', 148),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 149 AND form IS NULL), 'Kanto', 149),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 150 AND form IS NULL), 'Kanto', 150),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 151 AND form IS NULL), 'Kanto', 151);
-- Add metadata
INSERT INTO metadata (key, value) VALUES
('kanto_seeded', 'true'),
('kanto_seed_date', NOW()::TEXT),
('kanto_pokemon_count', '151');
@@ -0,0 +1,284 @@
-- Seed Johto region Pokémon (Gen 2)
-- Auto-generated from CSV files
-- Insert Johto region-game mappings
INSERT INTO region_game_mappings (region, game) VALUES
('Johto', 'Gold'),
('Johto', 'Silver'),
('Johto', 'Crystal'),
('Johto', 'HeartGold'),
('Johto', 'SoulSilver')
ON CONFLICT (region, game) DO NOTHING;
-- Insert Johto Pokémon entries
INSERT INTO pokedex_entries (
"pokedexNumber",
pokemon,
form,
"canGigantamax",
"regionToCatchIn",
"gamesToCatchIn"
) VALUES
(152, 'Chikorita', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(153, 'Bayleef', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(154, 'Meganium', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(154, 'Meganium', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(155, 'Cyndaquil', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(156, 'Quilava', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(157, 'Typhlosion', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(158, 'Totodile', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(159, 'Croconaw', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(160, 'Feraligatr', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(161, 'Sentret', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(162, 'Furret', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(163, 'Hoothoot', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(164, 'Noctowl', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(165, 'Ledyba', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(165, 'Ledyba', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(166, 'Ledian', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(166, 'Ledian', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(167, 'Spinarak', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(168, 'Ariados', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(169, 'Crobat', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(170, 'Chinchou', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(171, 'Lanturn', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(172, 'Pichu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(173, 'Cleffa', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(174, 'Igglybuff', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(175, 'Togepi', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(176, 'Togetic', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(177, 'Natu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(178, 'Xatu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(178, 'Xatu', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(179, 'Mareep', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(180, 'Flaaffy', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(181, 'Ampharos', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(182, 'Bellossom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(183, 'Marill', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(184, 'Azumarill', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(185, 'Sudowoodo', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(185, 'Sudowoodo', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(186, 'Politoed', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(186, 'Politoed', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(187, 'Hoppip', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(188, 'Skiploom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(189, 'Jumpluff', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(190, 'Aipom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(190, 'Aipom', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(191, 'Sunkern', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(192, 'Sunflora', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(193, 'Yanma', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(194, 'Wooper', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(194, 'Wooper', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(195, 'Quagsire', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(195, 'Quagsire', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(196, 'Espeon', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(197, 'Umbreon', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(198, 'Murkrow', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(198, 'Murkrow', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(199, 'Slowking', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(200, 'Misdreavus', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'A', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'B', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'C', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'D', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'E', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'F', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'G', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'H', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'I', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'J', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'K', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'L', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'M', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'N', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'O', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'P', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Q', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'R', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'S', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'T', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'U', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'V', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'W', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'X', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Y', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Z', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', '!', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', '?', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(202, 'Wobbuffet', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(202, 'Wobbuffet', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(203, 'Girafarig', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(203, 'Girafarig', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(204, 'Pineco', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(205, 'Forretress', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(206, 'Dunsparce', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(207, 'Gligar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(207, 'Gligar', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(208, 'Steelix', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(208, 'Steelix', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(209, 'Snubbull', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(210, 'Granbull', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(211, 'Qwilfish', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(212, 'Scizor', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(212, 'Scizor', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(213, 'Shuckle', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(214, 'Heracross', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(214, 'Heracross', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(215, 'Sneasel', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(215, 'Sneasel', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(216, 'Teddiursa', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(217, 'Ursaring', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(217, 'Ursaring', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(218, 'Slugma', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(219, 'Magcargo', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(220, 'Swinub', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(221, 'Piloswine', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(221, 'Piloswine', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(222, 'Corsola', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(223, 'Remoraid', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(224, 'Octillery', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(224, 'Octillery', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(225, 'Delibird', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(226, 'Mantine', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(227, 'Skarmory', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(228, 'Houndour', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(229, 'Houndoom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(229, 'Houndoom', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(230, 'Kingdra', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(231, 'Phanpy', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(232, 'Donphan', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(232, 'Donphan', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(233, 'Porygon2', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(234, 'Stantler', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(235, 'Smeargle', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(236, 'Tyrogue', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(237, 'Hitmontop', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(238, 'Smoochum', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(239, 'Elekid', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(240, 'Magby', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(241, 'Miltank', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(242, 'Blissey', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(243, 'Raikou', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(244, 'Entei', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(245, 'Suicune', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(246, 'Larvitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(247, 'Pupitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(248, 'Tyranitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(249, 'Lugia', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(250, 'Ho-Oh', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(251, 'Celebi', NULL, false, 'Johto', ARRAY['Crystal', 'HeartGold', 'SoulSilver'])
ON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;
-- Insert Johto regional dex numbers
INSERT INTO regional_dex_numbers (
pokedex_entry_id,
region,
dex_number
) VALUES
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 152 AND form IS NULL), 'Johto', 1),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 153 AND form IS NULL), 'Johto', 2),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 154 AND form IS NULL), 'Johto', 3),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 155 AND form IS NULL), 'Johto', 4),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 156 AND form IS NULL), 'Johto', 5),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 157 AND form IS NULL), 'Johto', 6),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 158 AND form IS NULL), 'Johto', 7),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 159 AND form IS NULL), 'Johto', 8),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 160 AND form IS NULL), 'Johto', 9),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 161 AND form IS NULL), 'Johto', 10),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 162 AND form IS NULL), 'Johto', 11),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 163 AND form IS NULL), 'Johto', 12),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 164 AND form IS NULL), 'Johto', 13),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 165 AND form IS NULL), 'Johto', 14),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 166 AND form IS NULL), 'Johto', 15),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 167 AND form IS NULL), 'Johto', 16),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 168 AND form IS NULL), 'Johto', 17),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 169 AND form IS NULL), 'Johto', 18),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 170 AND form IS NULL), 'Johto', 19),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 171 AND form IS NULL), 'Johto', 20),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 172 AND form IS NULL), 'Johto', 21),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 173 AND form IS NULL), 'Johto', 22),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 174 AND form IS NULL), 'Johto', 23),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 175 AND form IS NULL), 'Johto', 24),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 176 AND form IS NULL), 'Johto', 25),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 177 AND form IS NULL), 'Johto', 26),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 178 AND form IS NULL), 'Johto', 27),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 179 AND form IS NULL), 'Johto', 28),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 180 AND form IS NULL), 'Johto', 29),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 181 AND form IS NULL), 'Johto', 30),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 182 AND form IS NULL), 'Johto', 31),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 183 AND form IS NULL), 'Johto', 32),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 184 AND form IS NULL), 'Johto', 33),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 185 AND form IS NULL), 'Johto', 34),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 186 AND form IS NULL), 'Johto', 35),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 187 AND form IS NULL), 'Johto', 36),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 188 AND form IS NULL), 'Johto', 37),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 189 AND form IS NULL), 'Johto', 38),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 190 AND form IS NULL), 'Johto', 39),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 191 AND form IS NULL), 'Johto', 40),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 192 AND form IS NULL), 'Johto', 41),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 193 AND form IS NULL), 'Johto', 42),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 194 AND form IS NULL), 'Johto', 43),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 195 AND form IS NULL), 'Johto', 44),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 196 AND form IS NULL), 'Johto', 45),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 197 AND form IS NULL), 'Johto', 46),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 198 AND form IS NULL), 'Johto', 47),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 199 AND form IS NULL), 'Johto', 48),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 200 AND form IS NULL), 'Johto', 49),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 201 AND form = 'A'), 'Johto', 50),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 202 AND form IS NULL), 'Johto', 51),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 203 AND form IS NULL), 'Johto', 52),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 204 AND form IS NULL), 'Johto', 53),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 205 AND form IS NULL), 'Johto', 54),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 206 AND form IS NULL), 'Johto', 55),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 207 AND form IS NULL), 'Johto', 56),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 208 AND form IS NULL), 'Johto', 57),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 209 AND form IS NULL), 'Johto', 58),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 210 AND form IS NULL), 'Johto', 59),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 211 AND form IS NULL), 'Johto', 60),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 212 AND form IS NULL), 'Johto', 61),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 213 AND form IS NULL), 'Johto', 62),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 214 AND form IS NULL), 'Johto', 63),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 215 AND form IS NULL), 'Johto', 64),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 216 AND form IS NULL), 'Johto', 65),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 217 AND form IS NULL), 'Johto', 66),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 218 AND form IS NULL), 'Johto', 67),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 219 AND form IS NULL), 'Johto', 68),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 220 AND form IS NULL), 'Johto', 69),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 221 AND form IS NULL), 'Johto', 70),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 222 AND form IS NULL), 'Johto', 71),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 223 AND form IS NULL), 'Johto', 72),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 224 AND form IS NULL), 'Johto', 73),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 225 AND form IS NULL), 'Johto', 74),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 226 AND form IS NULL), 'Johto', 75),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 227 AND form IS NULL), 'Johto', 76),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 228 AND form IS NULL), 'Johto', 77),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 229 AND form IS NULL), 'Johto', 78),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 230 AND form IS NULL), 'Johto', 79),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 231 AND form IS NULL), 'Johto', 80),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 232 AND form IS NULL), 'Johto', 81),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 233 AND form IS NULL), 'Johto', 82),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 234 AND form IS NULL), 'Johto', 83),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 235 AND form IS NULL), 'Johto', 84),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 236 AND form IS NULL), 'Johto', 85),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 237 AND form IS NULL), 'Johto', 86),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 238 AND form IS NULL), 'Johto', 87),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 239 AND form IS NULL), 'Johto', 88),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 240 AND form IS NULL), 'Johto', 89),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 241 AND form IS NULL), 'Johto', 90),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 242 AND form IS NULL), 'Johto', 91),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 243 AND form IS NULL), 'Johto', 92),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 244 AND form IS NULL), 'Johto', 93),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 245 AND form IS NULL), 'Johto', 94),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 246 AND form IS NULL), 'Johto', 95),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 247 AND form IS NULL), 'Johto', 96),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 248 AND form IS NULL), 'Johto', 97),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 249 AND form IS NULL), 'Johto', 98),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 250 AND form IS NULL), 'Johto', 99),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 251 AND form IS NULL), 'Johto', 100);
-- Add metadata
INSERT INTO metadata (key, value) VALUES
('johto_seeded', 'true'),
('johto_seed_date', NOW()::TEXT),
('johto_pokemon_count', '99');