mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-07-12 18:43:45 +00:00
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:
@@ -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.
|
||||
@@ -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
|
||||
|
@@ -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,Farfetch’d,,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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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 +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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
+53
-15
@@ -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 });
|
||||
}
|
||||
};
|
||||
@@ -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 }
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -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 });
|
||||
|
||||
@@ -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}
|
||||
@@ -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}®ion=${catchRegion}&game=${catchGame}`
|
||||
: `/api/combined-data?page=${currentPage}&limit=${itemsPerPage}&enableForms=${showForms}®ion=${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}
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
@@ -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}
|
||||
@@ -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);
|
||||
|
||||
@@ -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, 'Farfetch’d', 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');
|
||||
Reference in New Issue
Block a user