mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-18 03:22:06 +00:00
Compare commits
217 Commits
v0.0.1
..
5c25a763c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c25a763c0 | |||
| 9ddca18f54 | |||
| 030571fd14 | |||
| 721c44dcd0 | |||
| b7d2db4959 | |||
| f382176804 | |||
| 3f8daea8d2 | |||
| c3a3d43883 | |||
| f2d8451c34 | |||
| ca7f9c0e48 | |||
| 27274171fe | |||
| 7240376e00 | |||
| 951c9b2878 | |||
| 2c5fc0d459 | |||
| 4b2f076e50 | |||
| 3ea194f87c | |||
| 86f1c21e4d | |||
| 4af33709a3 | |||
| 3a2c18bbeb | |||
| 92d6460765 | |||
| f9b7bbdf0a | |||
| de39dc78ea | |||
| 08c5e3271c | |||
| 676490b800 | |||
| bfd74a2985 | |||
| 7c32813cf2 | |||
| c5a47439d9 | |||
| 19e4d7b727 | |||
| 8255fb4c67 | |||
| 069f7f41fa | |||
| 955cba1aaa | |||
| c5a503f5b5 | |||
| 71cfbce7e3 | |||
| 4f2fe5e333 | |||
| 575aafbc28 | |||
| 6ecaa0f30f | |||
| e55d511141 | |||
| 86e8d6f5e6 | |||
| 8141bd624b | |||
| ab82f75dc6 | |||
| 3f5c14a8bc | |||
| 8775e05ba7 | |||
| c5c63ab8ef | |||
| dd9ced17d8 | |||
| 606fcbb201 | |||
| dfa0569119 | |||
| e819929898 | |||
| decf6ca1fb | |||
| 6d54c93700 | |||
| 07ce2b3421 | |||
| 0c25bba236 | |||
| eba34158fe | |||
| 71df0de3df | |||
| 46351c9af0 | |||
| 479b3ae7a0 | |||
| a26f38bca1 | |||
| f302721119 | |||
| a604fe2526 | |||
| 88d8344706 | |||
| bfef2fd4a7 | |||
| ee73c01afe | |||
| 3f0003b006 | |||
| 874a6bd78e | |||
| d40d4aa4e1 | |||
| bde3d71e6c | |||
| a871da5d59 | |||
| d437c05770 | |||
| 9d14b2d2f1 | |||
| ad8c8a847d | |||
| 23e8382bd1 | |||
| d50417ffef | |||
| ad66b874e9 | |||
| a7d878fffd | |||
| c10b526b3e | |||
| 1614ff52be | |||
| 75f7161b7a | |||
| 5657c2b2ab | |||
| f17b350e4e | |||
| 68cc2a0712 | |||
| 7c68d121ce | |||
| adc96baa90 | |||
| 463ab88545 | |||
| fb8dbdda3e | |||
| ee03ffc11f | |||
| 070604fb89 | |||
| b30be24fd5 | |||
| b1b6e63b6f | |||
| 845cc1c739 | |||
| 84c6de6b62 | |||
| fc09b5f2f1 | |||
| 789321ce03 | |||
| cb0773b03e | |||
| cbece0ec22 | |||
| 8fa394f102 | |||
| bd3f266a4a | |||
| 35ee92df0c | |||
| ca6831d10a | |||
| 2c2ab8b99c | |||
| 07af29e6e6 | |||
| 28bb39d558 | |||
| b9a49686c1 | |||
| 19a571e315 | |||
| 578df57a45 | |||
| 6ec40056e1 | |||
| d18b231ac6 | |||
| 0e2d7f7792 | |||
| 78c3230b92 | |||
| 2f5c0d68be | |||
| 073ba77dee | |||
| 31eb551c8a | |||
| 5e0e45494f | |||
| 852c6c3f34 | |||
| c063e9d2cd | |||
| c2c8fd1fb4 | |||
| 5b7fe52dc4 | |||
| 31e8a0d686 | |||
| 120f936eb7 | |||
| 8459ecf3fe | |||
| dd31a8d040 | |||
| ebfe2413f0 | |||
| 389f73d164 | |||
| fa38fa69ca | |||
| bc9ce84615 | |||
| 70f7bbdffc | |||
| 3aa716b941 | |||
| a4269259f9 | |||
| bfde0c49f5 | |||
| 02a2160f08 | |||
| 4d9d5e74d7 | |||
| e4ca722de7 | |||
| ad13540879 | |||
| 6c9ec0239c | |||
| 1c207455fb | |||
| c071dca26a | |||
| 8b32bca11c | |||
| 5bdf88b735 | |||
| f62b18b0ee | |||
| 5468515d4b | |||
| 6f4847f126 | |||
| 2ad7e219a2 | |||
| d3b7e7f5a9 | |||
| 7b5a948910 | |||
| bc25ab0e38 | |||
| 0285725b6d | |||
| 62c03f41eb | |||
| 80fb0d7414 | |||
| 1c9a9bcf8f | |||
| ed1ea42e50 | |||
| 0edc7f0974 | |||
| 7467a4425f | |||
| 90f6bd6e75 | |||
| 953524cecf | |||
| 308753d2e5 | |||
| 2cf3a03f8f | |||
| 8d86d93bc0 | |||
| 0e3be214ba | |||
| 704e618f84 | |||
| 64c5f375ac | |||
| e512c63e0b | |||
| e7f1930eff | |||
| 0e8af37309 | |||
| 6aac4c5985 | |||
| 212f674b44 | |||
| 7deb4632bc | |||
| 70f85be9b9 | |||
| 24792331db | |||
| d59d970812 | |||
| ff43601b1a | |||
| 425d66a9ce | |||
| 4761bdf31a | |||
| 6a4c6d81b2 | |||
| cdab9b4ab5 | |||
| c17dc6090c | |||
| 3a72225e16 | |||
| 046b956882 | |||
| 43c7dab2ee | |||
| 5bbe8b9abe | |||
| ec6aa1a5cf | |||
| e6b4cc84bc | |||
| 548e918fd9 | |||
| 5285d5d84d | |||
| 63de9c82a4 | |||
| 6213c773c3 | |||
| 99ed144a34 | |||
| 6db13fff6d | |||
| 499ee75c6a | |||
| 580dd83c69 | |||
| d80da3e415 | |||
| 2a4a4a1414 | |||
| f6bf7a7a85 | |||
| 0204870f5a | |||
| 454259d9e7 | |||
| 23385d2c44 | |||
| 44fa581ecd | |||
| b15889d6af | |||
| 16a067cd5d | |||
| 335f5c9046 | |||
| d66ac7ae4b | |||
| 22b9af0bbe | |||
| db5ae6fdc1 | |||
| 4ff4243125 | |||
| 59ff274a44 | |||
| 88b4d92905 | |||
| e699298a82 | |||
| ee191aa7cf | |||
| d708de8b2c | |||
| 93db09d669 | |||
| 1475e06b8e | |||
| 496cdb6666 | |||
| d55dcb066d | |||
| 7518579047 | |||
| aaf60f9634 | |||
| 4ca2ddb181 | |||
| 49e39c2742 | |||
| 26c552e994 | |||
| e3420d6e02 | |||
| bf087bd77b |
+22
-3
@@ -1,3 +1,22 @@
|
|||||||
MONGO_URL=""
|
PUBLIC_SUPABASE_URL="your-supabase-project-url"
|
||||||
PUBLIC_SUPABASE_URL=""
|
PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
|
||||||
PUBLIC_SUPABASE_ANON_KEY=""
|
SUPABASE_SERVICE_ROLE_KEY="your-supabase-service-role-key"
|
||||||
|
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER="false"
|
||||||
|
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID="your-google-client-id"
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET="your-google-client-secret"
|
||||||
|
DROPBOX_OAUTH_CLIENT_ID="your-dropbox-client-id"
|
||||||
|
DROPBOX_OAUTH_CLIENT_SECRET="your-dropbox-client-secret"
|
||||||
|
|
||||||
|
# Endpoint overrides for deterministic local provider tests. They are ignored unless
|
||||||
|
# ALLOW_PROVIDER_ENDPOINT_OVERRIDES is exactly "true", the local BDD stack variables are present,
|
||||||
|
# and every URL is loopback. These endpoints receive OAuth secrets, so never set this in a deployed
|
||||||
|
# environment. `npm run test:bdd` supplies the complete test context.
|
||||||
|
ALLOW_PROVIDER_ENDPOINT_OVERRIDES=""
|
||||||
|
GOOGLE_OAUTH_AUTHORIZE_URL=""
|
||||||
|
GOOGLE_OAUTH_TOKEN_URL=""
|
||||||
|
GOOGLE_DRIVE_API_URL=""
|
||||||
|
GOOGLE_DRIVE_UPLOAD_URL=""
|
||||||
|
DROPBOX_OAUTH_AUTHORIZE_URL=""
|
||||||
|
DROPBOX_OAUTH_TOKEN_URL=""
|
||||||
|
DROPBOX_UPLOAD_URL=""
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tests-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
# `$env/static/public` is resolved at build time, so every variable imported from it must be
|
||||||
|
# present for `vite build` and `svelte-check` to succeed on a clean checkout. These are the
|
||||||
|
# local-stack defaults already published in .env.local.example - never real credentials.
|
||||||
|
env:
|
||||||
|
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'false'
|
||||||
|
PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
|
||||||
|
PUBLIC_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
quality:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
- name: Check committed whitespace
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||||
|
git diff --check "${{ github.event.pull_request.base.sha }}...HEAD"
|
||||||
|
else
|
||||||
|
git diff --check "HEAD^...HEAD"
|
||||||
|
fi
|
||||||
|
- run: npm run check
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run test:fast
|
||||||
|
- name: Verify tests leave the checkout clean
|
||||||
|
run: test -z "$(git status --porcelain --untracked-files=all)"
|
||||||
|
- uses: actions/upload-artifact@v6
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: coverage
|
||||||
|
path: coverage/
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
integration:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
# `supabase start` applies migrations and seeds; no separate reset is needed.
|
||||||
|
- run: npx supabase start
|
||||||
|
- run: npm run test:integration
|
||||||
|
- if: always()
|
||||||
|
run: npx supabase stop
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 25
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run test:build
|
||||||
|
|
||||||
|
bdd:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
# Serve the committed sprites so offline sync doesn't download every Living Dex sprite from
|
||||||
|
# GitHub on each run.
|
||||||
|
env:
|
||||||
|
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER: 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
- uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: ~/.cache/ms-playwright
|
||||||
|
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
playwright-chromium-${{ runner.os }}-
|
||||||
|
- run: npx playwright install --with-deps chromium
|
||||||
|
- run: npx supabase start
|
||||||
|
- run: npm run test:bdd
|
||||||
|
- uses: actions/upload-artifact@v6
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: playwright-failures
|
||||||
|
path: |
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
if-no-files-found: ignore
|
||||||
|
- if: always()
|
||||||
|
run: npx supabase stop
|
||||||
@@ -10,3 +10,7 @@ vite.config.js.timestamp-*
|
|||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
/static/output.css
|
/static/output.css
|
||||||
.netlify
|
.netlify
|
||||||
|
.features-gen
|
||||||
|
coverage
|
||||||
|
playwright-report
|
||||||
|
test-results
|
||||||
|
|||||||
@@ -2,3 +2,14 @@
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
|
# Generated data exports. Nobody hand-edits these, and reformatting them buries real data
|
||||||
|
# changes under tens of thousands of lines of churn.
|
||||||
|
src/lib/helpers/pokeapi-pokemon.json
|
||||||
|
static/sprites/pokemon.json
|
||||||
|
src/lib/helpers/pokedex.json
|
||||||
|
src/lib/helpers/sprites.json
|
||||||
|
static/sprites-small/manifest.json
|
||||||
|
|
||||||
|
# Machine-local editor and tool settings.
|
||||||
|
**/*.local.json
|
||||||
|
|||||||
@@ -4,18 +4,24 @@ A web app to track completion of a living Pokédex.
|
|||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
Cloned the repository you can install the dependencies with `npm install`, and start a development server:
|
1. Clone the repository
|
||||||
|
2. Install the dependencies with `npm install`
|
||||||
|
3. Ensure that Docker is running
|
||||||
|
4. An example `.env` file is provided in the repository. You will need to copy `.env.example` to `.env` and fill in the values with your own credentials. For local development with Supabase running in Docker, you can use the following values:
|
||||||
|
|
||||||
```bash
|
- The `PUBLIC_SUPABASE_URL` will be `"http://127.0.0.1:54321"`
|
||||||
npm run dev
|
- The `PUBLIC_SUPABASE_ANON_KEY` will be the 'Publishable' authentication key displayed when you run Supabase in the terminal
|
||||||
|
- The `SUPABASE_SERVICE_ROLE_KEY` will be the 'Secret' authentication key displayed when you run Supabase in the terminal
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
5. Start local Supabase and a development server with `npm run dev:supabase`
|
||||||
npm run dev -- --open
|
6. The Pokédex data is automatically seeded via database migrations when Supabase starts
|
||||||
```
|
7. Create an account using the sign-up form and access the email it sends in [MailPit](http://127.0.0.1:54324/) to verify your email address.
|
||||||
|
N.B. All local emails are captured by MailPit when running Supabase in Docker.
|
||||||
|
8. You can now use [the app](http://localhost:5173/).
|
||||||
|
|
||||||
An example `.env` file is provided in the repository. You will need to copy `.env.example` to `.env` and fill in the values with your own credentials.
|
## Reference Data Updates
|
||||||
|
|
||||||
Once you have done this, you can seed the Pokédex data using the /api/seed endpoint. You will need to comment out the `return;` at the beginning of the `GET` function in `/src/routes/api/seed.ts` to do this. Make sure you remember to uncomment it afterwards.
|
The seed data lives in `supabase/migrations/20260118001000_seed_reference_data.sql`. Update that migration directly when new data is added. You can access a local copy of [Supabase](http://localhost:54323/) to check it.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -25,16 +31,100 @@ To create a production version:
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The test suite is split by responsibility so a failure points to the correct layer:
|
||||||
|
|
||||||
|
- `tests/unit` contains fast, isolated tests for utilities, repositories, and services.
|
||||||
|
- `tests/data` validates the tracked Pokémon, game, region, dex, and sprite reference files.
|
||||||
|
- `tests/integration` checks the migrated Supabase schema, views, constraints, RLS, and repositories.
|
||||||
|
- `tests/bdd/features` is the executable Gherkin specification for user-visible behaviour. Step
|
||||||
|
definitions and browser fixtures live beside it under `tests/bdd`.
|
||||||
|
- `tests/build` verifies generated service-worker and manifest artifacts after each supported build.
|
||||||
|
|
||||||
|
Run the offline suites while developing. `test:fast` includes the coverage run, so there is no need
|
||||||
|
to run both:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run test:fast
|
||||||
|
```
|
||||||
|
|
||||||
|
Coverage is measured across all of `src/lib` (excluding type-only models and the browser-only
|
||||||
|
store/action modules) with global thresholds set to the current baseline, so new untested code lowers
|
||||||
|
the number instead of being invisible to the gate. Ratchet the thresholds in `vitest.config.mts` up as
|
||||||
|
coverage grows, never down.
|
||||||
|
|
||||||
|
Database and BDD tests require Docker and the local Supabase stack. The wrappers read the local keys
|
||||||
|
from `supabase status` at run time, so no keys are hard-coded in the test files; the local stack's
|
||||||
|
well-known demo keys do appear in `.env.local.example`, and no real credentials are committed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run supabase:start
|
||||||
|
npm run supabase:reset
|
||||||
|
npm run test:integration
|
||||||
|
npm run test:bdd
|
||||||
|
```
|
||||||
|
|
||||||
|
`npm test` runs the complete CI-equivalent sequence and fails with setup instructions when Supabase is
|
||||||
|
not available. Individual layers are available as `test:unit`, `test:data`, `test:integration`,
|
||||||
|
`test:build`, and `test:bdd`.
|
||||||
|
|
||||||
|
Gherkin describes outcomes in domain language. Keep selectors, API calls, test-user provisioning, and
|
||||||
|
provider mocks in step definitions or support fixtures. `@product-review` marks a rule that should be
|
||||||
|
reviewed with product stakeholders, but does not skip it. Missing or ambiguous steps fail generation.
|
||||||
|
|
||||||
|
Google Drive and Dropbox scenarios use a local provider server (`scripts/mock-provider-server.mjs`)
|
||||||
|
and never contact real provider accounts. The endpoint overrides that point at it are refused unless
|
||||||
|
`ALLOW_PROVIDER_ENDPOINT_OVERRIDES=true`, the local test-stack/service-role variables are present,
|
||||||
|
and the override is a loopback URL. These endpoints receive the OAuth client secret and refresh
|
||||||
|
token, so they must not be redirectable in a deployed environment. `npm run test:bdd` supplies the
|
||||||
|
complete test context.
|
||||||
|
|
||||||
|
The mock's recorded requests, refresh counter and fail-uploads switch are reset before every scenario
|
||||||
|
by an auto fixture in `tests/bdd/fixtures.ts`. That reset is also why the suite runs with a single
|
||||||
|
worker: the mock is one shared process, so parallel scenarios would reset each other's state. A global
|
||||||
|
teardown deletes the users each run creates, so repeated local runs do not need a database reset.
|
||||||
|
|
||||||
|
Chromium is the only configured browser project. Playwright traces and screenshots are retained on
|
||||||
|
failure under `test-results`.
|
||||||
|
|
||||||
|
Password-reset scenarios follow recovery links generated by the local Supabase stack and verify both
|
||||||
|
the rejected old password and accepted replacement password. The application waits for Supabase to
|
||||||
|
confirm the recovery session before enabling the replacement form.
|
||||||
|
|
||||||
|
After sign-in, the application automatically stores a versioned, per-user read-only copy of every
|
||||||
|
Pokédex and its referenced artwork. Offline navigation opens a static viewer; all mutation and
|
||||||
|
authentication controls remain unavailable until connectivity returns. A successful sign-out removes
|
||||||
|
the user-specific snapshot and artwork caches from the device.
|
||||||
|
|
||||||
|
The current National Dex maximum is deliberately asserted as 1025. When adding a new generation,
|
||||||
|
update that expectation together with Pokémon data, the corresponding game/dex files, database seed,
|
||||||
|
and sprites. Data tests print the exact conflicting identities or broken references.
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
## Sprites
|
||||||
|
|
||||||
|
The app uses WebP sprites from `static/sprites-small`. During builds we generate this folder from the
|
||||||
|
full-resolution PNGs in `static/sprites`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run sprites:build
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to serve sprites locally, set `PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER="true"` in `.env`.
|
||||||
|
Otherwise the app defaults to GitHub raw for `static/sprites-small`.
|
||||||
|
|
||||||
## Hosting
|
## Hosting
|
||||||
|
|
||||||
The app is hosted on [Netlify](https://www.netlify.com/) at [pokedex.jcreek.co.uk](https://pokedex.jcreek.co.uk/).
|
The app is hosted on [Netlify](https://www.netlify.com/) at [pokedex.jcreek.co.uk](https://pokedex.jcreek.co.uk/).
|
||||||
|
|
||||||
The Pokédex data is stored in a [MongoDB](https://www.mongodb.com/) database hosted on [MongoDB Cloud](https://cloud.mongodb.com/).
|
The Pokédex data is stored in a [Supabase](https://supabase.com/) database.
|
||||||
|
|
||||||
User authentication is handled by [Supabase Auth](https://supabase.com/auth).
|
User authentication is handled by [Supabase Auth](https://supabase.com/auth).
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
The living dex tracker's sprite collection is taken from [PokéAPI Sprites](https://github.com/PokeAPI/sprites), which is licensed under [the Creative Commons CC0 1.0 Universal license](https://github.com/PokeAPI/sprites/blob/master/LICENCE.txt).
|
The living dex tracker's sprite collection is derived from [PokéAPI Sprites](https://github.com/PokeAPI/sprites)
|
||||||
|
and converted to smaller WebP files in `static/sprites-small`. PokéAPI sprites are licensed under
|
||||||
|
[the Creative Commons CC0 1.0 Universal license](https://github.com/PokeAPI/sprites/blob/master/LICENCE.txt).
|
||||||
|
|||||||
+17
-4
@@ -1,7 +1,20 @@
|
|||||||
import process from 'node:process'
|
import process from 'node:process';
|
||||||
import AdapterNode from '@sveltejs/adapter-node';
|
import AdapterNode from '@sveltejs/adapter-node';
|
||||||
import AdpaterStatic from '@sveltejs/adapter-static';
|
import AdapterNetlify from '@sveltejs/adapter-netlify';
|
||||||
|
|
||||||
export const nodeAdapter = process.env.NODE_ADAPTER === 'true'
|
export const nodeAdapter = process.env.NODE_ADAPTER === 'true';
|
||||||
|
|
||||||
export const adapter = nodeAdapter ? AdapterNode() : AdpaterStatic()
|
// Netlify is the deployment target; the node adapter exists so the service worker
|
||||||
|
// build tests can check the `build/client` layout a Node server produces.
|
||||||
|
export const adapter = nodeAdapter
|
||||||
|
? AdapterNode()
|
||||||
|
: AdapterNetlify({
|
||||||
|
// if true, will create a Netlify Edge Function rather
|
||||||
|
// than using standard Node-based functions
|
||||||
|
edge: false,
|
||||||
|
|
||||||
|
// if true, will split your app into multiple functions
|
||||||
|
// instead of creating a single one for the entire app.
|
||||||
|
// if `edge` is true, this option cannot be used
|
||||||
|
split: false
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
|
|
||||||
test('Test offline and trailing slashes', async ({ browser }) => {
|
|
||||||
// test offline + trailing slashes routes
|
|
||||||
const context = await browser.newContext();
|
|
||||||
const offlinePage = await context.newPage();
|
|
||||||
await offlinePage.goto('/');
|
|
||||||
const offlineSwURL = await offlinePage.evaluate(async () => {
|
|
||||||
const registration = await Promise.race([
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
navigator.serviceWorker.ready,
|
|
||||||
new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
// @ts-expect-error registration is of type unknown
|
|
||||||
return registration.active?.scriptURL;
|
|
||||||
});
|
|
||||||
const offlineSwName = 'sw.js';
|
|
||||||
expect(offlineSwURL).toBe(`http://localhost:4173/${offlineSwName}`);
|
|
||||||
await context.setOffline(true);
|
|
||||||
const aboutAnchor = offlinePage.getByRole('link', { name: 'About' });
|
|
||||||
expect(await aboutAnchor.getAttribute('href')).toBe('/about');
|
|
||||||
await aboutAnchor.click({ noWaitAfter: false });
|
|
||||||
const url = await offlinePage.evaluate(async () => {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
||||||
return location.href;
|
|
||||||
});
|
|
||||||
expect(url).toBe('http://localhost:4173/about');
|
|
||||||
expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy();
|
|
||||||
await offlinePage.reload({ waitUntil: 'load' });
|
|
||||||
expect(offlinePage.url()).toBe('http://localhost:4173/about');
|
|
||||||
expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy();
|
|
||||||
// Dispose context once it's no longer needed.
|
|
||||||
await context.close();
|
|
||||||
});
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
|
|
||||||
test('The service worker is registered and cache storage is present', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
|
|
||||||
const swURL = await page.evaluate(async () => {
|
|
||||||
const registration = await Promise.race([
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
navigator.serviceWorker.ready,
|
|
||||||
new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
// @ts-expect-error registration is of type unknown
|
|
||||||
return registration.active?.scriptURL;
|
|
||||||
});
|
|
||||||
const swName = 'sw.js';
|
|
||||||
expect(swURL).toBe(`http://localhost:4173/${swName}`);
|
|
||||||
|
|
||||||
const cacheContents = await page.evaluate(async () => {
|
|
||||||
const cacheState: Record<string, Array<string>> = {};
|
|
||||||
for (const cacheName of await caches.keys()) {
|
|
||||||
const cache = await caches.open(cacheName);
|
|
||||||
cacheState[cacheName] = (await cache.keys()).map((req) => req.url);
|
|
||||||
}
|
|
||||||
return cacheState;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(Object.keys(cacheContents).length).toEqual(1);
|
|
||||||
|
|
||||||
const key = 'workbox-precache-v2-http://localhost:4173/';
|
|
||||||
|
|
||||||
expect(Object.keys(cacheContents)[0]).toEqual(key);
|
|
||||||
|
|
||||||
const urls = cacheContents[key].map((url) => url.slice('http://localhost:4173/'.length));
|
|
||||||
|
|
||||||
/*
|
|
||||||
'http://localhost:4173/about?__WB_REVISION__=38251751d310c9b683a1426c22c135a2',
|
|
||||||
'http://localhost:4173/?__WB_REVISION__=073370aa3804305a787b01180cd6b8aa',
|
|
||||||
'http://localhost:4173/manifest.webmanifest?__WB_REVISION__=27df2fa4f35d014b42361148a2207da3'
|
|
||||||
*/
|
|
||||||
expect(urls.some((url) => url.startsWith('manifest.webmanifest?__WB_REVISION__='))).toEqual(true);
|
|
||||||
expect(urls.some((url) => url.startsWith('?__WB_REVISION__='))).toEqual(true);
|
|
||||||
expect(urls.some((url) => url.startsWith('about?__WB_REVISION__='))).toEqual(true);
|
|
||||||
// dontCacheBustURLsMatching: any asset in _app/immutable folder shouldn't have a revision (?__WB_REVISION__=)
|
|
||||||
expect(urls.some((url) => url.startsWith('_app/immutable/') && url.endsWith('.css'))).toEqual(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
expect(urls.some((url) => url.startsWith('_app/immutable/') && url.endsWith('.js'))).toEqual(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
expect(urls.some((url) => url.includes('_app/version.json?__WB_REVISION__='))).toEqual(true);
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
dexId,gameDisplayName,dexDisplayName,sortOrder,isDlc,parentDexId,file
|
||||||
|
red-kanto,Red,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||||
|
blue-kanto,Blue,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||||
|
yellow-kanto,Yellow,Kanto,1,false,,native-game-dexes/red-blue-yellow.csv
|
||||||
|
gold-johto,Gold,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||||
|
silver-johto,Silver,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||||
|
crystal-johto,Crystal,Johto,1,false,,native-game-dexes/gold-silver-crystal.csv
|
||||||
|
ruby-hoenn,Ruby,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||||
|
sapphire-hoenn,Sapphire,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||||
|
emerald-hoenn,Emerald,Hoenn,1,false,,native-game-dexes/ruby-sapphire-emerald.csv
|
||||||
|
firered-kanto,FireRed,Kanto,1,false,,native-game-dexes/fire-red-leaf-green.csv
|
||||||
|
leafgreen-kanto,LeafGreen,Kanto,1,false,,native-game-dexes/fire-red-leaf-green.csv
|
||||||
|
diamond-sinnoh,Diamond,Sinnoh,1,false,,native-game-dexes/diamond-pearl.csv
|
||||||
|
pearl-sinnoh,Pearl,Sinnoh,1,false,,native-game-dexes/diamond-pearl.csv
|
||||||
|
platinum-sinnoh,Platinum,Sinnoh,1,false,,native-game-dexes/platinum.csv
|
||||||
|
heart-gold-johto,Heart Gold,Johto,1,false,,native-game-dexes/heart-gold-soul-silver.csv
|
||||||
|
soul-silver-johto,Soul Silver,Johto,1,false,,native-game-dexes/heart-gold-soul-silver.csv
|
||||||
|
black-unova,Black,Unova,1,false,,native-game-dexes/black-white.csv
|
||||||
|
white-unova,White,Unova,1,false,,native-game-dexes/black-white.csv
|
||||||
|
black-2-unova,Black 2,Unova,1,false,,native-game-dexes/black-2-white-2.csv
|
||||||
|
white-2-unova,White 2,Unova,1,false,,native-game-dexes/black-2-white-2.csv
|
||||||
|
x-central-kalos,X,Central Kalos,1,false,,native-game-dexes/x-y-central-kalos.csv
|
||||||
|
x-coastal-kalos,X,Coastal Kalos,2,false,,native-game-dexes/x-y-coastal-kalos.csv
|
||||||
|
x-mountain-kalos,X,Mountain Kalos,3,false,,native-game-dexes/x-y-mountain-kalos.csv
|
||||||
|
y-central-kalos,Y,Central Kalos,1,false,,native-game-dexes/x-y-central-kalos.csv
|
||||||
|
y-coastal-kalos,Y,Coastal Kalos,2,false,,native-game-dexes/x-y-coastal-kalos.csv
|
||||||
|
y-mountain-kalos,Y,Mountain Kalos,3,false,,native-game-dexes/x-y-mountain-kalos.csv
|
||||||
|
omega-ruby-hoenn,Omega Ruby,Hoenn,1,false,,native-game-dexes/omega-ruby-alpha-sapphire.csv
|
||||||
|
alpha-sapphire-hoenn,Alpha Sapphire,Hoenn,1,false,,native-game-dexes/omega-ruby-alpha-sapphire.csv
|
||||||
|
sun-alola,Sun,Alola,1,false,,native-game-dexes/sun-moon-alola.csv
|
||||||
|
moon-alola,Moon,Alola,1,false,,native-game-dexes/sun-moon-alola.csv
|
||||||
|
ultra-sun-alola,Ultra Sun,Alola,1,false,,native-game-dexes/ultra-sun-ultra-moon-alola.csv
|
||||||
|
ultra-moon-alola,Ultra Moon,Alola,1,false,,native-game-dexes/ultra-sun-ultra-moon-alola.csv
|
||||||
|
lg-pikachu-kanto,LG: Pikachu,Kanto,1,false,,native-game-dexes/lets-go-pikachu-eevee.csv
|
||||||
|
lg-eevee-kanto,LG: Eevee,Kanto,1,false,,native-game-dexes/lets-go-pikachu-eevee.csv
|
||||||
|
sword-galar,Sword,Galar,1,false,,native-game-dexes/sword-shield.csv
|
||||||
|
sword-isle-armor,Sword,Isle of Armor,2,true,sword-galar,native-game-dexes/sword-shield-isle-armor.csv
|
||||||
|
sword-crown-tundra,Sword,Crown Tundra,3,true,sword-galar,native-game-dexes/sword-shield-crown-tundra.csv
|
||||||
|
shield-galar,Shield,Galar,1,false,,native-game-dexes/sword-shield.csv
|
||||||
|
shield-isle-armor,Shield,Isle of Armor,2,true,shield-galar,native-game-dexes/sword-shield-isle-armor.csv
|
||||||
|
shield-crown-tundra,Shield,Crown Tundra,3,true,shield-galar,native-game-dexes/sword-shield-crown-tundra.csv
|
||||||
|
brilliant-diamond-sinnoh,Brilliant Diamond,Sinnoh,1,false,,native-game-dexes/brilliant-diamond-shining-pearl.csv
|
||||||
|
shining-pearl-sinnoh,Shining Pearl,Sinnoh,1,false,,native-game-dexes/brilliant-diamond-shining-pearl.csv
|
||||||
|
legends-arceus-hisui,Legends: Arceus,Hisui,1,false,,native-game-dexes/legends-arceus.csv
|
||||||
|
scarlet-paldea,Scarlet,Paldea,1,false,,native-game-dexes/scarlet-violet.csv
|
||||||
|
scarlet-kitakami,Scarlet,Kitakami,2,true,scarlet-paldea,native-game-dexes/scarlet-violet-kitakami.csv
|
||||||
|
scarlet-blueberry,Scarlet,Blueberry,3,true,scarlet-paldea,native-game-dexes/scarlet-violet-blueberry.csv
|
||||||
|
violet-paldea,Violet,Paldea,1,false,,native-game-dexes/scarlet-violet.csv
|
||||||
|
violet-kitakami,Violet,Kitakami,2,true,violet-paldea,native-game-dexes/scarlet-violet-kitakami.csv
|
||||||
|
violet-blueberry,Violet,Blueberry,3,true,violet-paldea,native-game-dexes/scarlet-violet-blueberry.csv
|
||||||
|
legends-z-a-kalos,Legends: Z-A,Kalos,1,false,,native-game-dexes/legends-z-a.csv
|
||||||
|
legends-z-a-mega-dimension,Legends: Z-A,Mega Dimension,2,true,legends-z-a-kalos,native-game-dexes/legends-z-a-mega-dimension.csv
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
game,region,releaseYear
|
||||||
|
Red,Kanto,1996
|
||||||
|
Blue,Kanto,1996
|
||||||
|
Yellow,Kanto,1998
|
||||||
|
Gold,Johto,1999
|
||||||
|
Silver,Johto,1999
|
||||||
|
Crystal,Johto,2000
|
||||||
|
Ruby,Hoenn,2002
|
||||||
|
Sapphire,Hoenn,2002
|
||||||
|
Colosseum,Orre,2003
|
||||||
|
Emerald,Hoenn,2004
|
||||||
|
FireRed,Kanto,2004
|
||||||
|
LeafGreen,Kanto,2004
|
||||||
|
XD: Gale of Darkness,Orre,2005
|
||||||
|
Diamond,Sinnoh,2006
|
||||||
|
Pearl,Sinnoh,2006
|
||||||
|
Platinum,Sinnoh,2008
|
||||||
|
Ranch,Ranch,2008
|
||||||
|
Heart Gold,Johto,2009
|
||||||
|
Soul Silver,Johto,2009
|
||||||
|
Black,Unova,2010
|
||||||
|
White,Unova,2010
|
||||||
|
Black 2,Unova,2012
|
||||||
|
White 2,Unova,2012
|
||||||
|
Dream Radar,Unova,2012
|
||||||
|
X,Kalos,2013
|
||||||
|
Y,Kalos,2013
|
||||||
|
Omega Ruby,Hoenn,2014
|
||||||
|
Alpha Sapphire,Hoenn,2014
|
||||||
|
Sun,Alola,2016
|
||||||
|
Moon,Alola,2016
|
||||||
|
Moon (demo),Alola,2016
|
||||||
|
Go,Go,2016
|
||||||
|
Ultra Sun,Alola,2017
|
||||||
|
Ultra Moon,Alola,2017
|
||||||
|
LG: Pikachu,Kanto,2018
|
||||||
|
LG: Eevee,Kanto,2018
|
||||||
|
Sword,Galar,2019
|
||||||
|
Shield,Galar,2019
|
||||||
|
Home,Home,2020
|
||||||
|
Brilliant Diamond,Sinnoh,2021
|
||||||
|
Shining Pearl,Sinnoh,2021
|
||||||
|
Legends: Arceus,Hisui,2022
|
||||||
|
Scarlet,Paldea,2022
|
||||||
|
Violet,Paldea,2022
|
||||||
|
Legends: Z-A,Kalos,2025
|
||||||
|
@@ -0,0 +1,302 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
0,Victini,,
|
||||||
|
1,Snivy,,
|
||||||
|
2,Servine,,
|
||||||
|
3,Serperior,,
|
||||||
|
4,Tepig,,
|
||||||
|
5,Pignite,,
|
||||||
|
6,Emboar,,
|
||||||
|
7,Oshawott,,
|
||||||
|
8,Dewott,,
|
||||||
|
9,Samurott,,
|
||||||
|
10,Patrat,,
|
||||||
|
11,Watchog,,
|
||||||
|
12,Purrloin,,
|
||||||
|
13,Liepard,,
|
||||||
|
14,Pidove,,
|
||||||
|
15,Tranquill,,
|
||||||
|
16,Unfezant,,
|
||||||
|
17,Sewaddle,,
|
||||||
|
18,Swadloon,,
|
||||||
|
19,Leavanny,,
|
||||||
|
20,Sunkern,,
|
||||||
|
21,Sunflora,,
|
||||||
|
22,Lillipup,,
|
||||||
|
23,Herdier,,
|
||||||
|
24,Stoutland,,
|
||||||
|
25,Mareep,,
|
||||||
|
26,Flaaffy,,
|
||||||
|
27,Ampharos,,
|
||||||
|
28,Psyduck,,
|
||||||
|
29,Golduck,,
|
||||||
|
30,Azurill,,
|
||||||
|
31,Marill,,
|
||||||
|
32,Azumarill,,
|
||||||
|
33,Riolu,,
|
||||||
|
34,Lucario,,
|
||||||
|
35,Dunsparce,,
|
||||||
|
36,Audino,,
|
||||||
|
37,Pansage,,
|
||||||
|
38,Simisage,,
|
||||||
|
39,Pansear,,
|
||||||
|
40,Simisear,,
|
||||||
|
41,Panpour,,
|
||||||
|
42,Simipour,,
|
||||||
|
43,Venipede,,
|
||||||
|
44,Whirlipede,,
|
||||||
|
45,Scolipede,,
|
||||||
|
46,Koffing,,
|
||||||
|
47,Weezing,,
|
||||||
|
48,Magnemite,,
|
||||||
|
49,Magneton,,
|
||||||
|
50,Magnezone,,
|
||||||
|
51,Growlithe,,
|
||||||
|
52,Arcanine,,
|
||||||
|
53,Magby,,
|
||||||
|
54,Magmar,,
|
||||||
|
55,Magmortar,,
|
||||||
|
56,Elekid,,
|
||||||
|
57,Electabuzz,,
|
||||||
|
58,Electivire,,
|
||||||
|
59,Rattata,,
|
||||||
|
60,Raticate,,
|
||||||
|
61,Zubat,,
|
||||||
|
62,Golbat,,
|
||||||
|
63,Crobat,,
|
||||||
|
64,Grimer,,
|
||||||
|
65,Muk,,
|
||||||
|
66,Woobat,,
|
||||||
|
67,Swoobat,,
|
||||||
|
68,Roggenrola,,
|
||||||
|
69,Boldore,,
|
||||||
|
70,Gigalith,,
|
||||||
|
71,Onix,,
|
||||||
|
72,Steelix,,
|
||||||
|
73,Timburr,,
|
||||||
|
74,Gurdurr,,
|
||||||
|
75,Conkeldurr,,
|
||||||
|
76,Drilbur,,
|
||||||
|
77,Excadrill,,
|
||||||
|
78,Skitty,,
|
||||||
|
79,Delcatty,,
|
||||||
|
80,Buneary,,
|
||||||
|
81,Lopunny,,
|
||||||
|
82,Cottonee,,
|
||||||
|
83,Whimsicott,,
|
||||||
|
84,Petilil,,
|
||||||
|
85,Lilligant,,
|
||||||
|
86,Munna,,
|
||||||
|
87,Musharna,,
|
||||||
|
88,Cleffa,,
|
||||||
|
89,Clefairy,,
|
||||||
|
90,Clefable,,
|
||||||
|
91,Eevee,,
|
||||||
|
92,Vaporeon,,
|
||||||
|
93,Jolteon,,
|
||||||
|
94,Flareon,,
|
||||||
|
95,Espeon,,
|
||||||
|
96,Umbreon,,
|
||||||
|
97,Leafeon,,
|
||||||
|
98,Glaceon,,
|
||||||
|
99,Sandile,,
|
||||||
|
100,Krokorok,,
|
||||||
|
101,Krookodile,,
|
||||||
|
102,Darumaka,,
|
||||||
|
103,Darmanitan,,
|
||||||
|
104,Basculin,,
|
||||||
|
105,Trubbish,,
|
||||||
|
106,Garbodor,,
|
||||||
|
107,Minccino,,
|
||||||
|
108,Cinccino,,
|
||||||
|
109,Rufflet,,
|
||||||
|
110,Braviary,,
|
||||||
|
111,Vullaby,,
|
||||||
|
112,Mandibuzz,,
|
||||||
|
113,Sandshrew,,
|
||||||
|
114,Sandslash,,
|
||||||
|
115,Dwebble,,
|
||||||
|
116,Crustle,,
|
||||||
|
117,Scraggy,,
|
||||||
|
118,Scrafty,,
|
||||||
|
119,Maractus,,
|
||||||
|
120,Sigilyph,,
|
||||||
|
121,Trapinch,,
|
||||||
|
122,Vibrava,,
|
||||||
|
123,Flygon,,
|
||||||
|
124,Yamask,,
|
||||||
|
125,Cofagrigus,,
|
||||||
|
126,Tirtouga,,
|
||||||
|
127,Carracosta,,
|
||||||
|
128,Archen,,
|
||||||
|
129,Archeops,,
|
||||||
|
130,Klink,,
|
||||||
|
131,Klang,,
|
||||||
|
132,Klinklang,,
|
||||||
|
133,Budew,,
|
||||||
|
134,Roselia,,
|
||||||
|
135,Roserade,,
|
||||||
|
136,Gothita,,
|
||||||
|
137,Gothorita,,
|
||||||
|
138,Gothitelle,,
|
||||||
|
139,Solosis,,
|
||||||
|
140,Duosion,,
|
||||||
|
141,Reuniclus,,
|
||||||
|
142,Combee,,
|
||||||
|
143,Vespiquen,,
|
||||||
|
144,Emolga,,
|
||||||
|
145,Heracross,,
|
||||||
|
146,Pinsir,,
|
||||||
|
147,Blitzle,,
|
||||||
|
148,Zebstrika,,
|
||||||
|
149,Buizel,,
|
||||||
|
150,Floatzel,,
|
||||||
|
151,Zorua,,
|
||||||
|
152,Zoroark,,
|
||||||
|
153,Ducklett,,
|
||||||
|
154,Swanna,,
|
||||||
|
155,Karrablast,,
|
||||||
|
156,Escavalier,,
|
||||||
|
157,Shelmet,,
|
||||||
|
158,Accelgor,,
|
||||||
|
159,Deerling,,
|
||||||
|
160,Sawsbuck,,
|
||||||
|
161,Foongus,,
|
||||||
|
162,Amoonguss,,
|
||||||
|
163,Castform,,
|
||||||
|
164,Nosepass,,
|
||||||
|
165,Probopass,,
|
||||||
|
166,Aron,,
|
||||||
|
167,Lairon,,
|
||||||
|
168,Aggron,,
|
||||||
|
169,Baltoy,,
|
||||||
|
170,Claydol,,
|
||||||
|
171,Larvesta,,
|
||||||
|
172,Volcarona,,
|
||||||
|
173,Joltik,,
|
||||||
|
174,Galvantula,,
|
||||||
|
175,Ferroseed,,
|
||||||
|
176,Ferrothorn,,
|
||||||
|
177,Tynamo,,
|
||||||
|
178,Eelektrik,,
|
||||||
|
179,Eelektross,,
|
||||||
|
180,Frillish,,
|
||||||
|
181,Jellicent,,
|
||||||
|
182,Alomomola,,
|
||||||
|
183,Axew,,
|
||||||
|
184,Fraxure,,
|
||||||
|
185,Haxorus,,
|
||||||
|
186,Zangoose,,
|
||||||
|
187,Seviper,,
|
||||||
|
188,Elgyem,,
|
||||||
|
189,Beheeyem,,
|
||||||
|
190,Litwick,,
|
||||||
|
191,Lampent,,
|
||||||
|
192,Chandelure,,
|
||||||
|
193,Heatmor,,
|
||||||
|
194,Durant,,
|
||||||
|
195,Cubchoo,,
|
||||||
|
196,Beartic,,
|
||||||
|
197,Cryogonal,,
|
||||||
|
198,Tornadus,,
|
||||||
|
199,Thundurus,,
|
||||||
|
200,Landorus,,
|
||||||
|
201,Skorupi,,
|
||||||
|
202,Drapion,,
|
||||||
|
203,Skarmory,,
|
||||||
|
204,Numel,,
|
||||||
|
205,Camerupt,,
|
||||||
|
206,Spoink,,
|
||||||
|
207,Grumpig,,
|
||||||
|
208,Drifloon,,
|
||||||
|
209,Drifblim,,
|
||||||
|
210,Shuppet,,
|
||||||
|
211,Banette,,
|
||||||
|
212,Wingull,,
|
||||||
|
213,Pelipper,,
|
||||||
|
214,Lunatone,,
|
||||||
|
215,Solrock,,
|
||||||
|
216,Absol,,
|
||||||
|
217,Tangela,,
|
||||||
|
218,Tangrowth,,
|
||||||
|
219,Mienfoo,,
|
||||||
|
220,Mienshao,,
|
||||||
|
221,Gligar,,
|
||||||
|
222,Gliscor,,
|
||||||
|
223,Pawniard,,
|
||||||
|
224,Bisharp,,
|
||||||
|
225,Cobalion,,
|
||||||
|
226,Terrakion,,
|
||||||
|
227,Virizion,,
|
||||||
|
228,Tympole,,
|
||||||
|
229,Palpitoad,,
|
||||||
|
230,Seismitoad,,
|
||||||
|
231,Stunfisk,,
|
||||||
|
232,Shuckle,,
|
||||||
|
233,Mantyke,,
|
||||||
|
234,Mantine,,
|
||||||
|
235,Remoraid,,
|
||||||
|
236,Octillery,,
|
||||||
|
237,Corsola,,
|
||||||
|
238,Staryu,,
|
||||||
|
239,Starmie,,
|
||||||
|
240,Wailmer,,
|
||||||
|
241,Wailord,,
|
||||||
|
242,Lapras,,
|
||||||
|
243,Spheal,,
|
||||||
|
244,Sealeo,,
|
||||||
|
245,Walrein,,
|
||||||
|
246,Swablu,,
|
||||||
|
247,Altaria,,
|
||||||
|
248,Vulpix,,
|
||||||
|
249,Ninetales,,
|
||||||
|
250,Bronzor,,
|
||||||
|
251,Bronzong,,
|
||||||
|
252,Sneasel,,
|
||||||
|
253,Weavile,,
|
||||||
|
254,Delibird,,
|
||||||
|
255,Vanillite,,
|
||||||
|
256,Vanillish,,
|
||||||
|
257,Vanilluxe,,
|
||||||
|
258,Swinub,,
|
||||||
|
259,Piloswine,,
|
||||||
|
260,Mamoswine,,
|
||||||
|
261,Ditto,,
|
||||||
|
262,Beldum,,
|
||||||
|
263,Metang,,
|
||||||
|
264,Metagross,,
|
||||||
|
265,Seel,,
|
||||||
|
266,Dewgong,,
|
||||||
|
267,Throh,,
|
||||||
|
268,Sawk,,
|
||||||
|
269,Bouffalant,,
|
||||||
|
270,Druddigon,,
|
||||||
|
271,Golett,,
|
||||||
|
272,Golurk,,
|
||||||
|
273,Deino,,
|
||||||
|
274,Zweilous,,
|
||||||
|
275,Hydreigon,,
|
||||||
|
276,Slakoth,,
|
||||||
|
277,Vigoroth,,
|
||||||
|
278,Slaking,,
|
||||||
|
279,Corphish,,
|
||||||
|
280,Crawdaunt,,
|
||||||
|
281,Igglybuff,,
|
||||||
|
282,Jigglypuff,,
|
||||||
|
283,Wigglytuff,,
|
||||||
|
284,Lickitung,,
|
||||||
|
285,Lickilicky,,
|
||||||
|
286,Yanma,,
|
||||||
|
287,Yanmega,,
|
||||||
|
288,Tropius,,
|
||||||
|
289,Carnivine,,
|
||||||
|
290,Croagunk,,
|
||||||
|
291,Toxicroak,,
|
||||||
|
292,Larvitar,,
|
||||||
|
293,Pupitar,,
|
||||||
|
294,Tyranitar,,
|
||||||
|
295,Reshiram,,
|
||||||
|
296,Zekrom,,
|
||||||
|
297,Kyurem,,
|
||||||
|
298,Keldeo,,
|
||||||
|
299,Meloetta,,
|
||||||
|
300,Genesect,,
|
||||||
|
@@ -0,0 +1,157 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
0,Victini,,
|
||||||
|
1,Snivy,,
|
||||||
|
2,Servine,,
|
||||||
|
3,Serperior,,
|
||||||
|
4,Tepig,,
|
||||||
|
5,Pignite,,
|
||||||
|
6,Emboar,,
|
||||||
|
7,Oshawott,,
|
||||||
|
8,Dewott,,
|
||||||
|
9,Samurott,,
|
||||||
|
10,Patrat,,
|
||||||
|
11,Watchog,,
|
||||||
|
12,Lillipup,,
|
||||||
|
13,Herdier,,
|
||||||
|
14,Stoutland,,
|
||||||
|
15,Purrloin,,
|
||||||
|
16,Liepard,,
|
||||||
|
17,Pansage,,
|
||||||
|
18,Simisage,,
|
||||||
|
19,Pansear,,
|
||||||
|
20,Simisear,,
|
||||||
|
21,Panpour,,
|
||||||
|
22,Simipour,,
|
||||||
|
23,Munna,,
|
||||||
|
24,Musharna,,
|
||||||
|
25,Pidove,,
|
||||||
|
26,Tranquill,,
|
||||||
|
27,Unfezant,,
|
||||||
|
28,Blitzle,,
|
||||||
|
29,Zebstrika,,
|
||||||
|
30,Roggenrola,,
|
||||||
|
31,Boldore,,
|
||||||
|
32,Gigalith,,
|
||||||
|
33,Woobat,,
|
||||||
|
34,Swoobat,,
|
||||||
|
35,Drilbur,,
|
||||||
|
36,Excadrill,,
|
||||||
|
37,Audino,,
|
||||||
|
38,Timburr,,
|
||||||
|
39,Gurdurr,,
|
||||||
|
40,Conkeldurr,,
|
||||||
|
41,Tympole,,
|
||||||
|
42,Palpitoad,,
|
||||||
|
43,Seismitoad,,
|
||||||
|
44,Throh,,
|
||||||
|
45,Sawk,,
|
||||||
|
46,Sewaddle,,
|
||||||
|
47,Swadloon,,
|
||||||
|
48,Leavanny,,
|
||||||
|
49,Venipede,,
|
||||||
|
50,Whirlipede,,
|
||||||
|
51,Scolipede,,
|
||||||
|
52,Cottonee,,
|
||||||
|
53,Whimsicott,,
|
||||||
|
54,Petilil,,
|
||||||
|
55,Lilligant,,
|
||||||
|
56,Basculin,,
|
||||||
|
57,Sandile,,
|
||||||
|
58,Krokorok,,
|
||||||
|
59,Krookodile,,
|
||||||
|
60,Darumaka,,
|
||||||
|
61,Darmanitan,,
|
||||||
|
62,Maractus,,
|
||||||
|
63,Dwebble,,
|
||||||
|
64,Crustle,,
|
||||||
|
65,Scraggy,,
|
||||||
|
66,Scrafty,,
|
||||||
|
67,Sigilyph,,
|
||||||
|
68,Yamask,,
|
||||||
|
69,Cofagrigus,,
|
||||||
|
70,Tirtouga,,
|
||||||
|
71,Carracosta,,
|
||||||
|
72,Archen,,
|
||||||
|
73,Archeops,,
|
||||||
|
74,Trubbish,,
|
||||||
|
75,Garbodor,,
|
||||||
|
76,Zorua,,
|
||||||
|
77,Zoroark,,
|
||||||
|
78,Minccino,,
|
||||||
|
79,Cinccino,,
|
||||||
|
80,Gothita,,
|
||||||
|
81,Gothorita,,
|
||||||
|
82,Gothitelle,,
|
||||||
|
83,Solosis,,
|
||||||
|
84,Duosion,,
|
||||||
|
85,Reuniclus,,
|
||||||
|
86,Ducklett,,
|
||||||
|
87,Swanna,,
|
||||||
|
88,Vanillite,,
|
||||||
|
89,Vanillish,,
|
||||||
|
90,Vanilluxe,,
|
||||||
|
91,Deerling,,
|
||||||
|
92,Sawsbuck,,
|
||||||
|
93,Emolga,,
|
||||||
|
94,Karrablast,,
|
||||||
|
95,Escavalier,,
|
||||||
|
96,Foongus,,
|
||||||
|
97,Amoonguss,,
|
||||||
|
98,Frillish,,
|
||||||
|
99,Jellicent,,
|
||||||
|
100,Alomomola,,
|
||||||
|
101,Joltik,,
|
||||||
|
102,Galvantula,,
|
||||||
|
103,Ferroseed,,
|
||||||
|
104,Ferrothorn,,
|
||||||
|
105,Klink,,
|
||||||
|
106,Klang,,
|
||||||
|
107,Klinklang,,
|
||||||
|
108,Tynamo,,
|
||||||
|
109,Eelektrik,,
|
||||||
|
110,Eelektross,,
|
||||||
|
111,Elgyem,,
|
||||||
|
112,Beheeyem,,
|
||||||
|
113,Litwick,,
|
||||||
|
114,Lampent,,
|
||||||
|
115,Chandelure,,
|
||||||
|
116,Axew,,
|
||||||
|
117,Fraxure,,
|
||||||
|
118,Haxorus,,
|
||||||
|
119,Cubchoo,,
|
||||||
|
120,Beartic,,
|
||||||
|
121,Cryogonal,,
|
||||||
|
122,Shelmet,,
|
||||||
|
123,Accelgor,,
|
||||||
|
124,Stunfisk,,
|
||||||
|
125,Mienfoo,,
|
||||||
|
126,Mienshao,,
|
||||||
|
127,Druddigon,,
|
||||||
|
128,Golett,,
|
||||||
|
129,Golurk,,
|
||||||
|
130,Pawniard,,
|
||||||
|
131,Bisharp,,
|
||||||
|
132,Bouffalant,,
|
||||||
|
133,Rufflet,,
|
||||||
|
134,Braviary,,
|
||||||
|
135,Vullaby,,
|
||||||
|
136,Mandibuzz,,
|
||||||
|
137,Heatmor,,
|
||||||
|
138,Durant,,
|
||||||
|
139,Deino,,
|
||||||
|
140,Zweilous,,
|
||||||
|
141,Hydreigon,,
|
||||||
|
142,Larvesta,,
|
||||||
|
143,Volcarona,,
|
||||||
|
144,Cobalion,,
|
||||||
|
145,Terrakion,,
|
||||||
|
146,Virizion,,
|
||||||
|
147,Tornadus,,
|
||||||
|
148,Thundurus,,
|
||||||
|
149,Reshiram,,
|
||||||
|
150,Zekrom,,
|
||||||
|
151,Landorus,,
|
||||||
|
152,Kyurem,,
|
||||||
|
153,Keldeo,,
|
||||||
|
154,Meloetta,,
|
||||||
|
155,Genesect,,
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Turtwig,,
|
||||||
|
2,Grotle,,
|
||||||
|
3,Torterra,,
|
||||||
|
4,Chimchar,,
|
||||||
|
5,Monferno,,
|
||||||
|
6,Infernape,,
|
||||||
|
7,Piplup,,
|
||||||
|
8,Prinplup,,
|
||||||
|
9,Empoleon,,
|
||||||
|
10,Starly,,
|
||||||
|
11,Staravia,,
|
||||||
|
12,Staraptor,,
|
||||||
|
13,Bidoof,,
|
||||||
|
14,Bibarel,,
|
||||||
|
15,Kricketot,,
|
||||||
|
16,Kricketune,,
|
||||||
|
17,Shinx,,
|
||||||
|
18,Luxio,,
|
||||||
|
19,Luxray,,
|
||||||
|
20,Abra,,
|
||||||
|
21,Kadabra,,
|
||||||
|
22,Alakazam,,
|
||||||
|
23,Magikarp,,
|
||||||
|
24,Gyarados,,
|
||||||
|
25,Budew,,
|
||||||
|
26,Roselia,,
|
||||||
|
27,Roserade,,
|
||||||
|
28,Zubat,,
|
||||||
|
29,Golbat,,
|
||||||
|
30,Crobat,,
|
||||||
|
31,Geodude,,
|
||||||
|
32,Graveler,,
|
||||||
|
33,Golem,,
|
||||||
|
34,Onix,,
|
||||||
|
35,Steelix,,
|
||||||
|
36,Cranidos,,
|
||||||
|
37,Rampardos,,
|
||||||
|
38,Shieldon,,
|
||||||
|
39,Bastiodon,,
|
||||||
|
40,Machop,,
|
||||||
|
41,Machoke,,
|
||||||
|
42,Machamp,,
|
||||||
|
43,Psyduck,,
|
||||||
|
44,Golduck,,
|
||||||
|
45,Burmy,,
|
||||||
|
46,Wormadam,,
|
||||||
|
47,Mothim,,
|
||||||
|
48,Wurmple,,
|
||||||
|
49,Silcoon,,
|
||||||
|
50,Beautifly,,
|
||||||
|
51,Cascoon,,
|
||||||
|
52,Dustox,,
|
||||||
|
53,Combee,,
|
||||||
|
54,Vespiquen,,
|
||||||
|
55,Pachirisu,,
|
||||||
|
56,Buizel,,
|
||||||
|
57,Floatzel,,
|
||||||
|
58,Cherubi,,
|
||||||
|
59,Cherrim,,
|
||||||
|
60,Shellos,,
|
||||||
|
61,Gastrodon,,
|
||||||
|
62,Heracross,,
|
||||||
|
63,Aipom,,
|
||||||
|
64,Ambipom,,
|
||||||
|
65,Drifloon,,
|
||||||
|
66,Drifblim,,
|
||||||
|
67,Buneary,,
|
||||||
|
68,Lopunny,,
|
||||||
|
69,Gastly,,
|
||||||
|
70,Haunter,,
|
||||||
|
71,Gengar,,
|
||||||
|
72,Misdreavus,,
|
||||||
|
73,Mismagius,,
|
||||||
|
74,Murkrow,,
|
||||||
|
75,Honchkrow,,
|
||||||
|
76,Glameow,,
|
||||||
|
77,Purugly,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Barboach,,
|
||||||
|
81,Whiscash,,
|
||||||
|
82,Chingling,,
|
||||||
|
83,Chimecho,,
|
||||||
|
84,Stunky,,
|
||||||
|
85,Skuntank,,
|
||||||
|
86,Meditite,,
|
||||||
|
87,Medicham,,
|
||||||
|
88,Bronzor,,
|
||||||
|
89,Bronzong,,
|
||||||
|
90,Ponyta,,
|
||||||
|
91,Rapidash,,
|
||||||
|
92,Bonsly,,
|
||||||
|
93,Sudowoodo,,
|
||||||
|
94,Mime Jr.,,
|
||||||
|
95,Mr. Mime,,
|
||||||
|
96,Happiny,,
|
||||||
|
97,Chansey,,
|
||||||
|
98,Blissey,,
|
||||||
|
99,Cleffa,,
|
||||||
|
100,Clefairy,,
|
||||||
|
101,Clefable,,
|
||||||
|
102,Chatot,,
|
||||||
|
103,Pichu,,
|
||||||
|
104,Pikachu,,
|
||||||
|
105,Raichu,,
|
||||||
|
106,Hoothoot,,
|
||||||
|
107,Noctowl,,
|
||||||
|
108,Spiritomb,,
|
||||||
|
109,Gible,,
|
||||||
|
110,Gabite,,
|
||||||
|
111,Garchomp,,
|
||||||
|
112,Munchlax,,
|
||||||
|
113,Snorlax,,
|
||||||
|
114,Unown,,
|
||||||
|
115,Riolu,,
|
||||||
|
116,Lucario,,
|
||||||
|
117,Wooper,,
|
||||||
|
118,Quagsire,,
|
||||||
|
119,Wingull,,
|
||||||
|
120,Pelipper,,
|
||||||
|
121,Girafarig,,
|
||||||
|
122,Hippopotas,,
|
||||||
|
123,Hippowdon,,
|
||||||
|
124,Azurill,,
|
||||||
|
125,Marill,,
|
||||||
|
126,Azumarill,,
|
||||||
|
127,Skorupi,,
|
||||||
|
128,Drapion,,
|
||||||
|
129,Croagunk,,
|
||||||
|
130,Toxicroak,,
|
||||||
|
131,Carnivine,,
|
||||||
|
132,Remoraid,,
|
||||||
|
133,Octillery,,
|
||||||
|
134,Finneon,,
|
||||||
|
135,Lumineon,,
|
||||||
|
136,Tentacool,,
|
||||||
|
137,Tentacruel,,
|
||||||
|
138,Feebas,,
|
||||||
|
139,Milotic,,
|
||||||
|
140,Mantyke,,
|
||||||
|
141,Mantine,,
|
||||||
|
142,Snover,,
|
||||||
|
143,Abomasnow,,
|
||||||
|
144,Sneasel,,
|
||||||
|
145,Weavile,,
|
||||||
|
146,Uxie,,
|
||||||
|
147,Mesprit,,
|
||||||
|
148,Azelf,,
|
||||||
|
149,Dialga,,
|
||||||
|
150,Palkia,,
|
||||||
|
151,Manaphy,,
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Turtwig,,
|
||||||
|
2,Grotle,,
|
||||||
|
3,Torterra,,
|
||||||
|
4,Chimchar,,
|
||||||
|
5,Monferno,,
|
||||||
|
6,Infernape,,
|
||||||
|
7,Piplup,,
|
||||||
|
8,Prinplup,,
|
||||||
|
9,Empoleon,,
|
||||||
|
10,Starly,,
|
||||||
|
11,Staravia,,
|
||||||
|
12,Staraptor,,
|
||||||
|
13,Bidoof,,
|
||||||
|
14,Bibarel,,
|
||||||
|
15,Kricketot,,
|
||||||
|
16,Kricketune,,
|
||||||
|
17,Shinx,,
|
||||||
|
18,Luxio,,
|
||||||
|
19,Luxray,,
|
||||||
|
20,Abra,,
|
||||||
|
21,Kadabra,,
|
||||||
|
22,Alakazam,,
|
||||||
|
23,Magikarp,,
|
||||||
|
24,Gyarados,,
|
||||||
|
25,Budew,,
|
||||||
|
26,Roselia,,
|
||||||
|
27,Roserade,,
|
||||||
|
28,Zubat,,
|
||||||
|
29,Golbat,,
|
||||||
|
30,Crobat,,
|
||||||
|
31,Geodude,,
|
||||||
|
32,Graveler,,
|
||||||
|
33,Golem,,
|
||||||
|
34,Onix,,
|
||||||
|
35,Steelix,,
|
||||||
|
36,Cranidos,,
|
||||||
|
37,Rampardos,,
|
||||||
|
38,Shieldon,,
|
||||||
|
39,Bastiodon,,
|
||||||
|
40,Machop,,
|
||||||
|
41,Machoke,,
|
||||||
|
42,Machamp,,
|
||||||
|
43,Psyduck,,
|
||||||
|
44,Golduck,,
|
||||||
|
45,Burmy,,
|
||||||
|
46,Wormadam,,
|
||||||
|
47,Mothim,,
|
||||||
|
48,Wurmple,,
|
||||||
|
49,Silcoon,,
|
||||||
|
50,Beautifly,,
|
||||||
|
51,Cascoon,,
|
||||||
|
52,Dustox,,
|
||||||
|
53,Combee,,
|
||||||
|
54,Vespiquen,,
|
||||||
|
55,Pachirisu,,
|
||||||
|
56,Buizel,,
|
||||||
|
57,Floatzel,,
|
||||||
|
58,Cherubi,,
|
||||||
|
59,Cherrim,,
|
||||||
|
60,Shellos,,
|
||||||
|
61,Gastrodon,,
|
||||||
|
62,Heracross,,
|
||||||
|
63,Aipom,,
|
||||||
|
64,Ambipom,,
|
||||||
|
65,Drifloon,,
|
||||||
|
66,Drifblim,,
|
||||||
|
67,Buneary,,
|
||||||
|
68,Lopunny,,
|
||||||
|
69,Gastly,,
|
||||||
|
70,Haunter,,
|
||||||
|
71,Gengar,,
|
||||||
|
72,Misdreavus,,
|
||||||
|
73,Mismagius,,
|
||||||
|
74,Murkrow,,
|
||||||
|
75,Honchkrow,,
|
||||||
|
76,Glameow,,
|
||||||
|
77,Purugly,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Barboach,,
|
||||||
|
81,Whiscash,,
|
||||||
|
82,Chingling,,
|
||||||
|
83,Chimecho,,
|
||||||
|
84,Stunky,,
|
||||||
|
85,Skuntank,,
|
||||||
|
86,Meditite,,
|
||||||
|
87,Medicham,,
|
||||||
|
88,Bronzor,,
|
||||||
|
89,Bronzong,,
|
||||||
|
90,Ponyta,,
|
||||||
|
91,Rapidash,,
|
||||||
|
92,Bonsly,,
|
||||||
|
93,Sudowoodo,,
|
||||||
|
94,Mime Jr.,,
|
||||||
|
95,Mr. Mime,,
|
||||||
|
96,Happiny,,
|
||||||
|
97,Chansey,,
|
||||||
|
98,Blissey,,
|
||||||
|
99,Cleffa,,
|
||||||
|
100,Clefairy,,
|
||||||
|
101,Clefable,,
|
||||||
|
102,Chatot,,
|
||||||
|
103,Pichu,,
|
||||||
|
104,Pikachu,,
|
||||||
|
105,Raichu,,
|
||||||
|
106,Hoothoot,,
|
||||||
|
107,Noctowl,,
|
||||||
|
108,Spiritomb,,
|
||||||
|
109,Gible,,
|
||||||
|
110,Gabite,,
|
||||||
|
111,Garchomp,,
|
||||||
|
112,Munchlax,,
|
||||||
|
113,Snorlax,,
|
||||||
|
114,Unown,,
|
||||||
|
115,Riolu,,
|
||||||
|
116,Lucario,,
|
||||||
|
117,Wooper,,
|
||||||
|
118,Quagsire,,
|
||||||
|
119,Wingull,,
|
||||||
|
120,Pelipper,,
|
||||||
|
121,Girafarig,,
|
||||||
|
122,Hippopotas,,
|
||||||
|
123,Hippowdon,,
|
||||||
|
124,Azurill,,
|
||||||
|
125,Marill,,
|
||||||
|
126,Azumarill,,
|
||||||
|
127,Skorupi,,
|
||||||
|
128,Drapion,,
|
||||||
|
129,Croagunk,,
|
||||||
|
130,Toxicroak,,
|
||||||
|
131,Carnivine,,
|
||||||
|
132,Remoraid,,
|
||||||
|
133,Octillery,,
|
||||||
|
134,Finneon,,
|
||||||
|
135,Lumineon,,
|
||||||
|
136,Tentacool,,
|
||||||
|
137,Tentacruel,,
|
||||||
|
138,Feebas,,
|
||||||
|
139,Milotic,,
|
||||||
|
140,Mantyke,,
|
||||||
|
141,Mantine,,
|
||||||
|
142,Snover,,
|
||||||
|
143,Abomasnow,,
|
||||||
|
144,Sneasel,,
|
||||||
|
145,Weavile,,
|
||||||
|
146,Uxie,,
|
||||||
|
147,Mesprit,,
|
||||||
|
148,Azelf,,
|
||||||
|
149,Dialga,,
|
||||||
|
150,Palkia,,
|
||||||
|
151,Manaphy,,
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Bulbasaur,,
|
||||||
|
2,Ivysaur,,
|
||||||
|
3,Venusaur,,
|
||||||
|
4,Charmander,,
|
||||||
|
5,Charmeleon,,
|
||||||
|
6,Charizard,,
|
||||||
|
7,Squirtle,,
|
||||||
|
8,Wartortle,,
|
||||||
|
9,Blastoise,,
|
||||||
|
10,Caterpie,,
|
||||||
|
11,Metapod,,
|
||||||
|
12,Butterfree,,
|
||||||
|
13,Weedle,,
|
||||||
|
14,Kakuna,,
|
||||||
|
15,Beedrill,,
|
||||||
|
16,Pidgey,,
|
||||||
|
17,Pidgeotto,,
|
||||||
|
18,Pidgeot,,
|
||||||
|
19,Rattata,,
|
||||||
|
20,Raticate,,
|
||||||
|
21,Spearow,,
|
||||||
|
22,Fearow,,
|
||||||
|
23,Ekans,,
|
||||||
|
24,Arbok,,
|
||||||
|
25,Pikachu,,
|
||||||
|
26,Raichu,,
|
||||||
|
27,Sandshrew,,
|
||||||
|
28,Sandslash,,
|
||||||
|
29,Nidoran♀,,
|
||||||
|
30,Nidorina,,
|
||||||
|
31,Nidoqueen,,
|
||||||
|
32,Nidoran♂,,
|
||||||
|
33,Nidorino,,
|
||||||
|
34,Nidoking,,
|
||||||
|
35,Clefairy,,
|
||||||
|
36,Clefable,,
|
||||||
|
37,Vulpix,,
|
||||||
|
38,Ninetales,,
|
||||||
|
39,Jigglypuff,,
|
||||||
|
40,Wigglytuff,,
|
||||||
|
41,Zubat,,
|
||||||
|
42,Golbat,,
|
||||||
|
43,Oddish,,
|
||||||
|
44,Gloom,,
|
||||||
|
45,Vileplume,,
|
||||||
|
46,Paras,,
|
||||||
|
47,Parasect,,
|
||||||
|
48,Venonat,,
|
||||||
|
49,Venomoth,,
|
||||||
|
50,Diglett,,
|
||||||
|
51,Dugtrio,,
|
||||||
|
52,Meowth,,
|
||||||
|
53,Persian,,
|
||||||
|
54,Psyduck,,
|
||||||
|
55,Golduck,,
|
||||||
|
56,Mankey,,
|
||||||
|
57,Primeape,,
|
||||||
|
58,Growlithe,,
|
||||||
|
59,Arcanine,,
|
||||||
|
60,Poliwag,,
|
||||||
|
61,Poliwhirl,,
|
||||||
|
62,Poliwrath,,
|
||||||
|
63,Abra,,
|
||||||
|
64,Kadabra,,
|
||||||
|
65,Alakazam,,
|
||||||
|
66,Machop,,
|
||||||
|
67,Machoke,,
|
||||||
|
68,Machamp,,
|
||||||
|
69,Bellsprout,,
|
||||||
|
70,Weepinbell,,
|
||||||
|
71,Victreebel,,
|
||||||
|
72,Tentacool,,
|
||||||
|
73,Tentacruel,,
|
||||||
|
74,Geodude,,
|
||||||
|
75,Graveler,,
|
||||||
|
76,Golem,,
|
||||||
|
77,Ponyta,,
|
||||||
|
78,Rapidash,,
|
||||||
|
79,Slowpoke,,
|
||||||
|
80,Slowbro,,
|
||||||
|
81,Magnemite,,
|
||||||
|
82,Magneton,,
|
||||||
|
83,Farfetch’d,,
|
||||||
|
84,Doduo,,
|
||||||
|
85,Dodrio,,
|
||||||
|
86,Seel,,
|
||||||
|
87,Dewgong,,
|
||||||
|
88,Grimer,,
|
||||||
|
89,Muk,,
|
||||||
|
90,Shellder,,
|
||||||
|
91,Cloyster,,
|
||||||
|
92,Gastly,,
|
||||||
|
93,Haunter,,
|
||||||
|
94,Gengar,,
|
||||||
|
95,Onix,,
|
||||||
|
96,Drowzee,,
|
||||||
|
97,Hypno,,
|
||||||
|
98,Krabby,,
|
||||||
|
99,Kingler,,
|
||||||
|
100,Voltorb,,
|
||||||
|
101,Electrode,,
|
||||||
|
102,Exeggcute,,
|
||||||
|
103,Exeggutor,,
|
||||||
|
104,Cubone,,
|
||||||
|
105,Marowak,,
|
||||||
|
106,Hitmonlee,,
|
||||||
|
107,Hitmonchan,,
|
||||||
|
108,Lickitung,,
|
||||||
|
109,Koffing,,
|
||||||
|
110,Weezing,,
|
||||||
|
111,Rhyhorn,,
|
||||||
|
112,Rhydon,,
|
||||||
|
113,Chansey,,
|
||||||
|
114,Tangela,,
|
||||||
|
115,Kangaskhan,,
|
||||||
|
116,Horsea,,
|
||||||
|
117,Seadra,,
|
||||||
|
118,Goldeen,,
|
||||||
|
119,Seaking,,
|
||||||
|
120,Staryu,,
|
||||||
|
121,Starmie,,
|
||||||
|
122,Mr. Mime,,
|
||||||
|
123,Scyther,,
|
||||||
|
124,Jynx,,
|
||||||
|
125,Electabuzz,,
|
||||||
|
126,Magmar,,
|
||||||
|
127,Pinsir,,
|
||||||
|
128,Tauros,,
|
||||||
|
129,Magikarp,,
|
||||||
|
130,Gyarados,,
|
||||||
|
131,Lapras,,
|
||||||
|
132,Ditto,,
|
||||||
|
133,Eevee,,
|
||||||
|
134,Vaporeon,,
|
||||||
|
135,Jolteon,,
|
||||||
|
136,Flareon,,
|
||||||
|
137,Porygon,,
|
||||||
|
138,Omanyte,,
|
||||||
|
139,Omastar,,
|
||||||
|
140,Kabuto,,
|
||||||
|
141,Kabutops,,
|
||||||
|
142,Aerodactyl,,
|
||||||
|
143,Snorlax,,
|
||||||
|
144,Articuno,,
|
||||||
|
145,Zapdos,,
|
||||||
|
146,Moltres,,
|
||||||
|
147,Dratini,,
|
||||||
|
148,Dragonair,,
|
||||||
|
149,Dragonite,,
|
||||||
|
150,Mewtwo,,
|
||||||
|
151,Mew,,
|
||||||
|
@@ -0,0 +1,252 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Chikorita,,
|
||||||
|
2,Bayleef,,
|
||||||
|
3,Meganium,,
|
||||||
|
4,Cyndaquil,,
|
||||||
|
5,Quilava,,
|
||||||
|
6,Typhlosion,,
|
||||||
|
7,Totodile,,
|
||||||
|
8,Croconaw,,
|
||||||
|
9,Feraligatr,,
|
||||||
|
10,Pidgey,,
|
||||||
|
11,Pidgeotto,,
|
||||||
|
12,Pidgeot,,
|
||||||
|
13,Spearow,,
|
||||||
|
14,Fearow,,
|
||||||
|
15,Hoothoot,,
|
||||||
|
16,Noctowl,,
|
||||||
|
17,Rattata,,
|
||||||
|
18,Raticate,,
|
||||||
|
19,Sentret,,
|
||||||
|
20,Furret,,
|
||||||
|
21,Pichu,,
|
||||||
|
22,Pikachu,,
|
||||||
|
23,Raichu,,
|
||||||
|
24,Caterpie,,
|
||||||
|
25,Metapod,,
|
||||||
|
26,Butterfree,,
|
||||||
|
27,Weedle,,
|
||||||
|
28,Kakuna,,
|
||||||
|
29,Beedrill,,
|
||||||
|
30,Ledyba,,
|
||||||
|
31,Ledian,,
|
||||||
|
32,Spinarak,,
|
||||||
|
33,Ariados,,
|
||||||
|
34,Geodude,,
|
||||||
|
35,Graveler,,
|
||||||
|
36,Golem,,
|
||||||
|
37,Zubat,,
|
||||||
|
38,Golbat,,
|
||||||
|
39,Crobat,,
|
||||||
|
40,Cleffa,,
|
||||||
|
41,Clefairy,,
|
||||||
|
42,Clefable,,
|
||||||
|
43,Igglybuff,,
|
||||||
|
44,Jigglypuff,,
|
||||||
|
45,Wigglytuff,,
|
||||||
|
46,Togepi,,
|
||||||
|
47,Togetic,,
|
||||||
|
48,Sandshrew,,
|
||||||
|
49,Sandslash,,
|
||||||
|
50,Ekans,,
|
||||||
|
51,Arbok,,
|
||||||
|
52,Dunsparce,,
|
||||||
|
53,Mareep,,
|
||||||
|
54,Flaaffy,,
|
||||||
|
55,Ampharos,,
|
||||||
|
56,Wooper,,
|
||||||
|
57,Quagsire,,
|
||||||
|
58,Gastly,,
|
||||||
|
59,Haunter,,
|
||||||
|
60,Gengar,,
|
||||||
|
61,Unown,,
|
||||||
|
62,Onix,,
|
||||||
|
63,Steelix,,
|
||||||
|
64,Bellsprout,,
|
||||||
|
65,Weepinbell,,
|
||||||
|
66,Victreebel,,
|
||||||
|
67,Hoppip,,
|
||||||
|
68,Skiploom,,
|
||||||
|
69,Jumpluff,,
|
||||||
|
70,Paras,,
|
||||||
|
71,Parasect,,
|
||||||
|
72,Poliwag,,
|
||||||
|
73,Poliwhirl,,
|
||||||
|
74,Poliwrath,,
|
||||||
|
75,Politoed,,
|
||||||
|
76,Magikarp,,
|
||||||
|
77,Gyarados,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Slowpoke,,
|
||||||
|
81,Slowbro,,
|
||||||
|
82,Slowking,,
|
||||||
|
83,Oddish,,
|
||||||
|
84,Gloom,,
|
||||||
|
85,Vileplume,,
|
||||||
|
86,Bellossom,,
|
||||||
|
87,Drowzee,,
|
||||||
|
88,Hypno,,
|
||||||
|
89,Abra,,
|
||||||
|
90,Kadabra,,
|
||||||
|
91,Alakazam,,
|
||||||
|
92,Ditto,,
|
||||||
|
93,Pineco,,
|
||||||
|
94,Forretress,,
|
||||||
|
95,Nidoran♀,,
|
||||||
|
96,Nidorina,,
|
||||||
|
97,Nidoqueen,,
|
||||||
|
98,Nidoran♂,,
|
||||||
|
99,Nidorino,,
|
||||||
|
100,Nidoking,,
|
||||||
|
101,Yanma,,
|
||||||
|
102,Sunkern,,
|
||||||
|
103,Sunflora,,
|
||||||
|
104,Exeggcute,,
|
||||||
|
105,Exeggutor,,
|
||||||
|
106,Sudowoodo,,
|
||||||
|
107,Wobbuffet,,
|
||||||
|
108,Venonat,,
|
||||||
|
109,Venomoth,,
|
||||||
|
110,Scyther,,
|
||||||
|
111,Scizor,,
|
||||||
|
112,Pinsir,,
|
||||||
|
113,Heracross,,
|
||||||
|
114,Koffing,,
|
||||||
|
115,Weezing,,
|
||||||
|
116,Grimer,,
|
||||||
|
117,Muk,,
|
||||||
|
118,Magnemite,,
|
||||||
|
119,Magneton,,
|
||||||
|
120,Voltorb,,
|
||||||
|
121,Electrode,,
|
||||||
|
122,Aipom,,
|
||||||
|
123,Snubbull,,
|
||||||
|
124,Granbull,,
|
||||||
|
125,Vulpix,,
|
||||||
|
126,Ninetales,,
|
||||||
|
127,Growlithe,,
|
||||||
|
128,Arcanine,,
|
||||||
|
129,Stantler,,
|
||||||
|
130,Marill,,
|
||||||
|
131,Azumarill,,
|
||||||
|
132,Diglett,,
|
||||||
|
133,Dugtrio,,
|
||||||
|
134,Mankey,,
|
||||||
|
135,Primeape,,
|
||||||
|
136,Meowth,,
|
||||||
|
137,Persian,,
|
||||||
|
138,Psyduck,,
|
||||||
|
139,Golduck,,
|
||||||
|
140,Machop,,
|
||||||
|
141,Machoke,,
|
||||||
|
142,Machamp,,
|
||||||
|
143,Tyrogue,,
|
||||||
|
144,Hitmonlee,,
|
||||||
|
145,Hitmonchan,,
|
||||||
|
146,Hitmontop,,
|
||||||
|
147,Girafarig,,
|
||||||
|
148,Tauros,,
|
||||||
|
149,Miltank,,
|
||||||
|
150,Magby,,
|
||||||
|
151,Magmar,,
|
||||||
|
152,Smoochum,,
|
||||||
|
153,Jynx,,
|
||||||
|
154,Elekid,,
|
||||||
|
155,Electabuzz,,
|
||||||
|
156,Mr. Mime,,
|
||||||
|
157,Smeargle,,
|
||||||
|
158,Farfetch’d,,
|
||||||
|
159,Natu,,
|
||||||
|
160,Xatu,,
|
||||||
|
161,Qwilfish,,
|
||||||
|
162,Tentacool,,
|
||||||
|
163,Tentacruel,,
|
||||||
|
164,Krabby,,
|
||||||
|
165,Kingler,,
|
||||||
|
166,Shuckle,,
|
||||||
|
167,Staryu,,
|
||||||
|
168,Starmie,,
|
||||||
|
169,Shellder,,
|
||||||
|
170,Cloyster,,
|
||||||
|
171,Corsola,,
|
||||||
|
172,Remoraid,,
|
||||||
|
173,Octillery,,
|
||||||
|
174,Chinchou,,
|
||||||
|
175,Lanturn,,
|
||||||
|
176,Seel,,
|
||||||
|
177,Dewgong,,
|
||||||
|
178,Lickitung,,
|
||||||
|
179,Tangela,,
|
||||||
|
180,Eevee,,
|
||||||
|
181,Vaporeon,,
|
||||||
|
182,Jolteon,,
|
||||||
|
183,Flareon,,
|
||||||
|
184,Espeon,,
|
||||||
|
185,Umbreon,,
|
||||||
|
186,Horsea,,
|
||||||
|
187,Seadra,,
|
||||||
|
188,Kingdra,,
|
||||||
|
189,Gligar,,
|
||||||
|
190,Delibird,,
|
||||||
|
191,Swinub,,
|
||||||
|
192,Piloswine,,
|
||||||
|
193,Teddiursa,,
|
||||||
|
194,Ursaring,,
|
||||||
|
195,Phanpy,,
|
||||||
|
196,Donphan,,
|
||||||
|
197,Mantine,,
|
||||||
|
198,Skarmory,,
|
||||||
|
199,Doduo,,
|
||||||
|
200,Dodrio,,
|
||||||
|
201,Ponyta,,
|
||||||
|
202,Rapidash,,
|
||||||
|
203,Cubone,,
|
||||||
|
204,Marowak,,
|
||||||
|
205,Kangaskhan,,
|
||||||
|
206,Rhyhorn,,
|
||||||
|
207,Rhydon,,
|
||||||
|
208,Murkrow,,
|
||||||
|
209,Houndour,,
|
||||||
|
210,Houndoom,,
|
||||||
|
211,Slugma,,
|
||||||
|
212,Magcargo,,
|
||||||
|
213,Sneasel,,
|
||||||
|
214,Misdreavus,,
|
||||||
|
215,Porygon,,
|
||||||
|
216,Porygon2,,
|
||||||
|
217,Chansey,,
|
||||||
|
218,Blissey,,
|
||||||
|
219,Lapras,,
|
||||||
|
220,Omanyte,,
|
||||||
|
221,Omastar,,
|
||||||
|
222,Kabuto,,
|
||||||
|
223,Kabutops,,
|
||||||
|
224,Aerodactyl,,
|
||||||
|
225,Snorlax,,
|
||||||
|
226,Bulbasaur,,
|
||||||
|
227,Ivysaur,,
|
||||||
|
228,Venusaur,,
|
||||||
|
229,Charmander,,
|
||||||
|
230,Charmeleon,,
|
||||||
|
231,Charizard,,
|
||||||
|
232,Squirtle,,
|
||||||
|
233,Wartortle,,
|
||||||
|
234,Blastoise,,
|
||||||
|
235,Articuno,,
|
||||||
|
236,Zapdos,,
|
||||||
|
237,Moltres,,
|
||||||
|
238,Raikou,,
|
||||||
|
239,Entei,,
|
||||||
|
240,Suicune,,
|
||||||
|
241,Dratini,,
|
||||||
|
242,Dragonair,,
|
||||||
|
243,Dragonite,,
|
||||||
|
244,Larvitar,,
|
||||||
|
245,Pupitar,,
|
||||||
|
246,Tyranitar,,
|
||||||
|
247,Lugia,,
|
||||||
|
248,Ho-Oh,,
|
||||||
|
249,Mewtwo,,
|
||||||
|
250,Mew,,
|
||||||
|
251,Celebi,,
|
||||||
|
@@ -0,0 +1,257 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Chikorita,,
|
||||||
|
2,Bayleef,,
|
||||||
|
3,Meganium,,
|
||||||
|
4,Cyndaquil,,
|
||||||
|
5,Quilava,,
|
||||||
|
6,Typhlosion,,
|
||||||
|
7,Totodile,,
|
||||||
|
8,Croconaw,,
|
||||||
|
9,Feraligatr,,
|
||||||
|
10,Pidgey,,
|
||||||
|
11,Pidgeotto,,
|
||||||
|
12,Pidgeot,,
|
||||||
|
13,Spearow,,
|
||||||
|
14,Fearow,,
|
||||||
|
15,Hoothoot,,
|
||||||
|
16,Noctowl,,
|
||||||
|
17,Rattata,,
|
||||||
|
18,Raticate,,
|
||||||
|
19,Sentret,,
|
||||||
|
20,Furret,,
|
||||||
|
21,Pichu,,
|
||||||
|
22,Pikachu,,
|
||||||
|
23,Raichu,,
|
||||||
|
24,Caterpie,,
|
||||||
|
25,Metapod,,
|
||||||
|
26,Butterfree,,
|
||||||
|
27,Weedle,,
|
||||||
|
28,Kakuna,,
|
||||||
|
29,Beedrill,,
|
||||||
|
30,Ledyba,,
|
||||||
|
31,Ledian,,
|
||||||
|
32,Spinarak,,
|
||||||
|
33,Ariados,,
|
||||||
|
34,Geodude,,
|
||||||
|
35,Graveler,,
|
||||||
|
36,Golem,,
|
||||||
|
37,Zubat,,
|
||||||
|
38,Golbat,,
|
||||||
|
39,Crobat,,
|
||||||
|
40,Cleffa,,
|
||||||
|
41,Clefairy,,
|
||||||
|
42,Clefable,,
|
||||||
|
43,Igglybuff,,
|
||||||
|
44,Jigglypuff,,
|
||||||
|
45,Wigglytuff,,
|
||||||
|
46,Togepi,,
|
||||||
|
47,Togetic,,
|
||||||
|
48,Sandshrew,,
|
||||||
|
49,Sandslash,,
|
||||||
|
50,Ekans,,
|
||||||
|
51,Arbok,,
|
||||||
|
52,Dunsparce,,
|
||||||
|
53,Mareep,,
|
||||||
|
54,Flaaffy,,
|
||||||
|
55,Ampharos,,
|
||||||
|
56,Wooper,,
|
||||||
|
57,Quagsire,,
|
||||||
|
58,Gastly,,
|
||||||
|
59,Haunter,,
|
||||||
|
60,Gengar,,
|
||||||
|
61,Unown,,
|
||||||
|
62,Onix,,
|
||||||
|
63,Steelix,,
|
||||||
|
64,Bellsprout,,
|
||||||
|
65,Weepinbell,,
|
||||||
|
66,Victreebel,,
|
||||||
|
67,Hoppip,,
|
||||||
|
68,Skiploom,,
|
||||||
|
69,Jumpluff,,
|
||||||
|
70,Paras,,
|
||||||
|
71,Parasect,,
|
||||||
|
72,Poliwag,,
|
||||||
|
73,Poliwhirl,,
|
||||||
|
74,Poliwrath,,
|
||||||
|
75,Politoed,,
|
||||||
|
76,Magikarp,,
|
||||||
|
77,Gyarados,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Slowpoke,,
|
||||||
|
81,Slowbro,,
|
||||||
|
82,Slowking,,
|
||||||
|
83,Oddish,,
|
||||||
|
84,Gloom,,
|
||||||
|
85,Vileplume,,
|
||||||
|
86,Bellossom,,
|
||||||
|
87,Drowzee,,
|
||||||
|
88,Hypno,,
|
||||||
|
89,Abra,,
|
||||||
|
90,Kadabra,,
|
||||||
|
91,Alakazam,,
|
||||||
|
92,Ditto,,
|
||||||
|
93,Pineco,,
|
||||||
|
94,Forretress,,
|
||||||
|
95,Nidoran♀,,
|
||||||
|
96,Nidorina,,
|
||||||
|
97,Nidoqueen,,
|
||||||
|
98,Nidoran♂,,
|
||||||
|
99,Nidorino,,
|
||||||
|
100,Nidoking,,
|
||||||
|
101,Yanma,,
|
||||||
|
102,Yanmega,,
|
||||||
|
103,Sunkern,,
|
||||||
|
104,Sunflora,,
|
||||||
|
105,Exeggcute,,
|
||||||
|
106,Exeggutor,,
|
||||||
|
107,Sudowoodo,,
|
||||||
|
108,Wobbuffet,,
|
||||||
|
109,Venonat,,
|
||||||
|
110,Venomoth,,
|
||||||
|
111,Scyther,,
|
||||||
|
112,Scizor,,
|
||||||
|
113,Pinsir,,
|
||||||
|
114,Heracross,,
|
||||||
|
115,Koffing,,
|
||||||
|
116,Weezing,,
|
||||||
|
117,Grimer,,
|
||||||
|
118,Muk,,
|
||||||
|
119,Magnemite,,
|
||||||
|
120,Magneton,,
|
||||||
|
121,Voltorb,,
|
||||||
|
122,Electrode,,
|
||||||
|
123,Aipom,,
|
||||||
|
124,Ambipom,,
|
||||||
|
125,Snubbull,,
|
||||||
|
126,Granbull,,
|
||||||
|
127,Vulpix,,
|
||||||
|
128,Ninetales,,
|
||||||
|
129,Growlithe,,
|
||||||
|
130,Arcanine,,
|
||||||
|
131,Stantler,,
|
||||||
|
132,Marill,,
|
||||||
|
133,Azumarill,,
|
||||||
|
134,Diglett,,
|
||||||
|
135,Dugtrio,,
|
||||||
|
136,Mankey,,
|
||||||
|
137,Primeape,,
|
||||||
|
138,Meowth,,
|
||||||
|
139,Persian,,
|
||||||
|
140,Psyduck,,
|
||||||
|
141,Golduck,,
|
||||||
|
142,Machop,,
|
||||||
|
143,Machoke,,
|
||||||
|
144,Machamp,,
|
||||||
|
145,Tyrogue,,
|
||||||
|
146,Hitmonlee,,
|
||||||
|
147,Hitmonchan,,
|
||||||
|
148,Hitmontop,,
|
||||||
|
149,Girafarig,,
|
||||||
|
150,Tauros,,
|
||||||
|
151,Miltank,,
|
||||||
|
152,Magby,,
|
||||||
|
153,Magmar,,
|
||||||
|
154,Smoochum,,
|
||||||
|
155,Jynx,,
|
||||||
|
156,Elekid,,
|
||||||
|
157,Electabuzz,,
|
||||||
|
158,Mr. Mime,,
|
||||||
|
159,Smeargle,,
|
||||||
|
160,Farfetch’d,,
|
||||||
|
161,Natu,,
|
||||||
|
162,Xatu,,
|
||||||
|
163,Qwilfish,,
|
||||||
|
164,Tentacool,,
|
||||||
|
165,Tentacruel,,
|
||||||
|
166,Krabby,,
|
||||||
|
167,Kingler,,
|
||||||
|
168,Shuckle,,
|
||||||
|
169,Staryu,,
|
||||||
|
170,Starmie,,
|
||||||
|
171,Shellder,,
|
||||||
|
172,Cloyster,,
|
||||||
|
173,Corsola,,
|
||||||
|
174,Remoraid,,
|
||||||
|
175,Octillery,,
|
||||||
|
176,Chinchou,,
|
||||||
|
177,Lanturn,,
|
||||||
|
178,Seel,,
|
||||||
|
179,Dewgong,,
|
||||||
|
180,Lickitung,,
|
||||||
|
181,Lickilicky,,
|
||||||
|
182,Tangela,,
|
||||||
|
183,Tangrowth,,
|
||||||
|
184,Eevee,,
|
||||||
|
185,Vaporeon,,
|
||||||
|
186,Jolteon,,
|
||||||
|
187,Flareon,,
|
||||||
|
188,Espeon,,
|
||||||
|
189,Umbreon,,
|
||||||
|
190,Horsea,,
|
||||||
|
191,Seadra,,
|
||||||
|
192,Kingdra,,
|
||||||
|
193,Gligar,,
|
||||||
|
194,Delibird,,
|
||||||
|
195,Swinub,,
|
||||||
|
196,Piloswine,,
|
||||||
|
197,Mamoswine,,
|
||||||
|
198,Teddiursa,,
|
||||||
|
199,Ursaring,,
|
||||||
|
200,Phanpy,,
|
||||||
|
201,Donphan,,
|
||||||
|
202,Mantine,,
|
||||||
|
203,Skarmory,,
|
||||||
|
204,Doduo,,
|
||||||
|
205,Dodrio,,
|
||||||
|
206,Ponyta,,
|
||||||
|
207,Rapidash,,
|
||||||
|
208,Cubone,,
|
||||||
|
209,Marowak,,
|
||||||
|
210,Kangaskhan,,
|
||||||
|
211,Rhyhorn,,
|
||||||
|
212,Rhydon,,
|
||||||
|
213,Murkrow,,
|
||||||
|
214,Houndour,,
|
||||||
|
215,Houndoom,,
|
||||||
|
216,Slugma,,
|
||||||
|
217,Magcargo,,
|
||||||
|
218,Sneasel,,
|
||||||
|
219,Misdreavus,,
|
||||||
|
220,Porygon,,
|
||||||
|
221,Porygon2,,
|
||||||
|
222,Chansey,,
|
||||||
|
223,Blissey,,
|
||||||
|
224,Lapras,,
|
||||||
|
225,Omanyte,,
|
||||||
|
226,Omastar,,
|
||||||
|
227,Kabuto,,
|
||||||
|
228,Kabutops,,
|
||||||
|
229,Aerodactyl,,
|
||||||
|
230,Snorlax,,
|
||||||
|
231,Bulbasaur,,
|
||||||
|
232,Ivysaur,,
|
||||||
|
233,Venusaur,,
|
||||||
|
234,Charmander,,
|
||||||
|
235,Charmeleon,,
|
||||||
|
236,Charizard,,
|
||||||
|
237,Squirtle,,
|
||||||
|
238,Wartortle,,
|
||||||
|
239,Blastoise,,
|
||||||
|
240,Articuno,,
|
||||||
|
241,Zapdos,,
|
||||||
|
242,Moltres,,
|
||||||
|
243,Raikou,,
|
||||||
|
244,Entei,,
|
||||||
|
245,Suicune,,
|
||||||
|
246,Dratini,,
|
||||||
|
247,Dragonair,,
|
||||||
|
248,Dragonite,,
|
||||||
|
249,Larvitar,,
|
||||||
|
250,Pupitar,,
|
||||||
|
251,Tyranitar,,
|
||||||
|
252,Lugia,,
|
||||||
|
253,Ho-Oh,,
|
||||||
|
254,Mewtwo,,
|
||||||
|
255,Mew,,
|
||||||
|
256,Celebi,,
|
||||||
|
@@ -0,0 +1,243 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Rowlet,,
|
||||||
|
2,Dartrix,,
|
||||||
|
3,Decidueye,,
|
||||||
|
4,Cyndaquil,,
|
||||||
|
5,Quilava,,
|
||||||
|
6,Typhlosion,,
|
||||||
|
7,Oshawott,,
|
||||||
|
8,Dewott,,
|
||||||
|
9,Samurott,,
|
||||||
|
10,Bidoof,,
|
||||||
|
11,Bibarel,,
|
||||||
|
12,Starly,,
|
||||||
|
13,Staravia,,
|
||||||
|
14,Staraptor,,
|
||||||
|
15,Shinx,,
|
||||||
|
16,Luxio,,
|
||||||
|
17,Luxray,,
|
||||||
|
18,Wurmple,,
|
||||||
|
19,Silcoon,,
|
||||||
|
20,Beautifly,,
|
||||||
|
21,Cascoon,,
|
||||||
|
22,Dustox,,
|
||||||
|
23,Ponyta,,
|
||||||
|
24,Rapidash,,
|
||||||
|
25,Eevee,,
|
||||||
|
26,Vaporeon,,
|
||||||
|
27,Jolteon,,
|
||||||
|
28,Flareon,,
|
||||||
|
29,Espeon,,
|
||||||
|
30,Umbreon,,
|
||||||
|
31,Leafeon,,
|
||||||
|
32,Glaceon,,
|
||||||
|
33,Sylveon,,
|
||||||
|
34,Zubat,,
|
||||||
|
35,Golbat,,
|
||||||
|
36,Crobat,,
|
||||||
|
37,Drifloon,,
|
||||||
|
38,Drifblim,,
|
||||||
|
39,Kricketot,,
|
||||||
|
40,Kricketune,,
|
||||||
|
41,Buizel,,
|
||||||
|
42,Floatzel,,
|
||||||
|
43,Burmy,,
|
||||||
|
44,Wormadam,,
|
||||||
|
45,Mothim,,
|
||||||
|
46,Geodude,,
|
||||||
|
47,Graveler,,
|
||||||
|
48,Golem,,
|
||||||
|
49,Stantler,,
|
||||||
|
50,Wyrdeer,,
|
||||||
|
51,Munchlax,,
|
||||||
|
52,Snorlax,,
|
||||||
|
53,Paras,,
|
||||||
|
54,Parasect,,
|
||||||
|
55,Pichu,,
|
||||||
|
56,Pikachu,,
|
||||||
|
57,Raichu,,
|
||||||
|
58,Abra,,
|
||||||
|
59,Kadabra,,
|
||||||
|
60,Alakazam,,
|
||||||
|
61,Chimchar,,
|
||||||
|
62,Monferno,,
|
||||||
|
63,Infernape,,
|
||||||
|
64,Buneary,,
|
||||||
|
65,Lopunny,,
|
||||||
|
66,Cherubi,,
|
||||||
|
67,Cherrim,,
|
||||||
|
68,Psyduck,,
|
||||||
|
69,Golduck,,
|
||||||
|
70,Combee,,
|
||||||
|
71,Vespiquen,,
|
||||||
|
72,Scyther,,
|
||||||
|
73,Kleavor,,
|
||||||
|
74,Scizor,,
|
||||||
|
75,Heracross,,
|
||||||
|
76,Mime Jr.,,
|
||||||
|
77,Mr. Mime,,
|
||||||
|
78,Aipom,,
|
||||||
|
79,Ambipom,,
|
||||||
|
80,Magikarp,,
|
||||||
|
81,Gyarados,,
|
||||||
|
82,Shellos,,
|
||||||
|
83,Gastrodon,,
|
||||||
|
84,Qwilfish,,
|
||||||
|
85,Overqwil,,
|
||||||
|
86,Happiny,,
|
||||||
|
87,Chansey,,
|
||||||
|
88,Blissey,,
|
||||||
|
89,Budew,,
|
||||||
|
90,Roselia,,
|
||||||
|
91,Roserade,,
|
||||||
|
92,Carnivine,,
|
||||||
|
93,Petilil,,
|
||||||
|
94,Lilligant,,
|
||||||
|
95,Tangela,,
|
||||||
|
96,Tangrowth,,
|
||||||
|
97,Barboach,,
|
||||||
|
98,Whiscash,,
|
||||||
|
99,Croagunk,,
|
||||||
|
100,Toxicroak,,
|
||||||
|
101,Ralts,,
|
||||||
|
102,Kirlia,,
|
||||||
|
103,Gardevoir,,
|
||||||
|
104,Gallade,,
|
||||||
|
105,Yanma,,
|
||||||
|
106,Yanmega,,
|
||||||
|
107,Hippopotas,,
|
||||||
|
108,Hippowdon,,
|
||||||
|
109,Pachirisu,,
|
||||||
|
110,Stunky,,
|
||||||
|
111,Skuntank,,
|
||||||
|
112,Teddiursa,,
|
||||||
|
113,Ursaring,,
|
||||||
|
114,Ursaluna,,
|
||||||
|
115,Goomy,,
|
||||||
|
116,Sliggoo,,
|
||||||
|
117,Goodra,,
|
||||||
|
118,Onix,,
|
||||||
|
119,Steelix,,
|
||||||
|
120,Rhyhorn,,
|
||||||
|
121,Rhydon,,
|
||||||
|
122,Rhyperior,,
|
||||||
|
123,Bonsly,,
|
||||||
|
124,Sudowoodo,,
|
||||||
|
125,Lickitung,,
|
||||||
|
126,Lickilicky,,
|
||||||
|
127,Togepi,,
|
||||||
|
128,Togetic,,
|
||||||
|
129,Togekiss,,
|
||||||
|
130,Turtwig,,
|
||||||
|
131,Grotle,,
|
||||||
|
132,Torterra,,
|
||||||
|
133,Porygon,,
|
||||||
|
134,Porygon2,,
|
||||||
|
135,Porygon-Z,,
|
||||||
|
136,Gastly,,
|
||||||
|
137,Haunter,,
|
||||||
|
138,Gengar,,
|
||||||
|
139,Spiritomb,,
|
||||||
|
140,Murkrow,,
|
||||||
|
141,Honchkrow,,
|
||||||
|
142,Unown,,
|
||||||
|
143,Spheal,,
|
||||||
|
144,Sealeo,,
|
||||||
|
145,Walrein,,
|
||||||
|
146,Remoraid,,
|
||||||
|
147,Octillery,,
|
||||||
|
148,Skorupi,,
|
||||||
|
149,Drapion,,
|
||||||
|
150,Growlithe,,
|
||||||
|
151,Arcanine,,
|
||||||
|
152,Glameow,,
|
||||||
|
153,Purugly,,
|
||||||
|
154,Machop,,
|
||||||
|
155,Machoke,,
|
||||||
|
156,Machamp,,
|
||||||
|
157,Chatot,,
|
||||||
|
158,Duskull,,
|
||||||
|
159,Dusclops,,
|
||||||
|
160,Dusknoir,,
|
||||||
|
161,Piplup,,
|
||||||
|
162,Prinplup,,
|
||||||
|
163,Empoleon,,
|
||||||
|
164,Mantyke,,
|
||||||
|
165,Mantine,,
|
||||||
|
166,Basculin,,
|
||||||
|
167,Basculegion,,
|
||||||
|
168,Vulpix,,
|
||||||
|
169,Ninetales,,
|
||||||
|
170,Tentacool,,
|
||||||
|
171,Tentacruel,,
|
||||||
|
172,Finneon,,
|
||||||
|
173,Lumineon,,
|
||||||
|
174,Magby,,
|
||||||
|
175,Magmar,,
|
||||||
|
176,Magmortar,,
|
||||||
|
177,Magnemite,,
|
||||||
|
178,Magneton,,
|
||||||
|
179,Magnezone,,
|
||||||
|
180,Bronzor,,
|
||||||
|
181,Bronzong,,
|
||||||
|
182,Elekid,,
|
||||||
|
183,Electabuzz,,
|
||||||
|
184,Electivire,,
|
||||||
|
185,Gligar,,
|
||||||
|
186,Gliscor,,
|
||||||
|
187,Gible,,
|
||||||
|
188,Gabite,,
|
||||||
|
189,Garchomp,,
|
||||||
|
190,Nosepass,,
|
||||||
|
191,Probopass,,
|
||||||
|
192,Voltorb,,
|
||||||
|
193,Electrode,,
|
||||||
|
194,Rotom,,
|
||||||
|
195,Chingling,,
|
||||||
|
196,Chimecho,,
|
||||||
|
197,Misdreavus,,
|
||||||
|
198,Mismagius,,
|
||||||
|
199,Cleffa,,
|
||||||
|
200,Clefairy,,
|
||||||
|
201,Clefable,,
|
||||||
|
202,Sneasel,,
|
||||||
|
203,Sneasler,,
|
||||||
|
204,Weavile,,
|
||||||
|
205,Snorunt,,
|
||||||
|
206,Glalie,,
|
||||||
|
207,Froslass,,
|
||||||
|
208,Cranidos,,
|
||||||
|
209,Rampardos,,
|
||||||
|
210,Shieldon,,
|
||||||
|
211,Bastiodon,,
|
||||||
|
212,Swinub,,
|
||||||
|
213,Piloswine,,
|
||||||
|
214,Mamoswine,,
|
||||||
|
215,Bergmite,,
|
||||||
|
216,Avalugg,,
|
||||||
|
217,Snover,,
|
||||||
|
218,Abomasnow,,
|
||||||
|
219,Zorua,,
|
||||||
|
220,Zoroark,,
|
||||||
|
221,Rufflet,,
|
||||||
|
222,Braviary,,
|
||||||
|
223,Riolu,,
|
||||||
|
224,Lucario,,
|
||||||
|
225,Uxie,,
|
||||||
|
226,Mesprit,,
|
||||||
|
227,Azelf,,
|
||||||
|
228,Heatran,,
|
||||||
|
229,Regigigas,,
|
||||||
|
230,Cresselia,,
|
||||||
|
231,Tornadus,,
|
||||||
|
232,Thundurus,,
|
||||||
|
233,Landorus,,
|
||||||
|
234,Enamorus,,
|
||||||
|
235,Dialga,,
|
||||||
|
236,Palkia,,
|
||||||
|
237,Giratina,,
|
||||||
|
238,Arceus,,
|
||||||
|
239,Phione,,
|
||||||
|
240,Manaphy,,
|
||||||
|
241,Shaymin,,
|
||||||
|
242,Darkrai,,
|
||||||
|
@@ -0,0 +1,133 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Mankey,,
|
||||||
|
2,Primeape,,
|
||||||
|
3,Annihilape,,
|
||||||
|
4,Meowth,,
|
||||||
|
5,Persian,,
|
||||||
|
6,Perrserker,,
|
||||||
|
7,Farfetch’d,,
|
||||||
|
8,Sirfetch’d,,
|
||||||
|
9,Cubone,,
|
||||||
|
10,Marowak,,
|
||||||
|
11,Porygon,,
|
||||||
|
12,Porygon2,,
|
||||||
|
13,Porygon-Z,,
|
||||||
|
14,Capsakid,,
|
||||||
|
15,Scovillain,,
|
||||||
|
16,Tinkatink,,
|
||||||
|
17,Tinkatuff,,
|
||||||
|
18,Tinkaton,,
|
||||||
|
19,Cyclizar,,
|
||||||
|
20,Glimmet,,
|
||||||
|
21,Glimmora,,
|
||||||
|
22,Rotom,,
|
||||||
|
23,Greavard,,
|
||||||
|
24,Houndstone,,
|
||||||
|
25,Sandygast,,
|
||||||
|
26,Palossand,,
|
||||||
|
27,Kecleon,,
|
||||||
|
28,Flamigo,,
|
||||||
|
29,Cryogonal,,
|
||||||
|
30,Dondozo,,
|
||||||
|
31,Tatsugiri,,
|
||||||
|
32,Frigibax,,
|
||||||
|
33,Arctibax,,
|
||||||
|
34,Baxcalibur,,
|
||||||
|
35,Gimmighoul,,
|
||||||
|
36,Gholdengo,,
|
||||||
|
37,Qwilfish,,
|
||||||
|
38,Overqwil,,
|
||||||
|
39,Treecko,,
|
||||||
|
40,Grovyle,,
|
||||||
|
41,Sceptile,,
|
||||||
|
42,Torchic,,
|
||||||
|
43,Combusken,,
|
||||||
|
44,Blaziken,,
|
||||||
|
45,Mudkip,,
|
||||||
|
46,Marshtomp,,
|
||||||
|
47,Swampert,,
|
||||||
|
48,Feebas,,
|
||||||
|
49,Milotic,,
|
||||||
|
50,Chingling,,
|
||||||
|
51,Chimecho,,
|
||||||
|
52,Indeedee,,
|
||||||
|
53,Purrloin,,
|
||||||
|
54,Liepard,,
|
||||||
|
55,Munna,,
|
||||||
|
56,Musharna,,
|
||||||
|
57,Throh,,
|
||||||
|
58,Sawk,,
|
||||||
|
59,Yamask,,
|
||||||
|
60,Cofagrigus,,
|
||||||
|
61,Runerigus,,
|
||||||
|
62,Wimpod,,
|
||||||
|
63,Golisopod,,
|
||||||
|
64,Nickit,,
|
||||||
|
65,Thievul,,
|
||||||
|
66,Clobbopus,,
|
||||||
|
67,Grapploct,,
|
||||||
|
68,Mimikyu,,
|
||||||
|
69,Kleavor,,
|
||||||
|
70,Morpeko,,
|
||||||
|
71,Golett,,
|
||||||
|
72,Golurk,,
|
||||||
|
73,Rookidee,,
|
||||||
|
74,Corvisquire,,
|
||||||
|
75,Corviknight,,
|
||||||
|
76,Igglybuff,,
|
||||||
|
77,Jigglypuff,,
|
||||||
|
78,Wigglytuff,,
|
||||||
|
79,Fidough,,
|
||||||
|
80,Dachsbun,,
|
||||||
|
81,Starly,,
|
||||||
|
82,Staravia,,
|
||||||
|
83,Staraptor,,
|
||||||
|
84,Spoink,,
|
||||||
|
85,Grumpig,,
|
||||||
|
86,Squawkabilly,,
|
||||||
|
87,Crabrawler,,
|
||||||
|
88,Crabominable,,
|
||||||
|
89,Nacli,,
|
||||||
|
90,Naclstack,,
|
||||||
|
91,Garganacl,,
|
||||||
|
92,Gulpin,,
|
||||||
|
93,Swalot,,
|
||||||
|
94,Zubat,,
|
||||||
|
95,Golbat,,
|
||||||
|
96,Crobat,,
|
||||||
|
97,Charcadet,,
|
||||||
|
98,Armarouge,,
|
||||||
|
99,Ceruledge,,
|
||||||
|
100,Maschiff,,
|
||||||
|
101,Mabosstiff,,
|
||||||
|
102,Toxel,,
|
||||||
|
103,Toxtricity,,
|
||||||
|
104,Shroodle,,
|
||||||
|
105,Grafaiai,,
|
||||||
|
106,Zangoose,,
|
||||||
|
107,Seviper,,
|
||||||
|
108,Mime Jr.,,
|
||||||
|
109,Mr. Mime,,
|
||||||
|
110,Mr. Rime,,
|
||||||
|
111,Foongus,,
|
||||||
|
112,Amoonguss,,
|
||||||
|
113,Heatran,,
|
||||||
|
114,Volcanion,,
|
||||||
|
115,Cobalion,,
|
||||||
|
116,Terrakion,,
|
||||||
|
117,Virizion,,
|
||||||
|
118,Keldeo,,
|
||||||
|
119,Meloetta,,
|
||||||
|
120,Genesect,,
|
||||||
|
121,Hoopa,,
|
||||||
|
122,Marshadow,,
|
||||||
|
123,Meltan,,
|
||||||
|
124,Melmetal,,
|
||||||
|
125,Darkrai,,
|
||||||
|
126,Latias,,
|
||||||
|
127,Latios,,
|
||||||
|
128,Kyogre,,
|
||||||
|
129,Groudon,,
|
||||||
|
130,Rayquaza,,
|
||||||
|
131,Magearna,,
|
||||||
|
132,Zeraora,,
|
||||||
|
@@ -0,0 +1,233 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Chikorita,,
|
||||||
|
2,Bayleef,,
|
||||||
|
3,Meganium,,
|
||||||
|
4,Tepig,,
|
||||||
|
5,Pignite,,
|
||||||
|
6,Emboar,,
|
||||||
|
7,Totodile,,
|
||||||
|
8,Croconaw,,
|
||||||
|
9,Feraligatr,,
|
||||||
|
10,Fletchling,,
|
||||||
|
11,Fletchinder,,
|
||||||
|
12,Talonflame,,
|
||||||
|
13,Bunnelby,,
|
||||||
|
14,Diggersby,,
|
||||||
|
15,Scatterbug,,
|
||||||
|
16,Spewpa,,
|
||||||
|
17,Vivillon,,
|
||||||
|
18,Weedle,,
|
||||||
|
19,Kakuna,,
|
||||||
|
20,Beedrill,,
|
||||||
|
21,Pidgey,,
|
||||||
|
22,Pidgeotto,,
|
||||||
|
23,Pidgeot,,
|
||||||
|
24,Mareep,,
|
||||||
|
25,Flaaffy,,
|
||||||
|
26,Ampharos,,
|
||||||
|
27,Patrat,,
|
||||||
|
28,Watchog,,
|
||||||
|
29,Budew,,
|
||||||
|
30,Roselia,,
|
||||||
|
31,Roserade,,
|
||||||
|
32,Magikarp,,
|
||||||
|
33,Gyarados,,
|
||||||
|
34,Binacle,,
|
||||||
|
35,Barbaracle,,
|
||||||
|
36,Staryu,,
|
||||||
|
37,Starmie,,
|
||||||
|
38,Flabébé,,
|
||||||
|
39,Floette,,
|
||||||
|
40,Florges,,
|
||||||
|
41,Skiddo,,
|
||||||
|
42,Gogoat,,
|
||||||
|
43,Espurr,,
|
||||||
|
44,Meowstic,,
|
||||||
|
45,Litleo,,
|
||||||
|
46,Pyroar,,
|
||||||
|
47,Pancham,,
|
||||||
|
48,Pangoro,,
|
||||||
|
49,Trubbish,,
|
||||||
|
50,Garbodor,,
|
||||||
|
51,Dedenne,,
|
||||||
|
52,Pichu,,
|
||||||
|
53,Pikachu,,
|
||||||
|
54,Raichu,,
|
||||||
|
55,Cleffa,,
|
||||||
|
56,Clefairy,,
|
||||||
|
57,Clefable,,
|
||||||
|
58,Spinarak,,
|
||||||
|
59,Ariados,,
|
||||||
|
60,Ekans,,
|
||||||
|
61,Arbok,,
|
||||||
|
62,Abra,,
|
||||||
|
63,Kadabra,,
|
||||||
|
64,Alakazam,,
|
||||||
|
65,Gastly,,
|
||||||
|
66,Haunter,,
|
||||||
|
67,Gengar,,
|
||||||
|
68,Venipede,,
|
||||||
|
69,Whirlipede,,
|
||||||
|
70,Scolipede,,
|
||||||
|
71,Honedge,,
|
||||||
|
72,Doublade,,
|
||||||
|
73,Aegislash,,
|
||||||
|
74,Bellsprout,,
|
||||||
|
75,Weepinbell,,
|
||||||
|
76,Victreebel,,
|
||||||
|
77,Pansage,,
|
||||||
|
78,Simisage,,
|
||||||
|
79,Pansear,,
|
||||||
|
80,Simisear,,
|
||||||
|
81,Panpour,,
|
||||||
|
82,Simipour,,
|
||||||
|
83,Meditite,,
|
||||||
|
84,Medicham,,
|
||||||
|
85,Electrike,,
|
||||||
|
86,Manectric,,
|
||||||
|
87,Ralts,,
|
||||||
|
88,Kirlia,,
|
||||||
|
89,Gardevoir,,
|
||||||
|
90,Gallade,,
|
||||||
|
91,Houndour,,
|
||||||
|
92,Houndoom,,
|
||||||
|
93,Swablu,,
|
||||||
|
94,Altaria,,
|
||||||
|
95,Audino,,
|
||||||
|
96,Spritzee,,
|
||||||
|
97,Aromatisse,,
|
||||||
|
98,Swirlix,,
|
||||||
|
99,Slurpuff,,
|
||||||
|
100,Eevee,,
|
||||||
|
101,Vaporeon,,
|
||||||
|
102,Jolteon,,
|
||||||
|
103,Flareon,,
|
||||||
|
104,Espeon,,
|
||||||
|
105,Umbreon,,
|
||||||
|
106,Leafeon,,
|
||||||
|
107,Glaceon,,
|
||||||
|
108,Sylveon,,
|
||||||
|
109,Buneary,,
|
||||||
|
110,Lopunny,,
|
||||||
|
111,Shuppet,,
|
||||||
|
112,Banette,,
|
||||||
|
113,Vanillite,,
|
||||||
|
114,Vanillish,,
|
||||||
|
115,Vanilluxe,,
|
||||||
|
116,Numel,,
|
||||||
|
117,Camerupt,,
|
||||||
|
118,Hippopotas,,
|
||||||
|
119,Hippowdon,,
|
||||||
|
120,Drilbur,,
|
||||||
|
121,Excadrill,,
|
||||||
|
122,Sandile,,
|
||||||
|
123,Krokorok,,
|
||||||
|
124,Krookodile,,
|
||||||
|
125,Machop,,
|
||||||
|
126,Machoke,,
|
||||||
|
127,Machamp,,
|
||||||
|
128,Gible,,
|
||||||
|
129,Gabite,,
|
||||||
|
130,Garchomp,,
|
||||||
|
131,Carbink,,
|
||||||
|
132,Sableye,,
|
||||||
|
133,Mawile,,
|
||||||
|
134,Absol,,
|
||||||
|
135,Riolu,,
|
||||||
|
136,Lucario,,
|
||||||
|
137,Slowpoke,,
|
||||||
|
138,Slowbro,,
|
||||||
|
139,Slowking,,
|
||||||
|
140,Carvanha,,
|
||||||
|
141,Sharpedo,,
|
||||||
|
142,Tynamo,,
|
||||||
|
143,Eelektrik,,
|
||||||
|
144,Eelektross,,
|
||||||
|
145,Dratini,,
|
||||||
|
146,Dragonair,,
|
||||||
|
147,Dragonite,,
|
||||||
|
148,Bulbasaur,,
|
||||||
|
149,Ivysaur,,
|
||||||
|
150,Venusaur,,
|
||||||
|
151,Charmander,,
|
||||||
|
152,Charmeleon,,
|
||||||
|
153,Charizard,,
|
||||||
|
154,Squirtle,,
|
||||||
|
155,Wartortle,,
|
||||||
|
156,Blastoise,,
|
||||||
|
157,Stunfisk,,
|
||||||
|
158,Furfrou,,
|
||||||
|
159,Inkay,,
|
||||||
|
160,Malamar,,
|
||||||
|
161,Skrelp,,
|
||||||
|
162,Dragalge,,
|
||||||
|
163,Clauncher,,
|
||||||
|
164,Clawitzer,,
|
||||||
|
165,Goomy,,
|
||||||
|
166,Sliggoo,,
|
||||||
|
167,Goodra,,
|
||||||
|
168,Delibird,,
|
||||||
|
169,Snorunt,,
|
||||||
|
170,Glalie,,
|
||||||
|
171,Froslass,,
|
||||||
|
172,Snover,,
|
||||||
|
173,Abomasnow,,
|
||||||
|
174,Bergmite,,
|
||||||
|
175,Avalugg,,
|
||||||
|
176,Scyther,,
|
||||||
|
177,Scizor,,
|
||||||
|
178,Pinsir,,
|
||||||
|
179,Heracross,,
|
||||||
|
180,Emolga,,
|
||||||
|
181,Hawlucha,,
|
||||||
|
182,Phantump,,
|
||||||
|
183,Trevenant,,
|
||||||
|
184,Scraggy,,
|
||||||
|
185,Scrafty,,
|
||||||
|
186,Noibat,,
|
||||||
|
187,Noivern,,
|
||||||
|
188,Klefki,,
|
||||||
|
189,Litwick,,
|
||||||
|
190,Lampent,,
|
||||||
|
191,Chandelure,,
|
||||||
|
192,Aerodactyl,,
|
||||||
|
193,Tyrunt,,
|
||||||
|
194,Tyrantrum,,
|
||||||
|
195,Amaura,,
|
||||||
|
196,Aurorus,,
|
||||||
|
197,Onix,,
|
||||||
|
198,Steelix,,
|
||||||
|
199,Aron,,
|
||||||
|
200,Lairon,,
|
||||||
|
201,Aggron,,
|
||||||
|
202,Helioptile,,
|
||||||
|
203,Heliolisk,,
|
||||||
|
204,Pumpkaboo,,
|
||||||
|
205,Gourgeist,,
|
||||||
|
206,Larvitar,,
|
||||||
|
207,Pupitar,,
|
||||||
|
208,Tyranitar,,
|
||||||
|
209,Froakie,,
|
||||||
|
210,Frogadier,,
|
||||||
|
211,Greninja,,
|
||||||
|
212,Falinks,,
|
||||||
|
213,Chespin,,
|
||||||
|
214,Quilladin,,
|
||||||
|
215,Chesnaught,,
|
||||||
|
216,Skarmory,,
|
||||||
|
217,Fennekin,,
|
||||||
|
218,Braixen,,
|
||||||
|
219,Delphox,,
|
||||||
|
220,Bagon,,
|
||||||
|
221,Shelgon,,
|
||||||
|
222,Salamence,,
|
||||||
|
223,Kangaskhan,,
|
||||||
|
224,Drampa,,
|
||||||
|
225,Beldum,,
|
||||||
|
226,Metang,,
|
||||||
|
227,Metagross,,
|
||||||
|
228,Xerneas,,
|
||||||
|
229,Yveltal,,
|
||||||
|
230,Zygarde,,
|
||||||
|
231,Diancie,,
|
||||||
|
232,Mewtwo,,
|
||||||
|
@@ -0,0 +1,154 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Bulbasaur,,
|
||||||
|
2,Ivysaur,,
|
||||||
|
3,Venusaur,,
|
||||||
|
4,Charmander,,
|
||||||
|
5,Charmeleon,,
|
||||||
|
6,Charizard,,
|
||||||
|
7,Squirtle,,
|
||||||
|
8,Wartortle,,
|
||||||
|
9,Blastoise,,
|
||||||
|
10,Caterpie,,
|
||||||
|
11,Metapod,,
|
||||||
|
12,Butterfree,,
|
||||||
|
13,Weedle,,
|
||||||
|
14,Kakuna,,
|
||||||
|
15,Beedrill,,
|
||||||
|
16,Pidgey,,
|
||||||
|
17,Pidgeotto,,
|
||||||
|
18,Pidgeot,,
|
||||||
|
19,Rattata,,
|
||||||
|
20,Raticate,,
|
||||||
|
21,Spearow,,
|
||||||
|
22,Fearow,,
|
||||||
|
23,Ekans,,
|
||||||
|
24,Arbok,,
|
||||||
|
25,Pikachu,,
|
||||||
|
26,Raichu,,
|
||||||
|
27,Sandshrew,,
|
||||||
|
28,Sandslash,,
|
||||||
|
29,Nidoran♀,,
|
||||||
|
30,Nidorina,,
|
||||||
|
31,Nidoqueen,,
|
||||||
|
32,Nidoran♂,,
|
||||||
|
33,Nidorino,,
|
||||||
|
34,Nidoking,,
|
||||||
|
35,Clefairy,,
|
||||||
|
36,Clefable,,
|
||||||
|
37,Vulpix,,
|
||||||
|
38,Ninetales,,
|
||||||
|
39,Jigglypuff,,
|
||||||
|
40,Wigglytuff,,
|
||||||
|
41,Zubat,,
|
||||||
|
42,Golbat,,
|
||||||
|
43,Oddish,,
|
||||||
|
44,Gloom,,
|
||||||
|
45,Vileplume,,
|
||||||
|
46,Paras,,
|
||||||
|
47,Parasect,,
|
||||||
|
48,Venonat,,
|
||||||
|
49,Venomoth,,
|
||||||
|
50,Diglett,,
|
||||||
|
51,Dugtrio,,
|
||||||
|
52,Meowth,,
|
||||||
|
53,Persian,,
|
||||||
|
54,Psyduck,,
|
||||||
|
55,Golduck,,
|
||||||
|
56,Mankey,,
|
||||||
|
57,Primeape,,
|
||||||
|
58,Growlithe,,
|
||||||
|
59,Arcanine,,
|
||||||
|
60,Poliwag,,
|
||||||
|
61,Poliwhirl,,
|
||||||
|
62,Poliwrath,,
|
||||||
|
63,Abra,,
|
||||||
|
64,Kadabra,,
|
||||||
|
65,Alakazam,,
|
||||||
|
66,Machop,,
|
||||||
|
67,Machoke,,
|
||||||
|
68,Machamp,,
|
||||||
|
69,Bellsprout,,
|
||||||
|
70,Weepinbell,,
|
||||||
|
71,Victreebel,,
|
||||||
|
72,Tentacool,,
|
||||||
|
73,Tentacruel,,
|
||||||
|
74,Geodude,,
|
||||||
|
75,Graveler,,
|
||||||
|
76,Golem,,
|
||||||
|
77,Ponyta,,
|
||||||
|
78,Rapidash,,
|
||||||
|
79,Slowpoke,,
|
||||||
|
80,Slowbro,,
|
||||||
|
81,Magnemite,,
|
||||||
|
82,Magneton,,
|
||||||
|
83,Farfetch’d,,
|
||||||
|
84,Doduo,,
|
||||||
|
85,Dodrio,,
|
||||||
|
86,Seel,,
|
||||||
|
87,Dewgong,,
|
||||||
|
88,Grimer,,
|
||||||
|
89,Muk,,
|
||||||
|
90,Shellder,,
|
||||||
|
91,Cloyster,,
|
||||||
|
92,Gastly,,
|
||||||
|
93,Haunter,,
|
||||||
|
94,Gengar,,
|
||||||
|
95,Onix,,
|
||||||
|
96,Drowzee,,
|
||||||
|
97,Hypno,,
|
||||||
|
98,Krabby,,
|
||||||
|
99,Kingler,,
|
||||||
|
100,Voltorb,,
|
||||||
|
101,Electrode,,
|
||||||
|
102,Exeggcute,,
|
||||||
|
103,Exeggutor,,
|
||||||
|
104,Cubone,,
|
||||||
|
105,Marowak,,
|
||||||
|
106,Hitmonlee,,
|
||||||
|
107,Hitmonchan,,
|
||||||
|
108,Lickitung,,
|
||||||
|
109,Koffing,,
|
||||||
|
110,Weezing,,
|
||||||
|
111,Rhyhorn,,
|
||||||
|
112,Rhydon,,
|
||||||
|
113,Chansey,,
|
||||||
|
114,Tangela,,
|
||||||
|
115,Kangaskhan,,
|
||||||
|
116,Horsea,,
|
||||||
|
117,Seadra,,
|
||||||
|
118,Goldeen,,
|
||||||
|
119,Seaking,,
|
||||||
|
120,Staryu,,
|
||||||
|
121,Starmie,,
|
||||||
|
122,Mr. Mime,,
|
||||||
|
123,Scyther,,
|
||||||
|
124,Jynx,,
|
||||||
|
125,Electabuzz,,
|
||||||
|
126,Magmar,,
|
||||||
|
127,Pinsir,,
|
||||||
|
128,Tauros,,
|
||||||
|
129,Magikarp,,
|
||||||
|
130,Gyarados,,
|
||||||
|
131,Lapras,,
|
||||||
|
132,Ditto,,
|
||||||
|
133,Eevee,,
|
||||||
|
134,Vaporeon,,
|
||||||
|
135,Jolteon,,
|
||||||
|
136,Flareon,,
|
||||||
|
137,Porygon,,
|
||||||
|
138,Omanyte,,
|
||||||
|
139,Omastar,,
|
||||||
|
140,Kabuto,,
|
||||||
|
141,Kabutops,,
|
||||||
|
142,Aerodactyl,,
|
||||||
|
143,Snorlax,,
|
||||||
|
144,Articuno,,
|
||||||
|
145,Zapdos,,
|
||||||
|
146,Moltres,,
|
||||||
|
147,Dratini,,
|
||||||
|
148,Dragonair,,
|
||||||
|
149,Dragonite,,
|
||||||
|
150,Mewtwo,,
|
||||||
|
151,Mew,,
|
||||||
|
152,Meltan,,
|
||||||
|
153,Melmetal,,
|
||||||
|
@@ -0,0 +1,212 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Treecko,,
|
||||||
|
2,Grovyle,,
|
||||||
|
3,Sceptile,,
|
||||||
|
4,Torchic,,
|
||||||
|
5,Combusken,,
|
||||||
|
6,Blaziken,,
|
||||||
|
7,Mudkip,,
|
||||||
|
8,Marshtomp,,
|
||||||
|
9,Swampert,,
|
||||||
|
10,Poochyena,,
|
||||||
|
11,Mightyena,,
|
||||||
|
12,Zigzagoon,,
|
||||||
|
13,Linoone,,
|
||||||
|
14,Wurmple,,
|
||||||
|
15,Silcoon,,
|
||||||
|
16,Beautifly,,
|
||||||
|
17,Cascoon,,
|
||||||
|
18,Dustox,,
|
||||||
|
19,Lotad,,
|
||||||
|
20,Lombre,,
|
||||||
|
21,Ludicolo,,
|
||||||
|
22,Seedot,,
|
||||||
|
23,Nuzleaf,,
|
||||||
|
24,Shiftry,,
|
||||||
|
25,Taillow,,
|
||||||
|
26,Swellow,,
|
||||||
|
27,Wingull,,
|
||||||
|
28,Pelipper,,
|
||||||
|
29,Ralts,,
|
||||||
|
30,Kirlia,,
|
||||||
|
31,Gardevoir,,
|
||||||
|
32,Gallade,,
|
||||||
|
33,Surskit,,
|
||||||
|
34,Masquerain,,
|
||||||
|
35,Shroomish,,
|
||||||
|
36,Breloom,,
|
||||||
|
37,Slakoth,,
|
||||||
|
38,Vigoroth,,
|
||||||
|
39,Slaking,,
|
||||||
|
40,Abra,,
|
||||||
|
41,Kadabra,,
|
||||||
|
42,Alakazam,,
|
||||||
|
43,Nincada,,
|
||||||
|
44,Ninjask,,
|
||||||
|
45,Shedinja,,
|
||||||
|
46,Whismur,,
|
||||||
|
47,Loudred,,
|
||||||
|
48,Exploud,,
|
||||||
|
49,Makuhita,,
|
||||||
|
50,Hariyama,,
|
||||||
|
51,Goldeen,,
|
||||||
|
52,Seaking,,
|
||||||
|
53,Magikarp,,
|
||||||
|
54,Gyarados,,
|
||||||
|
55,Azurill,,
|
||||||
|
56,Marill,,
|
||||||
|
57,Azumarill,,
|
||||||
|
58,Geodude,,
|
||||||
|
59,Graveler,,
|
||||||
|
60,Golem,,
|
||||||
|
61,Nosepass,,
|
||||||
|
62,Probopass,,
|
||||||
|
63,Skitty,,
|
||||||
|
64,Delcatty,,
|
||||||
|
65,Zubat,,
|
||||||
|
66,Golbat,,
|
||||||
|
67,Crobat,,
|
||||||
|
68,Tentacool,,
|
||||||
|
69,Tentacruel,,
|
||||||
|
70,Sableye,,
|
||||||
|
71,Mawile,,
|
||||||
|
72,Aron,,
|
||||||
|
73,Lairon,,
|
||||||
|
74,Aggron,,
|
||||||
|
75,Machop,,
|
||||||
|
76,Machoke,,
|
||||||
|
77,Machamp,,
|
||||||
|
78,Meditite,,
|
||||||
|
79,Medicham,,
|
||||||
|
80,Electrike,,
|
||||||
|
81,Manectric,,
|
||||||
|
82,Plusle,,
|
||||||
|
83,Minun,,
|
||||||
|
84,Magnemite,,
|
||||||
|
85,Magneton,,
|
||||||
|
86,Magnezone,,
|
||||||
|
87,Voltorb,,
|
||||||
|
88,Electrode,,
|
||||||
|
89,Volbeat,,
|
||||||
|
90,Illumise,,
|
||||||
|
91,Oddish,,
|
||||||
|
92,Gloom,,
|
||||||
|
93,Vileplume,,
|
||||||
|
94,Bellossom,,
|
||||||
|
95,Doduo,,
|
||||||
|
96,Dodrio,,
|
||||||
|
97,Budew,,
|
||||||
|
98,Roselia,,
|
||||||
|
99,Roserade,,
|
||||||
|
100,Gulpin,,
|
||||||
|
101,Swalot,,
|
||||||
|
102,Carvanha,,
|
||||||
|
103,Sharpedo,,
|
||||||
|
104,Wailmer,,
|
||||||
|
105,Wailord,,
|
||||||
|
106,Numel,,
|
||||||
|
107,Camerupt,,
|
||||||
|
108,Slugma,,
|
||||||
|
109,Magcargo,,
|
||||||
|
110,Torkoal,,
|
||||||
|
111,Grimer,,
|
||||||
|
112,Muk,,
|
||||||
|
113,Koffing,,
|
||||||
|
114,Weezing,,
|
||||||
|
115,Spoink,,
|
||||||
|
116,Grumpig,,
|
||||||
|
117,Sandshrew,,
|
||||||
|
118,Sandslash,,
|
||||||
|
119,Spinda,,
|
||||||
|
120,Skarmory,,
|
||||||
|
121,Trapinch,,
|
||||||
|
122,Vibrava,,
|
||||||
|
123,Flygon,,
|
||||||
|
124,Cacnea,,
|
||||||
|
125,Cacturne,,
|
||||||
|
126,Swablu,,
|
||||||
|
127,Altaria,,
|
||||||
|
128,Zangoose,,
|
||||||
|
129,Seviper,,
|
||||||
|
130,Lunatone,,
|
||||||
|
131,Solrock,,
|
||||||
|
132,Barboach,,
|
||||||
|
133,Whiscash,,
|
||||||
|
134,Corphish,,
|
||||||
|
135,Crawdaunt,,
|
||||||
|
136,Baltoy,,
|
||||||
|
137,Claydol,,
|
||||||
|
138,Lileep,,
|
||||||
|
139,Cradily,,
|
||||||
|
140,Anorith,,
|
||||||
|
141,Armaldo,,
|
||||||
|
142,Igglybuff,,
|
||||||
|
143,Jigglypuff,,
|
||||||
|
144,Wigglytuff,,
|
||||||
|
145,Feebas,,
|
||||||
|
146,Milotic,,
|
||||||
|
147,Castform,,
|
||||||
|
148,Staryu,,
|
||||||
|
149,Starmie,,
|
||||||
|
150,Kecleon,,
|
||||||
|
151,Shuppet,,
|
||||||
|
152,Banette,,
|
||||||
|
153,Duskull,,
|
||||||
|
154,Dusclops,,
|
||||||
|
155,Dusknoir,,
|
||||||
|
156,Tropius,,
|
||||||
|
157,Chingling,,
|
||||||
|
158,Chimecho,,
|
||||||
|
159,Absol,,
|
||||||
|
160,Vulpix,,
|
||||||
|
161,Ninetales,,
|
||||||
|
162,Pichu,,
|
||||||
|
163,Pikachu,,
|
||||||
|
164,Raichu,,
|
||||||
|
165,Psyduck,,
|
||||||
|
166,Golduck,,
|
||||||
|
167,Wynaut,,
|
||||||
|
168,Wobbuffet,,
|
||||||
|
169,Natu,,
|
||||||
|
170,Xatu,,
|
||||||
|
171,Girafarig,,
|
||||||
|
172,Phanpy,,
|
||||||
|
173,Donphan,,
|
||||||
|
174,Pinsir,,
|
||||||
|
175,Heracross,,
|
||||||
|
176,Rhyhorn,,
|
||||||
|
177,Rhydon,,
|
||||||
|
178,Rhyperior,,
|
||||||
|
179,Snorunt,,
|
||||||
|
180,Glalie,,
|
||||||
|
181,Froslass,,
|
||||||
|
182,Spheal,,
|
||||||
|
183,Sealeo,,
|
||||||
|
184,Walrein,,
|
||||||
|
185,Clamperl,,
|
||||||
|
186,Huntail,,
|
||||||
|
187,Gorebyss,,
|
||||||
|
188,Relicanth,,
|
||||||
|
189,Corsola,,
|
||||||
|
190,Chinchou,,
|
||||||
|
191,Lanturn,,
|
||||||
|
192,Luvdisc,,
|
||||||
|
193,Horsea,,
|
||||||
|
194,Seadra,,
|
||||||
|
195,Kingdra,,
|
||||||
|
196,Bagon,,
|
||||||
|
197,Shelgon,,
|
||||||
|
198,Salamence,,
|
||||||
|
199,Beldum,,
|
||||||
|
200,Metang,,
|
||||||
|
201,Metagross,,
|
||||||
|
202,Regirock,,
|
||||||
|
203,Regice,,
|
||||||
|
204,Registeel,,
|
||||||
|
205,Latias,,
|
||||||
|
206,Latios,,
|
||||||
|
207,Kyogre,,
|
||||||
|
208,Groudon,,
|
||||||
|
209,Rayquaza,,
|
||||||
|
210,Jirachi,,
|
||||||
|
211,Deoxys,,
|
||||||
|
@@ -0,0 +1,211 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Turtwig,,
|
||||||
|
2,Grotle,,
|
||||||
|
3,Torterra,,
|
||||||
|
4,Chimchar,,
|
||||||
|
5,Monferno,,
|
||||||
|
6,Infernape,,
|
||||||
|
7,Piplup,,
|
||||||
|
8,Prinplup,,
|
||||||
|
9,Empoleon,,
|
||||||
|
10,Starly,,
|
||||||
|
11,Staravia,,
|
||||||
|
12,Staraptor,,
|
||||||
|
13,Bidoof,,
|
||||||
|
14,Bibarel,,
|
||||||
|
15,Kricketot,,
|
||||||
|
16,Kricketune,,
|
||||||
|
17,Shinx,,
|
||||||
|
18,Luxio,,
|
||||||
|
19,Luxray,,
|
||||||
|
20,Abra,,
|
||||||
|
21,Kadabra,,
|
||||||
|
22,Alakazam,,
|
||||||
|
23,Magikarp,,
|
||||||
|
24,Gyarados,,
|
||||||
|
25,Budew,,
|
||||||
|
26,Roselia,,
|
||||||
|
27,Roserade,,
|
||||||
|
28,Zubat,,
|
||||||
|
29,Golbat,,
|
||||||
|
30,Crobat,,
|
||||||
|
31,Geodude,,
|
||||||
|
32,Graveler,,
|
||||||
|
33,Golem,,
|
||||||
|
34,Onix,,
|
||||||
|
35,Steelix,,
|
||||||
|
36,Cranidos,,
|
||||||
|
37,Rampardos,,
|
||||||
|
38,Shieldon,,
|
||||||
|
39,Bastiodon,,
|
||||||
|
40,Machop,,
|
||||||
|
41,Machoke,,
|
||||||
|
42,Machamp,,
|
||||||
|
43,Psyduck,,
|
||||||
|
44,Golduck,,
|
||||||
|
45,Burmy,,
|
||||||
|
46,Wormadam,,
|
||||||
|
47,Mothim,,
|
||||||
|
48,Wurmple,,
|
||||||
|
49,Silcoon,,
|
||||||
|
50,Beautifly,,
|
||||||
|
51,Cascoon,,
|
||||||
|
52,Dustox,,
|
||||||
|
53,Combee,,
|
||||||
|
54,Vespiquen,,
|
||||||
|
55,Pachirisu,,
|
||||||
|
56,Buizel,,
|
||||||
|
57,Floatzel,,
|
||||||
|
58,Cherubi,,
|
||||||
|
59,Cherrim,,
|
||||||
|
60,Shellos,,
|
||||||
|
61,Gastrodon,,
|
||||||
|
62,Heracross,,
|
||||||
|
63,Aipom,,
|
||||||
|
64,Ambipom,,
|
||||||
|
65,Drifloon,,
|
||||||
|
66,Drifblim,,
|
||||||
|
67,Buneary,,
|
||||||
|
68,Lopunny,,
|
||||||
|
69,Gastly,,
|
||||||
|
70,Haunter,,
|
||||||
|
71,Gengar,,
|
||||||
|
72,Misdreavus,,
|
||||||
|
73,Mismagius,,
|
||||||
|
74,Murkrow,,
|
||||||
|
75,Honchkrow,,
|
||||||
|
76,Glameow,,
|
||||||
|
77,Purugly,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Barboach,,
|
||||||
|
81,Whiscash,,
|
||||||
|
82,Chingling,,
|
||||||
|
83,Chimecho,,
|
||||||
|
84,Stunky,,
|
||||||
|
85,Skuntank,,
|
||||||
|
86,Meditite,,
|
||||||
|
87,Medicham,,
|
||||||
|
88,Bronzor,,
|
||||||
|
89,Bronzong,,
|
||||||
|
90,Ponyta,,
|
||||||
|
91,Rapidash,,
|
||||||
|
92,Bonsly,,
|
||||||
|
93,Sudowoodo,,
|
||||||
|
94,Mime Jr.,,
|
||||||
|
95,Mr. Mime,,
|
||||||
|
96,Happiny,,
|
||||||
|
97,Chansey,,
|
||||||
|
98,Blissey,,
|
||||||
|
99,Cleffa,,
|
||||||
|
100,Clefairy,,
|
||||||
|
101,Clefable,,
|
||||||
|
102,Chatot,,
|
||||||
|
103,Pichu,,
|
||||||
|
104,Pikachu,,
|
||||||
|
105,Raichu,,
|
||||||
|
106,Hoothoot,,
|
||||||
|
107,Noctowl,,
|
||||||
|
108,Spiritomb,,
|
||||||
|
109,Gible,,
|
||||||
|
110,Gabite,,
|
||||||
|
111,Garchomp,,
|
||||||
|
112,Munchlax,,
|
||||||
|
113,Snorlax,,
|
||||||
|
114,Unown,,
|
||||||
|
115,Riolu,,
|
||||||
|
116,Lucario,,
|
||||||
|
117,Wooper,,
|
||||||
|
118,Quagsire,,
|
||||||
|
119,Wingull,,
|
||||||
|
120,Pelipper,,
|
||||||
|
121,Girafarig,,
|
||||||
|
122,Hippopotas,,
|
||||||
|
123,Hippowdon,,
|
||||||
|
124,Azurill,,
|
||||||
|
125,Marill,,
|
||||||
|
126,Azumarill,,
|
||||||
|
127,Skorupi,,
|
||||||
|
128,Drapion,,
|
||||||
|
129,Croagunk,,
|
||||||
|
130,Toxicroak,,
|
||||||
|
131,Carnivine,,
|
||||||
|
132,Remoraid,,
|
||||||
|
133,Octillery,,
|
||||||
|
134,Finneon,,
|
||||||
|
135,Lumineon,,
|
||||||
|
136,Tentacool,,
|
||||||
|
137,Tentacruel,,
|
||||||
|
138,Feebas,,
|
||||||
|
139,Milotic,,
|
||||||
|
140,Mantyke,,
|
||||||
|
141,Mantine,,
|
||||||
|
142,Snover,,
|
||||||
|
143,Abomasnow,,
|
||||||
|
144,Sneasel,,
|
||||||
|
145,Weavile,,
|
||||||
|
146,Uxie,,
|
||||||
|
147,Mesprit,,
|
||||||
|
148,Azelf,,
|
||||||
|
149,Dialga,,
|
||||||
|
150,Palkia,,
|
||||||
|
151,Manaphy,,
|
||||||
|
152,Rotom,,
|
||||||
|
153,Gligar,,
|
||||||
|
154,Gliscor,,
|
||||||
|
155,Nosepass,,
|
||||||
|
156,Probopass,,
|
||||||
|
157,Ralts,,
|
||||||
|
158,Kirlia,,
|
||||||
|
159,Gardevoir,,
|
||||||
|
160,Gallade,,
|
||||||
|
161,Lickitung,,
|
||||||
|
162,Lickilicky,,
|
||||||
|
163,Eevee,,
|
||||||
|
164,Vaporeon,,
|
||||||
|
165,Jolteon,,
|
||||||
|
166,Flareon,,
|
||||||
|
167,Espeon,,
|
||||||
|
168,Umbreon,,
|
||||||
|
169,Leafeon,,
|
||||||
|
170,Glaceon,,
|
||||||
|
171,Swablu,,
|
||||||
|
172,Altaria,,
|
||||||
|
173,Togepi,,
|
||||||
|
174,Togetic,,
|
||||||
|
175,Togekiss,,
|
||||||
|
176,Houndour,,
|
||||||
|
177,Houndoom,,
|
||||||
|
178,Magnemite,,
|
||||||
|
179,Magneton,,
|
||||||
|
180,Magnezone,,
|
||||||
|
181,Tangela,,
|
||||||
|
182,Tangrowth,,
|
||||||
|
183,Yanma,,
|
||||||
|
184,Yanmega,,
|
||||||
|
185,Tropius,,
|
||||||
|
186,Rhyhorn,,
|
||||||
|
187,Rhydon,,
|
||||||
|
188,Rhyperior,,
|
||||||
|
189,Duskull,,
|
||||||
|
190,Dusclops,,
|
||||||
|
191,Dusknoir,,
|
||||||
|
192,Porygon,,
|
||||||
|
193,Porygon2,,
|
||||||
|
194,Porygon-Z,,
|
||||||
|
195,Scyther,,
|
||||||
|
196,Scizor,,
|
||||||
|
197,Elekid,,
|
||||||
|
198,Electabuzz,,
|
||||||
|
199,Electivire,,
|
||||||
|
200,Magby,,
|
||||||
|
201,Magmar,,
|
||||||
|
202,Magmortar,,
|
||||||
|
203,Swinub,,
|
||||||
|
204,Piloswine,,
|
||||||
|
205,Mamoswine,,
|
||||||
|
206,Snorunt,,
|
||||||
|
207,Glalie,,
|
||||||
|
208,Froslass,,
|
||||||
|
209,Absol,,
|
||||||
|
210,Giratina,,
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Bulbasaur,,
|
||||||
|
2,Ivysaur,,
|
||||||
|
3,Venusaur,,
|
||||||
|
4,Charmander,,
|
||||||
|
5,Charmeleon,,
|
||||||
|
6,Charizard,,
|
||||||
|
7,Squirtle,,
|
||||||
|
8,Wartortle,,
|
||||||
|
9,Blastoise,,
|
||||||
|
10,Caterpie,,
|
||||||
|
11,Metapod,,
|
||||||
|
12,Butterfree,,
|
||||||
|
13,Weedle,,
|
||||||
|
14,Kakuna,,
|
||||||
|
15,Beedrill,,
|
||||||
|
16,Pidgey,,
|
||||||
|
17,Pidgeotto,,
|
||||||
|
18,Pidgeot,,
|
||||||
|
19,Rattata,,
|
||||||
|
20,Raticate,,
|
||||||
|
21,Spearow,,
|
||||||
|
22,Fearow,,
|
||||||
|
23,Ekans,,
|
||||||
|
24,Arbok,,
|
||||||
|
25,Pikachu,,
|
||||||
|
26,Raichu,,
|
||||||
|
27,Sandshrew,,
|
||||||
|
28,Sandslash,,
|
||||||
|
29,Nidoran♀,,
|
||||||
|
30,Nidorina,,
|
||||||
|
31,Nidoqueen,,
|
||||||
|
32,Nidoran♂,,
|
||||||
|
33,Nidorino,,
|
||||||
|
34,Nidoking,,
|
||||||
|
35,Clefairy,,
|
||||||
|
36,Clefable,,
|
||||||
|
37,Vulpix,,
|
||||||
|
38,Ninetales,,
|
||||||
|
39,Jigglypuff,,
|
||||||
|
40,Wigglytuff,,
|
||||||
|
41,Zubat,,
|
||||||
|
42,Golbat,,
|
||||||
|
43,Oddish,,
|
||||||
|
44,Gloom,,
|
||||||
|
45,Vileplume,,
|
||||||
|
46,Paras,,
|
||||||
|
47,Parasect,,
|
||||||
|
48,Venonat,,
|
||||||
|
49,Venomoth,,
|
||||||
|
50,Diglett,,
|
||||||
|
51,Dugtrio,,
|
||||||
|
52,Meowth,,
|
||||||
|
53,Persian,,
|
||||||
|
54,Psyduck,,
|
||||||
|
55,Golduck,,
|
||||||
|
56,Mankey,,
|
||||||
|
57,Primeape,,
|
||||||
|
58,Growlithe,,
|
||||||
|
59,Arcanine,,
|
||||||
|
60,Poliwag,,
|
||||||
|
61,Poliwhirl,,
|
||||||
|
62,Poliwrath,,
|
||||||
|
63,Abra,,
|
||||||
|
64,Kadabra,,
|
||||||
|
65,Alakazam,,
|
||||||
|
66,Machop,,
|
||||||
|
67,Machoke,,
|
||||||
|
68,Machamp,,
|
||||||
|
69,Bellsprout,,
|
||||||
|
70,Weepinbell,,
|
||||||
|
71,Victreebel,,
|
||||||
|
72,Tentacool,,
|
||||||
|
73,Tentacruel,,
|
||||||
|
74,Geodude,,
|
||||||
|
75,Graveler,,
|
||||||
|
76,Golem,,
|
||||||
|
77,Ponyta,,
|
||||||
|
78,Rapidash,,
|
||||||
|
79,Slowpoke,,
|
||||||
|
80,Slowbro,,
|
||||||
|
81,Magnemite,,
|
||||||
|
82,Magneton,,
|
||||||
|
83,Farfetch’d,,
|
||||||
|
84,Doduo,,
|
||||||
|
85,Dodrio,,
|
||||||
|
86,Seel,,
|
||||||
|
87,Dewgong,,
|
||||||
|
88,Grimer,,
|
||||||
|
89,Muk,,
|
||||||
|
90,Shellder,,
|
||||||
|
91,Cloyster,,
|
||||||
|
92,Gastly,,
|
||||||
|
93,Haunter,,
|
||||||
|
94,Gengar,,
|
||||||
|
95,Onix,,
|
||||||
|
96,Drowzee,,
|
||||||
|
97,Hypno,,
|
||||||
|
98,Krabby,,
|
||||||
|
99,Kingler,,
|
||||||
|
100,Voltorb,,
|
||||||
|
101,Electrode,,
|
||||||
|
102,Exeggcute,,
|
||||||
|
103,Exeggutor,,
|
||||||
|
104,Cubone,,
|
||||||
|
105,Marowak,,
|
||||||
|
106,Hitmonlee,,
|
||||||
|
107,Hitmonchan,,
|
||||||
|
108,Lickitung,,
|
||||||
|
109,Koffing,,
|
||||||
|
110,Weezing,,
|
||||||
|
111,Rhyhorn,,
|
||||||
|
112,Rhydon,,
|
||||||
|
113,Chansey,,
|
||||||
|
114,Tangela,,
|
||||||
|
115,Kangaskhan,,
|
||||||
|
116,Horsea,,
|
||||||
|
117,Seadra,,
|
||||||
|
118,Goldeen,,
|
||||||
|
119,Seaking,,
|
||||||
|
120,Staryu,,
|
||||||
|
121,Starmie,,
|
||||||
|
122,Mr. Mime,,
|
||||||
|
123,Scyther,,
|
||||||
|
124,Jynx,,
|
||||||
|
125,Electabuzz,,
|
||||||
|
126,Magmar,,
|
||||||
|
127,Pinsir,,
|
||||||
|
128,Tauros,,
|
||||||
|
129,Magikarp,,
|
||||||
|
130,Gyarados,,
|
||||||
|
131,Lapras,,
|
||||||
|
132,Ditto,,
|
||||||
|
133,Eevee,,
|
||||||
|
134,Vaporeon,,
|
||||||
|
135,Jolteon,,
|
||||||
|
136,Flareon,,
|
||||||
|
137,Porygon,,
|
||||||
|
138,Omanyte,,
|
||||||
|
139,Omastar,,
|
||||||
|
140,Kabuto,,
|
||||||
|
141,Kabutops,,
|
||||||
|
142,Aerodactyl,,
|
||||||
|
143,Snorlax,,
|
||||||
|
144,Articuno,,
|
||||||
|
145,Zapdos,,
|
||||||
|
146,Moltres,,
|
||||||
|
147,Dratini,,
|
||||||
|
148,Dragonair,,
|
||||||
|
149,Dragonite,,
|
||||||
|
150,Mewtwo,,
|
||||||
|
151,Mew,,
|
||||||
|
@@ -0,0 +1,203 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Treecko,,
|
||||||
|
2,Grovyle,,
|
||||||
|
3,Sceptile,,
|
||||||
|
4,Torchic,,
|
||||||
|
5,Combusken,,
|
||||||
|
6,Blaziken,,
|
||||||
|
7,Mudkip,,
|
||||||
|
8,Marshtomp,,
|
||||||
|
9,Swampert,,
|
||||||
|
10,Poochyena,,
|
||||||
|
11,Mightyena,,
|
||||||
|
12,Zigzagoon,,
|
||||||
|
13,Linoone,,
|
||||||
|
14,Wurmple,,
|
||||||
|
15,Silcoon,,
|
||||||
|
16,Beautifly,,
|
||||||
|
17,Cascoon,,
|
||||||
|
18,Dustox,,
|
||||||
|
19,Lotad,,
|
||||||
|
20,Lombre,,
|
||||||
|
21,Ludicolo,,
|
||||||
|
22,Seedot,,
|
||||||
|
23,Nuzleaf,,
|
||||||
|
24,Shiftry,,
|
||||||
|
25,Taillow,,
|
||||||
|
26,Swellow,,
|
||||||
|
27,Wingull,,
|
||||||
|
28,Pelipper,,
|
||||||
|
29,Ralts,,
|
||||||
|
30,Kirlia,,
|
||||||
|
31,Gardevoir,,
|
||||||
|
32,Surskit,,
|
||||||
|
33,Masquerain,,
|
||||||
|
34,Shroomish,,
|
||||||
|
35,Breloom,,
|
||||||
|
36,Slakoth,,
|
||||||
|
37,Vigoroth,,
|
||||||
|
38,Slaking,,
|
||||||
|
39,Abra,,
|
||||||
|
40,Kadabra,,
|
||||||
|
41,Alakazam,,
|
||||||
|
42,Nincada,,
|
||||||
|
43,Ninjask,,
|
||||||
|
44,Shedinja,,
|
||||||
|
45,Whismur,,
|
||||||
|
46,Loudred,,
|
||||||
|
47,Exploud,,
|
||||||
|
48,Makuhita,,
|
||||||
|
49,Hariyama,,
|
||||||
|
50,Goldeen,,
|
||||||
|
51,Seaking,,
|
||||||
|
52,Magikarp,,
|
||||||
|
53,Gyarados,,
|
||||||
|
54,Azurill,,
|
||||||
|
55,Marill,,
|
||||||
|
56,Azumarill,,
|
||||||
|
57,Geodude,,
|
||||||
|
58,Graveler,,
|
||||||
|
59,Golem,,
|
||||||
|
60,Nosepass,,
|
||||||
|
61,Skitty,,
|
||||||
|
62,Delcatty,,
|
||||||
|
63,Zubat,,
|
||||||
|
64,Golbat,,
|
||||||
|
65,Crobat,,
|
||||||
|
66,Tentacool,,
|
||||||
|
67,Tentacruel,,
|
||||||
|
68,Sableye,,
|
||||||
|
69,Mawile,,
|
||||||
|
70,Aron,,
|
||||||
|
71,Lairon,,
|
||||||
|
72,Aggron,,
|
||||||
|
73,Machop,,
|
||||||
|
74,Machoke,,
|
||||||
|
75,Machamp,,
|
||||||
|
76,Meditite,,
|
||||||
|
77,Medicham,,
|
||||||
|
78,Electrike,,
|
||||||
|
79,Manectric,,
|
||||||
|
80,Plusle,,
|
||||||
|
81,Minun,,
|
||||||
|
82,Magnemite,,
|
||||||
|
83,Magneton,,
|
||||||
|
84,Voltorb,,
|
||||||
|
85,Electrode,,
|
||||||
|
86,Volbeat,,
|
||||||
|
87,Illumise,,
|
||||||
|
88,Oddish,,
|
||||||
|
89,Gloom,,
|
||||||
|
90,Vileplume,,
|
||||||
|
91,Bellossom,,
|
||||||
|
92,Doduo,,
|
||||||
|
93,Dodrio,,
|
||||||
|
94,Roselia,,
|
||||||
|
95,Gulpin,,
|
||||||
|
96,Swalot,,
|
||||||
|
97,Carvanha,,
|
||||||
|
98,Sharpedo,,
|
||||||
|
99,Wailmer,,
|
||||||
|
100,Wailord,,
|
||||||
|
101,Numel,,
|
||||||
|
102,Camerupt,,
|
||||||
|
103,Slugma,,
|
||||||
|
104,Magcargo,,
|
||||||
|
105,Torkoal,,
|
||||||
|
106,Grimer,,
|
||||||
|
107,Muk,,
|
||||||
|
108,Koffing,,
|
||||||
|
109,Weezing,,
|
||||||
|
110,Spoink,,
|
||||||
|
111,Grumpig,,
|
||||||
|
112,Sandshrew,,
|
||||||
|
113,Sandslash,,
|
||||||
|
114,Spinda,,
|
||||||
|
115,Skarmory,,
|
||||||
|
116,Trapinch,,
|
||||||
|
117,Vibrava,,
|
||||||
|
118,Flygon,,
|
||||||
|
119,Cacnea,,
|
||||||
|
120,Cacturne,,
|
||||||
|
121,Swablu,,
|
||||||
|
122,Altaria,,
|
||||||
|
123,Zangoose,,
|
||||||
|
124,Seviper,,
|
||||||
|
125,Lunatone,,
|
||||||
|
126,Solrock,,
|
||||||
|
127,Barboach,,
|
||||||
|
128,Whiscash,,
|
||||||
|
129,Corphish,,
|
||||||
|
130,Crawdaunt,,
|
||||||
|
131,Baltoy,,
|
||||||
|
132,Claydol,,
|
||||||
|
133,Lileep,,
|
||||||
|
134,Cradily,,
|
||||||
|
135,Anorith,,
|
||||||
|
136,Armaldo,,
|
||||||
|
137,Igglybuff,,
|
||||||
|
138,Jigglypuff,,
|
||||||
|
139,Wigglytuff,,
|
||||||
|
140,Feebas,,
|
||||||
|
141,Milotic,,
|
||||||
|
142,Castform,,
|
||||||
|
143,Staryu,,
|
||||||
|
144,Starmie,,
|
||||||
|
145,Kecleon,,
|
||||||
|
146,Shuppet,,
|
||||||
|
147,Banette,,
|
||||||
|
148,Duskull,,
|
||||||
|
149,Dusclops,,
|
||||||
|
150,Tropius,,
|
||||||
|
151,Chimecho,,
|
||||||
|
152,Absol,,
|
||||||
|
153,Vulpix,,
|
||||||
|
154,Ninetales,,
|
||||||
|
155,Pichu,,
|
||||||
|
156,Pikachu,,
|
||||||
|
157,Raichu,,
|
||||||
|
158,Psyduck,,
|
||||||
|
159,Golduck,,
|
||||||
|
160,Wynaut,,
|
||||||
|
161,Wobbuffet,,
|
||||||
|
162,Natu,,
|
||||||
|
163,Xatu,,
|
||||||
|
164,Girafarig,,
|
||||||
|
165,Phanpy,,
|
||||||
|
166,Donphan,,
|
||||||
|
167,Pinsir,,
|
||||||
|
168,Heracross,,
|
||||||
|
169,Rhyhorn,,
|
||||||
|
170,Rhydon,,
|
||||||
|
171,Snorunt,,
|
||||||
|
172,Glalie,,
|
||||||
|
173,Spheal,,
|
||||||
|
174,Sealeo,,
|
||||||
|
175,Walrein,,
|
||||||
|
176,Clamperl,,
|
||||||
|
177,Huntail,,
|
||||||
|
178,Gorebyss,,
|
||||||
|
179,Relicanth,,
|
||||||
|
180,Corsola,,
|
||||||
|
181,Chinchou,,
|
||||||
|
182,Lanturn,,
|
||||||
|
183,Luvdisc,,
|
||||||
|
184,Horsea,,
|
||||||
|
185,Seadra,,
|
||||||
|
186,Kingdra,,
|
||||||
|
187,Bagon,,
|
||||||
|
188,Shelgon,,
|
||||||
|
189,Salamence,,
|
||||||
|
190,Beldum,,
|
||||||
|
191,Metang,,
|
||||||
|
192,Metagross,,
|
||||||
|
193,Regirock,,
|
||||||
|
194,Regice,,
|
||||||
|
195,Registeel,,
|
||||||
|
196,Latias,,
|
||||||
|
197,Latios,,
|
||||||
|
198,Kyogre,,
|
||||||
|
199,Groudon,,
|
||||||
|
200,Rayquaza,,
|
||||||
|
201,Jirachi,,
|
||||||
|
202,Deoxys,,
|
||||||
|
@@ -0,0 +1,244 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Doduo,,
|
||||||
|
2,Dodrio,,
|
||||||
|
3,Exeggcute,,
|
||||||
|
4,Exeggutor,,
|
||||||
|
5,Rhyhorn,,
|
||||||
|
6,Rhydon,,
|
||||||
|
7,Rhyperior,,
|
||||||
|
8,Venonat,,
|
||||||
|
9,Venomoth,,
|
||||||
|
10,Elekid,,
|
||||||
|
11,Electabuzz,,
|
||||||
|
12,Electivire,,
|
||||||
|
13,Magby,,
|
||||||
|
14,Magmar,,
|
||||||
|
15,Magmortar,,
|
||||||
|
16,Happiny,,
|
||||||
|
17,Chansey,,
|
||||||
|
18,Blissey,,
|
||||||
|
19,Scyther,,
|
||||||
|
20,Scizor,,
|
||||||
|
21,Kleavor,,
|
||||||
|
22,Tauros,,
|
||||||
|
23,Blitzle,,
|
||||||
|
24,Zebstrika,,
|
||||||
|
25,Girafarig,,
|
||||||
|
26,Farigiraf,,
|
||||||
|
27,Sandile,,
|
||||||
|
28,Krokorok,,
|
||||||
|
29,Krookodile,,
|
||||||
|
30,Rellor,,
|
||||||
|
31,Rabsca,,
|
||||||
|
32,Rufflet,,
|
||||||
|
33,Braviary,,
|
||||||
|
34,Vullaby,,
|
||||||
|
35,Mandibuzz,,
|
||||||
|
36,Litleo,,
|
||||||
|
37,Pyroar,,
|
||||||
|
38,Deerling,,
|
||||||
|
39,Sawsbuck,,
|
||||||
|
40,Smeargle,,
|
||||||
|
41,Rotom,,
|
||||||
|
42,Milcery,,
|
||||||
|
43,Alcremie,,
|
||||||
|
44,Trapinch,,
|
||||||
|
45,Vibrava,,
|
||||||
|
46,Flygon,,
|
||||||
|
47,Pikipek,,
|
||||||
|
48,Trumbeak,,
|
||||||
|
49,Toucannon,,
|
||||||
|
50,Tentacool,,
|
||||||
|
51,Tentacruel,,
|
||||||
|
52,Horsea,,
|
||||||
|
53,Seadra,,
|
||||||
|
54,Kingdra,,
|
||||||
|
55,Bruxish,,
|
||||||
|
56,Cottonee,,
|
||||||
|
57,Whimsicott,,
|
||||||
|
58,Comfey,,
|
||||||
|
59,Slakoth,,
|
||||||
|
60,Vigoroth,,
|
||||||
|
61,Slaking,,
|
||||||
|
62,Oddish,,
|
||||||
|
63,Gloom,,
|
||||||
|
64,Vileplume,,
|
||||||
|
65,Bellossom,,
|
||||||
|
66,Diglett,,
|
||||||
|
67,Dugtrio,,
|
||||||
|
68,Grimer,,
|
||||||
|
69,Muk,,
|
||||||
|
70,Zangoose,,
|
||||||
|
71,Seviper,,
|
||||||
|
72,Crabrawler,,
|
||||||
|
73,Crabominable,,
|
||||||
|
74,Oricorio,,
|
||||||
|
75,Slowpoke,,
|
||||||
|
76,Slowbro,,
|
||||||
|
77,Slowking,,
|
||||||
|
78,Chinchou,,
|
||||||
|
79,Lanturn,,
|
||||||
|
80,Inkay,,
|
||||||
|
81,Malamar,,
|
||||||
|
82,Luvdisc,,
|
||||||
|
83,Finneon,,
|
||||||
|
84,Lumineon,,
|
||||||
|
85,Alomomola,,
|
||||||
|
86,Torkoal,,
|
||||||
|
87,Fletchling,,
|
||||||
|
88,Fletchinder,,
|
||||||
|
89,Talonflame,,
|
||||||
|
90,Dewpider,,
|
||||||
|
91,Araquanid,,
|
||||||
|
92,Tyrogue,,
|
||||||
|
93,Hitmonlee,,
|
||||||
|
94,Hitmonchan,,
|
||||||
|
95,Hitmontop,,
|
||||||
|
96,Geodude,,
|
||||||
|
97,Graveler,,
|
||||||
|
98,Golem,,
|
||||||
|
99,Drilbur,,
|
||||||
|
100,Excadrill,,
|
||||||
|
101,Gothita,,
|
||||||
|
102,Gothorita,,
|
||||||
|
103,Gothitelle,,
|
||||||
|
104,Espurr,,
|
||||||
|
105,Meowstic,,
|
||||||
|
106,Minior,,
|
||||||
|
107,Cranidos,,
|
||||||
|
108,Rampardos,,
|
||||||
|
109,Shieldon,,
|
||||||
|
110,Bastiodon,,
|
||||||
|
111,Minccino,,
|
||||||
|
112,Cinccino,,
|
||||||
|
113,Skarmory,,
|
||||||
|
114,Swablu,,
|
||||||
|
115,Altaria,,
|
||||||
|
116,Magnemite,,
|
||||||
|
117,Magneton,,
|
||||||
|
118,Magnezone,,
|
||||||
|
119,Plusle,,
|
||||||
|
120,Minun,,
|
||||||
|
121,Scraggy,,
|
||||||
|
122,Scrafty,,
|
||||||
|
123,Golett,,
|
||||||
|
124,Golurk,,
|
||||||
|
125,Numel,,
|
||||||
|
126,Camerupt,,
|
||||||
|
127,Sinistea,,
|
||||||
|
128,Polteageist,,
|
||||||
|
129,Porygon,,
|
||||||
|
130,Porygon2,,
|
||||||
|
131,Porygon-Z,,
|
||||||
|
132,Joltik,,
|
||||||
|
133,Galvantula,,
|
||||||
|
134,Tynamo,,
|
||||||
|
135,Eelektrik,,
|
||||||
|
136,Eelektross,,
|
||||||
|
137,Beldum,,
|
||||||
|
138,Metang,,
|
||||||
|
139,Metagross,,
|
||||||
|
140,Axew,,
|
||||||
|
141,Fraxure,,
|
||||||
|
142,Haxorus,,
|
||||||
|
143,Seel,,
|
||||||
|
144,Dewgong,,
|
||||||
|
145,Lapras,,
|
||||||
|
146,Qwilfish,,
|
||||||
|
147,Overqwil,,
|
||||||
|
148,Solosis,,
|
||||||
|
149,Duosion,,
|
||||||
|
150,Reuniclus,,
|
||||||
|
151,Snubbull,,
|
||||||
|
152,Granbull,,
|
||||||
|
153,Cubchoo,,
|
||||||
|
154,Beartic,,
|
||||||
|
155,Sandshrew,,
|
||||||
|
156,Sandslash,,
|
||||||
|
157,Vulpix,,
|
||||||
|
158,Ninetales,,
|
||||||
|
159,Snover,,
|
||||||
|
160,Abomasnow,,
|
||||||
|
161,Duraludon,,
|
||||||
|
162,Archaludon,,
|
||||||
|
163,Hydrapple,,
|
||||||
|
164,Bulbasaur,,
|
||||||
|
165,Ivysaur,,
|
||||||
|
166,Venusaur,,
|
||||||
|
167,Charmander,,
|
||||||
|
168,Charmeleon,,
|
||||||
|
169,Charizard,,
|
||||||
|
170,Squirtle,,
|
||||||
|
171,Wartortle,,
|
||||||
|
172,Blastoise,,
|
||||||
|
173,Chikorita,,
|
||||||
|
174,Bayleef,,
|
||||||
|
175,Meganium,,
|
||||||
|
176,Cyndaquil,,
|
||||||
|
177,Quilava,,
|
||||||
|
178,Typhlosion,,
|
||||||
|
179,Totodile,,
|
||||||
|
180,Croconaw,,
|
||||||
|
181,Feraligatr,,
|
||||||
|
182,Treecko,,
|
||||||
|
183,Grovyle,,
|
||||||
|
184,Sceptile,,
|
||||||
|
185,Torchic,,
|
||||||
|
186,Combusken,,
|
||||||
|
187,Blaziken,,
|
||||||
|
188,Mudkip,,
|
||||||
|
189,Marshtomp,,
|
||||||
|
190,Swampert,,
|
||||||
|
191,Turtwig,,
|
||||||
|
192,Grotle,,
|
||||||
|
193,Torterra,,
|
||||||
|
194,Chimchar,,
|
||||||
|
195,Monferno,,
|
||||||
|
196,Infernape,,
|
||||||
|
197,Piplup,,
|
||||||
|
198,Prinplup,,
|
||||||
|
199,Empoleon,,
|
||||||
|
200,Snivy,,
|
||||||
|
201,Servine,,
|
||||||
|
202,Serperior,,
|
||||||
|
203,Tepig,,
|
||||||
|
204,Pignite,,
|
||||||
|
205,Emboar,,
|
||||||
|
206,Oshawott,,
|
||||||
|
207,Dewott,,
|
||||||
|
208,Samurott,,
|
||||||
|
209,Chespin,,
|
||||||
|
210,Quilladin,,
|
||||||
|
211,Chesnaught,,
|
||||||
|
212,Fennekin,,
|
||||||
|
213,Braixen,,
|
||||||
|
214,Delphox,,
|
||||||
|
215,Froakie,,
|
||||||
|
216,Frogadier,,
|
||||||
|
217,Greninja,,
|
||||||
|
218,Rowlet,,
|
||||||
|
219,Dartrix,,
|
||||||
|
220,Decidueye,,
|
||||||
|
221,Litten,,
|
||||||
|
222,Torracat,,
|
||||||
|
223,Incineroar,,
|
||||||
|
224,Popplio,,
|
||||||
|
225,Brionne,,
|
||||||
|
226,Primarina,,
|
||||||
|
227,Grookey,,
|
||||||
|
228,Thwackey,,
|
||||||
|
229,Rillaboom,,
|
||||||
|
230,Scorbunny,,
|
||||||
|
231,Raboot,,
|
||||||
|
232,Cinderace,,
|
||||||
|
233,Sobble,,
|
||||||
|
234,Drizzile,,
|
||||||
|
235,Inteleon,,
|
||||||
|
236,Gouging Fire,,
|
||||||
|
237,Raging Bolt,,
|
||||||
|
238,Iron Crown,,
|
||||||
|
239,Iron Boulder,,
|
||||||
|
240,Terapagos,,
|
||||||
|
241,Walking Wake,,
|
||||||
|
242,Iron Leaves,,
|
||||||
|
243,Pecharunt,,
|
||||||
|
@@ -0,0 +1,201 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Spinarak,,
|
||||||
|
2,Ariados,,
|
||||||
|
3,Yanma,,
|
||||||
|
4,Yanmega,,
|
||||||
|
5,Wooper,,
|
||||||
|
6,Quagsire,,
|
||||||
|
7,Poochyena,,
|
||||||
|
8,Mightyena,,
|
||||||
|
9,Volbeat,,
|
||||||
|
10,Illumise,,
|
||||||
|
11,Corphish,,
|
||||||
|
12,Crawdaunt,,
|
||||||
|
13,Sewaddle,,
|
||||||
|
14,Swadloon,,
|
||||||
|
15,Leavanny,,
|
||||||
|
16,Cutiefly,,
|
||||||
|
17,Ribombee,,
|
||||||
|
18,Ekans,,
|
||||||
|
19,Arbok,,
|
||||||
|
20,Pichu,,
|
||||||
|
21,Pikachu,,
|
||||||
|
22,Raichu,,
|
||||||
|
23,Bellsprout,,
|
||||||
|
24,Weepinbell,,
|
||||||
|
25,Victreebel,,
|
||||||
|
26,Sentret,,
|
||||||
|
27,Furret,,
|
||||||
|
28,Starly,,
|
||||||
|
29,Staravia,,
|
||||||
|
30,Staraptor,,
|
||||||
|
31,Fomantis,,
|
||||||
|
32,Lurantis,,
|
||||||
|
33,Applin,,
|
||||||
|
34,Flapple,,
|
||||||
|
35,Appletun,,
|
||||||
|
36,Dipplin,,
|
||||||
|
37,Vulpix,,
|
||||||
|
38,Ninetales,,
|
||||||
|
39,Poliwag,,
|
||||||
|
40,Poliwhirl,,
|
||||||
|
41,Poliwrath,,
|
||||||
|
42,Politoed,,
|
||||||
|
43,Magikarp,,
|
||||||
|
44,Gyarados,,
|
||||||
|
45,Hoothoot,,
|
||||||
|
46,Noctowl,,
|
||||||
|
47,Aipom,,
|
||||||
|
48,Ambipom,,
|
||||||
|
49,Heracross,,
|
||||||
|
50,Swinub,,
|
||||||
|
51,Piloswine,,
|
||||||
|
52,Mamoswine,,
|
||||||
|
53,Stantler,,
|
||||||
|
54,Seedot,,
|
||||||
|
55,Nuzleaf,,
|
||||||
|
56,Shiftry,,
|
||||||
|
57,Ralts,,
|
||||||
|
58,Kirlia,,
|
||||||
|
59,Gardevoir,,
|
||||||
|
60,Gallade,,
|
||||||
|
61,Kricketot,,
|
||||||
|
62,Kricketune,,
|
||||||
|
63,Pachirisu,,
|
||||||
|
64,Riolu,,
|
||||||
|
65,Lucario,,
|
||||||
|
66,Petilil,,
|
||||||
|
67,Lilligant,,
|
||||||
|
68,Phantump,,
|
||||||
|
69,Trevenant,,
|
||||||
|
70,Rockruff,,
|
||||||
|
71,Lycanroc,,
|
||||||
|
72,Skwovet,,
|
||||||
|
73,Greedent,,
|
||||||
|
74,Toedscool,,
|
||||||
|
75,Toedscruel,,
|
||||||
|
76,Poltchageist,,
|
||||||
|
77,Sinistcha,,
|
||||||
|
78,Growlithe,,
|
||||||
|
79,Arcanine,,
|
||||||
|
80,Geodude,,
|
||||||
|
81,Graveler,,
|
||||||
|
82,Golem,,
|
||||||
|
83,Bonsly,,
|
||||||
|
84,Sudowoodo,,
|
||||||
|
85,Timburr,,
|
||||||
|
86,Gurdurr,,
|
||||||
|
87,Conkeldurr,,
|
||||||
|
88,Noibat,,
|
||||||
|
89,Noivern,,
|
||||||
|
90,Arrokuda,,
|
||||||
|
91,Barraskewda,,
|
||||||
|
92,Hatenna,,
|
||||||
|
93,Hattrem,,
|
||||||
|
94,Hatterene,,
|
||||||
|
95,Morpeko,,
|
||||||
|
96,Orthworm,,
|
||||||
|
97,Tandemaus,,
|
||||||
|
98,Maushold,,
|
||||||
|
99,Mankey,,
|
||||||
|
100,Primeape,,
|
||||||
|
101,Annihilape,,
|
||||||
|
102,Munchlax,,
|
||||||
|
103,Snorlax,,
|
||||||
|
104,Lotad,,
|
||||||
|
105,Lombre,,
|
||||||
|
106,Ludicolo,,
|
||||||
|
107,Nosepass,,
|
||||||
|
108,Probopass,,
|
||||||
|
109,Shinx,,
|
||||||
|
110,Luxio,,
|
||||||
|
111,Luxray,,
|
||||||
|
112,Grubbin,,
|
||||||
|
113,Charjabug,,
|
||||||
|
114,Vikavolt,,
|
||||||
|
115,Oricorio,,
|
||||||
|
116,Sandshrew,,
|
||||||
|
117,Sandslash,,
|
||||||
|
118,Gastly,,
|
||||||
|
119,Haunter,,
|
||||||
|
120,Gengar,,
|
||||||
|
121,Gligar,,
|
||||||
|
122,Gliscor,,
|
||||||
|
123,Houndour,,
|
||||||
|
124,Houndoom,,
|
||||||
|
125,Spoink,,
|
||||||
|
126,Grumpig,,
|
||||||
|
127,Vullaby,,
|
||||||
|
128,Mandibuzz,,
|
||||||
|
129,Mudbray,,
|
||||||
|
130,Mudsdale,,
|
||||||
|
131,Jangmo-o,,
|
||||||
|
132,Hakamo-o,,
|
||||||
|
133,Kommo-o,,
|
||||||
|
134,Bombirdier,,
|
||||||
|
135,Koffing,,
|
||||||
|
136,Weezing,,
|
||||||
|
137,Mienfoo,,
|
||||||
|
138,Mienshao,,
|
||||||
|
139,Duskull,,
|
||||||
|
140,Dusclops,,
|
||||||
|
141,Dusknoir,,
|
||||||
|
142,Chingling,,
|
||||||
|
143,Chimecho,,
|
||||||
|
144,Slugma,,
|
||||||
|
145,Magcargo,,
|
||||||
|
146,Litwick,,
|
||||||
|
147,Lampent,,
|
||||||
|
148,Chandelure,,
|
||||||
|
149,Surskit,,
|
||||||
|
150,Masquerain,,
|
||||||
|
151,Cleffa,,
|
||||||
|
152,Clefairy,,
|
||||||
|
153,Clefable,,
|
||||||
|
154,Bronzor,,
|
||||||
|
155,Bronzong,,
|
||||||
|
156,Glimmet,,
|
||||||
|
157,Glimmora,,
|
||||||
|
158,Feebas,,
|
||||||
|
159,Milotic,,
|
||||||
|
160,Dunsparce,,
|
||||||
|
161,Dudunsparce,,
|
||||||
|
162,Barboach,,
|
||||||
|
163,Whiscash,,
|
||||||
|
164,Gible,,
|
||||||
|
165,Gabite,,
|
||||||
|
166,Garchomp,,
|
||||||
|
167,Carbink,,
|
||||||
|
168,Salandit,,
|
||||||
|
169,Salazzle,,
|
||||||
|
170,Sneasel,,
|
||||||
|
171,Weavile,,
|
||||||
|
172,Snorunt,,
|
||||||
|
173,Glalie,,
|
||||||
|
174,Froslass,,
|
||||||
|
175,Tynamo,,
|
||||||
|
176,Eelektrik,,
|
||||||
|
177,Eelektross,,
|
||||||
|
178,Goomy,,
|
||||||
|
179,Sliggoo,,
|
||||||
|
180,Goodra,,
|
||||||
|
181,Ducklett,,
|
||||||
|
182,Swanna,,
|
||||||
|
183,Chewtle,,
|
||||||
|
184,Drednaw,,
|
||||||
|
185,Cramorant,,
|
||||||
|
186,Pawniard,,
|
||||||
|
187,Bisharp,,
|
||||||
|
188,Kingambit,,
|
||||||
|
189,Mimikyu,,
|
||||||
|
190,Impidimp,,
|
||||||
|
191,Morgrem,,
|
||||||
|
192,Grimmsnarl,,
|
||||||
|
193,Indeedee,,
|
||||||
|
194,Basculin,,
|
||||||
|
195,Basculegion,,
|
||||||
|
196,Ursaluna,,
|
||||||
|
197,Okidogi,,
|
||||||
|
198,Munkidori,,
|
||||||
|
199,Fezandipiti,,
|
||||||
|
200,Ogerpon,,
|
||||||
|
@@ -0,0 +1,401 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Sprigatito,,
|
||||||
|
2,Floragato,,
|
||||||
|
3,Meowscarada,,
|
||||||
|
4,Fuecoco,,
|
||||||
|
5,Crocalor,,
|
||||||
|
6,Skeledirge,,
|
||||||
|
7,Quaxly,,
|
||||||
|
8,Quaxwell,,
|
||||||
|
9,Quaquaval,,
|
||||||
|
10,Lechonk,,
|
||||||
|
11,Oinkologne,,
|
||||||
|
12,Tarountula,,
|
||||||
|
13,Spidops,,
|
||||||
|
14,Nymble,,
|
||||||
|
15,Lokix,,
|
||||||
|
16,Hoppip,,
|
||||||
|
17,Skiploom,,
|
||||||
|
18,Jumpluff,,
|
||||||
|
19,Fletchling,,
|
||||||
|
20,Fletchinder,,
|
||||||
|
21,Talonflame,,
|
||||||
|
22,Pawmi,,
|
||||||
|
23,Pawmo,,
|
||||||
|
24,Pawmot,,
|
||||||
|
25,Houndour,,
|
||||||
|
26,Houndoom,,
|
||||||
|
27,Yungoos,,
|
||||||
|
28,Gumshoos,,
|
||||||
|
29,Skwovet,,
|
||||||
|
30,Greedent,,
|
||||||
|
31,Sunkern,,
|
||||||
|
32,Sunflora,,
|
||||||
|
33,Kricketot,,
|
||||||
|
34,Kricketune,,
|
||||||
|
35,Scatterbug,,
|
||||||
|
36,Spewpa,,
|
||||||
|
37,Vivillon,,
|
||||||
|
38,Combee,,
|
||||||
|
39,Vespiquen,,
|
||||||
|
40,Rookidee,,
|
||||||
|
41,Corvisquire,,
|
||||||
|
42,Corviknight,,
|
||||||
|
43,Happiny,,
|
||||||
|
44,Chansey,,
|
||||||
|
45,Blissey,,
|
||||||
|
46,Azurill,,
|
||||||
|
47,Marill,,
|
||||||
|
48,Azumarill,,
|
||||||
|
49,Surskit,,
|
||||||
|
50,Masquerain,,
|
||||||
|
51,Buizel,,
|
||||||
|
52,Floatzel,,
|
||||||
|
53,Wooper,,
|
||||||
|
54,Clodsire,,
|
||||||
|
55,Psyduck,,
|
||||||
|
56,Golduck,,
|
||||||
|
57,Chewtle,,
|
||||||
|
58,Drednaw,,
|
||||||
|
59,Igglybuff,,
|
||||||
|
60,Jigglypuff,,
|
||||||
|
61,Wigglytuff,,
|
||||||
|
62,Ralts,,
|
||||||
|
63,Kirlia,,
|
||||||
|
64,Gardevoir,,
|
||||||
|
65,Gallade,,
|
||||||
|
66,Drowzee,,
|
||||||
|
67,Hypno,,
|
||||||
|
68,Gastly,,
|
||||||
|
69,Haunter,,
|
||||||
|
70,Gengar,,
|
||||||
|
71,Tandemaus,,
|
||||||
|
72,Maushold,,
|
||||||
|
73,Pichu,,
|
||||||
|
74,Pikachu,,
|
||||||
|
75,Raichu,,
|
||||||
|
76,Fidough,,
|
||||||
|
77,Dachsbun,,
|
||||||
|
78,Slakoth,,
|
||||||
|
79,Vigoroth,,
|
||||||
|
80,Slaking,,
|
||||||
|
81,Bounsweet,,
|
||||||
|
82,Steenee,,
|
||||||
|
83,Tsareena,,
|
||||||
|
84,Smoliv,,
|
||||||
|
85,Dolliv,,
|
||||||
|
86,Arboliva,,
|
||||||
|
87,Bonsly,,
|
||||||
|
88,Sudowoodo,,
|
||||||
|
89,Rockruff,,
|
||||||
|
90,Lycanroc,,
|
||||||
|
91,Rolycoly,,
|
||||||
|
92,Carkol,,
|
||||||
|
93,Coalossal,,
|
||||||
|
94,Shinx,,
|
||||||
|
95,Luxio,,
|
||||||
|
96,Luxray,,
|
||||||
|
97,Starly,,
|
||||||
|
98,Staravia,,
|
||||||
|
99,Staraptor,,
|
||||||
|
100,Oricorio,,
|
||||||
|
101,Mareep,,
|
||||||
|
102,Flaaffy,,
|
||||||
|
103,Ampharos,,
|
||||||
|
104,Petilil,,
|
||||||
|
105,Lilligant,,
|
||||||
|
106,Shroomish,,
|
||||||
|
107,Breloom,,
|
||||||
|
108,Applin,,
|
||||||
|
109,Flapple,,
|
||||||
|
110,Appletun,,
|
||||||
|
111,Spoink,,
|
||||||
|
112,Grumpig,,
|
||||||
|
113,Squawkabilly,,
|
||||||
|
114,Misdreavus,,
|
||||||
|
115,Mismagius,,
|
||||||
|
116,Makuhita,,
|
||||||
|
117,Hariyama,,
|
||||||
|
118,Crabrawler,,
|
||||||
|
119,Crabominable,,
|
||||||
|
120,Salandit,,
|
||||||
|
121,Salazzle,,
|
||||||
|
122,Phanpy,,
|
||||||
|
123,Donphan,,
|
||||||
|
124,Cufant,,
|
||||||
|
125,Copperajah,,
|
||||||
|
126,Gible,,
|
||||||
|
127,Gabite,,
|
||||||
|
128,Garchomp,,
|
||||||
|
129,Nacli,,
|
||||||
|
130,Naclstack,,
|
||||||
|
131,Garganacl,,
|
||||||
|
132,Wingull,,
|
||||||
|
133,Pelipper,,
|
||||||
|
134,Magikarp,,
|
||||||
|
135,Gyarados,,
|
||||||
|
136,Arrokuda,,
|
||||||
|
137,Barraskewda,,
|
||||||
|
138,Basculin,,
|
||||||
|
139,Gulpin,,
|
||||||
|
140,Swalot,,
|
||||||
|
141,Meowth,,
|
||||||
|
142,Persian,,
|
||||||
|
143,Drifloon,,
|
||||||
|
144,Drifblim,,
|
||||||
|
145,Flabébé,,
|
||||||
|
146,Floette,,
|
||||||
|
147,Florges,,
|
||||||
|
148,Diglett,,
|
||||||
|
149,Dugtrio,,
|
||||||
|
150,Torkoal,,
|
||||||
|
151,Numel,,
|
||||||
|
152,Camerupt,,
|
||||||
|
153,Bronzor,,
|
||||||
|
154,Bronzong,,
|
||||||
|
155,Axew,,
|
||||||
|
156,Fraxure,,
|
||||||
|
157,Haxorus,,
|
||||||
|
158,Mankey,,
|
||||||
|
159,Primeape,,
|
||||||
|
160,Annihilape,,
|
||||||
|
161,Meditite,,
|
||||||
|
162,Medicham,,
|
||||||
|
163,Riolu,,
|
||||||
|
164,Lucario,,
|
||||||
|
165,Charcadet,,
|
||||||
|
166,Armarouge,,
|
||||||
|
167,Ceruledge,,
|
||||||
|
168,Barboach,,
|
||||||
|
169,Whiscash,,
|
||||||
|
170,Tadbulb,,
|
||||||
|
171,Bellibolt,,
|
||||||
|
172,Goomy,,
|
||||||
|
173,Sliggoo,,
|
||||||
|
174,Goodra,,
|
||||||
|
175,Croagunk,,
|
||||||
|
176,Toxicroak,,
|
||||||
|
177,Wattrel,,
|
||||||
|
178,Kilowattrel,,
|
||||||
|
179,Eevee,,
|
||||||
|
180,Vaporeon,,
|
||||||
|
181,Jolteon,,
|
||||||
|
182,Flareon,,
|
||||||
|
183,Espeon,,
|
||||||
|
184,Umbreon,,
|
||||||
|
185,Leafeon,,
|
||||||
|
186,Glaceon,,
|
||||||
|
187,Sylveon,,
|
||||||
|
188,Dunsparce,,
|
||||||
|
189,Dudunsparce,,
|
||||||
|
190,Deerling,,
|
||||||
|
191,Sawsbuck,,
|
||||||
|
192,Girafarig,,
|
||||||
|
193,Farigiraf,,
|
||||||
|
194,Grimer,,
|
||||||
|
195,Muk,,
|
||||||
|
196,Maschiff,,
|
||||||
|
197,Mabosstiff,,
|
||||||
|
198,Toxel,,
|
||||||
|
199,Toxtricity,,
|
||||||
|
200,Dedenne,,
|
||||||
|
201,Pachirisu,,
|
||||||
|
202,Shroodle,,
|
||||||
|
203,Grafaiai,,
|
||||||
|
204,Stantler,,
|
||||||
|
205,Foongus,,
|
||||||
|
206,Amoonguss,,
|
||||||
|
207,Voltorb,,
|
||||||
|
208,Electrode,,
|
||||||
|
209,Magnemite,,
|
||||||
|
210,Magneton,,
|
||||||
|
211,Magnezone,,
|
||||||
|
212,Ditto,,
|
||||||
|
213,Growlithe,,
|
||||||
|
214,Arcanine,,
|
||||||
|
215,Teddiursa,,
|
||||||
|
216,Ursaring,,
|
||||||
|
217,Zangoose,,
|
||||||
|
218,Seviper,,
|
||||||
|
219,Swablu,,
|
||||||
|
220,Altaria,,
|
||||||
|
221,Skiddo,,
|
||||||
|
222,Gogoat,,
|
||||||
|
223,Tauros,,
|
||||||
|
224,Litleo,,
|
||||||
|
225,Pyroar,,
|
||||||
|
226,Stunky,,
|
||||||
|
227,Skuntank,,
|
||||||
|
228,Zorua,,
|
||||||
|
229,Zoroark,,
|
||||||
|
230,Sneasel,,
|
||||||
|
231,Weavile,,
|
||||||
|
232,Murkrow,,
|
||||||
|
233,Honchkrow,,
|
||||||
|
234,Gothita,,
|
||||||
|
235,Gothorita,,
|
||||||
|
236,Gothitelle,,
|
||||||
|
237,Sinistea,,
|
||||||
|
238,Polteageist,,
|
||||||
|
239,Mimikyu,,
|
||||||
|
240,Klefki,,
|
||||||
|
241,Indeedee,,
|
||||||
|
242,Bramblin,,
|
||||||
|
243,Brambleghast,,
|
||||||
|
244,Toedscool,,
|
||||||
|
245,Toedscruel,,
|
||||||
|
246,Tropius,,
|
||||||
|
247,Fomantis,,
|
||||||
|
248,Lurantis,,
|
||||||
|
249,Klawf,,
|
||||||
|
250,Capsakid,,
|
||||||
|
251,Scovillain,,
|
||||||
|
252,Cacnea,,
|
||||||
|
253,Cacturne,,
|
||||||
|
254,Rellor,,
|
||||||
|
255,Rabsca,,
|
||||||
|
256,Venonat,,
|
||||||
|
257,Venomoth,,
|
||||||
|
258,Pineco,,
|
||||||
|
259,Forretress,,
|
||||||
|
260,Scyther,,
|
||||||
|
261,Scizor,,
|
||||||
|
262,Heracross,,
|
||||||
|
263,Flittle,,
|
||||||
|
264,Espathra,,
|
||||||
|
265,Hippopotas,,
|
||||||
|
266,Hippowdon,,
|
||||||
|
267,Sandile,,
|
||||||
|
268,Krokorok,,
|
||||||
|
269,Krookodile,,
|
||||||
|
270,Silicobra,,
|
||||||
|
271,Sandaconda,,
|
||||||
|
272,Mudbray,,
|
||||||
|
273,Mudsdale,,
|
||||||
|
274,Larvesta,,
|
||||||
|
275,Volcarona,,
|
||||||
|
276,Bagon,,
|
||||||
|
277,Shelgon,,
|
||||||
|
278,Salamence,,
|
||||||
|
279,Tinkatink,,
|
||||||
|
280,Tinkatuff,,
|
||||||
|
281,Tinkaton,,
|
||||||
|
282,Hatenna,,
|
||||||
|
283,Hattrem,,
|
||||||
|
284,Hatterene,,
|
||||||
|
285,Impidimp,,
|
||||||
|
286,Morgrem,,
|
||||||
|
287,Grimmsnarl,,
|
||||||
|
288,Wiglett,,
|
||||||
|
289,Wugtrio,,
|
||||||
|
290,Bombirdier,,
|
||||||
|
291,Finizen,,
|
||||||
|
292,Palafin,,
|
||||||
|
293,Varoom,,
|
||||||
|
294,Revavroom,,
|
||||||
|
295,Cyclizar,,
|
||||||
|
296,Orthworm,,
|
||||||
|
297,Sableye,,
|
||||||
|
298,Shuppet,,
|
||||||
|
299,Banette,,
|
||||||
|
300,Falinks,,
|
||||||
|
301,Hawlucha,,
|
||||||
|
302,Spiritomb,,
|
||||||
|
303,Noibat,,
|
||||||
|
304,Noivern,,
|
||||||
|
305,Dreepy,,
|
||||||
|
306,Drakloak,,
|
||||||
|
307,Dragapult,,
|
||||||
|
308,Glimmet,,
|
||||||
|
309,Glimmora,,
|
||||||
|
310,Rotom,,
|
||||||
|
311,Greavard,,
|
||||||
|
312,Houndstone,,
|
||||||
|
313,Oranguru,,
|
||||||
|
314,Passimian,,
|
||||||
|
315,Komala,,
|
||||||
|
316,Larvitar,,
|
||||||
|
317,Pupitar,,
|
||||||
|
318,Tyranitar,,
|
||||||
|
319,Stonjourner,,
|
||||||
|
320,Eiscue,,
|
||||||
|
321,Pincurchin,,
|
||||||
|
322,Sandygast,,
|
||||||
|
323,Palossand,,
|
||||||
|
324,Slowpoke,,
|
||||||
|
325,Slowbro,,
|
||||||
|
326,Slowking,,
|
||||||
|
327,Shellos,,
|
||||||
|
328,Gastrodon,,
|
||||||
|
329,Shellder,,
|
||||||
|
330,Cloyster,,
|
||||||
|
331,Qwilfish,,
|
||||||
|
332,Luvdisc,,
|
||||||
|
333,Finneon,,
|
||||||
|
334,Lumineon,,
|
||||||
|
335,Bruxish,,
|
||||||
|
336,Alomomola,,
|
||||||
|
337,Skrelp,,
|
||||||
|
338,Dragalge,,
|
||||||
|
339,Clauncher,,
|
||||||
|
340,Clawitzer,,
|
||||||
|
341,Tynamo,,
|
||||||
|
342,Eelektrik,,
|
||||||
|
343,Eelektross,,
|
||||||
|
344,Mareanie,,
|
||||||
|
345,Toxapex,,
|
||||||
|
346,Flamigo,,
|
||||||
|
347,Dratini,,
|
||||||
|
348,Dragonair,,
|
||||||
|
349,Dragonite,,
|
||||||
|
350,Snom,,
|
||||||
|
351,Frosmoth,,
|
||||||
|
352,Snover,,
|
||||||
|
353,Abomasnow,,
|
||||||
|
354,Delibird,,
|
||||||
|
355,Cubchoo,,
|
||||||
|
356,Beartic,,
|
||||||
|
357,Snorunt,,
|
||||||
|
358,Glalie,,
|
||||||
|
359,Froslass,,
|
||||||
|
360,Cryogonal,,
|
||||||
|
361,Cetoddle,,
|
||||||
|
362,Cetitan,,
|
||||||
|
363,Bergmite,,
|
||||||
|
364,Avalugg,,
|
||||||
|
365,Rufflet,,
|
||||||
|
366,Braviary,,
|
||||||
|
367,Pawniard,,
|
||||||
|
368,Bisharp,,
|
||||||
|
369,Kingambit,,
|
||||||
|
370,Deino,,
|
||||||
|
371,Zweilous,,
|
||||||
|
372,Hydreigon,,
|
||||||
|
373,Veluza,,
|
||||||
|
374,Dondozo,,
|
||||||
|
375,Tatsugiri,,
|
||||||
|
376,Great Tusk,,
|
||||||
|
377,Scream Tail,,
|
||||||
|
378,Brute Bonnet,,
|
||||||
|
379,Flutter Mane,,
|
||||||
|
380,Slither Wing,,
|
||||||
|
381,Sandy Shocks,,
|
||||||
|
382,Iron Treads,,
|
||||||
|
383,Iron Bundle,,
|
||||||
|
384,Iron Hands,,
|
||||||
|
385,Iron Jugulis,,
|
||||||
|
386,Iron Moth,,
|
||||||
|
387,Iron Thorns,,
|
||||||
|
388,Frigibax,,
|
||||||
|
389,Arctibax,,
|
||||||
|
390,Baxcalibur,,
|
||||||
|
391,Gimmighoul,,
|
||||||
|
392,Gholdengo,,
|
||||||
|
393,Wo-Chien,,
|
||||||
|
394,Chien-Pao,,
|
||||||
|
395,Ting-Lu,,
|
||||||
|
396,Chi-Yu,,
|
||||||
|
397,Roaring Moon,,
|
||||||
|
398,Iron Valiant,,
|
||||||
|
399,Koraidon,,
|
||||||
|
400,Miraidon,,
|
||||||
|
@@ -0,0 +1,131 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Caterpie,,
|
||||||
|
9,Metapod,,
|
||||||
|
10,Butterfree,,
|
||||||
|
11,Grubbin,,
|
||||||
|
12,Charjabug,,
|
||||||
|
13,Vikavolt,,
|
||||||
|
14,Bonsly,,
|
||||||
|
15,Sudowoodo,,
|
||||||
|
16,Happiny,,
|
||||||
|
17,Chansey,,
|
||||||
|
18,Blissey,,
|
||||||
|
19,Wingull,,
|
||||||
|
20,Pelipper,,
|
||||||
|
21,Crabrawler,,
|
||||||
|
22,Crabominable,,
|
||||||
|
23,Gastly,,
|
||||||
|
24,Haunter,,
|
||||||
|
25,Gengar,,
|
||||||
|
26,Zubat,,
|
||||||
|
27,Golbat,,
|
||||||
|
28,Crobat,,
|
||||||
|
29,Diglett,,
|
||||||
|
30,Dugtrio,,
|
||||||
|
31,Oricorio,,
|
||||||
|
32,Psyduck,,
|
||||||
|
33,Golduck,,
|
||||||
|
34,Magikarp,,
|
||||||
|
35,Gyarados,,
|
||||||
|
36,Barboach,,
|
||||||
|
37,Whiscash,,
|
||||||
|
38,Tentacool,,
|
||||||
|
39,Tentacruel,,
|
||||||
|
40,Finneon,,
|
||||||
|
41,Lumineon,,
|
||||||
|
42,Wishiwashi,,
|
||||||
|
43,Luvdisc,,
|
||||||
|
44,Corsola,,
|
||||||
|
45,Mareanie,,
|
||||||
|
46,Toxapex,,
|
||||||
|
47,Lillipup,,
|
||||||
|
48,Herdier,,
|
||||||
|
49,Stoutland,,
|
||||||
|
50,Eevee,,
|
||||||
|
51,Vaporeon,,
|
||||||
|
52,Jolteon,,
|
||||||
|
53,Flareon,,
|
||||||
|
54,Espeon,,
|
||||||
|
55,Umbreon,,
|
||||||
|
56,Leafeon,,
|
||||||
|
57,Glaceon,,
|
||||||
|
58,Sylveon,,
|
||||||
|
59,Mudbray,,
|
||||||
|
60,Mudsdale,,
|
||||||
|
61,Igglybuff,,
|
||||||
|
62,Jigglypuff,,
|
||||||
|
63,Wigglytuff,,
|
||||||
|
64,Tauros,,
|
||||||
|
65,Miltank,,
|
||||||
|
66,Surskit,,
|
||||||
|
67,Masquerain,,
|
||||||
|
68,Dewpider,,
|
||||||
|
69,Araquanid,,
|
||||||
|
70,Fomantis,,
|
||||||
|
71,Lurantis,,
|
||||||
|
72,Morelull,,
|
||||||
|
73,Shiinotic,,
|
||||||
|
74,Paras,,
|
||||||
|
75,Parasect,,
|
||||||
|
76,Poliwag,,
|
||||||
|
77,Poliwhirl,,
|
||||||
|
78,Poliwrath,,
|
||||||
|
79,Politoed,,
|
||||||
|
80,Goldeen,,
|
||||||
|
81,Seaking,,
|
||||||
|
82,Feebas,,
|
||||||
|
83,Milotic,,
|
||||||
|
84,Alomomola,,
|
||||||
|
85,Fletchling,,
|
||||||
|
86,Fletchinder,,
|
||||||
|
87,Talonflame,,
|
||||||
|
88,Salandit,,
|
||||||
|
89,Salazzle,,
|
||||||
|
90,Cubone,,
|
||||||
|
91,Marowak,,
|
||||||
|
92,Kangaskhan,,
|
||||||
|
93,Magby,,
|
||||||
|
94,Magmar,,
|
||||||
|
95,Magmortar,,
|
||||||
|
96,Stufful,,
|
||||||
|
97,Bewear,,
|
||||||
|
98,Bounsweet,,
|
||||||
|
99,Steenee,,
|
||||||
|
100,Tsareena,,
|
||||||
|
101,Comfey,,
|
||||||
|
102,Pinsir,,
|
||||||
|
103,Oranguru,,
|
||||||
|
104,Passimian,,
|
||||||
|
105,Goomy,,
|
||||||
|
106,Sliggoo,,
|
||||||
|
107,Goodra,,
|
||||||
|
108,Castform,,
|
||||||
|
109,Wimpod,,
|
||||||
|
110,Golisopod,,
|
||||||
|
111,Staryu,,
|
||||||
|
112,Starmie,,
|
||||||
|
113,Sandygast,,
|
||||||
|
114,Palossand,,
|
||||||
|
115,Cranidos,,
|
||||||
|
116,Rampardos,,
|
||||||
|
117,Shieldon,,
|
||||||
|
118,Bastiodon,,
|
||||||
|
119,Archen,,
|
||||||
|
120,Archeops,,
|
||||||
|
121,Tirtouga,,
|
||||||
|
122,Carracosta,,
|
||||||
|
123,Phantump,,
|
||||||
|
124,Trevenant,,
|
||||||
|
125,Nosepass,,
|
||||||
|
126,Probopass,,
|
||||||
|
127,Pyukumuku,,
|
||||||
|
128,Chinchou,,
|
||||||
|
129,Lanturn,,
|
||||||
|
130,Tapu Lele,,
|
||||||
|
@@ -0,0 +1,303 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Rowlet,,
|
||||||
|
2,Dartrix,,
|
||||||
|
3,Decidueye,,
|
||||||
|
4,Litten,,
|
||||||
|
5,Torracat,,
|
||||||
|
6,Incineroar,,
|
||||||
|
7,Popplio,,
|
||||||
|
8,Brionne,,
|
||||||
|
9,Primarina,,
|
||||||
|
10,Pikipek,,
|
||||||
|
11,Trumbeak,,
|
||||||
|
12,Toucannon,,
|
||||||
|
13,Yungoos,,
|
||||||
|
14,Gumshoos,,
|
||||||
|
15,Rattata,,
|
||||||
|
16,Raticate,,
|
||||||
|
17,Caterpie,,
|
||||||
|
18,Metapod,,
|
||||||
|
19,Butterfree,,
|
||||||
|
20,Ledyba,,
|
||||||
|
21,Ledian,,
|
||||||
|
22,Spinarak,,
|
||||||
|
23,Ariados,,
|
||||||
|
24,Pichu,,
|
||||||
|
25,Pikachu,,
|
||||||
|
26,Raichu,,
|
||||||
|
27,Grubbin,,
|
||||||
|
28,Charjabug,,
|
||||||
|
29,Vikavolt,,
|
||||||
|
30,Bonsly,,
|
||||||
|
31,Sudowoodo,,
|
||||||
|
32,Happiny,,
|
||||||
|
33,Chansey,,
|
||||||
|
34,Blissey,,
|
||||||
|
35,Munchlax,,
|
||||||
|
36,Snorlax,,
|
||||||
|
37,Slowpoke,,
|
||||||
|
38,Slowbro,,
|
||||||
|
39,Slowking,,
|
||||||
|
40,Wingull,,
|
||||||
|
41,Pelipper,,
|
||||||
|
42,Abra,,
|
||||||
|
43,Kadabra,,
|
||||||
|
44,Alakazam,,
|
||||||
|
45,Meowth,,
|
||||||
|
46,Persian,,
|
||||||
|
47,Magnemite,,
|
||||||
|
48,Magneton,,
|
||||||
|
49,Magnezone,,
|
||||||
|
50,Grimer,,
|
||||||
|
51,Muk,,
|
||||||
|
52,Growlithe,,
|
||||||
|
53,Arcanine,,
|
||||||
|
54,Drowzee,,
|
||||||
|
55,Hypno,,
|
||||||
|
56,Makuhita,,
|
||||||
|
57,Hariyama,,
|
||||||
|
58,Smeargle,,
|
||||||
|
59,Crabrawler,,
|
||||||
|
60,Crabominable,,
|
||||||
|
61,Gastly,,
|
||||||
|
62,Haunter,,
|
||||||
|
63,Gengar,,
|
||||||
|
64,Drifloon,,
|
||||||
|
65,Drifblim,,
|
||||||
|
66,Misdreavus,,
|
||||||
|
67,Mismagius,,
|
||||||
|
68,Zubat,,
|
||||||
|
69,Golbat,,
|
||||||
|
70,Crobat,,
|
||||||
|
71,Diglett,,
|
||||||
|
72,Dugtrio,,
|
||||||
|
73,Spearow,,
|
||||||
|
74,Fearow,,
|
||||||
|
75,Rufflet,,
|
||||||
|
76,Braviary,,
|
||||||
|
77,Vullaby,,
|
||||||
|
78,Mandibuzz,,
|
||||||
|
79,Mankey,,
|
||||||
|
80,Primeape,,
|
||||||
|
81,Delibird,,
|
||||||
|
82,Oricorio,,
|
||||||
|
83,Cutiefly,,
|
||||||
|
84,Ribombee,,
|
||||||
|
85,Petilil,,
|
||||||
|
86,Lilligant,,
|
||||||
|
87,Cottonee,,
|
||||||
|
88,Whimsicott,,
|
||||||
|
89,Psyduck,,
|
||||||
|
90,Golduck,,
|
||||||
|
91,Magikarp,,
|
||||||
|
92,Gyarados,,
|
||||||
|
93,Barboach,,
|
||||||
|
94,Whiscash,,
|
||||||
|
95,Machop,,
|
||||||
|
96,Machoke,,
|
||||||
|
97,Machamp,,
|
||||||
|
98,Roggenrola,,
|
||||||
|
99,Boldore,,
|
||||||
|
100,Gigalith,,
|
||||||
|
101,Carbink,,
|
||||||
|
102,Sableye,,
|
||||||
|
103,Rockruff,,
|
||||||
|
104,Lycanroc,,
|
||||||
|
105,Spinda,,
|
||||||
|
106,Tentacool,,
|
||||||
|
107,Tentacruel,,
|
||||||
|
108,Finneon,,
|
||||||
|
109,Lumineon,,
|
||||||
|
110,Wishiwashi,,
|
||||||
|
111,Luvdisc,,
|
||||||
|
112,Corsola,,
|
||||||
|
113,Mareanie,,
|
||||||
|
114,Toxapex,,
|
||||||
|
115,Shellder,,
|
||||||
|
116,Cloyster,,
|
||||||
|
117,Bagon,,
|
||||||
|
118,Shelgon,,
|
||||||
|
119,Salamence,,
|
||||||
|
120,Lillipup,,
|
||||||
|
121,Herdier,,
|
||||||
|
122,Stoutland,,
|
||||||
|
123,Eevee,,
|
||||||
|
124,Vaporeon,,
|
||||||
|
125,Jolteon,,
|
||||||
|
126,Flareon,,
|
||||||
|
127,Espeon,,
|
||||||
|
128,Umbreon,,
|
||||||
|
129,Leafeon,,
|
||||||
|
130,Glaceon,,
|
||||||
|
131,Sylveon,,
|
||||||
|
132,Mudbray,,
|
||||||
|
133,Mudsdale,,
|
||||||
|
134,Igglybuff,,
|
||||||
|
135,Jigglypuff,,
|
||||||
|
136,Wigglytuff,,
|
||||||
|
137,Tauros,,
|
||||||
|
138,Miltank,,
|
||||||
|
139,Surskit,,
|
||||||
|
140,Masquerain,,
|
||||||
|
141,Dewpider,,
|
||||||
|
142,Araquanid,,
|
||||||
|
143,Fomantis,,
|
||||||
|
144,Lurantis,,
|
||||||
|
145,Morelull,,
|
||||||
|
146,Shiinotic,,
|
||||||
|
147,Paras,,
|
||||||
|
148,Parasect,,
|
||||||
|
149,Poliwag,,
|
||||||
|
150,Poliwhirl,,
|
||||||
|
151,Poliwrath,,
|
||||||
|
152,Politoed,,
|
||||||
|
153,Goldeen,,
|
||||||
|
154,Seaking,,
|
||||||
|
155,Feebas,,
|
||||||
|
156,Milotic,,
|
||||||
|
157,Alomomola,,
|
||||||
|
158,Fletchling,,
|
||||||
|
159,Fletchinder,,
|
||||||
|
160,Talonflame,,
|
||||||
|
161,Salandit,,
|
||||||
|
162,Salazzle,,
|
||||||
|
163,Cubone,,
|
||||||
|
164,Marowak,,
|
||||||
|
165,Kangaskhan,,
|
||||||
|
166,Magby,,
|
||||||
|
167,Magmar,,
|
||||||
|
168,Magmortar,,
|
||||||
|
169,Stufful,,
|
||||||
|
170,Bewear,,
|
||||||
|
171,Bounsweet,,
|
||||||
|
172,Steenee,,
|
||||||
|
173,Tsareena,,
|
||||||
|
174,Comfey,,
|
||||||
|
175,Pinsir,,
|
||||||
|
176,Oranguru,,
|
||||||
|
177,Passimian,,
|
||||||
|
178,Goomy,,
|
||||||
|
179,Sliggoo,,
|
||||||
|
180,Goodra,,
|
||||||
|
181,Castform,,
|
||||||
|
182,Wimpod,,
|
||||||
|
183,Golisopod,,
|
||||||
|
184,Staryu,,
|
||||||
|
185,Starmie,,
|
||||||
|
186,Sandygast,,
|
||||||
|
187,Palossand,,
|
||||||
|
188,Cranidos,,
|
||||||
|
189,Rampardos,,
|
||||||
|
190,Shieldon,,
|
||||||
|
191,Bastiodon,,
|
||||||
|
192,Archen,,
|
||||||
|
193,Archeops,,
|
||||||
|
194,Tirtouga,,
|
||||||
|
195,Carracosta,,
|
||||||
|
196,Phantump,,
|
||||||
|
197,Trevenant,,
|
||||||
|
198,Nosepass,,
|
||||||
|
199,Probopass,,
|
||||||
|
200,Pyukumuku,,
|
||||||
|
201,Chinchou,,
|
||||||
|
202,Lanturn,,
|
||||||
|
203,Type: Null,,
|
||||||
|
204,Silvally,,
|
||||||
|
205,Zygarde,,
|
||||||
|
206,Trubbish,,
|
||||||
|
207,Garbodor,,
|
||||||
|
208,Skarmory,,
|
||||||
|
209,Ditto,,
|
||||||
|
210,Cleffa,,
|
||||||
|
211,Clefairy,,
|
||||||
|
212,Clefable,,
|
||||||
|
213,Minior,,
|
||||||
|
214,Beldum,,
|
||||||
|
215,Metang,,
|
||||||
|
216,Metagross,,
|
||||||
|
217,Porygon,,
|
||||||
|
218,Porygon2,,
|
||||||
|
219,Porygon-Z,,
|
||||||
|
220,Pancham,,
|
||||||
|
221,Pangoro,,
|
||||||
|
222,Komala,,
|
||||||
|
223,Torkoal,,
|
||||||
|
224,Turtonator,,
|
||||||
|
225,Togedemaru,,
|
||||||
|
226,Elekid,,
|
||||||
|
227,Electabuzz,,
|
||||||
|
228,Electivire,,
|
||||||
|
229,Geodude,,
|
||||||
|
230,Graveler,,
|
||||||
|
231,Golem,,
|
||||||
|
232,Sandile,,
|
||||||
|
233,Krokorok,,
|
||||||
|
234,Krookodile,,
|
||||||
|
235,Trapinch,,
|
||||||
|
236,Vibrava,,
|
||||||
|
237,Flygon,,
|
||||||
|
238,Gible,,
|
||||||
|
239,Gabite,,
|
||||||
|
240,Garchomp,,
|
||||||
|
241,Klefki,,
|
||||||
|
242,Mimikyu,,
|
||||||
|
243,Bruxish,,
|
||||||
|
244,Drampa,,
|
||||||
|
245,Absol,,
|
||||||
|
246,Snorunt,,
|
||||||
|
247,Glalie,,
|
||||||
|
248,Froslass,,
|
||||||
|
249,Sneasel,,
|
||||||
|
250,Weavile,,
|
||||||
|
251,Sandshrew,,
|
||||||
|
252,Sandslash,,
|
||||||
|
253,Vulpix,,
|
||||||
|
254,Ninetales,,
|
||||||
|
255,Vanillite,,
|
||||||
|
256,Vanillish,,
|
||||||
|
257,Vanilluxe,,
|
||||||
|
258,Snubbull,,
|
||||||
|
259,Granbull,,
|
||||||
|
260,Shellos,,
|
||||||
|
261,Gastrodon,,
|
||||||
|
262,Relicanth,,
|
||||||
|
263,Dhelmise,,
|
||||||
|
264,Carvanha,,
|
||||||
|
265,Sharpedo,,
|
||||||
|
266,Wailmer,,
|
||||||
|
267,Wailord,,
|
||||||
|
268,Lapras,,
|
||||||
|
269,Exeggcute,,
|
||||||
|
270,Exeggutor,,
|
||||||
|
271,Jangmo-o,,
|
||||||
|
272,Hakamo-o,,
|
||||||
|
273,Kommo-o,,
|
||||||
|
274,Emolga,,
|
||||||
|
275,Scyther,,
|
||||||
|
276,Scizor,,
|
||||||
|
277,Murkrow,,
|
||||||
|
278,Honchkrow,,
|
||||||
|
279,Riolu,,
|
||||||
|
280,Lucario,,
|
||||||
|
281,Dratini,,
|
||||||
|
282,Dragonair,,
|
||||||
|
283,Dragonite,,
|
||||||
|
284,Aerodactyl,,
|
||||||
|
285,Tapu Koko,,
|
||||||
|
286,Tapu Lele,,
|
||||||
|
287,Tapu Bulu,,
|
||||||
|
288,Tapu Fini,,
|
||||||
|
289,Cosmog,,
|
||||||
|
290,Cosmoem,,
|
||||||
|
291,Solgaleo,,
|
||||||
|
292,Lunala,,
|
||||||
|
293,Nihilego,,
|
||||||
|
294,Buzzwole,,
|
||||||
|
295,Pheromosa,,
|
||||||
|
296,Xurkitree,,
|
||||||
|
297,Celesteela,,
|
||||||
|
298,Kartana,,
|
||||||
|
299,Guzzlord,,
|
||||||
|
300,Necrozma,,
|
||||||
|
301,Magearna,,
|
||||||
|
302,Marshadow,,
|
||||||
|
@@ -0,0 +1,121 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Rowlet,,
|
||||||
|
2,Dartrix,,
|
||||||
|
3,Decidueye,,
|
||||||
|
4,Litten,,
|
||||||
|
5,Torracat,,
|
||||||
|
6,Incineroar,,
|
||||||
|
7,Popplio,,
|
||||||
|
8,Brionne,,
|
||||||
|
9,Primarina,,
|
||||||
|
10,Pikipek,,
|
||||||
|
11,Trumbeak,,
|
||||||
|
12,Toucannon,,
|
||||||
|
13,Yungoos,,
|
||||||
|
14,Gumshoos,,
|
||||||
|
15,Rattata,,
|
||||||
|
16,Raticate,,
|
||||||
|
17,Caterpie,,
|
||||||
|
18,Metapod,,
|
||||||
|
19,Butterfree,,
|
||||||
|
20,Ledyba,,
|
||||||
|
21,Ledian,,
|
||||||
|
22,Spinarak,,
|
||||||
|
23,Ariados,,
|
||||||
|
24,Pichu,,
|
||||||
|
25,Pikachu,,
|
||||||
|
26,Raichu,,
|
||||||
|
27,Grubbin,,
|
||||||
|
28,Charjabug,,
|
||||||
|
29,Vikavolt,,
|
||||||
|
30,Bonsly,,
|
||||||
|
31,Sudowoodo,,
|
||||||
|
32,Happiny,,
|
||||||
|
33,Chansey,,
|
||||||
|
34,Blissey,,
|
||||||
|
35,Munchlax,,
|
||||||
|
36,Snorlax,,
|
||||||
|
37,Slowpoke,,
|
||||||
|
38,Slowbro,,
|
||||||
|
39,Slowking,,
|
||||||
|
40,Wingull,,
|
||||||
|
41,Pelipper,,
|
||||||
|
42,Abra,,
|
||||||
|
43,Kadabra,,
|
||||||
|
44,Alakazam,,
|
||||||
|
45,Meowth,,
|
||||||
|
46,Persian,,
|
||||||
|
47,Magnemite,,
|
||||||
|
48,Magneton,,
|
||||||
|
49,Magnezone,,
|
||||||
|
50,Grimer,,
|
||||||
|
51,Muk,,
|
||||||
|
52,Growlithe,,
|
||||||
|
53,Arcanine,,
|
||||||
|
54,Drowzee,,
|
||||||
|
55,Hypno,,
|
||||||
|
56,Makuhita,,
|
||||||
|
57,Hariyama,,
|
||||||
|
58,Smeargle,,
|
||||||
|
59,Crabrawler,,
|
||||||
|
60,Crabominable,,
|
||||||
|
61,Gastly,,
|
||||||
|
62,Haunter,,
|
||||||
|
63,Gengar,,
|
||||||
|
64,Drifloon,,
|
||||||
|
65,Drifblim,,
|
||||||
|
66,Misdreavus,,
|
||||||
|
67,Mismagius,,
|
||||||
|
68,Zubat,,
|
||||||
|
69,Golbat,,
|
||||||
|
70,Crobat,,
|
||||||
|
71,Diglett,,
|
||||||
|
72,Dugtrio,,
|
||||||
|
73,Spearow,,
|
||||||
|
74,Fearow,,
|
||||||
|
75,Rufflet,,
|
||||||
|
76,Braviary,,
|
||||||
|
77,Vullaby,,
|
||||||
|
78,Mandibuzz,,
|
||||||
|
79,Mankey,,
|
||||||
|
80,Primeape,,
|
||||||
|
81,Delibird,,
|
||||||
|
82,Oricorio,,
|
||||||
|
83,Cutiefly,,
|
||||||
|
84,Ribombee,,
|
||||||
|
85,Petilil,,
|
||||||
|
86,Lilligant,,
|
||||||
|
87,Cottonee,,
|
||||||
|
88,Whimsicott,,
|
||||||
|
89,Psyduck,,
|
||||||
|
90,Golduck,,
|
||||||
|
91,Magikarp,,
|
||||||
|
92,Gyarados,,
|
||||||
|
93,Barboach,,
|
||||||
|
94,Whiscash,,
|
||||||
|
95,Machop,,
|
||||||
|
96,Machoke,,
|
||||||
|
97,Machamp,,
|
||||||
|
98,Roggenrola,,
|
||||||
|
99,Boldore,,
|
||||||
|
100,Gigalith,,
|
||||||
|
101,Carbink,,
|
||||||
|
102,Sableye,,
|
||||||
|
103,Rockruff,,
|
||||||
|
104,Lycanroc,,
|
||||||
|
105,Spinda,,
|
||||||
|
106,Tentacool,,
|
||||||
|
107,Tentacruel,,
|
||||||
|
108,Finneon,,
|
||||||
|
109,Lumineon,,
|
||||||
|
110,Wishiwashi,,
|
||||||
|
111,Luvdisc,,
|
||||||
|
112,Corsola,,
|
||||||
|
113,Mareanie,,
|
||||||
|
114,Toxapex,,
|
||||||
|
115,Shellder,,
|
||||||
|
116,Cloyster,,
|
||||||
|
117,Bagon,,
|
||||||
|
118,Shelgon,,
|
||||||
|
119,Salamence,,
|
||||||
|
120,Tapu Koko,,
|
||||||
|
@@ -0,0 +1,101 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Happiny,,
|
||||||
|
9,Chansey,,
|
||||||
|
10,Blissey,,
|
||||||
|
11,Wingull,,
|
||||||
|
12,Pelipper,,
|
||||||
|
13,Drowzee,,
|
||||||
|
14,Hypno,,
|
||||||
|
15,Makuhita,,
|
||||||
|
16,Hariyama,,
|
||||||
|
17,Crabrawler,,
|
||||||
|
18,Crabominable,,
|
||||||
|
19,Zubat,,
|
||||||
|
20,Golbat,,
|
||||||
|
21,Crobat,,
|
||||||
|
22,Diglett,,
|
||||||
|
23,Dugtrio,,
|
||||||
|
24,Spearow,,
|
||||||
|
25,Fearow,,
|
||||||
|
26,Rufflet,,
|
||||||
|
27,Braviary,,
|
||||||
|
28,Vullaby,,
|
||||||
|
29,Mandibuzz,,
|
||||||
|
30,Mankey,,
|
||||||
|
31,Primeape,,
|
||||||
|
32,Oricorio,,
|
||||||
|
33,Cutiefly,,
|
||||||
|
34,Ribombee,,
|
||||||
|
35,Petilil,,
|
||||||
|
36,Lilligant,,
|
||||||
|
37,Cottonee,,
|
||||||
|
38,Whimsicott,,
|
||||||
|
39,Psyduck,,
|
||||||
|
40,Golduck,,
|
||||||
|
41,Magikarp,,
|
||||||
|
42,Gyarados,,
|
||||||
|
43,Barboach,,
|
||||||
|
44,Whiscash,,
|
||||||
|
45,Machop,,
|
||||||
|
46,Machoke,,
|
||||||
|
47,Machamp,,
|
||||||
|
48,Roggenrola,,
|
||||||
|
49,Boldore,,
|
||||||
|
50,Gigalith,,
|
||||||
|
51,Carbink,,
|
||||||
|
52,Sableye,,
|
||||||
|
53,Rockruff,,
|
||||||
|
54,Lycanroc,,
|
||||||
|
55,Tentacool,,
|
||||||
|
56,Tentacruel,,
|
||||||
|
57,Finneon,,
|
||||||
|
58,Lumineon,,
|
||||||
|
59,Mudbray,,
|
||||||
|
60,Mudsdale,,
|
||||||
|
61,Tauros,,
|
||||||
|
62,Miltank,,
|
||||||
|
63,Stufful,,
|
||||||
|
64,Bewear,,
|
||||||
|
65,Pinsir,,
|
||||||
|
66,Goomy,,
|
||||||
|
67,Sliggoo,,
|
||||||
|
68,Goodra,,
|
||||||
|
69,Castform,,
|
||||||
|
70,Wimpod,,
|
||||||
|
71,Golisopod,,
|
||||||
|
72,Skarmory,,
|
||||||
|
73,Snubbull,,
|
||||||
|
74,Granbull,,
|
||||||
|
75,Shellos,,
|
||||||
|
76,Gastrodon,,
|
||||||
|
77,Relicanth,,
|
||||||
|
78,Dhelmise,,
|
||||||
|
79,Carvanha,,
|
||||||
|
80,Sharpedo,,
|
||||||
|
81,Wailmer,,
|
||||||
|
82,Wailord,,
|
||||||
|
83,Lapras,,
|
||||||
|
84,Exeggcute,,
|
||||||
|
85,Exeggutor,,
|
||||||
|
86,Jangmo-o,,
|
||||||
|
87,Hakamo-o,,
|
||||||
|
88,Kommo-o,,
|
||||||
|
89,Emolga,,
|
||||||
|
90,Scyther,,
|
||||||
|
91,Scizor,,
|
||||||
|
92,Murkrow,,
|
||||||
|
93,Honchkrow,,
|
||||||
|
94,Riolu,,
|
||||||
|
95,Lucario,,
|
||||||
|
96,Dratini,,
|
||||||
|
97,Dragonair,,
|
||||||
|
98,Dragonite,,
|
||||||
|
99,Aerodactyl,,
|
||||||
|
100,Tapu Fini,,
|
||||||
|
@@ -0,0 +1,131 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Ledyba,,
|
||||||
|
9,Ledian,,
|
||||||
|
10,Spinarak,,
|
||||||
|
11,Ariados,,
|
||||||
|
12,Grubbin,,
|
||||||
|
13,Charjabug,,
|
||||||
|
14,Vikavolt,,
|
||||||
|
15,Happiny,,
|
||||||
|
16,Chansey,,
|
||||||
|
17,Blissey,,
|
||||||
|
18,Slowpoke,,
|
||||||
|
19,Slowbro,,
|
||||||
|
20,Slowking,,
|
||||||
|
21,Wingull,,
|
||||||
|
22,Pelipper,,
|
||||||
|
23,Meowth,,
|
||||||
|
24,Persian,,
|
||||||
|
25,Magnemite,,
|
||||||
|
26,Magneton,,
|
||||||
|
27,Magnezone,,
|
||||||
|
28,Grimer,,
|
||||||
|
29,Muk,,
|
||||||
|
30,Crabrawler,,
|
||||||
|
31,Crabominable,,
|
||||||
|
32,Gastly,,
|
||||||
|
33,Haunter,,
|
||||||
|
34,Gengar,,
|
||||||
|
35,Zubat,,
|
||||||
|
36,Golbat,,
|
||||||
|
37,Crobat,,
|
||||||
|
38,Diglett,,
|
||||||
|
39,Dugtrio,,
|
||||||
|
40,Spearow,,
|
||||||
|
41,Fearow,,
|
||||||
|
42,Oricorio,,
|
||||||
|
43,Cutiefly,,
|
||||||
|
44,Ribombee,,
|
||||||
|
45,Petilil,,
|
||||||
|
46,Lilligant,,
|
||||||
|
47,Cottonee,,
|
||||||
|
48,Whimsicott,,
|
||||||
|
49,Psyduck,,
|
||||||
|
50,Golduck,,
|
||||||
|
51,Magikarp,,
|
||||||
|
52,Gyarados,,
|
||||||
|
53,Tentacool,,
|
||||||
|
54,Tentacruel,,
|
||||||
|
55,Finneon,,
|
||||||
|
56,Lumineon,,
|
||||||
|
57,Wishiwashi,,
|
||||||
|
58,Mudbray,,
|
||||||
|
59,Mudsdale,,
|
||||||
|
60,Surskit,,
|
||||||
|
61,Masquerain,,
|
||||||
|
62,Dewpider,,
|
||||||
|
63,Araquanid,,
|
||||||
|
64,Morelull,,
|
||||||
|
65,Shiinotic,,
|
||||||
|
66,Paras,,
|
||||||
|
67,Parasect,,
|
||||||
|
68,Poliwag,,
|
||||||
|
69,Poliwhirl,,
|
||||||
|
70,Poliwrath,,
|
||||||
|
71,Politoed,,
|
||||||
|
72,Goldeen,,
|
||||||
|
73,Seaking,,
|
||||||
|
74,Goomy,,
|
||||||
|
75,Sliggoo,,
|
||||||
|
76,Goodra,,
|
||||||
|
77,Castform,,
|
||||||
|
78,Trubbish,,
|
||||||
|
79,Garbodor,,
|
||||||
|
80,Skarmory,,
|
||||||
|
81,Ditto,,
|
||||||
|
82,Cleffa,,
|
||||||
|
83,Clefairy,,
|
||||||
|
84,Clefable,,
|
||||||
|
85,Minior,,
|
||||||
|
86,Beldum,,
|
||||||
|
87,Metang,,
|
||||||
|
88,Metagross,,
|
||||||
|
89,Porygon,,
|
||||||
|
90,Porygon2,,
|
||||||
|
91,Porygon-Z,,
|
||||||
|
92,Pancham,,
|
||||||
|
93,Pangoro,,
|
||||||
|
94,Komala,,
|
||||||
|
95,Torkoal,,
|
||||||
|
96,Turtonator,,
|
||||||
|
97,Togedemaru,,
|
||||||
|
98,Elekid,,
|
||||||
|
99,Electabuzz,,
|
||||||
|
100,Electivire,,
|
||||||
|
101,Geodude,,
|
||||||
|
102,Graveler,,
|
||||||
|
103,Golem,,
|
||||||
|
104,Sandile,,
|
||||||
|
105,Krokorok,,
|
||||||
|
106,Krookodile,,
|
||||||
|
107,Trapinch,,
|
||||||
|
108,Vibrava,,
|
||||||
|
109,Flygon,,
|
||||||
|
110,Gible,,
|
||||||
|
111,Gabite,,
|
||||||
|
112,Garchomp,,
|
||||||
|
113,Klefki,,
|
||||||
|
114,Mimikyu,,
|
||||||
|
115,Bruxish,,
|
||||||
|
116,Drampa,,
|
||||||
|
117,Absol,,
|
||||||
|
118,Snorunt,,
|
||||||
|
119,Glalie,,
|
||||||
|
120,Froslass,,
|
||||||
|
121,Sneasel,,
|
||||||
|
122,Weavile,,
|
||||||
|
123,Sandshrew,,
|
||||||
|
124,Sandslash,,
|
||||||
|
125,Vulpix,,
|
||||||
|
126,Ninetales,,
|
||||||
|
127,Vanillite,,
|
||||||
|
128,Vanillish,,
|
||||||
|
129,Vanilluxe,,
|
||||||
|
130,Tapu Bulu,,
|
||||||
|
@@ -0,0 +1,211 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Snom,,
|
||||||
|
2,Frosmoth,,
|
||||||
|
3,Wooloo,,
|
||||||
|
4,Dubwool,,
|
||||||
|
5,Skwovet,,
|
||||||
|
6,Greedent,,
|
||||||
|
7,Swinub,,
|
||||||
|
8,Piloswine,,
|
||||||
|
9,Mamoswine,,
|
||||||
|
10,Mime Jr.,,
|
||||||
|
11,Mr. Mime,,
|
||||||
|
12,Mr. Rime,,
|
||||||
|
13,Smoochum,,
|
||||||
|
14,Jynx,,
|
||||||
|
15,Elekid,,
|
||||||
|
16,Electabuzz,,
|
||||||
|
17,Electivire,,
|
||||||
|
18,Magby,,
|
||||||
|
19,Magmar,,
|
||||||
|
20,Magmortar,,
|
||||||
|
21,Audino,,
|
||||||
|
22,Vanillite,,
|
||||||
|
23,Vanillish,,
|
||||||
|
24,Vanilluxe,,
|
||||||
|
25,Snorunt,,
|
||||||
|
26,Glalie,,
|
||||||
|
27,Froslass,,
|
||||||
|
28,Sneasel,,
|
||||||
|
29,Weavile,,
|
||||||
|
30,Cryogonal,,
|
||||||
|
31,Snover,,
|
||||||
|
32,Abomasnow,,
|
||||||
|
33,Phantump,,
|
||||||
|
34,Trevenant,,
|
||||||
|
35,Swablu,,
|
||||||
|
36,Altaria,,
|
||||||
|
37,Impidimp,,
|
||||||
|
38,Morgrem,,
|
||||||
|
39,Grimmsnarl,,
|
||||||
|
40,Hatenna,,
|
||||||
|
41,Hattrem,,
|
||||||
|
42,Hatterene,,
|
||||||
|
43,Cleffa,,
|
||||||
|
44,Clefairy,,
|
||||||
|
45,Clefable,,
|
||||||
|
46,Mimikyu,,
|
||||||
|
47,Spiritomb,,
|
||||||
|
48,Litwick,,
|
||||||
|
49,Lampent,,
|
||||||
|
50,Chandelure,,
|
||||||
|
51,Gothita,,
|
||||||
|
52,Gothorita,,
|
||||||
|
53,Gothitelle,,
|
||||||
|
54,Solosis,,
|
||||||
|
55,Duosion,,
|
||||||
|
56,Reuniclus,,
|
||||||
|
57,Timburr,,
|
||||||
|
58,Gurdurr,,
|
||||||
|
59,Conkeldurr,,
|
||||||
|
60,Barboach,,
|
||||||
|
61,Whiscash,,
|
||||||
|
62,Magikarp,,
|
||||||
|
63,Gyarados,,
|
||||||
|
64,Basculin,,
|
||||||
|
65,Nidoran♀,,
|
||||||
|
66,Nidorina,,
|
||||||
|
67,Nidoqueen,,
|
||||||
|
68,Nidoran♂,,
|
||||||
|
69,Nidorino,,
|
||||||
|
70,Nidoking,,
|
||||||
|
71,Zigzagoon,,
|
||||||
|
72,Linoone,,
|
||||||
|
73,Obstagoon,,
|
||||||
|
74,Eevee,,
|
||||||
|
75,Vaporeon,,
|
||||||
|
76,Jolteon,,
|
||||||
|
77,Flareon,,
|
||||||
|
78,Umbreon,,
|
||||||
|
79,Espeon,,
|
||||||
|
80,Glaceon,,
|
||||||
|
81,Leafeon,,
|
||||||
|
82,Sylveon,,
|
||||||
|
83,Tyrunt,,
|
||||||
|
84,Tyrantrum,,
|
||||||
|
85,Amaura,,
|
||||||
|
86,Aurorus,,
|
||||||
|
87,Bronzor,,
|
||||||
|
88,Bronzong,,
|
||||||
|
89,Stonjourner,,
|
||||||
|
90,Eiscue,,
|
||||||
|
91,Dewpider,,
|
||||||
|
92,Araquanid,,
|
||||||
|
93,Joltik,,
|
||||||
|
94,Galvantula,,
|
||||||
|
95,Karrablast,,
|
||||||
|
96,Escavalier,,
|
||||||
|
97,Shelmet,,
|
||||||
|
98,Accelgor,,
|
||||||
|
99,Sizzlipede,,
|
||||||
|
100,Centiskorch,,
|
||||||
|
101,Durant,,
|
||||||
|
102,Heatmor,,
|
||||||
|
103,Darumaka,,
|
||||||
|
104,Darmanitan,,
|
||||||
|
105,Ponyta,,
|
||||||
|
106,Rapidash,,
|
||||||
|
107,Absol,,
|
||||||
|
108,Cufant,,
|
||||||
|
109,Copperajah,,
|
||||||
|
110,Dreepy,,
|
||||||
|
111,Drakloak,,
|
||||||
|
112,Dragapult,,
|
||||||
|
113,Bagon,,
|
||||||
|
114,Shelgon,,
|
||||||
|
115,Salamence,,
|
||||||
|
116,Gible,,
|
||||||
|
117,Gabite,,
|
||||||
|
118,Garchomp,,
|
||||||
|
119,Druddigon,,
|
||||||
|
120,Delibird,,
|
||||||
|
121,Cubchoo,,
|
||||||
|
122,Beartic,,
|
||||||
|
123,Omanyte,,
|
||||||
|
124,Omastar,,
|
||||||
|
125,Kabuto,,
|
||||||
|
126,Kabutops,,
|
||||||
|
127,Aerodactyl,,
|
||||||
|
128,Carbink,,
|
||||||
|
129,Beldum,,
|
||||||
|
130,Metang,,
|
||||||
|
131,Metagross,,
|
||||||
|
132,Sinistea,,
|
||||||
|
133,Polteageist,,
|
||||||
|
134,Riolu,,
|
||||||
|
135,Lucario,,
|
||||||
|
136,Deino,,
|
||||||
|
137,Zweilous,,
|
||||||
|
138,Hydreigon,,
|
||||||
|
139,Larvitar,,
|
||||||
|
140,Pupitar,,
|
||||||
|
141,Tyranitar,,
|
||||||
|
142,Bergmite,,
|
||||||
|
143,Avalugg,,
|
||||||
|
144,Zubat,,
|
||||||
|
145,Golbat,,
|
||||||
|
146,Crobat,,
|
||||||
|
147,Tirtouga,,
|
||||||
|
148,Carracosta,,
|
||||||
|
149,Archen,,
|
||||||
|
150,Archeops,,
|
||||||
|
151,Baltoy,,
|
||||||
|
152,Claydol,,
|
||||||
|
153,Golett,,
|
||||||
|
154,Golurk,,
|
||||||
|
155,Yamper,,
|
||||||
|
156,Boltund,,
|
||||||
|
157,Morpeko,,
|
||||||
|
158,Pincurchin,,
|
||||||
|
159,Spheal,,
|
||||||
|
160,Sealeo,,
|
||||||
|
161,Walrein,,
|
||||||
|
162,Dhelmise,,
|
||||||
|
163,Rookidee,,
|
||||||
|
164,Corvisquire,,
|
||||||
|
165,Corviknight,,
|
||||||
|
166,Gossifleur,,
|
||||||
|
167,Eldegoss,,
|
||||||
|
168,Cottonee,,
|
||||||
|
169,Whimsicott,,
|
||||||
|
170,Shuckle,,
|
||||||
|
171,Indeedee,,
|
||||||
|
172,Munchlax,,
|
||||||
|
173,Snorlax,,
|
||||||
|
174,Sableye,,
|
||||||
|
175,Mawile,,
|
||||||
|
176,Rolycoly,,
|
||||||
|
177,Carkol,,
|
||||||
|
178,Coalossal,,
|
||||||
|
179,Ferroseed,,
|
||||||
|
180,Ferrothorn,,
|
||||||
|
181,Noibat,,
|
||||||
|
182,Noivern,,
|
||||||
|
183,Lileep,,
|
||||||
|
184,Cradily,,
|
||||||
|
185,Anorith,,
|
||||||
|
186,Armaldo,,
|
||||||
|
187,Relicanth,,
|
||||||
|
188,Feebas,,
|
||||||
|
189,Milotic,,
|
||||||
|
190,Lapras,,
|
||||||
|
191,Aron,,
|
||||||
|
192,Lairon,,
|
||||||
|
193,Aggron,,
|
||||||
|
194,Dratini,,
|
||||||
|
195,Dragonair,,
|
||||||
|
196,Dragonite,,
|
||||||
|
197,Regirock,,
|
||||||
|
198,Regice,,
|
||||||
|
199,Registeel,,
|
||||||
|
200,Regieleki,,
|
||||||
|
201,Regidrago,,
|
||||||
|
202,Articuno,,
|
||||||
|
203,Zapdos,,
|
||||||
|
204,Moltres,,
|
||||||
|
205,Cobalion,,
|
||||||
|
206,Terrakion,,
|
||||||
|
207,Virizion,,
|
||||||
|
208,Glastrier,,
|
||||||
|
209,Spectrier,,
|
||||||
|
210,Calyrex,,
|
||||||
|
@@ -0,0 +1,212 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Slowpoke,,
|
||||||
|
2,Slowbro,,
|
||||||
|
3,Slowking,,
|
||||||
|
4,Buneary,,
|
||||||
|
5,Lopunny,,
|
||||||
|
6,Happiny,,
|
||||||
|
7,Chansey,,
|
||||||
|
8,Blissey,,
|
||||||
|
9,Skwovet,,
|
||||||
|
10,Greedent,,
|
||||||
|
11,Igglybuff,,
|
||||||
|
12,Jigglypuff,,
|
||||||
|
13,Wigglytuff,,
|
||||||
|
14,Blipbug,,
|
||||||
|
15,Dottler,,
|
||||||
|
16,Orbeetle,,
|
||||||
|
17,Fomantis,,
|
||||||
|
18,Lurantis,,
|
||||||
|
19,Applin,,
|
||||||
|
20,Flapple,,
|
||||||
|
21,Appletun,,
|
||||||
|
22,Fletchling,,
|
||||||
|
23,Fletchinder,,
|
||||||
|
24,Talonflame,,
|
||||||
|
25,Shinx,,
|
||||||
|
26,Luxio,,
|
||||||
|
27,Luxray,,
|
||||||
|
28,Klefki,,
|
||||||
|
29,Pawniard,,
|
||||||
|
30,Bisharp,,
|
||||||
|
31,Abra,,
|
||||||
|
32,Kadabra,,
|
||||||
|
33,Alakazam,,
|
||||||
|
34,Ralts,,
|
||||||
|
35,Kirlia,,
|
||||||
|
36,Gardevoir,,
|
||||||
|
37,Gallade,,
|
||||||
|
38,Krabby,,
|
||||||
|
39,Kingler,,
|
||||||
|
40,Tentacool,,
|
||||||
|
41,Tentacruel,,
|
||||||
|
42,Magikarp,,
|
||||||
|
43,Gyarados,,
|
||||||
|
44,Remoraid,,
|
||||||
|
45,Octillery,,
|
||||||
|
46,Mantyke,,
|
||||||
|
47,Mantine,,
|
||||||
|
48,Wingull,,
|
||||||
|
49,Pelipper,,
|
||||||
|
50,Skorupi,,
|
||||||
|
51,Drapion,,
|
||||||
|
52,Dunsparce,,
|
||||||
|
53,Bouffalant,,
|
||||||
|
54,Lickitung,,
|
||||||
|
55,Lickilicky,,
|
||||||
|
56,Chewtle,,
|
||||||
|
57,Drednaw,,
|
||||||
|
58,Wooper,,
|
||||||
|
59,Quagsire,,
|
||||||
|
60,Goomy,,
|
||||||
|
61,Sliggoo,,
|
||||||
|
62,Goodra,,
|
||||||
|
63,Druddigon,,
|
||||||
|
64,Shelmet,,
|
||||||
|
65,Accelgor,,
|
||||||
|
66,Karrablast,,
|
||||||
|
67,Escavalier,,
|
||||||
|
68,Bulbasaur,,
|
||||||
|
69,Ivysaur,,
|
||||||
|
70,Venusaur,,
|
||||||
|
71,Squirtle,,
|
||||||
|
72,Wartortle,,
|
||||||
|
73,Blastoise,,
|
||||||
|
74,Venipede,,
|
||||||
|
75,Whirlipede,,
|
||||||
|
76,Scolipede,,
|
||||||
|
77,Foongus,,
|
||||||
|
78,Amoonguss,,
|
||||||
|
79,Comfey,,
|
||||||
|
80,Tangela,,
|
||||||
|
81,Tangrowth,,
|
||||||
|
82,Croagunk,,
|
||||||
|
83,Toxicroak,,
|
||||||
|
84,Pichu,,
|
||||||
|
85,Pikachu,,
|
||||||
|
86,Raichu,,
|
||||||
|
87,Zorua,,
|
||||||
|
88,Zoroark,,
|
||||||
|
89,Oranguru,,
|
||||||
|
90,Passimian,,
|
||||||
|
91,Corphish,,
|
||||||
|
92,Crawdaunt,,
|
||||||
|
93,Cramorant,,
|
||||||
|
94,Goldeen,,
|
||||||
|
95,Seaking,,
|
||||||
|
96,Arrokuda,,
|
||||||
|
97,Barraskewda,,
|
||||||
|
98,Staryu,,
|
||||||
|
99,Starmie,,
|
||||||
|
100,Kubfu,,
|
||||||
|
101,Urshifu,,
|
||||||
|
102,Emolga,,
|
||||||
|
103,Dedenne,,
|
||||||
|
104,Morpeko,,
|
||||||
|
105,Magnemite,,
|
||||||
|
106,Magneton,,
|
||||||
|
107,Magnezone,,
|
||||||
|
108,Inkay,,
|
||||||
|
109,Malamar,,
|
||||||
|
110,Wishiwashi,,
|
||||||
|
111,Carvanha,,
|
||||||
|
112,Sharpedo,,
|
||||||
|
113,Lillipup,,
|
||||||
|
114,Herdier,,
|
||||||
|
115,Stoutland,,
|
||||||
|
116,Tauros,,
|
||||||
|
117,Miltank,,
|
||||||
|
118,Scyther,,
|
||||||
|
119,Scizor,,
|
||||||
|
120,Pinsir,,
|
||||||
|
121,Heracross,,
|
||||||
|
122,Dwebble,,
|
||||||
|
123,Crustle,,
|
||||||
|
124,Wimpod,,
|
||||||
|
125,Golisopod,,
|
||||||
|
126,Pincurchin,,
|
||||||
|
127,Mareanie,,
|
||||||
|
128,Toxapex,,
|
||||||
|
129,Clobbopus,,
|
||||||
|
130,Grapploct,,
|
||||||
|
131,Shellder,,
|
||||||
|
132,Cloyster,,
|
||||||
|
133,Sandygast,,
|
||||||
|
134,Palossand,,
|
||||||
|
135,Drifloon,,
|
||||||
|
136,Drifblim,,
|
||||||
|
137,Barboach,,
|
||||||
|
138,Whiscash,,
|
||||||
|
139,Azurill,,
|
||||||
|
140,Marill,,
|
||||||
|
141,Azumarill,,
|
||||||
|
142,Poliwag,,
|
||||||
|
143,Poliwhirl,,
|
||||||
|
144,Poliwrath,,
|
||||||
|
145,Politoed,,
|
||||||
|
146,Psyduck,,
|
||||||
|
147,Golduck,,
|
||||||
|
148,Whismur,,
|
||||||
|
149,Loudred,,
|
||||||
|
150,Exploud,,
|
||||||
|
151,Woobat,,
|
||||||
|
152,Swoobat,,
|
||||||
|
153,Skarmory,,
|
||||||
|
154,Roggenrola,,
|
||||||
|
155,Boldore,,
|
||||||
|
156,Gigalith,,
|
||||||
|
157,Rockruff,,
|
||||||
|
158,Lycanroc,,
|
||||||
|
159,Salandit,,
|
||||||
|
160,Salazzle,,
|
||||||
|
161,Scraggy,,
|
||||||
|
162,Scrafty,,
|
||||||
|
163,Mienfoo,,
|
||||||
|
164,Mienshao,,
|
||||||
|
165,Jangmo-o,,
|
||||||
|
166,Hakamo-o,,
|
||||||
|
167,Kommo-o,,
|
||||||
|
168,Sandshrew,,
|
||||||
|
169,Sandslash,,
|
||||||
|
170,Cubone,,
|
||||||
|
171,Marowak,,
|
||||||
|
172,Kangaskhan,,
|
||||||
|
173,Torkoal,,
|
||||||
|
174,Silicobra,,
|
||||||
|
175,Sandaconda,,
|
||||||
|
176,Sandile,,
|
||||||
|
177,Krokorok,,
|
||||||
|
178,Krookodile,,
|
||||||
|
179,Rufflet,,
|
||||||
|
180,Braviary,,
|
||||||
|
181,Vullaby,,
|
||||||
|
182,Mandibuzz,,
|
||||||
|
183,Rhyhorn,,
|
||||||
|
184,Rhydon,,
|
||||||
|
185,Rhyperior,,
|
||||||
|
186,Larvesta,,
|
||||||
|
187,Volcarona,,
|
||||||
|
188,Chinchou,,
|
||||||
|
189,Lanturn,,
|
||||||
|
190,Wailmer,,
|
||||||
|
191,Wailord,,
|
||||||
|
192,Frillish,,
|
||||||
|
193,Jellicent,,
|
||||||
|
194,Skrelp,,
|
||||||
|
195,Dragalge,,
|
||||||
|
196,Clauncher,,
|
||||||
|
197,Clawitzer,,
|
||||||
|
198,Horsea,,
|
||||||
|
199,Seadra,,
|
||||||
|
200,Kingdra,,
|
||||||
|
201,Petilil,,
|
||||||
|
202,Lilligant,,
|
||||||
|
203,Combee,,
|
||||||
|
204,Vespiquen,,
|
||||||
|
205,Exeggcute,,
|
||||||
|
206,Exeggutor,,
|
||||||
|
207,Ditto,,
|
||||||
|
208,Porygon,,
|
||||||
|
209,Porygon2,,
|
||||||
|
210,Porygon-Z,,
|
||||||
|
211,Zarude,,
|
||||||
|
@@ -0,0 +1,401 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Grookey,,
|
||||||
|
2,Thwackey,,
|
||||||
|
3,Rillaboom,,
|
||||||
|
4,Scorbunny,,
|
||||||
|
5,Raboot,,
|
||||||
|
6,Cinderace,,
|
||||||
|
7,Sobble,,
|
||||||
|
8,Drizzile,,
|
||||||
|
9,Inteleon,,
|
||||||
|
10,Blipbug,,
|
||||||
|
11,Dottler,,
|
||||||
|
12,Orbeetle,,
|
||||||
|
13,Caterpie,,
|
||||||
|
14,Metapod,,
|
||||||
|
15,Butterfree,,
|
||||||
|
16,Grubbin,,
|
||||||
|
17,Charjabug,,
|
||||||
|
18,Vikavolt,,
|
||||||
|
19,Hoothoot,,
|
||||||
|
20,Noctowl,,
|
||||||
|
21,Rookidee,,
|
||||||
|
22,Corvisquire,,
|
||||||
|
23,Corviknight,,
|
||||||
|
24,Skwovet,,
|
||||||
|
25,Greedent,,
|
||||||
|
26,Pidove,,
|
||||||
|
27,Tranquill,,
|
||||||
|
28,Unfezant,,
|
||||||
|
29,Nickit,,
|
||||||
|
30,Thievul,,
|
||||||
|
31,Zigzagoon,,
|
||||||
|
32,Linoone,,
|
||||||
|
33,Obstagoon,,
|
||||||
|
34,Wooloo,,
|
||||||
|
35,Dubwool,,
|
||||||
|
36,Lotad,,
|
||||||
|
37,Lombre,,
|
||||||
|
38,Ludicolo,,
|
||||||
|
39,Seedot,,
|
||||||
|
40,Nuzleaf,,
|
||||||
|
41,Shiftry,,
|
||||||
|
42,Chewtle,,
|
||||||
|
43,Drednaw,,
|
||||||
|
44,Purrloin,,
|
||||||
|
45,Liepard,,
|
||||||
|
46,Yamper,,
|
||||||
|
47,Boltund,,
|
||||||
|
48,Bunnelby,,
|
||||||
|
49,Diggersby,,
|
||||||
|
50,Minccino,,
|
||||||
|
51,Cinccino,,
|
||||||
|
52,Bounsweet,,
|
||||||
|
53,Steenee,,
|
||||||
|
54,Tsareena,,
|
||||||
|
55,Oddish,,
|
||||||
|
56,Gloom,,
|
||||||
|
57,Vileplume,,
|
||||||
|
58,Bellossom,,
|
||||||
|
59,Budew,,
|
||||||
|
60,Roselia,,
|
||||||
|
61,Roserade,,
|
||||||
|
62,Wingull,,
|
||||||
|
63,Pelipper,,
|
||||||
|
64,Joltik,,
|
||||||
|
65,Galvantula,,
|
||||||
|
66,Electrike,,
|
||||||
|
67,Manectric,,
|
||||||
|
68,Vulpix,,
|
||||||
|
69,Ninetales,,
|
||||||
|
70,Growlithe,,
|
||||||
|
71,Arcanine,,
|
||||||
|
72,Vanillite,,
|
||||||
|
73,Vanillish,,
|
||||||
|
74,Vanilluxe,,
|
||||||
|
75,Swinub,,
|
||||||
|
76,Piloswine,,
|
||||||
|
77,Mamoswine,,
|
||||||
|
78,Delibird,,
|
||||||
|
79,Snorunt,,
|
||||||
|
80,Glalie,,
|
||||||
|
81,Froslass,,
|
||||||
|
82,Baltoy,,
|
||||||
|
83,Claydol,,
|
||||||
|
84,Mudbray,,
|
||||||
|
85,Mudsdale,,
|
||||||
|
86,Dwebble,,
|
||||||
|
87,Crustle,,
|
||||||
|
88,Golett,,
|
||||||
|
89,Golurk,,
|
||||||
|
90,Munna,,
|
||||||
|
91,Musharna,,
|
||||||
|
92,Natu,,
|
||||||
|
93,Xatu,,
|
||||||
|
94,Stufful,,
|
||||||
|
95,Bewear,,
|
||||||
|
96,Snover,,
|
||||||
|
97,Abomasnow,,
|
||||||
|
98,Krabby,,
|
||||||
|
99,Kingler,,
|
||||||
|
100,Wooper,,
|
||||||
|
101,Quagsire,,
|
||||||
|
102,Corphish,,
|
||||||
|
103,Crawdaunt,,
|
||||||
|
104,Nincada,,
|
||||||
|
105,Ninjask,,
|
||||||
|
106,Shedinja,,
|
||||||
|
107,Tyrogue,,
|
||||||
|
108,Hitmonlee,,
|
||||||
|
109,Hitmonchan,,
|
||||||
|
110,Hitmontop,,
|
||||||
|
111,Pancham,,
|
||||||
|
112,Pangoro,,
|
||||||
|
113,Klink,,
|
||||||
|
114,Klang,,
|
||||||
|
115,Klinklang,,
|
||||||
|
116,Combee,,
|
||||||
|
117,Vespiquen,,
|
||||||
|
118,Bronzor,,
|
||||||
|
119,Bronzong,,
|
||||||
|
120,Ralts,,
|
||||||
|
121,Kirlia,,
|
||||||
|
122,Gardevoir,,
|
||||||
|
123,Gallade,,
|
||||||
|
124,Drifloon,,
|
||||||
|
125,Drifblim,,
|
||||||
|
126,Gossifleur,,
|
||||||
|
127,Eldegoss,,
|
||||||
|
128,Cherubi,,
|
||||||
|
129,Cherrim,,
|
||||||
|
130,Stunky,,
|
||||||
|
131,Skuntank,,
|
||||||
|
132,Tympole,,
|
||||||
|
133,Palpitoad,,
|
||||||
|
134,Seismitoad,,
|
||||||
|
135,Duskull,,
|
||||||
|
136,Dusclops,,
|
||||||
|
137,Dusknoir,,
|
||||||
|
138,Machop,,
|
||||||
|
139,Machoke,,
|
||||||
|
140,Machamp,,
|
||||||
|
141,Gastly,,
|
||||||
|
142,Haunter,,
|
||||||
|
143,Gengar,,
|
||||||
|
144,Magikarp,,
|
||||||
|
145,Gyarados,,
|
||||||
|
146,Goldeen,,
|
||||||
|
147,Seaking,,
|
||||||
|
148,Remoraid,,
|
||||||
|
149,Octillery,,
|
||||||
|
150,Shellder,,
|
||||||
|
151,Cloyster,,
|
||||||
|
152,Feebas,,
|
||||||
|
153,Milotic,,
|
||||||
|
154,Basculin,,
|
||||||
|
155,Wishiwashi,,
|
||||||
|
156,Pyukumuku,,
|
||||||
|
157,Trubbish,,
|
||||||
|
158,Garbodor,,
|
||||||
|
159,Sizzlipede,,
|
||||||
|
160,Centiskorch,,
|
||||||
|
161,Rolycoly,,
|
||||||
|
162,Carkol,,
|
||||||
|
163,Coalossal,,
|
||||||
|
164,Diglett,,
|
||||||
|
165,Dugtrio,,
|
||||||
|
166,Drilbur,,
|
||||||
|
167,Excadrill,,
|
||||||
|
168,Roggenrola,,
|
||||||
|
169,Boldore,,
|
||||||
|
170,Gigalith,,
|
||||||
|
171,Timburr,,
|
||||||
|
172,Gurdurr,,
|
||||||
|
173,Conkeldurr,,
|
||||||
|
174,Woobat,,
|
||||||
|
175,Swoobat,,
|
||||||
|
176,Noibat,,
|
||||||
|
177,Noivern,,
|
||||||
|
178,Onix,,
|
||||||
|
179,Steelix,,
|
||||||
|
180,Arrokuda,,
|
||||||
|
181,Barraskewda,,
|
||||||
|
182,Meowth,,
|
||||||
|
183,Perrserker,,
|
||||||
|
184,Persian,,
|
||||||
|
185,Milcery,,
|
||||||
|
186,Alcremie,,
|
||||||
|
187,Cutiefly,,
|
||||||
|
188,Ribombee,,
|
||||||
|
189,Ferroseed,,
|
||||||
|
190,Ferrothorn,,
|
||||||
|
191,Pumpkaboo,,
|
||||||
|
192,Gourgeist,,
|
||||||
|
193,Pichu,,
|
||||||
|
194,Pikachu,,
|
||||||
|
195,Raichu,,
|
||||||
|
196,Eevee,,
|
||||||
|
197,Vaporeon,,
|
||||||
|
198,Jolteon,,
|
||||||
|
199,Flareon,,
|
||||||
|
200,Espeon,,
|
||||||
|
201,Umbreon,,
|
||||||
|
202,Leafeon,,
|
||||||
|
203,Glaceon,,
|
||||||
|
204,Sylveon,,
|
||||||
|
205,Applin,,
|
||||||
|
206,Flapple,,
|
||||||
|
207,Appletun,,
|
||||||
|
208,Espurr,,
|
||||||
|
209,Meowstic,,
|
||||||
|
210,Swirlix,,
|
||||||
|
211,Slurpuff,,
|
||||||
|
212,Spritzee,,
|
||||||
|
213,Aromatisse,,
|
||||||
|
214,Dewpider,,
|
||||||
|
215,Araquanid,,
|
||||||
|
216,Wynaut,,
|
||||||
|
217,Wobbuffet,,
|
||||||
|
218,Farfetch’d,,
|
||||||
|
219,Sirfetch’d,,
|
||||||
|
220,Chinchou,,
|
||||||
|
221,Lanturn,,
|
||||||
|
222,Croagunk,,
|
||||||
|
223,Toxicroak,,
|
||||||
|
224,Scraggy,,
|
||||||
|
225,Scrafty,,
|
||||||
|
226,Stunfisk,,
|
||||||
|
227,Shuckle,,
|
||||||
|
228,Barboach,,
|
||||||
|
229,Whiscash,,
|
||||||
|
230,Shellos,,
|
||||||
|
231,Gastrodon,,
|
||||||
|
232,Wimpod,,
|
||||||
|
233,Golisopod,,
|
||||||
|
234,Binacle,,
|
||||||
|
235,Barbaracle,,
|
||||||
|
236,Corsola,,
|
||||||
|
237,Cursola,,
|
||||||
|
238,Impidimp,,
|
||||||
|
239,Morgrem,,
|
||||||
|
240,Grimmsnarl,,
|
||||||
|
241,Hatenna,,
|
||||||
|
242,Hattrem,,
|
||||||
|
243,Hatterene,,
|
||||||
|
244,Salandit,,
|
||||||
|
245,Salazzle,,
|
||||||
|
246,Pawniard,,
|
||||||
|
247,Bisharp,,
|
||||||
|
248,Throh,,
|
||||||
|
249,Sawk,,
|
||||||
|
250,Koffing,,
|
||||||
|
251,Weezing,,
|
||||||
|
252,Bonsly,,
|
||||||
|
253,Sudowoodo,,
|
||||||
|
254,Cleffa,,
|
||||||
|
255,Clefairy,,
|
||||||
|
256,Clefable,,
|
||||||
|
257,Togepi,,
|
||||||
|
258,Togetic,,
|
||||||
|
259,Togekiss,,
|
||||||
|
260,Munchlax,,
|
||||||
|
261,Snorlax,,
|
||||||
|
262,Cottonee,,
|
||||||
|
263,Whimsicott,,
|
||||||
|
264,Rhyhorn,,
|
||||||
|
265,Rhydon,,
|
||||||
|
266,Rhyperior,,
|
||||||
|
267,Gothita,,
|
||||||
|
268,Gothorita,,
|
||||||
|
269,Gothitelle,,
|
||||||
|
270,Solosis,,
|
||||||
|
271,Duosion,,
|
||||||
|
272,Reuniclus,,
|
||||||
|
273,Karrablast,,
|
||||||
|
274,Escavalier,,
|
||||||
|
275,Shelmet,,
|
||||||
|
276,Accelgor,,
|
||||||
|
277,Elgyem,,
|
||||||
|
278,Beheeyem,,
|
||||||
|
279,Cubchoo,,
|
||||||
|
280,Beartic,,
|
||||||
|
281,Rufflet,,
|
||||||
|
282,Braviary,,
|
||||||
|
283,Vullaby,,
|
||||||
|
284,Mandibuzz,,
|
||||||
|
285,Skorupi,,
|
||||||
|
286,Drapion,,
|
||||||
|
287,Litwick,,
|
||||||
|
288,Lampent,,
|
||||||
|
289,Chandelure,,
|
||||||
|
290,Inkay,,
|
||||||
|
291,Malamar,,
|
||||||
|
292,Sneasel,,
|
||||||
|
293,Weavile,,
|
||||||
|
294,Sableye,,
|
||||||
|
295,Mawile,,
|
||||||
|
296,Maractus,,
|
||||||
|
297,Sigilyph,,
|
||||||
|
298,Riolu,,
|
||||||
|
299,Lucario,,
|
||||||
|
300,Torkoal,,
|
||||||
|
301,Mimikyu,,
|
||||||
|
302,Cufant,,
|
||||||
|
303,Copperajah,,
|
||||||
|
304,Qwilfish,,
|
||||||
|
305,Frillish,,
|
||||||
|
306,Jellicent,,
|
||||||
|
307,Mareanie,,
|
||||||
|
308,Toxapex,,
|
||||||
|
309,Cramorant,,
|
||||||
|
310,Toxel,,
|
||||||
|
311,Toxtricity,,
|
||||||
|
312,Silicobra,,
|
||||||
|
313,Sandaconda,,
|
||||||
|
314,Hippopotas,,
|
||||||
|
315,Hippowdon,,
|
||||||
|
316,Durant,,
|
||||||
|
317,Heatmor,,
|
||||||
|
318,Helioptile,,
|
||||||
|
319,Heliolisk,,
|
||||||
|
320,Hawlucha,,
|
||||||
|
321,Trapinch,,
|
||||||
|
322,Vibrava,,
|
||||||
|
323,Flygon,,
|
||||||
|
324,Axew,,
|
||||||
|
325,Fraxure,,
|
||||||
|
326,Haxorus,,
|
||||||
|
327,Yamask,,
|
||||||
|
328,Runerigus,,
|
||||||
|
329,Cofagrigus,,
|
||||||
|
330,Honedge,,
|
||||||
|
331,Doublade,,
|
||||||
|
332,Aegislash,,
|
||||||
|
333,Ponyta,,
|
||||||
|
334,Rapidash,,
|
||||||
|
335,Sinistea,,
|
||||||
|
336,Polteageist,,
|
||||||
|
337,Indeedee,,
|
||||||
|
338,Phantump,,
|
||||||
|
339,Trevenant,,
|
||||||
|
340,Morelull,,
|
||||||
|
341,Shiinotic,,
|
||||||
|
342,Oranguru,,
|
||||||
|
343,Passimian,,
|
||||||
|
344,Morpeko,,
|
||||||
|
345,Falinks,,
|
||||||
|
346,Drampa,,
|
||||||
|
347,Turtonator,,
|
||||||
|
348,Togedemaru,,
|
||||||
|
349,Snom,,
|
||||||
|
350,Frosmoth,,
|
||||||
|
351,Clobbopus,,
|
||||||
|
352,Grapploct,,
|
||||||
|
353,Pincurchin,,
|
||||||
|
354,Mantyke,,
|
||||||
|
355,Mantine,,
|
||||||
|
356,Wailmer,,
|
||||||
|
357,Wailord,,
|
||||||
|
358,Bergmite,,
|
||||||
|
359,Avalugg,,
|
||||||
|
360,Dhelmise,,
|
||||||
|
361,Lapras,,
|
||||||
|
362,Lunatone,,
|
||||||
|
363,Solrock,,
|
||||||
|
364,Mime Jr.,,
|
||||||
|
365,Mr. Mime,,
|
||||||
|
366,Mr. Rime,,
|
||||||
|
367,Darumaka,,
|
||||||
|
368,Darmanitan,,
|
||||||
|
369,Stonjourner,,
|
||||||
|
370,Eiscue,,
|
||||||
|
371,Duraludon,,
|
||||||
|
372,Rotom,,
|
||||||
|
373,Ditto,,
|
||||||
|
374,Dracozolt,,
|
||||||
|
375,Arctozolt,,
|
||||||
|
376,Dracovish,,
|
||||||
|
377,Arctovish,,
|
||||||
|
378,Charmander,,
|
||||||
|
379,Charmeleon,,
|
||||||
|
380,Charizard,,
|
||||||
|
381,Type: Null,,
|
||||||
|
382,Silvally,,
|
||||||
|
383,Larvitar,,
|
||||||
|
384,Pupitar,,
|
||||||
|
385,Tyranitar,,
|
||||||
|
386,Deino,,
|
||||||
|
387,Zweilous,,
|
||||||
|
388,Hydreigon,,
|
||||||
|
389,Goomy,,
|
||||||
|
390,Sliggoo,,
|
||||||
|
391,Goodra,,
|
||||||
|
392,Jangmo-o,,
|
||||||
|
393,Hakamo-o,,
|
||||||
|
394,Kommo-o,,
|
||||||
|
395,Dreepy,,
|
||||||
|
396,Drakloak,,
|
||||||
|
397,Dragapult,,
|
||||||
|
398,Zacian,,
|
||||||
|
399,Zamazenta,,
|
||||||
|
400,Eternatus,,
|
||||||
|
@@ -0,0 +1,161 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Caterpie,,
|
||||||
|
9,Metapod,,
|
||||||
|
10,Butterfree,,
|
||||||
|
11,Grubbin,,
|
||||||
|
12,Charjabug,,
|
||||||
|
13,Vikavolt,,
|
||||||
|
14,Bonsly,,
|
||||||
|
15,Sudowoodo,,
|
||||||
|
16,Happiny,,
|
||||||
|
17,Chansey,,
|
||||||
|
18,Blissey,,
|
||||||
|
19,Wingull,,
|
||||||
|
20,Pelipper,,
|
||||||
|
21,Crabrawler,,
|
||||||
|
22,Crabominable,,
|
||||||
|
23,Gastly,,
|
||||||
|
24,Haunter,,
|
||||||
|
25,Gengar,,
|
||||||
|
26,Zubat,,
|
||||||
|
27,Golbat,,
|
||||||
|
28,Crobat,,
|
||||||
|
29,Diglett,,
|
||||||
|
30,Dugtrio,,
|
||||||
|
31,Oricorio,,
|
||||||
|
32,Psyduck,,
|
||||||
|
33,Golduck,,
|
||||||
|
34,Magikarp,,
|
||||||
|
35,Gyarados,,
|
||||||
|
36,Barboach,,
|
||||||
|
37,Whiscash,,
|
||||||
|
38,Tentacool,,
|
||||||
|
39,Tentacruel,,
|
||||||
|
40,Finneon,,
|
||||||
|
41,Lumineon,,
|
||||||
|
42,Wishiwashi,,
|
||||||
|
43,Luvdisc,,
|
||||||
|
44,Corsola,,
|
||||||
|
45,Mareanie,,
|
||||||
|
46,Toxapex,,
|
||||||
|
47,Remoraid,,
|
||||||
|
48,Octillery,,
|
||||||
|
49,Mantyke,,
|
||||||
|
50,Mantine,,
|
||||||
|
51,Lillipup,,
|
||||||
|
52,Herdier,,
|
||||||
|
53,Stoutland,,
|
||||||
|
54,Eevee,,
|
||||||
|
55,Vaporeon,,
|
||||||
|
56,Jolteon,,
|
||||||
|
57,Flareon,,
|
||||||
|
58,Espeon,,
|
||||||
|
59,Umbreon,,
|
||||||
|
60,Leafeon,,
|
||||||
|
61,Glaceon,,
|
||||||
|
62,Sylveon,,
|
||||||
|
63,Mareep,,
|
||||||
|
64,Flaaffy,,
|
||||||
|
65,Ampharos,,
|
||||||
|
66,Mudbray,,
|
||||||
|
67,Mudsdale,,
|
||||||
|
68,Igglybuff,,
|
||||||
|
69,Jigglypuff,,
|
||||||
|
70,Wigglytuff,,
|
||||||
|
71,Tauros,,
|
||||||
|
72,Miltank,,
|
||||||
|
73,Surskit,,
|
||||||
|
74,Masquerain,,
|
||||||
|
75,Dewpider,,
|
||||||
|
76,Araquanid,,
|
||||||
|
77,Fomantis,,
|
||||||
|
78,Lurantis,,
|
||||||
|
79,Morelull,,
|
||||||
|
80,Shiinotic,,
|
||||||
|
81,Paras,,
|
||||||
|
82,Parasect,,
|
||||||
|
83,Poliwag,,
|
||||||
|
84,Poliwhirl,,
|
||||||
|
85,Poliwrath,,
|
||||||
|
86,Politoed,,
|
||||||
|
87,Goldeen,,
|
||||||
|
88,Seaking,,
|
||||||
|
89,Basculin,,
|
||||||
|
90,Feebas,,
|
||||||
|
91,Milotic,,
|
||||||
|
92,Alomomola,,
|
||||||
|
93,Fletchling,,
|
||||||
|
94,Fletchinder,,
|
||||||
|
95,Talonflame,,
|
||||||
|
96,Salandit,,
|
||||||
|
97,Salazzle,,
|
||||||
|
98,Cubone,,
|
||||||
|
99,Marowak,,
|
||||||
|
100,Kangaskhan,,
|
||||||
|
101,Magby,,
|
||||||
|
102,Magmar,,
|
||||||
|
103,Magmortar,,
|
||||||
|
104,Larvesta,,
|
||||||
|
105,Volcarona,,
|
||||||
|
106,Stufful,,
|
||||||
|
107,Bewear,,
|
||||||
|
108,Bounsweet,,
|
||||||
|
109,Steenee,,
|
||||||
|
110,Tsareena,,
|
||||||
|
111,Comfey,,
|
||||||
|
112,Pinsir,,
|
||||||
|
113,Hoothoot,,
|
||||||
|
114,Noctowl,,
|
||||||
|
115,Kecleon,,
|
||||||
|
116,Oranguru,,
|
||||||
|
117,Passimian,,
|
||||||
|
118,Goomy,,
|
||||||
|
119,Sliggoo,,
|
||||||
|
120,Goodra,,
|
||||||
|
121,Castform,,
|
||||||
|
122,Wimpod,,
|
||||||
|
123,Golisopod,,
|
||||||
|
124,Staryu,,
|
||||||
|
125,Starmie,,
|
||||||
|
126,Sandygast,,
|
||||||
|
127,Palossand,,
|
||||||
|
128,Omanyte,,
|
||||||
|
129,Omastar,,
|
||||||
|
130,Kabuto,,
|
||||||
|
131,Kabutops,,
|
||||||
|
132,Lileep,,
|
||||||
|
133,Cradily,,
|
||||||
|
134,Anorith,,
|
||||||
|
135,Armaldo,,
|
||||||
|
136,Cranidos,,
|
||||||
|
137,Rampardos,,
|
||||||
|
138,Shieldon,,
|
||||||
|
139,Bastiodon,,
|
||||||
|
140,Archen,,
|
||||||
|
141,Archeops,,
|
||||||
|
142,Tirtouga,,
|
||||||
|
143,Carracosta,,
|
||||||
|
144,Tyrunt,,
|
||||||
|
145,Tyrantrum,,
|
||||||
|
146,Amaura,,
|
||||||
|
147,Aurorus,,
|
||||||
|
148,Larvitar,,
|
||||||
|
149,Pupitar,,
|
||||||
|
150,Tyranitar,,
|
||||||
|
151,Phantump,,
|
||||||
|
152,Trevenant,,
|
||||||
|
153,Natu,,
|
||||||
|
154,Xatu,,
|
||||||
|
155,Nosepass,,
|
||||||
|
156,Probopass,,
|
||||||
|
157,Pyukumuku,,
|
||||||
|
158,Chinchou,,
|
||||||
|
159,Lanturn,,
|
||||||
|
160,Tapu Lele,,
|
||||||
|
@@ -0,0 +1,404 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Rowlet,,
|
||||||
|
2,Dartrix,,
|
||||||
|
3,Decidueye,,
|
||||||
|
4,Litten,,
|
||||||
|
5,Torracat,,
|
||||||
|
6,Incineroar,,
|
||||||
|
7,Popplio,,
|
||||||
|
8,Brionne,,
|
||||||
|
9,Primarina,,
|
||||||
|
10,Pikipek,,
|
||||||
|
11,Trumbeak,,
|
||||||
|
12,Toucannon,,
|
||||||
|
13,Yungoos,,
|
||||||
|
14,Gumshoos,,
|
||||||
|
15,Rattata,,
|
||||||
|
16,Raticate,,
|
||||||
|
17,Caterpie,,
|
||||||
|
18,Metapod,,
|
||||||
|
19,Butterfree,,
|
||||||
|
20,Ledyba,,
|
||||||
|
21,Ledian,,
|
||||||
|
22,Spinarak,,
|
||||||
|
23,Ariados,,
|
||||||
|
24,Buneary,,
|
||||||
|
25,Lopunny,,
|
||||||
|
26,Inkay,,
|
||||||
|
27,Malamar,,
|
||||||
|
28,Zorua,,
|
||||||
|
29,Zoroark,,
|
||||||
|
30,Furfrou,,
|
||||||
|
31,Pichu,,
|
||||||
|
32,Pikachu,,
|
||||||
|
33,Raichu,,
|
||||||
|
34,Grubbin,,
|
||||||
|
35,Charjabug,,
|
||||||
|
36,Vikavolt,,
|
||||||
|
37,Bonsly,,
|
||||||
|
38,Sudowoodo,,
|
||||||
|
39,Happiny,,
|
||||||
|
40,Chansey,,
|
||||||
|
41,Blissey,,
|
||||||
|
42,Munchlax,,
|
||||||
|
43,Snorlax,,
|
||||||
|
44,Slowpoke,,
|
||||||
|
45,Slowbro,,
|
||||||
|
46,Slowking,,
|
||||||
|
47,Wingull,,
|
||||||
|
48,Pelipper,,
|
||||||
|
49,Abra,,
|
||||||
|
50,Kadabra,,
|
||||||
|
51,Alakazam,,
|
||||||
|
52,Meowth,,
|
||||||
|
53,Persian,,
|
||||||
|
54,Magnemite,,
|
||||||
|
55,Magneton,,
|
||||||
|
56,Magnezone,,
|
||||||
|
57,Grimer,,
|
||||||
|
58,Muk,,
|
||||||
|
59,Mime Jr.,,
|
||||||
|
60,Mr. Mime,,
|
||||||
|
61,Ekans,,
|
||||||
|
62,Arbok,,
|
||||||
|
63,Dunsparce,,
|
||||||
|
64,Growlithe,,
|
||||||
|
65,Arcanine,,
|
||||||
|
66,Drowzee,,
|
||||||
|
67,Hypno,,
|
||||||
|
68,Makuhita,,
|
||||||
|
69,Hariyama,,
|
||||||
|
70,Smeargle,,
|
||||||
|
71,Crabrawler,,
|
||||||
|
72,Crabominable,,
|
||||||
|
73,Gastly,,
|
||||||
|
74,Haunter,,
|
||||||
|
75,Gengar,,
|
||||||
|
76,Drifloon,,
|
||||||
|
77,Drifblim,,
|
||||||
|
78,Murkrow,,
|
||||||
|
79,Honchkrow,,
|
||||||
|
80,Zubat,,
|
||||||
|
81,Golbat,,
|
||||||
|
82,Crobat,,
|
||||||
|
83,Noibat,,
|
||||||
|
84,Noivern,,
|
||||||
|
85,Diglett,,
|
||||||
|
86,Dugtrio,,
|
||||||
|
87,Spearow,,
|
||||||
|
88,Fearow,,
|
||||||
|
89,Rufflet,,
|
||||||
|
90,Braviary,,
|
||||||
|
91,Vullaby,,
|
||||||
|
92,Mandibuzz,,
|
||||||
|
93,Mankey,,
|
||||||
|
94,Primeape,,
|
||||||
|
95,Delibird,,
|
||||||
|
96,Hawlucha,,
|
||||||
|
97,Oricorio,,
|
||||||
|
98,Cutiefly,,
|
||||||
|
99,Ribombee,,
|
||||||
|
100,Flabébé,,
|
||||||
|
101,Floette,,
|
||||||
|
102,Florges,,
|
||||||
|
103,Petilil,,
|
||||||
|
104,Lilligant,,
|
||||||
|
105,Cottonee,,
|
||||||
|
106,Whimsicott,,
|
||||||
|
107,Psyduck,,
|
||||||
|
108,Golduck,,
|
||||||
|
109,Smoochum,,
|
||||||
|
110,Jynx,,
|
||||||
|
111,Magikarp,,
|
||||||
|
112,Gyarados,,
|
||||||
|
113,Barboach,,
|
||||||
|
114,Whiscash,,
|
||||||
|
115,Seel,,
|
||||||
|
116,Dewgong,,
|
||||||
|
117,Machop,,
|
||||||
|
118,Machoke,,
|
||||||
|
119,Machamp,,
|
||||||
|
120,Roggenrola,,
|
||||||
|
121,Boldore,,
|
||||||
|
122,Gigalith,,
|
||||||
|
123,Carbink,,
|
||||||
|
124,Sableye,,
|
||||||
|
125,Mawile,,
|
||||||
|
126,Rockruff,,
|
||||||
|
127,Lycanroc,,
|
||||||
|
128,Spinda,,
|
||||||
|
129,Tentacool,,
|
||||||
|
130,Tentacruel,,
|
||||||
|
131,Finneon,,
|
||||||
|
132,Lumineon,,
|
||||||
|
133,Wishiwashi,,
|
||||||
|
134,Luvdisc,,
|
||||||
|
135,Corsola,,
|
||||||
|
136,Mareanie,,
|
||||||
|
137,Toxapex,,
|
||||||
|
138,Shellder,,
|
||||||
|
139,Cloyster,,
|
||||||
|
140,Clamperl,,
|
||||||
|
141,Huntail,,
|
||||||
|
142,Gorebyss,,
|
||||||
|
143,Remoraid,,
|
||||||
|
144,Octillery,,
|
||||||
|
145,Mantyke,,
|
||||||
|
146,Mantine,,
|
||||||
|
147,Bagon,,
|
||||||
|
148,Shelgon,,
|
||||||
|
149,Salamence,,
|
||||||
|
150,Lillipup,,
|
||||||
|
151,Herdier,,
|
||||||
|
152,Stoutland,,
|
||||||
|
153,Eevee,,
|
||||||
|
154,Vaporeon,,
|
||||||
|
155,Jolteon,,
|
||||||
|
156,Flareon,,
|
||||||
|
157,Espeon,,
|
||||||
|
158,Umbreon,,
|
||||||
|
159,Leafeon,,
|
||||||
|
160,Glaceon,,
|
||||||
|
161,Sylveon,,
|
||||||
|
162,Mareep,,
|
||||||
|
163,Flaaffy,,
|
||||||
|
164,Ampharos,,
|
||||||
|
165,Mudbray,,
|
||||||
|
166,Mudsdale,,
|
||||||
|
167,Igglybuff,,
|
||||||
|
168,Jigglypuff,,
|
||||||
|
169,Wigglytuff,,
|
||||||
|
170,Tauros,,
|
||||||
|
171,Miltank,,
|
||||||
|
172,Surskit,,
|
||||||
|
173,Masquerain,,
|
||||||
|
174,Dewpider,,
|
||||||
|
175,Araquanid,,
|
||||||
|
176,Fomantis,,
|
||||||
|
177,Lurantis,,
|
||||||
|
178,Morelull,,
|
||||||
|
179,Shiinotic,,
|
||||||
|
180,Paras,,
|
||||||
|
181,Parasect,,
|
||||||
|
182,Poliwag,,
|
||||||
|
183,Poliwhirl,,
|
||||||
|
184,Poliwrath,,
|
||||||
|
185,Politoed,,
|
||||||
|
186,Goldeen,,
|
||||||
|
187,Seaking,,
|
||||||
|
188,Basculin,,
|
||||||
|
189,Feebas,,
|
||||||
|
190,Milotic,,
|
||||||
|
191,Alomomola,,
|
||||||
|
192,Fletchling,,
|
||||||
|
193,Fletchinder,,
|
||||||
|
194,Talonflame,,
|
||||||
|
195,Salandit,,
|
||||||
|
196,Salazzle,,
|
||||||
|
197,Cubone,,
|
||||||
|
198,Marowak,,
|
||||||
|
199,Kangaskhan,,
|
||||||
|
200,Magby,,
|
||||||
|
201,Magmar,,
|
||||||
|
202,Magmortar,,
|
||||||
|
203,Larvesta,,
|
||||||
|
204,Volcarona,,
|
||||||
|
205,Stufful,,
|
||||||
|
206,Bewear,,
|
||||||
|
207,Bounsweet,,
|
||||||
|
208,Steenee,,
|
||||||
|
209,Tsareena,,
|
||||||
|
210,Comfey,,
|
||||||
|
211,Pinsir,,
|
||||||
|
212,Hoothoot,,
|
||||||
|
213,Noctowl,,
|
||||||
|
214,Kecleon,,
|
||||||
|
215,Oranguru,,
|
||||||
|
216,Passimian,,
|
||||||
|
217,Goomy,,
|
||||||
|
218,Sliggoo,,
|
||||||
|
219,Goodra,,
|
||||||
|
220,Castform,,
|
||||||
|
221,Wimpod,,
|
||||||
|
222,Golisopod,,
|
||||||
|
223,Staryu,,
|
||||||
|
224,Starmie,,
|
||||||
|
225,Sandygast,,
|
||||||
|
226,Palossand,,
|
||||||
|
227,Omanyte,,
|
||||||
|
228,Omastar,,
|
||||||
|
229,Kabuto,,
|
||||||
|
230,Kabutops,,
|
||||||
|
231,Lileep,,
|
||||||
|
232,Cradily,,
|
||||||
|
233,Anorith,,
|
||||||
|
234,Armaldo,,
|
||||||
|
235,Cranidos,,
|
||||||
|
236,Rampardos,,
|
||||||
|
237,Shieldon,,
|
||||||
|
238,Bastiodon,,
|
||||||
|
239,Archen,,
|
||||||
|
240,Archeops,,
|
||||||
|
241,Tirtouga,,
|
||||||
|
242,Carracosta,,
|
||||||
|
243,Tyrunt,,
|
||||||
|
244,Tyrantrum,,
|
||||||
|
245,Amaura,,
|
||||||
|
246,Aurorus,,
|
||||||
|
247,Larvitar,,
|
||||||
|
248,Pupitar,,
|
||||||
|
249,Tyranitar,,
|
||||||
|
250,Phantump,,
|
||||||
|
251,Trevenant,,
|
||||||
|
252,Natu,,
|
||||||
|
253,Xatu,,
|
||||||
|
254,Nosepass,,
|
||||||
|
255,Probopass,,
|
||||||
|
256,Pyukumuku,,
|
||||||
|
257,Chinchou,,
|
||||||
|
258,Lanturn,,
|
||||||
|
259,Type: Null,,
|
||||||
|
260,Silvally,,
|
||||||
|
261,Poipole,,
|
||||||
|
262,Naganadel,,
|
||||||
|
263,Zygarde,,
|
||||||
|
264,Trubbish,,
|
||||||
|
265,Garbodor,,
|
||||||
|
266,Minccino,,
|
||||||
|
267,Cinccino,,
|
||||||
|
268,Pineco,,
|
||||||
|
269,Forretress,,
|
||||||
|
270,Skarmory,,
|
||||||
|
271,Ditto,,
|
||||||
|
272,Cleffa,,
|
||||||
|
273,Clefairy,,
|
||||||
|
274,Clefable,,
|
||||||
|
275,Elgyem,,
|
||||||
|
276,Beheeyem,,
|
||||||
|
277,Minior,,
|
||||||
|
278,Beldum,,
|
||||||
|
279,Metang,,
|
||||||
|
280,Metagross,,
|
||||||
|
281,Porygon,,
|
||||||
|
282,Porygon2,,
|
||||||
|
283,Porygon-Z,,
|
||||||
|
284,Pancham,,
|
||||||
|
285,Pangoro,,
|
||||||
|
286,Komala,,
|
||||||
|
287,Torkoal,,
|
||||||
|
288,Turtonator,,
|
||||||
|
289,Houndour,,
|
||||||
|
290,Houndoom,,
|
||||||
|
291,Dedenne,,
|
||||||
|
292,Togedemaru,,
|
||||||
|
293,Electrike,,
|
||||||
|
294,Manectric,,
|
||||||
|
295,Elekid,,
|
||||||
|
296,Electabuzz,,
|
||||||
|
297,Electivire,,
|
||||||
|
298,Geodude,,
|
||||||
|
299,Graveler,,
|
||||||
|
300,Golem,,
|
||||||
|
301,Sandile,,
|
||||||
|
302,Krokorok,,
|
||||||
|
303,Krookodile,,
|
||||||
|
304,Trapinch,,
|
||||||
|
305,Vibrava,,
|
||||||
|
306,Flygon,,
|
||||||
|
307,Gible,,
|
||||||
|
308,Gabite,,
|
||||||
|
309,Garchomp,,
|
||||||
|
310,Baltoy,,
|
||||||
|
311,Claydol,,
|
||||||
|
312,Golett,,
|
||||||
|
313,Golurk,,
|
||||||
|
314,Klefki,,
|
||||||
|
315,Mimikyu,,
|
||||||
|
316,Shuppet,,
|
||||||
|
317,Banette,,
|
||||||
|
318,Frillish,,
|
||||||
|
319,Jellicent,,
|
||||||
|
320,Bruxish,,
|
||||||
|
321,Drampa,,
|
||||||
|
322,Absol,,
|
||||||
|
323,Snorunt,,
|
||||||
|
324,Glalie,,
|
||||||
|
325,Froslass,,
|
||||||
|
326,Sneasel,,
|
||||||
|
327,Weavile,,
|
||||||
|
328,Sandshrew,,
|
||||||
|
329,Sandslash,,
|
||||||
|
330,Vulpix,,
|
||||||
|
331,Ninetales,,
|
||||||
|
332,Vanillite,,
|
||||||
|
333,Vanillish,,
|
||||||
|
334,Vanilluxe,,
|
||||||
|
335,Scraggy,,
|
||||||
|
336,Scrafty,,
|
||||||
|
337,Pawniard,,
|
||||||
|
338,Bisharp,,
|
||||||
|
339,Snubbull,,
|
||||||
|
340,Granbull,,
|
||||||
|
341,Shellos,,
|
||||||
|
342,Gastrodon,,
|
||||||
|
343,Relicanth,,
|
||||||
|
344,Dhelmise,,
|
||||||
|
345,Carvanha,,
|
||||||
|
346,Sharpedo,,
|
||||||
|
347,Skrelp,,
|
||||||
|
348,Dragalge,,
|
||||||
|
349,Clauncher,,
|
||||||
|
350,Clawitzer,,
|
||||||
|
351,Wailmer,,
|
||||||
|
352,Wailord,,
|
||||||
|
353,Lapras,,
|
||||||
|
354,Tropius,,
|
||||||
|
355,Exeggcute,,
|
||||||
|
356,Exeggutor,,
|
||||||
|
357,Corphish,,
|
||||||
|
358,Crawdaunt,,
|
||||||
|
359,Mienfoo,,
|
||||||
|
360,Mienshao,,
|
||||||
|
361,Jangmo-o,,
|
||||||
|
362,Hakamo-o,,
|
||||||
|
363,Kommo-o,,
|
||||||
|
364,Emolga,,
|
||||||
|
365,Scyther,,
|
||||||
|
366,Scizor,,
|
||||||
|
367,Heracross,,
|
||||||
|
368,Aipom,,
|
||||||
|
369,Ambipom,,
|
||||||
|
370,Litleo,,
|
||||||
|
371,Pyroar,,
|
||||||
|
372,Misdreavus,,
|
||||||
|
373,Mismagius,,
|
||||||
|
374,Druddigon,,
|
||||||
|
375,Lickitung,,
|
||||||
|
376,Lickilicky,,
|
||||||
|
377,Riolu,,
|
||||||
|
378,Lucario,,
|
||||||
|
379,Dratini,,
|
||||||
|
380,Dragonair,,
|
||||||
|
381,Dragonite,,
|
||||||
|
382,Aerodactyl,,
|
||||||
|
383,Tapu Koko,,
|
||||||
|
384,Tapu Lele,,
|
||||||
|
385,Tapu Bulu,,
|
||||||
|
386,Tapu Fini,,
|
||||||
|
387,Cosmog,,
|
||||||
|
388,Cosmoem,,
|
||||||
|
389,Solgaleo,,
|
||||||
|
390,Lunala,,
|
||||||
|
391,Stakataka,,
|
||||||
|
392,Blacephalon,,
|
||||||
|
393,Nihilego,,
|
||||||
|
394,Buzzwole,,
|
||||||
|
395,Pheromosa,,
|
||||||
|
396,Xurkitree,,
|
||||||
|
397,Celesteela,,
|
||||||
|
398,Kartana,,
|
||||||
|
399,Guzzlord,,
|
||||||
|
400,Necrozma,,
|
||||||
|
401,Magearna,,
|
||||||
|
402,Marshadow,,
|
||||||
|
403,Zeraora,,
|
||||||
|
@@ -0,0 +1,151 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Rowlet,,
|
||||||
|
2,Dartrix,,
|
||||||
|
3,Decidueye,,
|
||||||
|
4,Litten,,
|
||||||
|
5,Torracat,,
|
||||||
|
6,Incineroar,,
|
||||||
|
7,Popplio,,
|
||||||
|
8,Brionne,,
|
||||||
|
9,Primarina,,
|
||||||
|
10,Pikipek,,
|
||||||
|
11,Trumbeak,,
|
||||||
|
12,Toucannon,,
|
||||||
|
13,Yungoos,,
|
||||||
|
14,Gumshoos,,
|
||||||
|
15,Rattata,,
|
||||||
|
16,Raticate,,
|
||||||
|
17,Caterpie,,
|
||||||
|
18,Metapod,,
|
||||||
|
19,Butterfree,,
|
||||||
|
20,Ledyba,,
|
||||||
|
21,Ledian,,
|
||||||
|
22,Spinarak,,
|
||||||
|
23,Ariados,,
|
||||||
|
24,Buneary,,
|
||||||
|
25,Lopunny,,
|
||||||
|
26,Inkay,,
|
||||||
|
27,Malamar,,
|
||||||
|
28,Zorua,,
|
||||||
|
29,Zoroark,,
|
||||||
|
30,Furfrou,,
|
||||||
|
31,Pichu,,
|
||||||
|
32,Pikachu,,
|
||||||
|
33,Raichu,,
|
||||||
|
34,Grubbin,,
|
||||||
|
35,Charjabug,,
|
||||||
|
36,Vikavolt,,
|
||||||
|
37,Bonsly,,
|
||||||
|
38,Sudowoodo,,
|
||||||
|
39,Happiny,,
|
||||||
|
40,Chansey,,
|
||||||
|
41,Blissey,,
|
||||||
|
42,Munchlax,,
|
||||||
|
43,Snorlax,,
|
||||||
|
44,Slowpoke,,
|
||||||
|
45,Slowbro,,
|
||||||
|
46,Slowking,,
|
||||||
|
47,Wingull,,
|
||||||
|
48,Pelipper,,
|
||||||
|
49,Abra,,
|
||||||
|
50,Kadabra,,
|
||||||
|
51,Alakazam,,
|
||||||
|
52,Meowth,,
|
||||||
|
53,Persian,,
|
||||||
|
54,Magnemite,,
|
||||||
|
55,Magneton,,
|
||||||
|
56,Magnezone,,
|
||||||
|
57,Grimer,,
|
||||||
|
58,Muk,,
|
||||||
|
59,Mime Jr.,,
|
||||||
|
60,Mr. Mime,,
|
||||||
|
61,Ekans,,
|
||||||
|
62,Arbok,,
|
||||||
|
63,Dunsparce,,
|
||||||
|
64,Growlithe,,
|
||||||
|
65,Arcanine,,
|
||||||
|
66,Drowzee,,
|
||||||
|
67,Hypno,,
|
||||||
|
68,Makuhita,,
|
||||||
|
69,Hariyama,,
|
||||||
|
70,Smeargle,,
|
||||||
|
71,Crabrawler,,
|
||||||
|
72,Crabominable,,
|
||||||
|
73,Gastly,,
|
||||||
|
74,Haunter,,
|
||||||
|
75,Gengar,,
|
||||||
|
76,Drifloon,,
|
||||||
|
77,Drifblim,,
|
||||||
|
78,Murkrow,,
|
||||||
|
79,Honchkrow,,
|
||||||
|
80,Zubat,,
|
||||||
|
81,Golbat,,
|
||||||
|
82,Crobat,,
|
||||||
|
83,Noibat,,
|
||||||
|
84,Noivern,,
|
||||||
|
85,Diglett,,
|
||||||
|
86,Dugtrio,,
|
||||||
|
87,Spearow,,
|
||||||
|
88,Fearow,,
|
||||||
|
89,Rufflet,,
|
||||||
|
90,Braviary,,
|
||||||
|
91,Vullaby,,
|
||||||
|
92,Mandibuzz,,
|
||||||
|
93,Mankey,,
|
||||||
|
94,Primeape,,
|
||||||
|
95,Delibird,,
|
||||||
|
96,Hawlucha,,
|
||||||
|
97,Oricorio,,
|
||||||
|
98,Cutiefly,,
|
||||||
|
99,Ribombee,,
|
||||||
|
100,Flabébé,,
|
||||||
|
101,Floette,,
|
||||||
|
102,Florges,,
|
||||||
|
103,Petilil,,
|
||||||
|
104,Lilligant,,
|
||||||
|
105,Cottonee,,
|
||||||
|
106,Whimsicott,,
|
||||||
|
107,Psyduck,,
|
||||||
|
108,Golduck,,
|
||||||
|
109,Smoochum,,
|
||||||
|
110,Jynx,,
|
||||||
|
111,Magikarp,,
|
||||||
|
112,Gyarados,,
|
||||||
|
113,Barboach,,
|
||||||
|
114,Whiscash,,
|
||||||
|
115,Seel,,
|
||||||
|
116,Dewgong,,
|
||||||
|
117,Machop,,
|
||||||
|
118,Machoke,,
|
||||||
|
119,Machamp,,
|
||||||
|
120,Roggenrola,,
|
||||||
|
121,Boldore,,
|
||||||
|
122,Gigalith,,
|
||||||
|
123,Carbink,,
|
||||||
|
124,Sableye,,
|
||||||
|
125,Mawile,,
|
||||||
|
126,Rockruff,,
|
||||||
|
127,Lycanroc,,
|
||||||
|
128,Spinda,,
|
||||||
|
129,Tentacool,,
|
||||||
|
130,Tentacruel,,
|
||||||
|
131,Finneon,,
|
||||||
|
132,Lumineon,,
|
||||||
|
133,Wishiwashi,,
|
||||||
|
134,Luvdisc,,
|
||||||
|
135,Corsola,,
|
||||||
|
136,Mareanie,,
|
||||||
|
137,Toxapex,,
|
||||||
|
138,Shellder,,
|
||||||
|
139,Cloyster,,
|
||||||
|
140,Clamperl,,
|
||||||
|
141,Huntail,,
|
||||||
|
142,Gorebyss,,
|
||||||
|
143,Remoraid,,
|
||||||
|
144,Octillery,,
|
||||||
|
145,Mantyke,,
|
||||||
|
146,Mantine,,
|
||||||
|
147,Bagon,,
|
||||||
|
148,Shelgon,,
|
||||||
|
149,Salamence,,
|
||||||
|
150,Tapu Koko,,
|
||||||
|
@@ -0,0 +1,131 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Buneary,,
|
||||||
|
9,Lopunny,,
|
||||||
|
10,Inkay,,
|
||||||
|
11,Malamar,,
|
||||||
|
12,Zorua,,
|
||||||
|
13,Zoroark,,
|
||||||
|
14,Furfrou,,
|
||||||
|
15,Happiny,,
|
||||||
|
16,Chansey,,
|
||||||
|
17,Blissey,,
|
||||||
|
18,Wingull,,
|
||||||
|
19,Pelipper,,
|
||||||
|
20,Drowzee,,
|
||||||
|
21,Hypno,,
|
||||||
|
22,Makuhita,,
|
||||||
|
23,Hariyama,,
|
||||||
|
24,Crabrawler,,
|
||||||
|
25,Crabominable,,
|
||||||
|
26,Zubat,,
|
||||||
|
27,Golbat,,
|
||||||
|
28,Crobat,,
|
||||||
|
29,Noibat,,
|
||||||
|
30,Noivern,,
|
||||||
|
31,Diglett,,
|
||||||
|
32,Dugtrio,,
|
||||||
|
33,Spearow,,
|
||||||
|
34,Fearow,,
|
||||||
|
35,Rufflet,,
|
||||||
|
36,Braviary,,
|
||||||
|
37,Vullaby,,
|
||||||
|
38,Mandibuzz,,
|
||||||
|
39,Mankey,,
|
||||||
|
40,Primeape,,
|
||||||
|
41,Oricorio,,
|
||||||
|
42,Cutiefly,,
|
||||||
|
43,Ribombee,,
|
||||||
|
44,Flabébé,,
|
||||||
|
45,Floette,,
|
||||||
|
46,Florges,,
|
||||||
|
47,Petilil,,
|
||||||
|
48,Lilligant,,
|
||||||
|
49,Cottonee,,
|
||||||
|
50,Whimsicott,,
|
||||||
|
51,Psyduck,,
|
||||||
|
52,Golduck,,
|
||||||
|
53,Magikarp,,
|
||||||
|
54,Gyarados,,
|
||||||
|
55,Barboach,,
|
||||||
|
56,Whiscash,,
|
||||||
|
57,Machop,,
|
||||||
|
58,Machoke,,
|
||||||
|
59,Machamp,,
|
||||||
|
60,Roggenrola,,
|
||||||
|
61,Boldore,,
|
||||||
|
62,Gigalith,,
|
||||||
|
63,Carbink,,
|
||||||
|
64,Sableye,,
|
||||||
|
65,Rockruff,,
|
||||||
|
66,Lycanroc,,
|
||||||
|
67,Tentacool,,
|
||||||
|
68,Tentacruel,,
|
||||||
|
69,Finneon,,
|
||||||
|
70,Lumineon,,
|
||||||
|
71,Mudbray,,
|
||||||
|
72,Mudsdale,,
|
||||||
|
73,Tauros,,
|
||||||
|
74,Miltank,,
|
||||||
|
75,Basculin,,
|
||||||
|
76,Stufful,,
|
||||||
|
77,Bewear,,
|
||||||
|
78,Pinsir,,
|
||||||
|
79,Goomy,,
|
||||||
|
80,Sliggoo,,
|
||||||
|
81,Goodra,,
|
||||||
|
82,Castform,,
|
||||||
|
83,Wimpod,,
|
||||||
|
84,Golisopod,,
|
||||||
|
85,Skarmory,,
|
||||||
|
86,Snubbull,,
|
||||||
|
87,Granbull,,
|
||||||
|
88,Shellos,,
|
||||||
|
89,Gastrodon,,
|
||||||
|
90,Relicanth,,
|
||||||
|
91,Dhelmise,,
|
||||||
|
92,Carvanha,,
|
||||||
|
93,Sharpedo,,
|
||||||
|
94,Skrelp,,
|
||||||
|
95,Dragalge,,
|
||||||
|
96,Clauncher,,
|
||||||
|
97,Clawitzer,,
|
||||||
|
98,Wailmer,,
|
||||||
|
99,Wailord,,
|
||||||
|
100,Lapras,,
|
||||||
|
101,Tropius,,
|
||||||
|
102,Exeggcute,,
|
||||||
|
103,Exeggutor,,
|
||||||
|
104,Corphish,,
|
||||||
|
105,Crawdaunt,,
|
||||||
|
106,Mienfoo,,
|
||||||
|
107,Mienshao,,
|
||||||
|
108,Jangmo-o,,
|
||||||
|
109,Hakamo-o,,
|
||||||
|
110,Kommo-o,,
|
||||||
|
111,Emolga,,
|
||||||
|
112,Scyther,,
|
||||||
|
113,Scizor,,
|
||||||
|
114,Heracross,,
|
||||||
|
115,Aipom,,
|
||||||
|
116,Ambipom,,
|
||||||
|
117,Litleo,,
|
||||||
|
118,Pyroar,,
|
||||||
|
119,Misdreavus,,
|
||||||
|
120,Mismagius,,
|
||||||
|
121,Druddigon,,
|
||||||
|
122,Lickitung,,
|
||||||
|
123,Lickilicky,,
|
||||||
|
124,Riolu,,
|
||||||
|
125,Lucario,,
|
||||||
|
126,Dratini,,
|
||||||
|
127,Dragonair,,
|
||||||
|
128,Dragonite,,
|
||||||
|
129,Aerodactyl,,
|
||||||
|
130,Tapu Fini,,
|
||||||
|
@@ -0,0 +1,161 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Pikipek,,
|
||||||
|
2,Trumbeak,,
|
||||||
|
3,Toucannon,,
|
||||||
|
4,Yungoos,,
|
||||||
|
5,Gumshoos,,
|
||||||
|
6,Rattata,,
|
||||||
|
7,Raticate,,
|
||||||
|
8,Ledyba,,
|
||||||
|
9,Ledian,,
|
||||||
|
10,Spinarak,,
|
||||||
|
11,Ariados,,
|
||||||
|
12,Grubbin,,
|
||||||
|
13,Charjabug,,
|
||||||
|
14,Vikavolt,,
|
||||||
|
15,Happiny,,
|
||||||
|
16,Chansey,,
|
||||||
|
17,Blissey,,
|
||||||
|
18,Slowpoke,,
|
||||||
|
19,Slowbro,,
|
||||||
|
20,Slowking,,
|
||||||
|
21,Wingull,,
|
||||||
|
22,Pelipper,,
|
||||||
|
23,Meowth,,
|
||||||
|
24,Persian,,
|
||||||
|
25,Magnemite,,
|
||||||
|
26,Magneton,,
|
||||||
|
27,Magnezone,,
|
||||||
|
28,Grimer,,
|
||||||
|
29,Muk,,
|
||||||
|
30,Crabrawler,,
|
||||||
|
31,Crabominable,,
|
||||||
|
32,Gastly,,
|
||||||
|
33,Haunter,,
|
||||||
|
34,Gengar,,
|
||||||
|
35,Zubat,,
|
||||||
|
36,Golbat,,
|
||||||
|
37,Crobat,,
|
||||||
|
38,Diglett,,
|
||||||
|
39,Dugtrio,,
|
||||||
|
40,Spearow,,
|
||||||
|
41,Fearow,,
|
||||||
|
42,Oricorio,,
|
||||||
|
43,Cutiefly,,
|
||||||
|
44,Ribombee,,
|
||||||
|
45,Flabébé,,
|
||||||
|
46,Floette,,
|
||||||
|
47,Florges,,
|
||||||
|
48,Petilil,,
|
||||||
|
49,Lilligant,,
|
||||||
|
50,Cottonee,,
|
||||||
|
51,Whimsicott,,
|
||||||
|
52,Psyduck,,
|
||||||
|
53,Golduck,,
|
||||||
|
54,Magikarp,,
|
||||||
|
55,Gyarados,,
|
||||||
|
56,Tentacool,,
|
||||||
|
57,Tentacruel,,
|
||||||
|
58,Finneon,,
|
||||||
|
59,Lumineon,,
|
||||||
|
60,Wishiwashi,,
|
||||||
|
61,Clamperl,,
|
||||||
|
62,Huntail,,
|
||||||
|
63,Gorebyss,,
|
||||||
|
64,Mudbray,,
|
||||||
|
65,Mudsdale,,
|
||||||
|
66,Surskit,,
|
||||||
|
67,Masquerain,,
|
||||||
|
68,Dewpider,,
|
||||||
|
69,Araquanid,,
|
||||||
|
70,Morelull,,
|
||||||
|
71,Shiinotic,,
|
||||||
|
72,Paras,,
|
||||||
|
73,Parasect,,
|
||||||
|
74,Poliwag,,
|
||||||
|
75,Poliwhirl,,
|
||||||
|
76,Poliwrath,,
|
||||||
|
77,Politoed,,
|
||||||
|
78,Goldeen,,
|
||||||
|
79,Seaking,,
|
||||||
|
80,Basculin,,
|
||||||
|
81,Goomy,,
|
||||||
|
82,Sliggoo,,
|
||||||
|
83,Goodra,,
|
||||||
|
84,Castform,,
|
||||||
|
85,Trubbish,,
|
||||||
|
86,Garbodor,,
|
||||||
|
87,Minccino,,
|
||||||
|
88,Cinccino,,
|
||||||
|
89,Pineco,,
|
||||||
|
90,Forretress,,
|
||||||
|
91,Skarmory,,
|
||||||
|
92,Ditto,,
|
||||||
|
93,Cleffa,,
|
||||||
|
94,Clefairy,,
|
||||||
|
95,Clefable,,
|
||||||
|
96,Elgyem,,
|
||||||
|
97,Beheeyem,,
|
||||||
|
98,Minior,,
|
||||||
|
99,Beldum,,
|
||||||
|
100,Metang,,
|
||||||
|
101,Metagross,,
|
||||||
|
102,Porygon,,
|
||||||
|
103,Porygon2,,
|
||||||
|
104,Porygon-Z,,
|
||||||
|
105,Pancham,,
|
||||||
|
106,Pangoro,,
|
||||||
|
107,Komala,,
|
||||||
|
108,Torkoal,,
|
||||||
|
109,Turtonator,,
|
||||||
|
110,Houndour,,
|
||||||
|
111,Houndoom,,
|
||||||
|
112,Dedenne,,
|
||||||
|
113,Togedemaru,,
|
||||||
|
114,Electrike,,
|
||||||
|
115,Manectric,,
|
||||||
|
116,Elekid,,
|
||||||
|
117,Electabuzz,,
|
||||||
|
118,Electivire,,
|
||||||
|
119,Geodude,,
|
||||||
|
120,Graveler,,
|
||||||
|
121,Golem,,
|
||||||
|
122,Sandile,,
|
||||||
|
123,Krokorok,,
|
||||||
|
124,Krookodile,,
|
||||||
|
125,Trapinch,,
|
||||||
|
126,Vibrava,,
|
||||||
|
127,Flygon,,
|
||||||
|
128,Gible,,
|
||||||
|
129,Gabite,,
|
||||||
|
130,Garchomp,,
|
||||||
|
131,Baltoy,,
|
||||||
|
132,Claydol,,
|
||||||
|
133,Golett,,
|
||||||
|
134,Golurk,,
|
||||||
|
135,Klefki,,
|
||||||
|
136,Mimikyu,,
|
||||||
|
137,Shuppet,,
|
||||||
|
138,Banette,,
|
||||||
|
139,Frillish,,
|
||||||
|
140,Jellicent,,
|
||||||
|
141,Bruxish,,
|
||||||
|
142,Drampa,,
|
||||||
|
143,Absol,,
|
||||||
|
144,Snorunt,,
|
||||||
|
145,Glalie,,
|
||||||
|
146,Froslass,,
|
||||||
|
147,Sneasel,,
|
||||||
|
148,Weavile,,
|
||||||
|
149,Sandshrew,,
|
||||||
|
150,Sandslash,,
|
||||||
|
151,Vulpix,,
|
||||||
|
152,Ninetales,,
|
||||||
|
153,Vanillite,,
|
||||||
|
154,Vanillish,,
|
||||||
|
155,Vanilluxe,,
|
||||||
|
156,Scraggy,,
|
||||||
|
157,Scrafty,,
|
||||||
|
158,Pawniard,,
|
||||||
|
159,Bisharp,,
|
||||||
|
160,Tapu Bulu,,
|
||||||
|
@@ -0,0 +1,154 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Chespin,,
|
||||||
|
2,Quilladin,,
|
||||||
|
3,Chesnaught,,
|
||||||
|
4,Fennekin,,
|
||||||
|
5,Braixen,,
|
||||||
|
6,Delphox,,
|
||||||
|
7,Froakie,,
|
||||||
|
8,Frogadier,,
|
||||||
|
9,Greninja,,
|
||||||
|
10,Bunnelby,,
|
||||||
|
11,Diggersby,,
|
||||||
|
12,Zigzagoon,,
|
||||||
|
13,Linoone,,
|
||||||
|
14,Fletchling,,
|
||||||
|
15,Fletchinder,,
|
||||||
|
16,Talonflame,,
|
||||||
|
17,Pidgey,,
|
||||||
|
18,Pidgeotto,,
|
||||||
|
19,Pidgeot,,
|
||||||
|
20,Scatterbug,,
|
||||||
|
21,Spewpa,,
|
||||||
|
22,Vivillon,,
|
||||||
|
23,Caterpie,,
|
||||||
|
24,Metapod,,
|
||||||
|
25,Butterfree,,
|
||||||
|
26,Weedle,,
|
||||||
|
27,Kakuna,,
|
||||||
|
28,Beedrill,,
|
||||||
|
29,Pansage,,
|
||||||
|
30,Simisage,,
|
||||||
|
31,Pansear,,
|
||||||
|
32,Simisear,,
|
||||||
|
33,Panpour,,
|
||||||
|
34,Simipour,,
|
||||||
|
35,Pichu,,
|
||||||
|
36,Pikachu,,
|
||||||
|
37,Raichu,,
|
||||||
|
38,Bidoof,,
|
||||||
|
39,Bibarel,,
|
||||||
|
40,Dunsparce,,
|
||||||
|
41,Azurill,,
|
||||||
|
42,Marill,,
|
||||||
|
43,Azumarill,,
|
||||||
|
44,Burmy,,
|
||||||
|
45,Wormadam,,
|
||||||
|
46,Mothim,,
|
||||||
|
47,Surskit,,
|
||||||
|
48,Masquerain,,
|
||||||
|
49,Magikarp,,
|
||||||
|
50,Gyarados,,
|
||||||
|
51,Corphish,,
|
||||||
|
52,Crawdaunt,,
|
||||||
|
53,Goldeen,,
|
||||||
|
54,Seaking,,
|
||||||
|
55,Carvanha,,
|
||||||
|
56,Sharpedo,,
|
||||||
|
57,Litleo,,
|
||||||
|
58,Pyroar,,
|
||||||
|
59,Psyduck,,
|
||||||
|
60,Golduck,,
|
||||||
|
61,Farfetch’d,,
|
||||||
|
62,Riolu,,
|
||||||
|
63,Lucario,,
|
||||||
|
64,Ralts,,
|
||||||
|
65,Kirlia,,
|
||||||
|
66,Gardevoir,,
|
||||||
|
67,Gallade,,
|
||||||
|
68,Flabébé,,
|
||||||
|
69,Floette,,
|
||||||
|
70,Florges,,
|
||||||
|
71,Budew,,
|
||||||
|
72,Roselia,,
|
||||||
|
73,Roserade,,
|
||||||
|
74,Ledyba,,
|
||||||
|
75,Ledian,,
|
||||||
|
76,Combee,,
|
||||||
|
77,Vespiquen,,
|
||||||
|
78,Skitty,,
|
||||||
|
79,Delcatty,,
|
||||||
|
80,Bulbasaur,,
|
||||||
|
81,Ivysaur,,
|
||||||
|
82,Venusaur,,
|
||||||
|
83,Charmander,,
|
||||||
|
84,Charmeleon,,
|
||||||
|
85,Charizard,,
|
||||||
|
86,Squirtle,,
|
||||||
|
87,Wartortle,,
|
||||||
|
88,Blastoise,,
|
||||||
|
89,Skiddo,,
|
||||||
|
90,Gogoat,,
|
||||||
|
91,Pancham,,
|
||||||
|
92,Pangoro,,
|
||||||
|
93,Furfrou,,
|
||||||
|
94,Doduo,,
|
||||||
|
95,Dodrio,,
|
||||||
|
96,Plusle,,
|
||||||
|
97,Minun,,
|
||||||
|
98,Gulpin,,
|
||||||
|
99,Swalot,,
|
||||||
|
100,Scraggy,,
|
||||||
|
101,Scrafty,,
|
||||||
|
102,Abra,,
|
||||||
|
103,Kadabra,,
|
||||||
|
104,Alakazam,,
|
||||||
|
105,Oddish,,
|
||||||
|
106,Gloom,,
|
||||||
|
107,Vileplume,,
|
||||||
|
108,Bellossom,,
|
||||||
|
109,Sentret,,
|
||||||
|
110,Furret,,
|
||||||
|
111,Nincada,,
|
||||||
|
112,Ninjask,,
|
||||||
|
113,Shedinja,,
|
||||||
|
114,Espurr,,
|
||||||
|
115,Meowstic,,
|
||||||
|
116,Kecleon,,
|
||||||
|
117,Honedge,,
|
||||||
|
118,Doublade,,
|
||||||
|
119,Aegislash,,
|
||||||
|
120,Venipede,,
|
||||||
|
121,Whirlipede,,
|
||||||
|
122,Scolipede,,
|
||||||
|
123,Audino,,
|
||||||
|
124,Smeargle,,
|
||||||
|
125,Croagunk,,
|
||||||
|
126,Toxicroak,,
|
||||||
|
127,Ducklett,,
|
||||||
|
128,Swanna,,
|
||||||
|
129,Spritzee,,
|
||||||
|
130,Aromatisse,,
|
||||||
|
131,Swirlix,,
|
||||||
|
132,Slurpuff,,
|
||||||
|
133,Volbeat,,
|
||||||
|
134,Illumise,,
|
||||||
|
135,Hoppip,,
|
||||||
|
136,Skiploom,,
|
||||||
|
137,Jumpluff,,
|
||||||
|
138,Munchlax,,
|
||||||
|
139,Snorlax,,
|
||||||
|
140,Whismur,,
|
||||||
|
141,Loudred,,
|
||||||
|
142,Exploud,,
|
||||||
|
143,Meditite,,
|
||||||
|
144,Medicham,,
|
||||||
|
145,Zubat,,
|
||||||
|
146,Golbat,,
|
||||||
|
147,Crobat,,
|
||||||
|
148,Axew,,
|
||||||
|
149,Fraxure,,
|
||||||
|
150,Haxorus,,
|
||||||
|
151,Diancie,,
|
||||||
|
152,Hoopa,,
|
||||||
|
153,Volcanion,,
|
||||||
|
@@ -0,0 +1,154 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Drifloon,,
|
||||||
|
2,Drifblim,,
|
||||||
|
3,Mienfoo,,
|
||||||
|
4,Mienshao,,
|
||||||
|
5,Zangoose,,
|
||||||
|
6,Seviper,,
|
||||||
|
7,Spoink,,
|
||||||
|
8,Grumpig,,
|
||||||
|
9,Absol,,
|
||||||
|
10,Inkay,,
|
||||||
|
11,Malamar,,
|
||||||
|
12,Lunatone,,
|
||||||
|
13,Solrock,,
|
||||||
|
14,Bagon,,
|
||||||
|
15,Shelgon,,
|
||||||
|
16,Salamence,,
|
||||||
|
17,Wingull,,
|
||||||
|
18,Pelipper,,
|
||||||
|
19,Taillow,,
|
||||||
|
20,Swellow,,
|
||||||
|
21,Binacle,,
|
||||||
|
22,Barbaracle,,
|
||||||
|
23,Dwebble,,
|
||||||
|
24,Crustle,,
|
||||||
|
25,Tentacool,,
|
||||||
|
26,Tentacruel,,
|
||||||
|
27,Wailmer,,
|
||||||
|
28,Wailord,,
|
||||||
|
29,Luvdisc,,
|
||||||
|
30,Skrelp,,
|
||||||
|
31,Dragalge,,
|
||||||
|
32,Clauncher,,
|
||||||
|
33,Clawitzer,,
|
||||||
|
34,Staryu,,
|
||||||
|
35,Starmie,,
|
||||||
|
36,Shellder,,
|
||||||
|
37,Cloyster,,
|
||||||
|
38,Qwilfish,,
|
||||||
|
39,Horsea,,
|
||||||
|
40,Seadra,,
|
||||||
|
41,Kingdra,,
|
||||||
|
42,Relicanth,,
|
||||||
|
43,Sandile,,
|
||||||
|
44,Krokorok,,
|
||||||
|
45,Krookodile,,
|
||||||
|
46,Helioptile,,
|
||||||
|
47,Heliolisk,,
|
||||||
|
48,Hippopotas,,
|
||||||
|
49,Hippowdon,,
|
||||||
|
50,Rhyhorn,,
|
||||||
|
51,Rhydon,,
|
||||||
|
52,Rhyperior,,
|
||||||
|
53,Onix,,
|
||||||
|
54,Steelix,,
|
||||||
|
55,Woobat,,
|
||||||
|
56,Swoobat,,
|
||||||
|
57,Machop,,
|
||||||
|
58,Machoke,,
|
||||||
|
59,Machamp,,
|
||||||
|
60,Cubone,,
|
||||||
|
61,Marowak,,
|
||||||
|
62,Kangaskhan,,
|
||||||
|
63,Mawile,,
|
||||||
|
64,Tyrunt,,
|
||||||
|
65,Tyrantrum,,
|
||||||
|
66,Amaura,,
|
||||||
|
67,Aurorus,,
|
||||||
|
68,Aerodactyl,,
|
||||||
|
69,Ferroseed,,
|
||||||
|
70,Ferrothorn,,
|
||||||
|
71,Snubbull,,
|
||||||
|
72,Granbull,,
|
||||||
|
73,Electrike,,
|
||||||
|
74,Manectric,,
|
||||||
|
75,Houndour,,
|
||||||
|
76,Houndoom,,
|
||||||
|
77,Eevee,,
|
||||||
|
78,Vaporeon,,
|
||||||
|
79,Jolteon,,
|
||||||
|
80,Flareon,,
|
||||||
|
81,Espeon,,
|
||||||
|
82,Umbreon,,
|
||||||
|
83,Leafeon,,
|
||||||
|
84,Glaceon,,
|
||||||
|
85,Sylveon,,
|
||||||
|
86,Emolga,,
|
||||||
|
87,Yanma,,
|
||||||
|
88,Yanmega,,
|
||||||
|
89,Hawlucha,,
|
||||||
|
90,Sigilyph,,
|
||||||
|
91,Golett,,
|
||||||
|
92,Golurk,,
|
||||||
|
93,Nosepass,,
|
||||||
|
94,Probopass,,
|
||||||
|
95,Makuhita,,
|
||||||
|
96,Hariyama,,
|
||||||
|
97,Throh,,
|
||||||
|
98,Sawk,,
|
||||||
|
99,Starly,,
|
||||||
|
100,Staravia,,
|
||||||
|
101,Staraptor,,
|
||||||
|
102,Stunky,,
|
||||||
|
103,Skuntank,,
|
||||||
|
104,Nidoran♀,,
|
||||||
|
105,Nidorina,,
|
||||||
|
106,Nidoqueen,,
|
||||||
|
107,Nidoran♂,,
|
||||||
|
108,Nidorino,,
|
||||||
|
109,Nidoking,,
|
||||||
|
110,Dedenne,,
|
||||||
|
111,Chingling,,
|
||||||
|
112,Chimecho,,
|
||||||
|
113,Mime Jr.,,
|
||||||
|
114,Mr. Mime,,
|
||||||
|
115,Solosis,,
|
||||||
|
116,Duosion,,
|
||||||
|
117,Reuniclus,,
|
||||||
|
118,Wynaut,,
|
||||||
|
119,Wobbuffet,,
|
||||||
|
120,Roggenrola,,
|
||||||
|
121,Boldore,,
|
||||||
|
122,Gigalith,,
|
||||||
|
123,Sableye,,
|
||||||
|
124,Carbink,,
|
||||||
|
125,Tauros,,
|
||||||
|
126,Miltank,,
|
||||||
|
127,Mareep,,
|
||||||
|
128,Flaaffy,,
|
||||||
|
129,Ampharos,,
|
||||||
|
130,Pinsir,,
|
||||||
|
131,Heracross,,
|
||||||
|
132,Pachirisu,,
|
||||||
|
133,Slowpoke,,
|
||||||
|
134,Slowbro,,
|
||||||
|
135,Slowking,,
|
||||||
|
136,Exeggcute,,
|
||||||
|
137,Exeggutor,,
|
||||||
|
138,Chatot,,
|
||||||
|
139,Mantyke,,
|
||||||
|
140,Mantine,,
|
||||||
|
141,Clamperl,,
|
||||||
|
142,Huntail,,
|
||||||
|
143,Gorebyss,,
|
||||||
|
144,Remoraid,,
|
||||||
|
145,Octillery,,
|
||||||
|
146,Corsola,,
|
||||||
|
147,Chinchou,,
|
||||||
|
148,Lanturn,,
|
||||||
|
149,Alomomola,,
|
||||||
|
150,Lapras,,
|
||||||
|
151,Articuno,,
|
||||||
|
152,Zapdos,,
|
||||||
|
153,Moltres,,
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
dexNumber,pokemon,form,notes
|
||||||
|
1,Diglett,,
|
||||||
|
2,Dugtrio,,
|
||||||
|
3,Trapinch,,
|
||||||
|
4,Vibrava,,
|
||||||
|
5,Flygon,,
|
||||||
|
6,Gible,,
|
||||||
|
7,Gabite,,
|
||||||
|
8,Garchomp,,
|
||||||
|
9,Geodude,,
|
||||||
|
10,Graveler,,
|
||||||
|
11,Golem,,
|
||||||
|
12,Slugma,,
|
||||||
|
13,Magcargo,,
|
||||||
|
14,Shuckle,,
|
||||||
|
15,Skorupi,,
|
||||||
|
16,Drapion,,
|
||||||
|
17,Wooper,,
|
||||||
|
18,Quagsire,,
|
||||||
|
19,Goomy,,
|
||||||
|
20,Sliggoo,,
|
||||||
|
21,Goodra,,
|
||||||
|
22,Karrablast,,
|
||||||
|
23,Escavalier,,
|
||||||
|
24,Shelmet,,
|
||||||
|
25,Accelgor,,
|
||||||
|
26,Bellsprout,,
|
||||||
|
27,Weepinbell,,
|
||||||
|
28,Victreebel,,
|
||||||
|
29,Carnivine,,
|
||||||
|
30,Gastly,,
|
||||||
|
31,Haunter,,
|
||||||
|
32,Gengar,,
|
||||||
|
33,Poliwag,,
|
||||||
|
34,Poliwhirl,,
|
||||||
|
35,Poliwrath,,
|
||||||
|
36,Politoed,,
|
||||||
|
37,Ekans,,
|
||||||
|
38,Arbok,,
|
||||||
|
39,Stunfisk,,
|
||||||
|
40,Barboach,,
|
||||||
|
41,Whiscash,,
|
||||||
|
42,Purrloin,,
|
||||||
|
43,Liepard,,
|
||||||
|
44,Poochyena,,
|
||||||
|
45,Mightyena,,
|
||||||
|
46,Patrat,,
|
||||||
|
47,Watchog,,
|
||||||
|
48,Pawniard,,
|
||||||
|
49,Bisharp,,
|
||||||
|
50,Klefki,,
|
||||||
|
51,Murkrow,,
|
||||||
|
52,Honchkrow,,
|
||||||
|
53,Foongus,,
|
||||||
|
54,Amoonguss,,
|
||||||
|
55,Lotad,,
|
||||||
|
56,Lombre,,
|
||||||
|
57,Ludicolo,,
|
||||||
|
58,Buizel,,
|
||||||
|
59,Floatzel,,
|
||||||
|
60,Basculin,,
|
||||||
|
61,Phantump,,
|
||||||
|
62,Trevenant,,
|
||||||
|
63,Pumpkaboo,,
|
||||||
|
64,Gourgeist,,
|
||||||
|
65,Litwick,,
|
||||||
|
66,Lampent,,
|
||||||
|
67,Chandelure,,
|
||||||
|
68,Rotom,,
|
||||||
|
69,Magnemite,,
|
||||||
|
70,Magneton,,
|
||||||
|
71,Magnezone,,
|
||||||
|
72,Voltorb,,
|
||||||
|
73,Electrode,,
|
||||||
|
74,Trubbish,,
|
||||||
|
75,Garbodor,,
|
||||||
|
76,Swinub,,
|
||||||
|
77,Piloswine,,
|
||||||
|
78,Mamoswine,,
|
||||||
|
79,Bergmite,,
|
||||||
|
80,Avalugg,,
|
||||||
|
81,Cubchoo,,
|
||||||
|
82,Beartic,,
|
||||||
|
83,Smoochum,,
|
||||||
|
84,Jynx,,
|
||||||
|
85,Vanillite,,
|
||||||
|
86,Vanillish,,
|
||||||
|
87,Vanilluxe,,
|
||||||
|
88,Snover,,
|
||||||
|
89,Abomasnow,,
|
||||||
|
90,Delibird,,
|
||||||
|
91,Sneasel,,
|
||||||
|
92,Weavile,,
|
||||||
|
93,Timburr,,
|
||||||
|
94,Gurdurr,,
|
||||||
|
95,Conkeldurr,,
|
||||||
|
96,Torkoal,,
|
||||||
|
97,Sandshrew,,
|
||||||
|
98,Sandslash,,
|
||||||
|
99,Aron,,
|
||||||
|
100,Lairon,,
|
||||||
|
101,Aggron,,
|
||||||
|
102,Larvitar,,
|
||||||
|
103,Pupitar,,
|
||||||
|
104,Tyranitar,,
|
||||||
|
105,Heatmor,,
|
||||||
|
106,Durant,,
|
||||||
|
107,Spinarak,,
|
||||||
|
108,Ariados,,
|
||||||
|
109,Spearow,,
|
||||||
|
110,Fearow,,
|
||||||
|
111,Cryogonal,,
|
||||||
|
112,Skarmory,,
|
||||||
|
113,Noibat,,
|
||||||
|
114,Noivern,,
|
||||||
|
115,Gligar,,
|
||||||
|
116,Gliscor,,
|
||||||
|
117,Hoothoot,,
|
||||||
|
118,Noctowl,,
|
||||||
|
119,Igglybuff,,
|
||||||
|
120,Jigglypuff,,
|
||||||
|
121,Wigglytuff,,
|
||||||
|
122,Shuppet,,
|
||||||
|
123,Banette,,
|
||||||
|
124,Zorua,,
|
||||||
|
125,Zoroark,,
|
||||||
|
126,Gothita,,
|
||||||
|
127,Gothorita,,
|
||||||
|
128,Gothitelle,,
|
||||||
|
129,Bonsly,,
|
||||||
|
130,Sudowoodo,,
|
||||||
|
131,Spinda,,
|
||||||
|
132,Teddiursa,,
|
||||||
|
133,Ursaring,,
|
||||||
|
134,Lickitung,,
|
||||||
|
135,Lickilicky,,
|
||||||
|
136,Scyther,,
|
||||||
|
137,Scizor,,
|
||||||
|
138,Ditto,,
|
||||||
|
139,Swablu,,
|
||||||
|
140,Altaria,,
|
||||||
|
141,Druddigon,,
|
||||||
|
142,Deino,,
|
||||||
|
143,Zweilous,,
|
||||||
|
144,Hydreigon,,
|
||||||
|
145,Dratini,,
|
||||||
|
146,Dragonair,,
|
||||||
|
147,Dragonite,,
|
||||||
|
148,Xerneas,,
|
||||||
|
149,Yveltal,,
|
||||||
|
150,Zygarde,,
|
||||||
|
151,Mewtwo,,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
|||||||
|
region,releaseOrder
|
||||||
|
Kanto,1
|
||||||
|
Johto,2
|
||||||
|
Hoenn,3
|
||||||
|
Orre,4
|
||||||
|
Sinnoh,5
|
||||||
|
Ranch,6
|
||||||
|
Unova,7
|
||||||
|
Kalos,8
|
||||||
|
Alola,9
|
||||||
|
Go,10
|
||||||
|
Galar,11
|
||||||
|
Home,12
|
||||||
|
Hisui,13
|
||||||
|
Paldea,14
|
||||||
|
+8
-2
@@ -1,3 +1,9 @@
|
|||||||
[build]
|
[build]
|
||||||
command = "npm run build"
|
command = "npm ci && ./node_modules/.bin/supabase link --project-ref $SUPABASE_PROJECT_REF && ./node_modules/.bin/supabase db push && npm run build"
|
||||||
publish = "build"
|
publish = "build"
|
||||||
|
|
||||||
|
[build.environment]
|
||||||
|
NODE_VERSION = "22"
|
||||||
|
|
||||||
|
[context.deploy-preview]
|
||||||
|
command = "npm ci && npm run build"
|
||||||
|
|||||||
Generated
+2393
-1094
File diff suppressed because it is too large
Load Diff
+41
-18
@@ -1,16 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "LivingDexTracker",
|
"name": "livingdextracker",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
"dev": "npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||||
"dev-generate": "GENERATE_SW=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
"dev-generate": "GENERATE_SW=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||||
"dev-generate-suppress-w": "GENERATE_SW=true SUPPRESS_WARNING=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
"dev-generate-suppress-w": "GENERATE_SW=true SUPPRESS_WARNING=true npx tailwindcss -i ./static/input.css -o ./static/output.css && vite dev",
|
||||||
"build-generate-sw": "GENERATE_SW=true vite build",
|
"sprites:build": "node scripts/optimize-sprites.mjs",
|
||||||
"build-generate-sw-node": "NODE_ADAPTER=true GENERATE_SW=true vite build",
|
"sprites:manifest": "node scripts/sprite-manifest.mjs",
|
||||||
"build": "npx tailwindcss -i ./static/input.css -o ./static/output.css && vite build",
|
"build-generate-sw": "npm run tailwind && GENERATE_SW=true vite build",
|
||||||
"build-inject-manifest-node": "NODE_ADAPTER=true vite build",
|
"build-generate-sw-node": "npm run tailwind && NODE_ADAPTER=true GENERATE_SW=true vite build",
|
||||||
"build-self-destroying": "SELF_DESTROYING_SW=true vite build",
|
"build": "npm run tailwind && vite build",
|
||||||
|
"build-inject-manifest": "npm run tailwind && vite build",
|
||||||
|
"build-inject-manifest-node": "npm run tailwind && NODE_ADAPTER=true vite build",
|
||||||
|
"build-self-destroying": "npm run tailwind && SELF_DESTROYING_SW=true vite build",
|
||||||
"preview": "vite preview --port=4173",
|
"preview": "vite preview --port=4173",
|
||||||
"preview-node": "PORT=4173 node build",
|
"preview-node": "PORT=4173 node build",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
@@ -19,19 +22,36 @@
|
|||||||
"lint-fix": "npm run lint --fix",
|
"lint-fix": "npm run lint --fix",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"tailwind": "npx tailwindcss -i ./static/input.css -o ./static/output.css",
|
"tailwind": "npx tailwindcss -i ./static/input.css -o ./static/output.css",
|
||||||
"test-generate-sw": "npm run build-generate-sw && GENERATE_SW=true vitest run && GENERATE_SW=true playwright test",
|
"test:unit": "vitest run tests/unit",
|
||||||
"test-generate-sw-node": "npm run build-generate-sw-node && NODE_ADAPTER=true GENERATE_SW=true vitest run && NODE_ADAPTER=true GENERATE_SW=true playwright test",
|
"test:data": "vitest run tests/data",
|
||||||
"test-inject-manifest": "npm run build-inject-manifest && vitest run && playwright test",
|
"test:coverage": "vitest run tests/unit --coverage",
|
||||||
"test-inject-manifest-node": "npm run build-inject-manifest-node && NODE_ADAPTER=true vitest run && NODE_ADAPTER=true playwright test",
|
"test:fast": "npm run test:coverage && npm run test:data",
|
||||||
"test": "npm run test-generate-sw && npm run test-generate-sw-node && npm run test-inject-manifest && npm run test-inject-manifest-node"
|
"test:integration": "node scripts/run-with-local-supabase.mjs vitest run --config vitest.integration.config.mts",
|
||||||
|
"test:bdd:generate": "bddgen",
|
||||||
|
"test:bdd:inner": "npm run build-inject-manifest && npm run test:bdd:generate && playwright test --project=chromium",
|
||||||
|
"test:bdd": "ALLOW_PROVIDER_ENDPOINT_OVERRIDES=true GOOGLE_OAUTH_CLIENT_ID=mock-google GOOGLE_OAUTH_CLIENT_SECRET=mock-google-secret DROPBOX_OAUTH_CLIENT_ID=mock-dropbox DROPBOX_OAUTH_CLIENT_SECRET=mock-dropbox-secret GOOGLE_OAUTH_AUTHORIZE_URL=http://127.0.0.1:4199/google/authorize GOOGLE_OAUTH_TOKEN_URL=http://127.0.0.1:4199/google/token GOOGLE_DRIVE_API_URL=http://127.0.0.1:4199/google/drive GOOGLE_DRIVE_UPLOAD_URL=http://127.0.0.1:4199/google/upload DROPBOX_OAUTH_AUTHORIZE_URL=http://127.0.0.1:4199/dropbox/authorize DROPBOX_OAUTH_TOKEN_URL=http://127.0.0.1:4199/dropbox/token DROPBOX_UPLOAD_URL=http://127.0.0.1:4199/dropbox/files/upload node scripts/run-with-local-supabase.mjs npm run test:bdd:inner",
|
||||||
|
"test:build:generate-static": "npm run build-generate-sw && GENERATE_SW=true vitest run --config vitest.build.config.mts",
|
||||||
|
"test:build:generate-node": "npm run build-generate-sw-node && NODE_ADAPTER=true GENERATE_SW=true vitest run --config vitest.build.config.mts",
|
||||||
|
"test:build:inject-static": "npm run build-inject-manifest && vitest run --config vitest.build.config.mts",
|
||||||
|
"test:build:inject-node": "npm run build-inject-manifest-node && NODE_ADAPTER=true vitest run --config vitest.build.config.mts",
|
||||||
|
"test:build": "npm run test:build:generate-static && npm run test:build:generate-node && npm run test:build:inject-static && npm run test:build:inject-node",
|
||||||
|
"test:ci": "npm run test:fast && npm run test:integration && npm run test:build && npm run test:bdd",
|
||||||
|
"test": "npm run test:ci",
|
||||||
|
"supabase:start": "supabase start",
|
||||||
|
"supabase:stop": "supabase stop",
|
||||||
|
"supabase:reset": "supabase db reset",
|
||||||
|
"supabase:studio": "supabase studio",
|
||||||
|
"migrate:convert-tsv": "node scripts/convert-tsv-to-sql.js",
|
||||||
|
"dev:local": "./scripts/dev-local.sh && npm run dev",
|
||||||
|
"dev:supabase": "supabase start && npm run dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.37.1",
|
"@playwright/test": "1.55.1",
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/adapter-netlify": "^4.1.0",
|
"@sveltejs/adapter-netlify": "^4.1.0",
|
||||||
"@sveltejs/adapter-node": "^2.0.0",
|
"@sveltejs/adapter-node": "^2.0.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.0",
|
"@sveltejs/adapter-static": "^3.0.0",
|
||||||
"@sveltejs/kit": "^2.0.6",
|
"@sveltejs/kit": "^2.5.3",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/eslint": "^8.56.0",
|
"@types/eslint": "^8.56.0",
|
||||||
@@ -39,14 +59,17 @@
|
|||||||
"@typescript-eslint/parser": "^7.0.0",
|
"@typescript-eslint/parser": "^7.0.0",
|
||||||
"@vite-pwa/assets-generator": "^0.2.4",
|
"@vite-pwa/assets-generator": "^0.2.4",
|
||||||
"@vite-pwa/sveltekit": "^0.4.0",
|
"@vite-pwa/sveltekit": "^0.4.0",
|
||||||
|
"@vitest/coverage-v8": "^1.6.1",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"daisyui": "^4.10.1",
|
"daisyui": "^4.10.1",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.35.1",
|
||||||
|
"playwright-bdd": "^8.5.1",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
|
"supabase": "2.72.7",
|
||||||
"svelte": "^4.2.8",
|
"svelte": "^4.2.8",
|
||||||
"svelte-check": "^3.6.2",
|
"svelte-check": "^3.6.2",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
@@ -56,12 +79,12 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supabase/ssr": "^0.1.0",
|
"@supabase/ssr": "^0.12.7",
|
||||||
"@supabase/supabase-js": "^2.42.0",
|
"@supabase/supabase-js": "^2.116.0",
|
||||||
"mongoose": "^8.0.3",
|
"nanoid": "^5.0.4",
|
||||||
"nanoid": "^5.0.4"
|
"sharp": "^0.33.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.13.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-73
@@ -1,10 +1,18 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test'
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
import { defineBddConfig } from 'playwright-bdd';
|
||||||
|
|
||||||
const url = 'http://localhost:4173'
|
const url = 'http://localhost:4173';
|
||||||
|
|
||||||
|
const testDir = defineBddConfig({
|
||||||
|
features: 'tests/bdd/features/**/*.feature',
|
||||||
|
steps: ['tests/bdd/steps/**/*.ts', 'tests/bdd/fixtures.ts'],
|
||||||
|
outputDir: '.features-gen',
|
||||||
|
missingSteps: 'fail-on-gen'
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { nodeAdapter } from './adapter.mjs'
|
import { nodeAdapter } from './adapter.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@@ -16,81 +24,90 @@ import { nodeAdapter } from './adapter.mjs'
|
|||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './client-test',
|
testDir,
|
||||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
globalTeardown: './tests/bdd/globalTeardown.ts',
|
||||||
outputDir: 'test-results/',
|
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||||
timeout: 5 * 1000,
|
outputDir: 'test-results/',
|
||||||
expect: {
|
timeout: 90 * 1000,
|
||||||
/**
|
expect: {
|
||||||
* Maximum time expect() should wait for the condition to be met.
|
/**
|
||||||
* For example in `await expect(locator).toHaveText();`
|
* Maximum time expect() should wait for the condition to be met.
|
||||||
*/
|
* For example in `await expect(locator).toHaveText();`
|
||||||
timeout: 1000,
|
*/
|
||||||
},
|
timeout: 10 * 1000
|
||||||
/* Run tests in files in parallel */
|
},
|
||||||
fullyParallel: true,
|
/* Run tests in files in parallel */
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
fullyParallel: false,
|
||||||
forbidOnly: !!process.env.CI,
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
/* Retry on CI only */
|
forbidOnly: !!process.env.CI,
|
||||||
retries: 0,
|
/* Retry on CI only */
|
||||||
/* Opt out of parallel tests on CI. */
|
retries: 0,
|
||||||
workers: process.env.CI ? 1 : undefined,
|
/* Opt out of parallel tests on CI. */
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
workers: 1,
|
||||||
reporter: 'line',
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
reporter: [['line'], ['./tests/support/no-skips-reporter.ts']],
|
||||||
use: {
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
use: {
|
||||||
actionTimeout: 0,
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
actionTimeout: 0,
|
||||||
baseURL: url,
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
//offline: true,
|
baseURL: url,
|
||||||
|
//offline: true,
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'retain-on-failure',
|
||||||
},
|
screenshot: 'only-on-failure'
|
||||||
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'chromium',
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices['Desktop Chrome'] }
|
||||||
},
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// name: 'firefox',
|
// name: 'firefox',
|
||||||
// use: { ...devices['Desktop Firefox'] },
|
// use: { ...devices['Desktop Firefox'] },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// name: 'webkit',
|
// name: 'webkit',
|
||||||
// use: { ...devices['Desktop Safari'] },
|
// use: { ...devices['Desktop Safari'] },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
/* Test against mobile viewports. */
|
||||||
// {
|
// {
|
||||||
// name: 'Mobile Chrome',
|
// name: 'Mobile Chrome',
|
||||||
// use: { ...devices['Pixel 5'] },
|
// use: { ...devices['Pixel 5'] },
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// name: 'Mobile Safari',
|
// name: 'Mobile Safari',
|
||||||
// use: { ...devices['iPhone 12'] },
|
// use: { ...devices['iPhone 12'] },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
/* Test against branded browsers. */
|
/* Test against branded browsers. */
|
||||||
// {
|
// {
|
||||||
// name: 'Microsoft Edge',
|
// name: 'Microsoft Edge',
|
||||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// name: 'Google Chrome',
|
// name: 'Google Chrome',
|
||||||
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
// },
|
// },
|
||||||
],
|
],
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
webServer: {
|
webServer: [
|
||||||
command: nodeAdapter ? 'pnpm run preview-node' : 'pnpm run preview',
|
{
|
||||||
url,
|
command: 'node scripts/mock-provider-server.mjs',
|
||||||
reuseExistingServer: !process.env.CI,
|
url: 'http://127.0.0.1:4199/__mock/state',
|
||||||
},
|
reuseExistingServer: !process.env.CI
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: nodeAdapter ? 'npm run preview-node' : 'npm run preview',
|
||||||
|
url,
|
||||||
|
reuseExistingServer: !process.env.CI
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {}
|
||||||
},
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import process from 'node:process'
|
import process from 'node:process';
|
||||||
|
|
||||||
export const generateSW = process.env.GENERATE_SW === 'true'
|
export const generateSW = process.env.GENERATE_SW === 'true';
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
#!/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);
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { createServer } from 'node:http';
|
||||||
|
|
||||||
|
const port = Number(process.env.MOCK_PROVIDER_PORT ?? 4199);
|
||||||
|
const state = { requests: [], failUploads: false, refreshes: 0 };
|
||||||
|
|
||||||
|
function send(response, status, body, headers = {}) {
|
||||||
|
response.writeHead(status, { 'Content-Type': 'application/json', ...headers });
|
||||||
|
response.end(typeof body === 'string' ? body : JSON.stringify(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = createServer(async (request, response) => {
|
||||||
|
const url = new URL(request.url ?? '/', `http://127.0.0.1:${port}`);
|
||||||
|
let body = '';
|
||||||
|
for await (const chunk of request) body += chunk;
|
||||||
|
|
||||||
|
// Control-plane calls (including Playwright's webServer readiness polling of /__mock/state)
|
||||||
|
// must not show up as provider traffic the assertions then reason about.
|
||||||
|
if (!url.pathname.startsWith('/__mock/')) {
|
||||||
|
state.requests.push({ method: request.method, path: url.pathname, query: url.search, body });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname === '/__mock/state') return send(response, 200, state);
|
||||||
|
if (url.pathname === '/__mock/reset') {
|
||||||
|
state.requests = [];
|
||||||
|
state.failUploads = false;
|
||||||
|
state.refreshes = 0;
|
||||||
|
return send(response, 200, { ok: true });
|
||||||
|
}
|
||||||
|
if (url.pathname === '/__mock/fail-uploads') {
|
||||||
|
state.failUploads = true;
|
||||||
|
return send(response, 200, { ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.endsWith('/authorize')) {
|
||||||
|
const redirectUri = url.searchParams.get('redirect_uri');
|
||||||
|
const oauthState = url.searchParams.get('state');
|
||||||
|
if (!redirectUri || !oauthState) return send(response, 400, { error: 'missing redirect data' });
|
||||||
|
const callback = new URL(redirectUri);
|
||||||
|
callback.searchParams.set('code', 'mock-authorization-code');
|
||||||
|
callback.searchParams.set('state', oauthState);
|
||||||
|
response.writeHead(302, { Location: callback.toString() });
|
||||||
|
return response.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.endsWith('/token')) {
|
||||||
|
if (body.includes('grant_type=refresh_token')) state.refreshes++;
|
||||||
|
return send(response, 200, {
|
||||||
|
access_token: 'mock-access-token',
|
||||||
|
refresh_token: 'mock-refresh-token',
|
||||||
|
expires_in: 3600,
|
||||||
|
scope: 'files.content.write drive.file'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.includes('/upload') || url.pathname.endsWith('/files/upload')) {
|
||||||
|
if (state.failUploads) return send(response, 503, { error: 'mock upload failure' });
|
||||||
|
return send(response, 200, { id: 'mock-file-id', name: 'pokedex.csv' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.endsWith('/drive/files')) {
|
||||||
|
if (request.method === 'GET') return send(response, 200, { files: [{ id: 'mock-folder-id' }] });
|
||||||
|
return send(response, 200, { id: 'mock-folder-id' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return send(response, 404, { error: `No mock route for ${url.pathname}` });
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port, '127.0.0.1', () => {
|
||||||
|
console.log(`Mock provider server listening on http://127.0.0.1:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const signal of ['SIGTERM', 'SIGINT']) {
|
||||||
|
process.on(signal, () => server.close(() => process.exit(0)));
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import path from 'node:path';
|
||||||
|
import process from 'node:process';
|
||||||
|
import { mkdir, readdir, rename } from 'node:fs/promises';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { writeSpriteManifest } from './sprite-manifest.mjs';
|
||||||
|
|
||||||
|
const inputDir = process.env.SPRITE_INPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites');
|
||||||
|
const outputDir =
|
||||||
|
process.env.SPRITE_OUTPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites-small');
|
||||||
|
const format = (process.env.SPRITE_FORMAT ?? 'webp').toLowerCase();
|
||||||
|
const quality = Number(process.env.SPRITE_QUALITY ?? 80);
|
||||||
|
const maxSize = Number(process.env.SPRITE_MAX_SIZE ?? 0);
|
||||||
|
|
||||||
|
if (!['png', 'webp'].includes(format)) {
|
||||||
|
console.error(`Unsupported SPRITE_FORMAT "${format}". Use "png" or "webp".`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function walk(dir, files = []) {
|
||||||
|
const entries = await readdir(dir, { withFileTypes: true });
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await walk(fullPath, files);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entry.isFile() && entry.name.toLowerCase().endsWith('.png')) {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = await walk(inputDir);
|
||||||
|
console.log(`Optimizing ${files.length} sprite images from ${inputDir}`);
|
||||||
|
|
||||||
|
for (const [index, file] of files.entries()) {
|
||||||
|
const relative = path.relative(inputDir, file);
|
||||||
|
const baseName = path.basename(file, path.extname(file));
|
||||||
|
const targetDir = path.join(outputDir, path.dirname(relative));
|
||||||
|
await mkdir(targetDir, { recursive: true });
|
||||||
|
|
||||||
|
const targetExt = format === 'png' ? '.png' : '.webp';
|
||||||
|
const targetPath = path.join(targetDir, `${baseName}${targetExt}`);
|
||||||
|
const tempPath = format === 'png' && outputDir === inputDir ? `${targetPath}.tmp` : targetPath;
|
||||||
|
|
||||||
|
const pipeline = sharp(file);
|
||||||
|
if (maxSize > 0) {
|
||||||
|
pipeline.resize({
|
||||||
|
width: maxSize,
|
||||||
|
height: maxSize,
|
||||||
|
fit: 'inside',
|
||||||
|
withoutEnlargement: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format === 'png') {
|
||||||
|
await pipeline.png({ compressionLevel: 9, palette: true, quality }).toFile(tempPath);
|
||||||
|
} else {
|
||||||
|
await pipeline.webp({ quality }).toFile(tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempPath !== targetPath) {
|
||||||
|
await rename(tempPath, targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((index + 1) % 250 === 0) {
|
||||||
|
console.log(`Processed ${index + 1}/${files.length}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = await writeSpriteManifest(outputDir);
|
||||||
|
console.log(`Sprite optimization complete. Manifest lists ${manifest.files.length} sprites.`);
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
|
||||||
|
const [, , command, ...args] = process.argv;
|
||||||
|
if (!command) {
|
||||||
|
console.error('Usage: node scripts/run-with-local-supabase.mjs <command> [...args]');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SETUP_HINT = 'Run "npm run supabase:start" followed by "npm run supabase:reset".';
|
||||||
|
// npx is a shell script on Windows, where spawn needs a shell to find it.
|
||||||
|
const useShell = process.platform === 'win32';
|
||||||
|
|
||||||
|
function fail(message, detail) {
|
||||||
|
console.error(message);
|
||||||
|
if (detail) console.error(String(detail).trim());
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = spawnSync('npx', ['supabase', 'status', '--output', 'json'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
shell: useShell
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status.error) {
|
||||||
|
fail(
|
||||||
|
'Unable to run "npx supabase status" - is the supabase CLI installed?',
|
||||||
|
status.error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.status !== 0) {
|
||||||
|
const stderr = status.stderr ?? '';
|
||||||
|
// Distinguish a stopped stack from a genuinely broken CLI invocation, so the hint is only
|
||||||
|
// printed when it is actually the advice the reader needs.
|
||||||
|
if (/not running|supabase start/i.test(stderr)) {
|
||||||
|
fail(`Local Supabase is required but is not running.\n${SETUP_HINT}`, stderr);
|
||||||
|
}
|
||||||
|
fail(`"supabase status" failed with exit code ${status.status}.`, stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let values;
|
||||||
|
try {
|
||||||
|
values = JSON.parse(status.stdout);
|
||||||
|
} catch (error) {
|
||||||
|
fail('Unable to parse "supabase status --output json".', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = values.API_URL ?? values.api_url ?? 'http://127.0.0.1:54321';
|
||||||
|
const anonKey = values.ANON_KEY ?? values.PUBLISHABLE_KEY ?? values.anon_key;
|
||||||
|
const serviceRoleKey = values.SERVICE_ROLE_KEY ?? values.SECRET_KEY ?? values.service_role_key;
|
||||||
|
|
||||||
|
if (!anonKey || !serviceRoleKey) {
|
||||||
|
fail(`Supabase status did not return an anonymous and service-role key.\n${SETUP_HINT}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLoopbackUrl(value) {
|
||||||
|
try {
|
||||||
|
return ['127.0.0.1', 'localhost', '[::1]'].includes(new URL(value).hostname);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoopbackUrl(apiUrl)) {
|
||||||
|
fail(`Refusing to run local-stack tests against non-loopback Supabase URL: ${apiUrl}`);
|
||||||
|
}
|
||||||
|
const providerUrlVariables = [
|
||||||
|
'MOCK_PROVIDER_URL',
|
||||||
|
'GOOGLE_OAUTH_AUTHORIZE_URL',
|
||||||
|
'GOOGLE_OAUTH_TOKEN_URL',
|
||||||
|
'GOOGLE_DRIVE_API_URL',
|
||||||
|
'GOOGLE_DRIVE_UPLOAD_URL',
|
||||||
|
'DROPBOX_OAUTH_AUTHORIZE_URL',
|
||||||
|
'DROPBOX_OAUTH_TOKEN_URL',
|
||||||
|
'DROPBOX_UPLOAD_URL'
|
||||||
|
];
|
||||||
|
for (const name of providerUrlVariables) {
|
||||||
|
const value = process.env[name];
|
||||||
|
if (value && !isLoopbackUrl(value)) {
|
||||||
|
fail(`Refusing to run provider tests with non-loopback ${name}: ${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A running-but-unseeded database is the most common broken state, and it surfaces downstream as
|
||||||
|
// a confusing assertion failure. Check it here instead.
|
||||||
|
const probe = await fetch(`${apiUrl}/rest/v1/pokedex_entries?select=id&limit=1`, {
|
||||||
|
headers: { apikey: anonKey, Authorization: `Bearer ${anonKey}` }
|
||||||
|
}).catch((error) => {
|
||||||
|
fail(`Unable to reach the local Supabase REST API at ${apiUrl}.\n${SETUP_HINT}`, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!probe.ok) {
|
||||||
|
fail(
|
||||||
|
`The local Supabase database has no readable pokedex_entries (HTTP ${probe.status}).\n${SETUP_HINT}`,
|
||||||
|
await probe.text()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (((await probe.json()) ?? []).length === 0) {
|
||||||
|
fail(`The local Supabase database is empty - migrations or seeds have not run.\n${SETUP_HINT}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = spawnSync(command, args, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: useShell,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
// Never allow an exported production value to redirect a local integration run.
|
||||||
|
PUBLIC_SUPABASE_URL: apiUrl,
|
||||||
|
PUBLIC_SUPABASE_ANON_KEY: anonKey,
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY: serviceRoleKey,
|
||||||
|
TEST_SUPABASE_URL: apiUrl,
|
||||||
|
TEST_SUPABASE_ANON_KEY: anonKey,
|
||||||
|
E2E_SERVICE_ROLE_KEY: serviceRoleKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (child.error) fail(`Unable to run "${command}".`, child.error.message);
|
||||||
|
// A signalled child reports status === null; exiting 0 there would hide the failure.
|
||||||
|
process.exit(child.signal ? 1 : child.status ?? 1);
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Lists every sprite (all forms, shiny and female variants) with its size, so the offline worker can
|
||||||
|
// save the complete set on request and tell whether anything is still missing.
|
||||||
|
import path from 'node:path';
|
||||||
|
import process from 'node:process';
|
||||||
|
import { readdir, stat, writeFile } from 'node:fs/promises';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
export const SPRITE_MANIFEST_NAME = 'manifest.json';
|
||||||
|
|
||||||
|
async function walk(dir, files = []) {
|
||||||
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) await walk(fullPath, files);
|
||||||
|
else if (entry.isFile() && entry.name.endsWith('.webp')) files.push(fullPath);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builds the manifest for `<spritesDir>/home`, with paths relative to that folder. */
|
||||||
|
export async function buildSpriteManifest(spritesDir) {
|
||||||
|
const homeDir = path.join(spritesDir, 'home');
|
||||||
|
const files = await walk(homeDir);
|
||||||
|
const entries = await Promise.all(
|
||||||
|
files.map(async (file) => [
|
||||||
|
path.relative(homeDir, file).split(path.sep).join('/'),
|
||||||
|
(await stat(file)).size
|
||||||
|
])
|
||||||
|
);
|
||||||
|
entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
||||||
|
return { version: 1, files: entries };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function writeSpriteManifest(spritesDir) {
|
||||||
|
const manifest = await buildSpriteManifest(spritesDir);
|
||||||
|
await writeFile(path.join(spritesDir, SPRITE_MANIFEST_NAME), `${JSON.stringify(manifest)}\n`);
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||||
|
const spritesDir =
|
||||||
|
process.env.SPRITE_OUTPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites-small');
|
||||||
|
const manifest = await writeSpriteManifest(spritesDir);
|
||||||
|
const bytes = manifest.files.reduce((total, [, size]) => total + size, 0);
|
||||||
|
console.log(
|
||||||
|
`Wrote ${manifest.files.length} sprites (${(bytes / 1048576).toFixed(1)} MB) to ${SPRITE_MANIFEST_NAME}`
|
||||||
|
);
|
||||||
|
}
|
||||||
Vendored
+6
-1
@@ -1,6 +1,7 @@
|
|||||||
import 'vite-plugin-pwa/svelte';
|
import 'vite-plugin-pwa/svelte';
|
||||||
import 'vite-plugin-pwa/info';
|
import 'vite-plugin-pwa/info';
|
||||||
import 'vite-plugin-pwa/pwa-assets';
|
import 'vite-plugin-pwa/pwa-assets';
|
||||||
|
import { SupabaseClient, Session, User } from '@supabase/supabase-js';
|
||||||
|
|
||||||
// See https://kit.svelte.dev/docs/types#app
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
@@ -11,12 +12,16 @@ declare global {
|
|||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
interface Locals {
|
interface Locals {
|
||||||
|
supabase: SupabaseClient;
|
||||||
|
safeGetSession(): Promise<{ session: Session | null; user: User | null }>;
|
||||||
userid: string;
|
userid: string;
|
||||||
buildDate: string;
|
buildDate: string;
|
||||||
periodicUpdates: boolean;
|
periodicUpdates: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface PageData {}
|
interface PageData {
|
||||||
|
session: Session | null;
|
||||||
|
}
|
||||||
// interface PageState {}
|
// interface PageState {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -1,9 +1,22 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="pokeball">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
const storedTheme = localStorage.getItem('theme');
|
||||||
|
const allowed = ['pokeball', 'dark'];
|
||||||
|
const theme = storedTheme && allowed.includes(storedTheme) ? storedTheme : 'pokeball';
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback to default theme if localStorage is not accessible
|
||||||
|
document.documentElement.setAttribute('data-theme', 'pokeball');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
<link rel="stylesheet" href="%sveltekit.assets%/output.css" />
|
<link rel="stylesheet" href="%sveltekit.assets%/output.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+8
-10
@@ -5,18 +5,16 @@ import type { Handle } from '@sveltejs/kit';
|
|||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||||
cookies: {
|
cookies: {
|
||||||
get: (key) => event.cookies.get(key),
|
getAll: () => event.cookies.getAll(),
|
||||||
/**
|
/**
|
||||||
* Note: You have to add the `path` variable to the
|
* Note: You have to add the `path` variable to the set method due to sveltekit's cookie
|
||||||
* set and remove method due to sveltekit's cookie API
|
* API requiring this to be set, setting the path to '/' will replicate previous/standard
|
||||||
* requiring this to be set, setting the path to an empty string
|
* behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
||||||
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
|
||||||
*/
|
*/
|
||||||
set: (key, value, options) => {
|
setAll: (cookiesToSet) => {
|
||||||
event.cookies.set(key, value, { ...options, path: '/' });
|
cookiesToSet.forEach(({ name, value, options }) => {
|
||||||
},
|
event.cookies.set(name, value, { ...options, path: '/' });
|
||||||
remove: (key, options) => {
|
});
|
||||||
event.cookies.delete(key, { ...options, path: '/' });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
type InViewParams = {
|
||||||
|
rootMargin?: string;
|
||||||
|
threshold?: number | number[];
|
||||||
|
once?: boolean;
|
||||||
|
enabled?: boolean;
|
||||||
|
onChange?: (inView: boolean, entry: IntersectionObserverEntry) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const observers = new Map<string, IntersectionObserver>();
|
||||||
|
const callbacks = new WeakMap<Element, (entry: IntersectionObserverEntry) => void>();
|
||||||
|
|
||||||
|
function normalizeThreshold(threshold: number | number[] | undefined) {
|
||||||
|
if (threshold === undefined) return [0];
|
||||||
|
return Array.isArray(threshold) ? threshold : [threshold];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildObserverKey(rootMargin: string, threshold: number | number[]) {
|
||||||
|
return `${rootMargin}|${JSON.stringify(normalizeThreshold(threshold))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObserver(rootMargin: string, threshold: number | number[]) {
|
||||||
|
const key = buildObserverKey(rootMargin, threshold);
|
||||||
|
const existing = observers.get(key);
|
||||||
|
if (existing) return existing;
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const callback = callbacks.get(entry.target);
|
||||||
|
if (callback) {
|
||||||
|
callback(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ rootMargin, threshold }
|
||||||
|
);
|
||||||
|
observers.set(key, observer);
|
||||||
|
return observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inView(node: Element, params: InViewParams = {}) {
|
||||||
|
let options: Required<InViewParams> = {
|
||||||
|
rootMargin: '200px 0px',
|
||||||
|
threshold: 0,
|
||||||
|
once: true,
|
||||||
|
enabled: true,
|
||||||
|
onChange: () => undefined,
|
||||||
|
...params
|
||||||
|
};
|
||||||
|
|
||||||
|
let observer = getObserver(options.rootMargin, options.threshold);
|
||||||
|
|
||||||
|
const handleEntry = (entry: IntersectionObserverEntry) => {
|
||||||
|
const isIntersecting = entry.isIntersecting || entry.intersectionRatio > 0;
|
||||||
|
options.onChange(isIntersecting, entry);
|
||||||
|
if (isIntersecting && options.once) {
|
||||||
|
observer.unobserve(node);
|
||||||
|
callbacks.delete(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startObserving = () => {
|
||||||
|
callbacks.set(node, handleEntry);
|
||||||
|
observer.observe(node);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopObserving = () => {
|
||||||
|
observer.unobserve(node);
|
||||||
|
callbacks.delete(node);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.enabled) {
|
||||||
|
startObserving();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
update(next: InViewParams = {}) {
|
||||||
|
const nextOptions: Required<InViewParams> = {
|
||||||
|
...options,
|
||||||
|
...next,
|
||||||
|
onChange: next.onChange ?? options.onChange
|
||||||
|
};
|
||||||
|
const observerKeyChanged =
|
||||||
|
buildObserverKey(options.rootMargin, options.threshold) !==
|
||||||
|
buildObserverKey(nextOptions.rootMargin, nextOptions.threshold);
|
||||||
|
const enabledChanged = options.enabled !== nextOptions.enabled;
|
||||||
|
|
||||||
|
if (observerKeyChanged || enabledChanged) {
|
||||||
|
stopObserving();
|
||||||
|
}
|
||||||
|
|
||||||
|
options = nextOptions;
|
||||||
|
|
||||||
|
if (observerKeyChanged) {
|
||||||
|
observer = getObserver(options.rootMargin, options.threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.enabled) {
|
||||||
|
startObserving();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
stopObserving();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
currentPage = Math.max(currentPage - 1, 1);
|
currentPage = Math.max(currentPage - 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItemsPerPage(event: any) {
|
function setItemsPerPage(event: Event) {
|
||||||
itemsPerPage = parseInt(event.target.value, 10);
|
itemsPerPage = parseInt((event.target as HTMLSelectElement).value, 10);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,100 +1,65 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import pokeApiPokemon from '$lib/helpers/pokeapi-pokemon.json';
|
|
||||||
import { PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER } from '$env/static/public';
|
import { PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER } from '$env/static/public';
|
||||||
|
import { inView } from '$lib/actions/inView';
|
||||||
|
import { resolveSpriteUrl } from '$lib/utils/spriteUrl';
|
||||||
|
|
||||||
export let pokemonName: string;
|
export let pokemonName: string;
|
||||||
export let pokedexNumber: string | number;
|
export let pokedexNumber: string | number;
|
||||||
export let form: string | undefined;
|
export let form: string | undefined;
|
||||||
|
export let spriteKey: string | undefined;
|
||||||
export let shiny: boolean | undefined = false;
|
export let shiny: boolean | undefined = false;
|
||||||
|
export let loadingStrategy: 'eager' | 'lazy' | 'inView' = 'inView';
|
||||||
|
|
||||||
let imagePath = null as string | null;
|
let imagePath = null as string | null;
|
||||||
let blah = '';
|
let isInView = false;
|
||||||
|
|
||||||
function setImagePath() {
|
$: {
|
||||||
let rootFolder =
|
if (!spriteKey?.trim()) {
|
||||||
|
console.warn('Missing sprite key for pokemon entry', {
|
||||||
|
pokemonName,
|
||||||
|
pokedexNumber,
|
||||||
|
form
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePath = resolveSpriteUrl(
|
||||||
|
{ pokedexNumber: Number(pokedexNumber), form, spriteKey },
|
||||||
|
!!shiny,
|
||||||
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true'
|
||||||
? '/sprites/home'
|
);
|
||||||
: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home';
|
|
||||||
|
|
||||||
if (form === 'Female') {
|
|
||||||
rootFolder += '/female';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shiny) {
|
|
||||||
rootFolder += '/shiny';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove leading zeros
|
|
||||||
const strippedPokedexNumber = pokedexNumber.toString().replace(/^0+/, '');
|
|
||||||
|
|
||||||
// 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
|
|
||||||
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('2', 'two')
|
|
||||||
.replaceAll('3', 'three')
|
|
||||||
.replaceAll('4', 'four');
|
|
||||||
|
|
||||||
switch (sanitisedForm) {
|
|
||||||
case 'alolan':
|
|
||||||
sanitisedForm = 'alola';
|
|
||||||
break;
|
|
||||||
case 'galarian':
|
|
||||||
sanitisedForm = 'galar';
|
|
||||||
break;
|
|
||||||
case 'hisuian':
|
|
||||||
sanitisedForm = 'hisui';
|
|
||||||
break;
|
|
||||||
// case 'rainbow-ribbon':
|
|
||||||
// sanitisedForm = 'rainbow-swirl-ribbon-sweet';
|
|
||||||
// break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (strippedPokedexNumber == '869') {
|
|
||||||
// console.log(sanitisedPokemonName, sanitisedForm);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
} else {
|
|
||||||
// If the form is not contained in the identifier, use the species_id
|
|
||||||
pokeApiId = pokeApiPokemon.find(
|
|
||||||
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
|
||||||
)?.id;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pokeApiId = pokeApiPokemon.find(
|
|
||||||
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
|
||||||
)?.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
imagePath = `${rootFolder}/${pokeApiId}.png`;
|
|
||||||
blah = `${pokeApiId}.png`;
|
|
||||||
// blah = `${strippedPokedexNumber}${form?.length && form !== 'Female' ? '-' + form : ''}.png`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(setImagePath);
|
$: if (loadingStrategy !== 'inView') {
|
||||||
|
isInView = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInView(inView: boolean) {
|
||||||
|
if (inView) {
|
||||||
|
isInView = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if imagePath}
|
{#if imagePath}
|
||||||
<!-- {blah} -->
|
<span
|
||||||
<img src={imagePath} alt="sprite" />
|
use:inView={{
|
||||||
|
enabled: loadingStrategy === 'inView',
|
||||||
|
once: true,
|
||||||
|
rootMargin: '200px 0px',
|
||||||
|
onChange: handleInView
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if loadingStrategy === 'inView' && !isInView}
|
||||||
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
|
{:else}
|
||||||
|
<img
|
||||||
|
src={imagePath}
|
||||||
|
alt="sprite"
|
||||||
|
loading={loadingStrategy === 'lazy' ? 'lazy' : 'eager'}
|
||||||
|
decoding="async"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="loading loading-spinner loading-xs"></span>
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@@ -8,49 +9,141 @@
|
|||||||
|
|
||||||
let email = '';
|
let email = '';
|
||||||
let password = '';
|
let password = '';
|
||||||
|
let isLoading = false;
|
||||||
|
let errorMessage = '';
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signInWithEmail() {
|
async function signInWithEmail() {
|
||||||
// TODO use the data and error from the response
|
isLoading = true;
|
||||||
const { data, error } = await supabase.auth
|
errorMessage = '';
|
||||||
.signInWithPassword({
|
|
||||||
|
try {
|
||||||
|
const { data, error } = await supabase.auth.signInWithPassword({
|
||||||
email: email,
|
email: email,
|
||||||
password: password
|
password: password
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
emitSignedInEvent();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Sign in error:', error);
|
||||||
|
errorMessage = error.message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
emitSignedInEvent();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Sign in error:', err);
|
||||||
|
errorMessage = 'An unexpected error occurred. Please try again.';
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyPress(event: KeyboardEvent) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
signInWithEmail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label class="input input-bordered flex items-center gap-2">
|
<div class="space-y-4">
|
||||||
<svg
|
<!-- Error Message -->
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
{#if errorMessage}
|
||||||
viewBox="0 0 16 16"
|
<div class="alert alert-error text-sm">
|
||||||
fill="currentColor"
|
<svg
|
||||||
class="w-4 h-4 opacity-70"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
><path
|
class="h-5 w-5 shrink-0 stroke-current"
|
||||||
d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z"
|
fill="none"
|
||||||
/><path
|
viewBox="0 0 24 24"
|
||||||
d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z"
|
>
|
||||||
/></svg
|
<path
|
||||||
>
|
stroke-linecap="round"
|
||||||
<input type="text" class="grow" placeholder="Email" bind:value={email} />
|
stroke-linejoin="round"
|
||||||
</label>
|
stroke-width="2"
|
||||||
<label class="input input-bordered flex items-center gap-2">
|
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
<svg
|
/>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</svg>
|
||||||
viewBox="0 0 16 16"
|
<span>{errorMessage}</span>
|
||||||
fill="currentColor"
|
</div>
|
||||||
class="w-4 h-4 opacity-70"
|
{/if}
|
||||||
><path
|
|
||||||
fill-rule="evenodd"
|
<!-- Email Input -->
|
||||||
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
|
<div class="form-control">
|
||||||
clip-rule="evenodd"
|
<label class="label" for="signin-email">
|
||||||
/></svg
|
<span class="label-text font-medium">Email</span>
|
||||||
>
|
</label>
|
||||||
<input type="password" class="grow" placeholder="Password" bind:value={password} />
|
<div class="relative">
|
||||||
</label>
|
<input
|
||||||
<button class="btn btn-active btn-primary" on:click={signInWithEmail}>Sign In</button>
|
id="signin-email"
|
||||||
|
type="email"
|
||||||
|
placeholder="your@email.com"
|
||||||
|
class="input input-bordered w-full pl-10"
|
||||||
|
bind:value={email}
|
||||||
|
on:keypress={handleKeyPress}
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="h-4 w-4 opacity-70 absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Password Input -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="signin-password">
|
||||||
|
<span class="label-text font-medium">Password</span>
|
||||||
|
</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input
|
||||||
|
id="signin-password"
|
||||||
|
type="password"
|
||||||
|
placeholder="••••••••"
|
||||||
|
class="input input-bordered w-full pl-10"
|
||||||
|
bind:value={password}
|
||||||
|
on:keypress={handleKeyPress}
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="h-4 w-4 opacity-70 absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sign In Button -->
|
||||||
|
<div class="form-control mt-6">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary w-full"
|
||||||
|
on:click={signInWithEmail}
|
||||||
|
disabled={isLoading || !email || !password}
|
||||||
|
>
|
||||||
|
{#if isLoading}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
Signing in...
|
||||||
|
{:else}
|
||||||
|
Sign In
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,46 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { clearOfflineData } from '$lib/stores/offlineSync';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
let errorMessage = '';
|
||||||
|
let isSigningOut = false;
|
||||||
|
|
||||||
function emitSignedOutEvent() {
|
function emitSignedOutEvent() {
|
||||||
dispatch('signedOut', {});
|
dispatch('signedOut', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signOut() {
|
async function signOut() {
|
||||||
// TODO use the error from the response
|
errorMessage = '';
|
||||||
const { error } = await supabase.auth.signOut().then(() => {
|
isSigningOut = true;
|
||||||
|
try {
|
||||||
|
const { error } = await supabase.auth.signOut();
|
||||||
|
if (error) {
|
||||||
|
errorMessage = `Sign out failed: ${error.message || 'Please try again.'}`;
|
||||||
|
dispatch('signOutFailed', { message: errorMessage });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await clearOfflineData();
|
||||||
|
} catch (cacheError) {
|
||||||
|
console.error('Signed out, but failed to clear offline data', cacheError);
|
||||||
|
}
|
||||||
emitSignedOutEvent();
|
emitSignedOutEvent();
|
||||||
});
|
await goto('/', { invalidateAll: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sign out failed', error);
|
||||||
|
errorMessage = 'Sign out failed. Please try again.';
|
||||||
|
dispatch('signOutFailed', { message: errorMessage });
|
||||||
|
} finally {
|
||||||
|
isSigningOut = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button on:click={signOut}>Sign Out</button>
|
<button on:click={signOut} disabled={isSigningOut}>
|
||||||
|
{isSigningOut ? 'Signing Out…' : 'Sign Out'}
|
||||||
|
</button>
|
||||||
|
|||||||
@@ -1,39 +1,48 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
let email = '';
|
let email = '';
|
||||||
let password = '';
|
let password = '';
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signUpNewUser() {
|
async function signUpNewUser() {
|
||||||
try {
|
try {
|
||||||
|
// `redirectTo` is not a signUp option - it was silently ignored, so the confirmation
|
||||||
|
// link has always used Supabase's configured site URL. Sending the user to /welcome
|
||||||
|
// would need `emailRedirectTo` with an absolute, allow-listed URL.
|
||||||
const { data, error } = await supabase.auth.signUp({
|
const { data, error } = await supabase.auth.signUp({
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password
|
||||||
options: {
|
|
||||||
// Redirect URL after successful sign-up
|
|
||||||
redirectTo: '/welcome'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error;
|
console.error('Sign up error:', error);
|
||||||
|
alert(`Sign up failed: ${error.message}`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle success (optional)
|
if (data) {
|
||||||
} catch (error) {
|
// Emit signedUp event to notify parent component
|
||||||
console.error('Sign up error:', error.message);
|
dispatch('signedUp', {});
|
||||||
// Handle error
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Sign up error:', err);
|
||||||
|
alert('Sign up failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form class="card-body">
|
<form class="card-body">
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label">
|
<label class="label" for="signup-email">
|
||||||
<span class="label-text">Email</span>
|
<span class="label-text">Email</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id="signup-email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="email"
|
placeholder="email"
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
@@ -42,10 +51,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label">
|
<label class="label" for="signup-password">
|
||||||
<span class="label-text">Password</span>
|
<span class="label-text">Password</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id="signup-password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="password"
|
placeholder="password"
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
@@ -54,6 +64,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control mt-6">
|
<div class="form-control mt-6">
|
||||||
<button class="btn btn btn-primary" on:click={signUpNewUser}>Sign Up</button>
|
<button class="btn btn-primary" on:click={signUpNewUser}>Sign Up</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.card-body {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { theme, applyTheme } from '$lib/stores/theme.js';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let currentTheme: 'pokeball' | 'dark' = 'pokeball';
|
||||||
|
|
||||||
|
// Subscribe to theme changes
|
||||||
|
$: (currentTheme = $theme), applyTheme($theme);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// Initialize theme from localStorage
|
||||||
|
theme.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
theme.toggle();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-ghost btn-circle"
|
||||||
|
on:click={toggleTheme}
|
||||||
|
aria-label="Toggle dark mode"
|
||||||
|
title="Toggle dark mode"
|
||||||
|
>
|
||||||
|
{#if currentTheme === 'pokeball'}
|
||||||
|
<!-- Sun icon for light mode (clicking will switch to dark) -->
|
||||||
|
<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="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else}
|
||||||
|
<!-- Moon icon for dark mode (clicking will switch to light) -->
|
||||||
|
<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="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
@@ -7,14 +7,16 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative inline-block">
|
<div class="relative inline-block">
|
||||||
<div
|
<button
|
||||||
class="tooltip-trigger cursor-pointer"
|
type="button"
|
||||||
|
class="tooltip-trigger cursor-pointer bg-transparent border-0 p-0 appearance-none"
|
||||||
|
aria-expanded={show}
|
||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
on:mouseenter={() => (show = true)}
|
on:mouseenter={() => (show = true)}
|
||||||
on:mouseleave={() => (show = false)}
|
on:mouseleave={() => (show = false)}
|
||||||
>
|
>
|
||||||
<slot name="hover-target" />
|
<slot name="hover-target" />
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
{#if show}
|
{#if show}
|
||||||
<div class="tooltip-content absolute z-10 mt-2 w-52">
|
<div class="tooltip-content absolute z-10 mt-2 w-52">
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<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>
|
||||||
|
{#if pokedex.dexScopes?.length}
|
||||||
|
<div class="text-sm text-base-content/70">
|
||||||
|
<span class="font-semibold">Dexes:</span>
|
||||||
|
{pokedex.dexScopes.length}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{: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>
|
||||||
@@ -1,19 +1,66 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { CatchRecord } from '$lib/models/CatchRecord';
|
import type { CatchRecord } from '$lib/models/CatchRecord';
|
||||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
import type { SharedCatchStatus } from '$lib/models/SharedPokedex';
|
||||||
|
import type { CatchInformationItem, PokedexEntry } from '$lib/models/PokedexEntry';
|
||||||
import PokemonSprite from '../PokemonSprite.svelte';
|
import PokemonSprite from '../PokemonSprite.svelte';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
export let pokedexEntry: PokedexEntry;
|
export let pokedexEntry: PokedexEntry;
|
||||||
export let catchRecord: CatchRecord;
|
export let catchRecord: CatchRecord | null;
|
||||||
export let showOrigins: boolean;
|
export let showOrigins: boolean;
|
||||||
export let showForms: boolean;
|
export let showForms: boolean;
|
||||||
export let showShiny: boolean;
|
export let showShiny: boolean;
|
||||||
|
export let userId: string | null = null;
|
||||||
|
export let pokedexId: string;
|
||||||
|
export let readOnly = false;
|
||||||
|
export let sharedCatchStatus: SharedCatchStatus | null = null;
|
||||||
|
|
||||||
|
// Create a default catch record if none exists
|
||||||
|
$: if (!readOnly && !catchRecord) {
|
||||||
|
catchRecord = {
|
||||||
|
_id: '', // Empty string, not temp ID - will be created by server
|
||||||
|
userId: userId || '',
|
||||||
|
pokemonId: pokedexEntry._id,
|
||||||
|
pokedexId: pokedexId,
|
||||||
|
haveToEvolve: false,
|
||||||
|
caught: false,
|
||||||
|
inHome: false,
|
||||||
|
hasGigantamaxed: false,
|
||||||
|
personalNotes: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
function updateCatchRecord() {
|
type UpdateCatchSource = 'toggle' | 'notes' | 'notes-blur';
|
||||||
dispatch('updateCatch', { pokedexEntry, catchRecord });
|
|
||||||
|
const isCatchInformationItem = (
|
||||||
|
value: string | CatchInformationItem
|
||||||
|
): value is CatchInformationItem => typeof value !== 'string';
|
||||||
|
|
||||||
|
function updateCatchRecord(source: UpdateCatchSource) {
|
||||||
|
if (readOnly) return;
|
||||||
|
dispatch('updateCatch', { pokedexEntry, catchRecord, source });
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCaughtChange() {
|
||||||
|
if (readOnly) return;
|
||||||
|
if (!catchRecord) return;
|
||||||
|
// Mutually exclusive with "needs to evolve"
|
||||||
|
if (catchRecord.caught) {
|
||||||
|
catchRecord.haveToEvolve = false;
|
||||||
|
}
|
||||||
|
updateCatchRecord('toggle');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNeedsToEvolveChange() {
|
||||||
|
if (readOnly) return;
|
||||||
|
if (!catchRecord) return;
|
||||||
|
// Mutually exclusive with "caught"
|
||||||
|
if (catchRecord.haveToEvolve) {
|
||||||
|
catchRecord.caught = false;
|
||||||
|
}
|
||||||
|
updateCatchRecord('toggle');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -22,123 +69,132 @@
|
|||||||
>
|
>
|
||||||
<div class="dex-column pokedex-entry-container">
|
<div class="dex-column pokedex-entry-container">
|
||||||
<div class="flex mb-2">
|
<div class="flex mb-2">
|
||||||
<div class="sprite-container flex justify-center items-center bg-white rounded-lg p-2">
|
<div class="pl-2">
|
||||||
|
<h3 class="text-xl font-bold pt-1">{pokedexEntry.pokemon}</h3>
|
||||||
|
<sub class="text-primary-content/80"
|
||||||
|
>#{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</sub
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex mb-2">
|
||||||
|
<div class="sprite-container flex justify-center items-center bg-base-100 rounded-lg p-2">
|
||||||
<PokemonSprite
|
<PokemonSprite
|
||||||
pokemonName={pokedexEntry.pokemon}
|
pokemonName={pokedexEntry.pokemon}
|
||||||
pokedexNumber={pokedexEntry.pokedexNumber}
|
pokedexNumber={pokedexEntry.pokedexNumber}
|
||||||
form={pokedexEntry.form}
|
form={pokedexEntry.form}
|
||||||
|
spriteKey={pokedexEntry.spriteKey}
|
||||||
shiny={showShiny}
|
shiny={showShiny}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pl-2">
|
|
||||||
<h3 class="text-xl font-bold pt-1 text-secondary">{pokedexEntry.pokemon}</h3>
|
|
||||||
<sub class="text-gray-200">#{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</sub>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showForms}
|
{#if showForms}
|
||||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||||
<p><strong>Form:</strong> {pokedexEntry.form ? pokedexEntry.form : '-'}</p>
|
<p><strong>Form:</strong> {pokedexEntry.form ? pokedexEntry.form : '-'}</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="bg-white text-black rounded-lg p-4">
|
{#if pokedexEntry.evolutionInformation.trim().length > 0}
|
||||||
{#if showForms}
|
<div class="bg-base-100 text-base-content rounded-lg p-4">
|
||||||
<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>
|
<p><strong>How to evolve: </strong>{pokedexEntry.evolutionInformation}</p>
|
||||||
{:else}
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if readOnly || catchRecord}
|
||||||
|
<div
|
||||||
|
class="dex-column catch-record-container bg-base-100 text-base-content rounded-lg p-4 mb-4 md:mb-0"
|
||||||
|
>
|
||||||
|
{#if readOnly}
|
||||||
|
<h3 class="text-lg font-semibold mb-2">Progress</h3>
|
||||||
|
<dl class="grid grid-cols-2 gap-x-4 gap-y-2">
|
||||||
|
<dt>Caught</dt>
|
||||||
|
<dd class="font-semibold">{sharedCatchStatus?.caught ? 'Yes' : 'No'}</dd>
|
||||||
|
<dt>Needs to evolve</dt>
|
||||||
|
<dd class="font-semibold">{sharedCatchStatus?.haveToEvolve ? 'Yes' : 'No'}</dd>
|
||||||
|
<dt>In HOME</dt>
|
||||||
|
<dd class="font-semibold">{sharedCatchStatus?.inHome ? 'Yes' : 'No'}</dd>
|
||||||
|
{#if pokedexEntry.canGigantamax && showForms}
|
||||||
|
<dt>Has Gigantamaxed</dt>
|
||||||
|
<dd class="font-semibold">{sharedCatchStatus?.hasGigantamaxed ? 'Yes' : 'No'}</dd>
|
||||||
|
{/if}
|
||||||
|
</dl>
|
||||||
|
{:else if catchRecord}
|
||||||
|
<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"
|
||||||
|
on:change={onCaughtChange}
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
on:change={onNeedsToEvolveChange}
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
on:change={() => updateCatchRecord('toggle')}
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
on:change={() => updateCatchRecord('toggle')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<p>
|
<p>
|
||||||
<strong>How to evolve: </strong>Currently missing - can you
|
<label
|
||||||
<a
|
class="block font-bold mb-1"
|
||||||
href="https://github.com/jcreek/LivingDexTracker"
|
for={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}>Notes:</label
|
||||||
class="underline text-primary hover:text-secondary">help contribute</a
|
>
|
||||||
>?
|
<textarea
|
||||||
|
bind:value={catchRecord.personalNotes}
|
||||||
|
id={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}
|
||||||
|
class="textarea textarea-bordered w-full"
|
||||||
|
style="min-height: 120px;"
|
||||||
|
on:input={() => updateCatchRecord('notes')}
|
||||||
|
on:change={() => updateCatchRecord('notes-blur')}
|
||||||
|
></textarea>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
|
|
||||||
<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}
|
|
||||||
<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}`}
|
|
||||||
>Notes:</label
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
bind:value={catchRecord.personalNotes}
|
|
||||||
id={`personalNotesInput-${catchRecord._id}`}
|
|
||||||
class="form-textarea w-full p-2 border rounded"
|
|
||||||
on:change={updateCatchRecord}
|
|
||||||
></textarea>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dex-column additional-details-container">
|
<div class="dex-column additional-details-container">
|
||||||
{#if showOrigins}
|
{#if showOrigins}
|
||||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||||
<h3 class="text-xl font-semibold mb-4">Origin Dex Requirements</h3>
|
<h3 class="text-xl font-semibold mb-4">Origin Dex Requirements</h3>
|
||||||
<p><strong>Region to Catch In:</strong> {pokedexEntry.regionToCatchIn}</p>
|
<p><strong>Region to Catch In:</strong> {pokedexEntry.regionToCatchIn}</p>
|
||||||
<p><strong>Games to catch in:</strong></p>
|
<p><strong>Games to catch in:</strong></p>
|
||||||
@@ -153,16 +209,20 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if pokedexEntry.catchInformation.length > 0}
|
{#if pokedexEntry.catchInformation.length > 0}
|
||||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||||
<p><strong>Where to catch: </strong></p>
|
<p><strong>Where to catch: </strong></p>
|
||||||
<ul class="list-disc list-inside">
|
<ul class="list-disc list-inside">
|
||||||
{#each pokedexEntry.catchInformation as info}
|
{#each pokedexEntry.catchInformation as info}
|
||||||
<li>
|
<li>
|
||||||
<ul>
|
{#if isCatchInformationItem(info)}
|
||||||
<li><strong>Game:</strong> {info.game}</li>
|
<ul>
|
||||||
<li><strong>Location:</strong> {info.location}</li>
|
<li><strong>Game:</strong> {info.game}</li>
|
||||||
<li><strong>Notes:</strong> {info.notes}</li>
|
<li><strong>Location:</strong> {info.location}</li>
|
||||||
</ul>
|
<li><strong>Notes:</strong> {info.notes}</li>
|
||||||
|
</ul>
|
||||||
|
{:else}
|
||||||
|
{info}
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -175,7 +235,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="bg-white text-black rounded-lg p-4 mb-2">
|
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||||
<p>
|
<p>
|
||||||
<strong>Where to catch: </strong>Currently missing - can you
|
<strong>Where to catch: </strong>Currently missing - can you
|
||||||
<a
|
<a
|
||||||
@@ -185,39 +245,22 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if pokedexEntry.notes}
|
||||||
|
<div class="bg-base-100 text-base-content rounded-lg p-4 mb-2">
|
||||||
|
<p><strong>Dex Notes:</strong> {pokedexEntry.notes}</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dex-column {
|
.dex-column {
|
||||||
width: 300px;
|
flex: 1;
|
||||||
|
min-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sprite-container {
|
.sprite-container {
|
||||||
width: 68px;
|
width: 100%;
|
||||||
height: 68px;
|
aspect-ratio: 1;
|
||||||
}
|
|
||||||
|
|
||||||
.form-checkbox {
|
|
||||||
appearance: none;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 2px solid #000;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-checkbox:checked {
|
|
||||||
background-color: #00bfff;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-textarea {
|
|
||||||
min-height: 3rem;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<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,
|
||||||
|
dexScopes: []
|
||||||
|
};
|
||||||
|
export let mode: 'create' | 'edit' = 'create';
|
||||||
|
export let onSubmit: () => void;
|
||||||
|
export let onCancel: () => void;
|
||||||
|
|
||||||
|
type GameDex = {
|
||||||
|
id: string;
|
||||||
|
displayName: string;
|
||||||
|
sortOrder: number;
|
||||||
|
isDlc: boolean;
|
||||||
|
parentDexId: string | null;
|
||||||
|
};
|
||||||
|
type GameSummary = {
|
||||||
|
displayName: string;
|
||||||
|
releaseYear: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
let gameDexes: Record<string, GameDex[]> = {};
|
||||||
|
let gameOrder: string[] = [];
|
||||||
|
let gameList: GameSummary[] = [];
|
||||||
|
let availableDexes: GameDex[] = [];
|
||||||
|
let loadingDexes = true;
|
||||||
|
let lastGameScope: string | null = null;
|
||||||
|
let dexScopesInitialized = false;
|
||||||
|
let hasSeenGameScope = false;
|
||||||
|
|
||||||
|
// Fetch available game dexes from the database
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/game-dexes');
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
gameDexes = data.gameDexes || {};
|
||||||
|
gameOrder = data.gameOrder || Object.keys(gameDexes);
|
||||||
|
gameList = data.games || [];
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch game dexes');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching game dexes:', error);
|
||||||
|
} finally {
|
||||||
|
loadingDexes = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
$: hasAtLeastOneType =
|
||||||
|
pokedex.isLivingDex || pokedex.isShinyDex || pokedex.isOriginDex || pokedex.isFormDex;
|
||||||
|
$: hasDexScope = !pokedex.gameScope || (pokedex.dexScopes && pokedex.dexScopes.length > 0);
|
||||||
|
$: canSubmit = pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType && hasDexScope;
|
||||||
|
|
||||||
|
$: if (pokedex.gameScope !== lastGameScope) {
|
||||||
|
const shouldResetDexes = mode === 'create' || hasSeenGameScope;
|
||||||
|
lastGameScope = pokedex.gameScope || null;
|
||||||
|
dexScopesInitialized = false;
|
||||||
|
if (shouldResetDexes) {
|
||||||
|
pokedex.dexScopes = [];
|
||||||
|
}
|
||||||
|
if (!pokedex.gameScope) {
|
||||||
|
availableDexes = [];
|
||||||
|
}
|
||||||
|
hasSeenGameScope = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (!loadingDexes && pokedex.gameScope) {
|
||||||
|
availableDexes = gameDexes[pokedex.gameScope] || [];
|
||||||
|
if (!dexScopesInitialized) {
|
||||||
|
if (!pokedex.dexScopes || pokedex.dexScopes.length === 0) {
|
||||||
|
pokedex.dexScopes = availableDexes.map((dex) => dex.id);
|
||||||
|
}
|
||||||
|
dexScopesInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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">
|
||||||
|
<fieldset class="w-full">
|
||||||
|
<legend class="label">
|
||||||
|
<span class="label-text">Type(s)</span>
|
||||||
|
<span class="label-text-alt text-error"
|
||||||
|
>{!hasAtLeastOneType ? 'At least one type required' : ''}</span
|
||||||
|
>
|
||||||
|
</legend>
|
||||||
|
<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>
|
||||||
|
</fieldset>
|
||||||
|
</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={loadingDexes}
|
||||||
|
>
|
||||||
|
<option value={null}>All Games</option>
|
||||||
|
{#if loadingDexes}
|
||||||
|
<option disabled>Loading games...</option>
|
||||||
|
{:else if gameList.length > 0}
|
||||||
|
{#each gameList as game}
|
||||||
|
<option value={game.displayName}>{game.displayName}</option>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#each gameOrder.length > 0 ? gameOrder : Object.keys(gameDexes) as game}
|
||||||
|
<option value={game}>{game}</option>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if pokedex.gameScope}
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<fieldset class="w-full">
|
||||||
|
<legend class="label">
|
||||||
|
<span class="label-text">Dex Scope</span>
|
||||||
|
<span class="label-text-alt text-error"
|
||||||
|
>{!hasDexScope ? 'Select at least one dex' : ''}</span
|
||||||
|
>
|
||||||
|
</legend>
|
||||||
|
{#if availableDexes.length === 0}
|
||||||
|
<p class="text-sm text-error">No dexes found for this game.</p>
|
||||||
|
{:else}
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
{#each availableDexes as dex}
|
||||||
|
<label class="label cursor-pointer justify-start gap-3">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox"
|
||||||
|
value={dex.id}
|
||||||
|
bind:group={pokedex.dexScopes}
|
||||||
|
/>
|
||||||
|
<span class="label-text">{dex.displayName}</span>
|
||||||
|
</label>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<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,111 @@
|
|||||||
|
<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 bg-primary text-primary-content">
|
||||||
|
<button class="close-button" on:click={onClose} aria-label="Close"> ✕ </button>
|
||||||
|
<div class="modal-content">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="modal-backdrop bg-black/50"
|
||||||
|
aria-label="Close modal"
|
||||||
|
on:click={handleBackdropClick}
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.modal-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-box-custom {
|
||||||
|
max-width: 72rem;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 90vh;
|
||||||
|
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,94 +1,544 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import type { CatchRecord } from '$lib/models/CatchRecord';
|
import type { CatchRecord } from '$lib/models/CatchRecord';
|
||||||
import type { CombinedData } from '$lib/models/CombinedData';
|
import type { CombinedData } from '$lib/models/CombinedData';
|
||||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
import type { SharedCatchStatus, SharedCombinedData } from '$lib/models/SharedPokedex';
|
||||||
|
import { calculateBoxPlacement } from '$lib/utils/boxPlacement';
|
||||||
import PokemonSprite from '$lib/components/PokemonSprite.svelte';
|
import PokemonSprite from '$lib/components/PokemonSprite.svelte';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
|
|
||||||
export let showShiny = false;
|
export let showShiny = false;
|
||||||
export let combinedData: CombinedData[] | null;
|
type DisplayData = CombinedData | SharedCombinedData;
|
||||||
export let boxNumbers = Array<number>;
|
type DisplayStatus = CatchRecord | SharedCatchStatus | null;
|
||||||
export let currentPlacement = 'boxPlacementForms';
|
|
||||||
|
export let combinedData: DisplayData[] | null;
|
||||||
|
export let readOnly = false;
|
||||||
|
export let boxNumbers: number[] = [];
|
||||||
export let creatingRecords = false;
|
export let creatingRecords = false;
|
||||||
export let totalRecordsCreated = 0;
|
export let totalRecordsCreated = 0;
|
||||||
export let failedToLoad = false;
|
export let failedToLoad = false;
|
||||||
export let markBoxAsNotCaught = (boxNumber: number) => {};
|
export let markBoxAsNotCaught: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsCaught = (boxNumber: number) => {};
|
export let markBoxAsCaught: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsNeedsToEvolve = (boxNumber: number) => {};
|
export let markBoxAsNeedsToEvolve: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsInHome = (boxNumber: number) => {};
|
export let markBoxAsInHome: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsNotInHome = (boxNumber: number) => {};
|
export let markBoxAsNotInHome: (boxNumber: number) => void = () => {};
|
||||||
export let createCatchRecords = () => {};
|
export let createCatchRecords = () => {};
|
||||||
|
export let onPokemonClick: (pokemon: DisplayData) => void = () => {};
|
||||||
|
|
||||||
function cellBackgroundColourClass(catchRecord: CatchRecord) {
|
let filterNotCaught = false;
|
||||||
if (catchRecord.caught) {
|
let filterNeedsToEvolve = false;
|
||||||
return 'bg-green-100/50';
|
let filterInHome = false;
|
||||||
} else if (catchRecord.haveToEvolve) {
|
let filterNotInHome = false;
|
||||||
return 'bg-yellow-100/50';
|
|
||||||
} else {
|
// Bulk actions menu state (one open menu at a time)
|
||||||
return '';
|
let openBulkMenuForBox: number | null = null;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const close = () => {
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
};
|
||||||
|
window.addEventListener('click', close);
|
||||||
|
return () => window.removeEventListener('click', close);
|
||||||
|
});
|
||||||
|
|
||||||
|
let filteredCombinedData: DisplayData[] = [];
|
||||||
|
let filteredTotal = 0;
|
||||||
|
let overallTotal = 0;
|
||||||
|
let overallCaughtCount = 0;
|
||||||
|
let overallNotCaughtCount = 0;
|
||||||
|
let overallNeedsToEvolveCount = 0;
|
||||||
|
let overallInHomeCount = 0;
|
||||||
|
let overallNotInHomeCount = 0;
|
||||||
|
let filtersActive = false;
|
||||||
|
let filtersKey = '';
|
||||||
|
|
||||||
|
function normalizedStatus(catchRecord: DisplayStatus) {
|
||||||
|
return {
|
||||||
|
caught: !!catchRecord?.caught,
|
||||||
|
needsToEvolve: !!catchRecord?.haveToEvolve,
|
||||||
|
inHome: !!catchRecord?.inHome
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchesFilters(catchRecord: DisplayStatus) {
|
||||||
|
const status = normalizedStatus(catchRecord);
|
||||||
|
if (!filtersActive) return true;
|
||||||
|
|
||||||
|
const isCaught = status.caught || status.needsToEvolve;
|
||||||
|
const isNotCaught = !isCaught;
|
||||||
|
const isNeedsToEvolve = status.needsToEvolve;
|
||||||
|
const isInHome = status.inHome;
|
||||||
|
const isNotInHome = !status.inHome;
|
||||||
|
|
||||||
|
// OR within each group, AND between groups.
|
||||||
|
const progressGroupActive = filterNotCaught || filterNeedsToEvolve;
|
||||||
|
const homeGroupActive = filterInHome || filterNotInHome;
|
||||||
|
|
||||||
|
const progressMatch =
|
||||||
|
!progressGroupActive ||
|
||||||
|
(filterNotCaught && isNotCaught) ||
|
||||||
|
(filterNeedsToEvolve && isNeedsToEvolve);
|
||||||
|
|
||||||
|
const homeMatch =
|
||||||
|
!homeGroupActive || (filterInHome && isInHome) || (filterNotInHome && isNotInHome);
|
||||||
|
|
||||||
|
return progressMatch && homeMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: filtersActive = filterNotCaught || filterNeedsToEvolve || filterInHome || filterNotInHome;
|
||||||
|
$: filtersKey = `${filterNotCaught}-${filterNeedsToEvolve}-${filterInHome}-${filterNotInHome}`;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
// Ensure this recalculates when any filter changes (Svelte doesn't track function internals).
|
||||||
|
filtersKey;
|
||||||
|
filteredCombinedData = combinedData
|
||||||
|
? combinedData.filter(({ catchRecord }) => matchesFilters(catchRecord))
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$: filteredTotal = filteredCombinedData.length;
|
||||||
|
$: overallTotal = combinedData?.length ?? 0;
|
||||||
|
|
||||||
|
$: overallCaughtCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||||
|
const status = normalizedStatus(catchRecord);
|
||||||
|
// Treat "needs to evolve" as a subset of "caught" (it is caught, just not finished).
|
||||||
|
return acc + (status.caught || status.needsToEvolve ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
$: overallNotCaughtCount = overallTotal - overallCaughtCount;
|
||||||
|
|
||||||
|
$: overallNeedsToEvolveCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||||
|
return acc + (normalizedStatus(catchRecord).needsToEvolve ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
$: overallInHomeCount = (combinedData ?? []).reduce((acc, { catchRecord }) => {
|
||||||
|
return acc + (normalizedStatus(catchRecord).inHome ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
$: overallNotInHomeCount = overallTotal - overallInHomeCount;
|
||||||
|
|
||||||
|
const BOX_VIEW_LAYOUT_STORAGE_KEY = 'livingdex:boxViewLayout:v1';
|
||||||
|
type BoxViewLayout = 'comfortable' | 'compact' | 'ultra';
|
||||||
|
let boxViewLayout: BoxViewLayout = 'comfortable';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(BOX_VIEW_LAYOUT_STORAGE_KEY);
|
||||||
|
if (stored === 'comfortable' || stored === 'compact' || stored === 'ultra') {
|
||||||
|
boxViewLayout = stored;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore (privacy mode / disabled storage)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function persistBoxViewLayout(next: BoxViewLayout) {
|
||||||
|
boxViewLayout = next;
|
||||||
|
try {
|
||||||
|
localStorage.setItem(BOX_VIEW_LAYOUT_STORAGE_KEY, next);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cellBackgroundColourStyle(pokedexEntry: PokedexEntry, catchRecord: CatchRecord) {
|
function onBoxViewLayoutChange(event: Event) {
|
||||||
if (catchRecord.caught || catchRecord.haveToEvolve) {
|
const next = (event.currentTarget as HTMLSelectElement).value;
|
||||||
|
if (next === 'comfortable' || next === 'compact' || next === 'ultra') {
|
||||||
|
persistBoxViewLayout(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: boxesPerRow = boxViewLayout === 'comfortable' ? 2 : boxViewLayout === 'compact' ? 3 : 4;
|
||||||
|
$: cellPaddingRem =
|
||||||
|
boxViewLayout === 'comfortable' ? 1 : boxViewLayout === 'compact' ? 0.6 : 0.45;
|
||||||
|
$: spriteSizePx = boxViewLayout === 'comfortable' ? 64 : boxViewLayout === 'compact' ? 52 : 44;
|
||||||
|
|
||||||
|
function cellStatusClasses(catchRecord: DisplayStatus) {
|
||||||
|
// Keep borders/layout unchanged; rely on clearer fills + badges instead.
|
||||||
|
if (catchRecord?.caught) {
|
||||||
|
// Match legend (green-600) while keeping sprites readable.
|
||||||
|
return 'bg-green-600/15';
|
||||||
|
}
|
||||||
|
if (catchRecord?.haveToEvolve) {
|
||||||
|
// Match legend (yellow-500) while keeping sprites readable.
|
||||||
|
return 'bg-yellow-500/20';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusLabel(catchRecord: DisplayStatus) {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (catchRecord?.caught) parts.push('Caught');
|
||||||
|
if (catchRecord?.haveToEvolve) parts.push('Needs to evolve');
|
||||||
|
if (catchRecord?.inHome) parts.push('In HOME');
|
||||||
|
return parts.length ? parts.join(', ') : 'Not caught';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cellBackgroundColourStyle(index: number, catchRecord: DisplayStatus) {
|
||||||
|
if (catchRecord?.caught || catchRecord?.haveToEvolve) {
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
if (pokedexEntry[currentPlacement].column % 2 === 0) {
|
const placement = calculateBoxPlacement(index);
|
||||||
return 'background-color: #ffffff';
|
if (placement.column % 2 === 0) {
|
||||||
|
return 'background-color: var(--ld-box-bg-even);';
|
||||||
} else {
|
} else {
|
||||||
return 'background-color: #f9f9f9;';
|
return 'background-color: var(--ld-box-bg-odd);';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onInHomeFilterChange(event: Event) {
|
||||||
|
const checked = (event.currentTarget as HTMLInputElement).checked;
|
||||||
|
if (checked) filterNotInHome = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNotInHomeFilterChange(event: Event) {
|
||||||
|
const checked = (event.currentTarget as HTMLInputElement).checked;
|
||||||
|
if (checked) filterInHome = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const POKEMON_PER_BOX = 30;
|
||||||
|
const BOX_POSITIONS = Array.from({ length: POKEMON_PER_BOX }, (_, i) => i);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="flex-1 p-4 w-full">
|
<main class="flex-1 p-4 w-full">
|
||||||
<div class="max-w-fit mx-auto">
|
<div class="max-w-fit mx-auto">
|
||||||
{#if combinedData && combinedData.length > 0}
|
{#if combinedData && combinedData.length > 0}
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto">
|
||||||
<div class="flex flex-wrap -mx-2">
|
<div class="card bg-base-100 shadow mb-4">
|
||||||
|
<div class="card-body p-4 flex flex-col gap-3">
|
||||||
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
|
<label class="label p-0" for="box-view-layout">
|
||||||
|
<span class="label-text font-semibold">Box view layout</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="box-view-layout"
|
||||||
|
class="select select-bordered select-sm"
|
||||||
|
bind:value={boxViewLayout}
|
||||||
|
on:change={onBoxViewLayoutChange}
|
||||||
|
aria-label="Choose box view layout density"
|
||||||
|
>
|
||||||
|
<option value="comfortable">Comfortable (2 boxes/row)</option>
|
||||||
|
<option value="compact">Compact (3 boxes/row)</option>
|
||||||
|
<option value="ultra">Ultra (4 boxes/row)</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2 text-sm">
|
||||||
|
<span class="font-semibold">Legend:</span>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center gap-1 rounded-full border border-green-700 bg-green-600 px-2 py-0.5 text-white"
|
||||||
|
>
|
||||||
|
<span class="status-badge status-badge--caught" aria-hidden="true">
|
||||||
|
<svg
|
||||||
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>Caught</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center gap-1 rounded-full border border-yellow-700 bg-yellow-500 px-2 py-0.5 text-white"
|
||||||
|
>
|
||||||
|
<span class="status-badge status-badge--evolve" aria-hidden="true">
|
||||||
|
<svg
|
||||||
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12 19V5" />
|
||||||
|
<path d="M5 12l7-7 7 7" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>Caught but needs to evolve</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center gap-1 rounded-full border border-sky-700 bg-sky-600 px-2 py-0.5 text-white"
|
||||||
|
>
|
||||||
|
<span class="status-badge status-badge--home" aria-hidden="true">
|
||||||
|
<svg
|
||||||
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="#ffffff"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12 3 3 10.5V21a1 1 0 0 0 1 1h5v-6h6v6h5a1 1 0 0 0 1-1V10.5L12 3Z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>In Home</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<div class="flex flex-wrap items-center gap-4">
|
||||||
|
<span class="font-semibold">Filters:</span>
|
||||||
|
<label class="label cursor-pointer gap-2 p-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox checkbox-sm"
|
||||||
|
bind:checked={filterNotCaught}
|
||||||
|
/>
|
||||||
|
<span class="label-text">Not caught</span>
|
||||||
|
</label>
|
||||||
|
<label class="label cursor-pointer gap-2 p-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox checkbox-sm"
|
||||||
|
bind:checked={filterNeedsToEvolve}
|
||||||
|
/>
|
||||||
|
<span class="label-text">Needs to evolve</span>
|
||||||
|
</label>
|
||||||
|
<label class="label cursor-pointer gap-2 p-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox checkbox-sm"
|
||||||
|
bind:checked={filterInHome}
|
||||||
|
on:change={onInHomeFilterChange}
|
||||||
|
/>
|
||||||
|
<span class="label-text">In HOME</span>
|
||||||
|
</label>
|
||||||
|
<label class="label cursor-pointer gap-2 p-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox checkbox-sm"
|
||||||
|
bind:checked={filterNotInHome}
|
||||||
|
on:change={onNotInHomeFilterChange}
|
||||||
|
/>
|
||||||
|
<span class="label-text">Not in HOME</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-wrap items-center justify-between gap-2 rounded-box bg-base-200/60 px-3 py-2"
|
||||||
|
>
|
||||||
|
<div class="text-sm text-base-content/70">
|
||||||
|
<span class="font-semibold text-base-content">Caught</span>
|
||||||
|
{overallCaughtCount}
|
||||||
|
<span class="mx-2">•</span>
|
||||||
|
<span class="font-semibold text-base-content">Not caught</span>
|
||||||
|
{overallNotCaughtCount}
|
||||||
|
<span class="mx-2">•</span>
|
||||||
|
<span class="font-semibold text-base-content">Needs to evolve</span>
|
||||||
|
{overallNeedsToEvolveCount}
|
||||||
|
<span class="mx-2">•</span>
|
||||||
|
<span class="font-semibold text-base-content">In HOME</span>
|
||||||
|
{overallInHomeCount}
|
||||||
|
<span class="mx-2">•</span>
|
||||||
|
<span class="font-semibold text-base-content">Not in HOME</span>
|
||||||
|
{overallNotInHomeCount}
|
||||||
|
</div>
|
||||||
|
<div class="badge badge-outline">
|
||||||
|
Showing {filteredTotal} of {overallTotal}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="boxes-grid"
|
||||||
|
style="--boxes-per-row: {boxesPerRow}; --cell-padding: {cellPaddingRem}rem; --sprite-size: {spriteSizePx}px;"
|
||||||
|
>
|
||||||
{#each boxNumbers as boxNumber}
|
{#each boxNumbers as boxNumber}
|
||||||
<div class="mb-8 md:w-1/2 px-2">
|
{@const bulkMenuId = `box-${boxNumber}-bulk-menu`}
|
||||||
<h2 class="text-xl font-bold mb-4">Box {boxNumber}</h2>
|
<div class="mb-8">
|
||||||
<button class="btn" on:click={markBoxAsNotCaught(boxNumber)}>
|
<div class="flex flex-wrap items-center justify-between gap-3 mb-4 relative z-20">
|
||||||
Mark box as not 'caught'
|
<h2 class="text-xl font-bold">Box {boxNumber}</h2>
|
||||||
</button>
|
{#if !readOnly}<div class="relative">
|
||||||
<button class="btn" on:click={markBoxAsCaught(boxNumber)}>
|
<button
|
||||||
Mark box as 'caught'
|
type="button"
|
||||||
</button>
|
class="btn btn-sm btn-outline relative z-[210]"
|
||||||
<button class="btn" on:click={markBoxAsNeedsToEvolve(boxNumber)}
|
aria-label="Open bulk actions menu"
|
||||||
>Mark box as 'needs to evolve'</button
|
aria-haspopup="menu"
|
||||||
>
|
aria-controls={bulkMenuId}
|
||||||
<button class="btn" on:click={markBoxAsInHome(boxNumber)}
|
aria-expanded={openBulkMenuForBox === boxNumber}
|
||||||
>Mark box as 'in Home'</button
|
on:click={(event) => {
|
||||||
>
|
event.stopPropagation();
|
||||||
<button class="btn" on:click={markBoxAsNotInHome(boxNumber)}
|
openBulkMenuForBox = openBulkMenuForBox === boxNumber ? null : boxNumber;
|
||||||
>Mark box as not 'in Home'</button
|
}}
|
||||||
>
|
on:keydown={(event) => {
|
||||||
|
if (event.key === 'Escape') openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
⋯
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{#if openBulkMenuForBox === boxNumber}
|
||||||
|
<ul
|
||||||
|
id={bulkMenuId}
|
||||||
|
class="menu bg-base-100 rounded-box absolute right-0 mt-2 z-[220] w-56 p-2 shadow border border-base-300"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
markBoxAsNotCaught(boxNumber);
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mark box as Not caught
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
markBoxAsCaught(boxNumber);
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mark box as Caught
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
markBoxAsNeedsToEvolve(boxNumber);
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mark box as Needs to evolve
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
markBoxAsInHome(boxNumber);
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mark box as In HOME
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
markBoxAsNotInHome(boxNumber);
|
||||||
|
openBulkMenuForBox = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mark box as Not in HOME
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>{/if}
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-6">
|
<div class="grid grid-cols-6">
|
||||||
{#each combinedData as { pokedexEntry, catchRecord }}
|
{#each BOX_POSITIONS as positionInBox}
|
||||||
{#if pokedexEntry[currentPlacement].box === boxNumber}
|
{@const globalIndex = (boxNumber - 1) * POKEMON_PER_BOX + positionInBox}
|
||||||
<div
|
{@const placement = calculateBoxPlacement(globalIndex)}
|
||||||
class="pokemon-box {cellBackgroundColourClass(catchRecord)}"
|
{@const entry = combinedData?.[globalIndex]}
|
||||||
style="grid-column-start: {pokedexEntry[currentPlacement]
|
{@const pokedexEntry = entry?.pokedexEntry}
|
||||||
.column}; grid-row-start: {pokedexEntry[currentPlacement].row};
|
{@const catchRecord = entry?.catchRecord ?? null}
|
||||||
{cellBackgroundColourStyle(pokedexEntry, catchRecord)}"
|
{@const isFilteredOut =
|
||||||
|
!!entry && filtersActive && !!filtersKey && !matchesFilters(catchRecord)}
|
||||||
|
{#if entry && pokedexEntry}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="pokemon-box {cellStatusClasses(catchRecord)} {isFilteredOut
|
||||||
|
? 'pokemon-box--filtered-out'
|
||||||
|
: '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(globalIndex, catchRecord)}"
|
||||||
|
aria-disabled={isFilteredOut}
|
||||||
|
on:click={() => {
|
||||||
|
if (!isFilteredOut) onPokemonClick({ pokedexEntry, catchRecord });
|
||||||
|
}}
|
||||||
|
aria-label="View details for {pokedexEntry.pokemon}. Status: {statusLabel(
|
||||||
|
catchRecord
|
||||||
|
)}"
|
||||||
>
|
>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<div slot="hover-target">
|
<div slot="hover-target" class="w-full h-full">
|
||||||
{#if catchRecord.inHome}
|
{#if catchRecord?.caught}
|
||||||
<span
|
<span
|
||||||
class="absolute -top-5 -right-4 z-2 p-1 text-secondary text-lg font-extrabold"
|
class="status-badge status-badge--caught absolute left-0.5 status-badge-top z-10"
|
||||||
>✓</span
|
title="Caught"
|
||||||
>
|
>
|
||||||
|
<svg
|
||||||
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Caught</span>
|
||||||
|
</span>
|
||||||
|
{:else if catchRecord?.haveToEvolve}
|
||||||
|
<span
|
||||||
|
class="status-badge status-badge--evolve absolute left-0.5 status-badge-top z-10"
|
||||||
|
title="Caught but needs to evolve"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M12 19V5" />
|
||||||
|
<path d="M5 12l7-7 7 7" />
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Caught but needs to evolve</span>
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<PokemonSprite
|
{#if catchRecord?.inHome}
|
||||||
pokemonName={pokedexEntry.pokemon}
|
<span
|
||||||
pokedexNumber={pokedexEntry.pokedexNumber}
|
class="status-badge status-badge--home absolute right-0.5 status-badge-top z-10"
|
||||||
form={pokedexEntry.form}
|
title="In Pokémon HOME"
|
||||||
shiny={showShiny}
|
>
|
||||||
/>
|
<svg
|
||||||
<span class="md:hidden">ⓘ</span>
|
class="status-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 3 3 10.5V21a1 1 0 0 0 1 1h5v-6h6v6h5a1 1 0 0 0 1-1V10.5L12 3Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">In HOME</span>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
<div class="pokemon-box-inner">
|
||||||
|
<PokemonSprite
|
||||||
|
pokemonName={pokedexEntry.pokemon}
|
||||||
|
pokedexNumber={pokedexEntry.pokedexNumber}
|
||||||
|
form={pokedexEntry.form}
|
||||||
|
spriteKey={pokedexEntry.spriteKey}
|
||||||
|
shiny={showShiny}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div slot="tooltip">
|
<div slot="tooltip">
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
@@ -97,13 +547,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</div>
|
<div>{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</div>
|
||||||
<div>
|
<div>
|
||||||
Caught: {catchRecord.caught ? 'Yes' : 'No'} <br />
|
Caught: {catchRecord?.caught ? 'Yes' : 'No'} <br />
|
||||||
Needs to Evolve: {catchRecord.haveToEvolve ? 'Yes' : 'No'} <br />
|
Caught but needs to Evolve: {catchRecord?.haveToEvolve ? 'Yes' : 'No'}
|
||||||
In Home: {catchRecord.inHome ? 'Yes' : 'No'}
|
<br />
|
||||||
|
In Home: {catchRecord?.inHome ? 'Yes' : 'No'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="pokemon-box pokemon-box--empty"
|
||||||
|
disabled
|
||||||
|
style="grid-column-start: {placement.column}; grid-row-start: {placement.row};
|
||||||
|
{cellBackgroundColourStyle(globalIndex, null)}"
|
||||||
|
aria-label="Empty box slot"
|
||||||
|
>
|
||||||
|
<div class="pokemon-box-inner" aria-hidden="true">
|
||||||
|
<span class="sprite-placeholder" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -124,7 +588,9 @@
|
|||||||
If you're seeing this, you probably haven't created your Pokédex data yet. Please do so by
|
If you're seeing this, you probably haven't created your Pokédex data yet. Please do so by
|
||||||
clicking this button.
|
clicking this button.
|
||||||
</p>
|
</p>
|
||||||
<button class="btn" on:click={createCatchRecords}>Create Pokédex data</button>
|
{#if !readOnly}
|
||||||
|
<button class="btn" on:click={createCatchRecords}>Create Pokédex data</button>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="min-w-max mx-auto">
|
<div class="min-w-max mx-auto">
|
||||||
@@ -136,8 +602,150 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/*
|
||||||
|
Theme-aware backgrounds for non-caught box slots.
|
||||||
|
- Light mode (`pokeball`) keeps the original exact colors.
|
||||||
|
- Dark mode maps to DaisyUI theme base tokens so it stays consistent with the active theme.
|
||||||
|
*/
|
||||||
|
:global(:root) {
|
||||||
|
--ld-box-bg-even: var(--fallback-b1, oklch(var(--b1) / 1));
|
||||||
|
--ld-box-bg-odd: var(--fallback-b3, oklch(var(--b3) / 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme='pokeball']) {
|
||||||
|
--ld-box-bg-even: #ffffff;
|
||||||
|
--ld-box-bg-odd: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxes-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.boxes-grid {
|
||||||
|
grid-template-columns: repeat(var(--boxes-per-row), minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.pokemon-box {
|
.pokemon-box {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
padding: 1rem;
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
/*
|
||||||
|
On small screens the grid columns get narrow; without an explicit ratio,
|
||||||
|
the button height becomes content-driven (padding + sprite) and you end up
|
||||||
|
with rectangular cells. Force each slot to be square.
|
||||||
|
*/
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pokemon-box--filtered-out {
|
||||||
|
opacity: 0.25;
|
||||||
|
filter: grayscale(1);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pokemon-box--empty {
|
||||||
|
opacity: 0.55;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pokemon-box-inner {
|
||||||
|
padding: var(--cell-padding, 1rem);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pokemon-box :global(img) {
|
||||||
|
width: var(--sprite-size, 64px);
|
||||||
|
height: var(--sprite-size, 64px);
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprite-placeholder {
|
||||||
|
width: var(--sprite-size, 64px);
|
||||||
|
height: var(--sprite-size, 64px);
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: keep 6 columns but prevent sprite/padding from forcing tall cells */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.pokemon-box-inner {
|
||||||
|
/* cap padding so the sprite can fit inside small squares */
|
||||||
|
padding: min(var(--cell-padding, 1rem), 0.35rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pokemon-box :global(img) {
|
||||||
|
/* allow the sprite to shrink with the square cell */
|
||||||
|
width: min(var(--sprite-size, 64px), 100%);
|
||||||
|
height: min(var(--sprite-size, 64px), 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprite-placeholder {
|
||||||
|
width: min(var(--sprite-size, 64px), 100%);
|
||||||
|
height: min(var(--sprite-size, 64px), 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
width: 0.95rem;
|
||||||
|
height: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-top {
|
||||||
|
top: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
width: 0.85rem;
|
||||||
|
height: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.15rem;
|
||||||
|
height: 1.15rem;
|
||||||
|
border-radius: 0.35rem;
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-top {
|
||||||
|
top: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge--caught {
|
||||||
|
color: #16a34a; /* green-600 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge--evolve {
|
||||||
|
color: #eab308; /* yellow-500 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge--home {
|
||||||
|
color: #0284c7; /* sky-600 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,51 +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 = () => {};
|
|
||||||
</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}
|
|
||||||
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>
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
// Supabase-based CatchRecord interface
|
||||||
|
export interface CatchRecord {
|
||||||
export interface CatchRecord extends Document {
|
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
||||||
userId: string;
|
userId: string;
|
||||||
pokedexEntryId: Types.ObjectId;
|
pokemonId: string;
|
||||||
|
pokedexId: string;
|
||||||
haveToEvolve: boolean;
|
haveToEvolve: boolean;
|
||||||
caught: boolean;
|
caught: boolean;
|
||||||
inHome: boolean;
|
inHome: boolean;
|
||||||
@@ -10,15 +11,18 @@ export interface CatchRecord extends Document {
|
|||||||
personalNotes: string;
|
personalNotes: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const catchRecordSchema = new Schema<CatchRecord>({
|
// Database record type for Supabase
|
||||||
userId: String,
|
export interface CatchRecordDB {
|
||||||
pokedexEntryId: { type: Schema.Types.ObjectId, ref: 'PokedexEntry', required: true },
|
id: string;
|
||||||
haveToEvolve: { type: Boolean, default: false },
|
userId: string;
|
||||||
caught: { type: Boolean, default: false },
|
/** Numeric foreign key in database (references `pokemon.id`) */
|
||||||
inHome: { type: Boolean, default: false },
|
pokemonId: number;
|
||||||
hasGigantamaxed: { type: Boolean, default: false },
|
pokedexId: string; // NEW: Foreign key to pokedexes table
|
||||||
personalNotes: String
|
haveToEvolve: boolean;
|
||||||
});
|
caught: boolean;
|
||||||
|
inHome: boolean;
|
||||||
const CatchRecordModel = mongoose.model<CatchRecord>('CatchRecord', catchRecordSchema);
|
hasGigantamaxed: boolean;
|
||||||
export default CatchRecordModel;
|
personalNotes: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import { type CatchRecord } from './CatchRecord';
|
|||||||
|
|
||||||
export interface CombinedData {
|
export interface CombinedData {
|
||||||
pokedexEntry: PokedexEntry;
|
pokedexEntry: PokedexEntry;
|
||||||
catchRecord: CatchRecord;
|
catchRecord: CatchRecord | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { CombinedData } from './CombinedData';
|
||||||
|
import type { Pokedex } from './Pokedex';
|
||||||
|
|
||||||
|
export const OFFLINE_SNAPSHOT_VERSION = 1;
|
||||||
|
|
||||||
|
export type OfflinePokedexSnapshot = {
|
||||||
|
pokedex: Pokedex;
|
||||||
|
entries: CombinedData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OfflineSnapshot = {
|
||||||
|
version: typeof OFFLINE_SNAPSHOT_VERSION;
|
||||||
|
generatedAt: string;
|
||||||
|
userId: string;
|
||||||
|
pokedexes: OfflinePokedexSnapshot[];
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
export interface Pokedex {
|
||||||
|
_id: string;
|
||||||
|
shareToken: string;
|
||||||
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
isLivingDex: boolean;
|
||||||
|
isShinyDex: boolean;
|
||||||
|
isOriginDex: boolean;
|
||||||
|
isFormDex: boolean;
|
||||||
|
gameScope: string | null;
|
||||||
|
dexScopes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PokedexDB {
|
||||||
|
id: string;
|
||||||
|
shareToken: string;
|
||||||
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
isLivingDex: boolean;
|
||||||
|
isShinyDex: boolean;
|
||||||
|
isOriginDex: boolean;
|
||||||
|
isFormDex: boolean;
|
||||||
|
gameScope: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
@@ -1,51 +1,42 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
// Supabase-based PokedexEntry interface
|
||||||
|
export type CatchInformationItem = {
|
||||||
|
game: string;
|
||||||
|
location: string;
|
||||||
|
notes: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface PokedexEntry extends Document {
|
export interface PokedexEntry {
|
||||||
|
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
||||||
pokedexNumber: number;
|
pokedexNumber: number;
|
||||||
boxPlacement: { box: number; row: number; column: number };
|
|
||||||
boxPlacementForms: { box: number; row: number; column: number };
|
|
||||||
pokemon: string;
|
pokemon: string;
|
||||||
form: string;
|
form: string;
|
||||||
|
spriteKey: string;
|
||||||
canGigantamax: boolean;
|
canGigantamax: boolean;
|
||||||
regionToCatchIn: string;
|
regionToCatchIn: string;
|
||||||
gamesToCatchIn: string[];
|
gamesToCatchIn: string[];
|
||||||
regionToEvolveIn: string;
|
regionToEvolveIn: string;
|
||||||
evolutionInformation: string;
|
evolutionInformation: string;
|
||||||
catchInformation: [
|
catchInformation: Array<string | CatchInformationItem>;
|
||||||
{
|
notes: string;
|
||||||
game: string;
|
// Note: Regional dex numbers stored in separate regional_dex_numbers table
|
||||||
location: string;
|
|
||||||
notes: string;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pokedexEntrySchema = new Schema<PokedexEntry>({
|
// Database record type for Supabase
|
||||||
pokedexNumber: Number,
|
export interface PokedexEntryDB {
|
||||||
boxPlacement: {
|
id: number;
|
||||||
box: Number,
|
pokedexNumber: number;
|
||||||
row: Number,
|
pokemon: string;
|
||||||
column: Number
|
form: string | null;
|
||||||
},
|
spriteKey: string | null;
|
||||||
boxPlacementForms: {
|
canGigantamax: boolean;
|
||||||
box: Number,
|
isDefaultForm: boolean;
|
||||||
row: Number,
|
regionToCatchIn: string | null;
|
||||||
column: Number
|
gamesToCatchIn: string[] | null;
|
||||||
},
|
regionToEvolveIn: string | null;
|
||||||
pokemon: String,
|
evolutionInformation: string | null;
|
||||||
form: String,
|
catchInformation: string[] | null;
|
||||||
canGigantamax: Boolean,
|
notes: string | null;
|
||||||
regionToCatchIn: String,
|
// Note: Regional dex numbers stored in separate regional_dex_numbers table
|
||||||
gamesToCatchIn: Array<string>,
|
createdAt: string;
|
||||||
regionToEvolveIn: String,
|
updatedAt: string;
|
||||||
evolutionInformation: String,
|
}
|
||||||
catchInformation: [
|
|
||||||
{
|
|
||||||
game: String,
|
|
||||||
location: String,
|
|
||||||
notes: String
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<PokedexEntry>('PokedexEntry', pokedexEntrySchema);
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export type ExportProvider = 'google_drive' | 'dropbox';
|
||||||
|
|
||||||
|
export interface PokedexExportIntegration {
|
||||||
|
_id: string;
|
||||||
|
userId: string;
|
||||||
|
pokedexId: string | null;
|
||||||
|
provider: ExportProvider;
|
||||||
|
enabled: boolean;
|
||||||
|
fileName: string | null;
|
||||||
|
folderId: string | null;
|
||||||
|
path: string | null;
|
||||||
|
accessToken: string;
|
||||||
|
refreshToken: string | null;
|
||||||
|
accessTokenExpiresAt: string | null;
|
||||||
|
metadata: Record<string, unknown> | null;
|
||||||
|
lastExportedAt: string | null;
|
||||||
|
lastError: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PokedexExportIntegrationDB {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
pokedexId: string | null;
|
||||||
|
provider: ExportProvider;
|
||||||
|
enabled: boolean;
|
||||||
|
fileName: string | null;
|
||||||
|
folderId: string | null;
|
||||||
|
path: string | null;
|
||||||
|
accessToken: string;
|
||||||
|
refreshToken: string | null;
|
||||||
|
accessTokenExpiresAt: string | null;
|
||||||
|
metadata: Record<string, unknown> | null;
|
||||||
|
lastExportedAt: string | null;
|
||||||
|
lastError: string | null;
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
|
||||||
|
|
||||||
export interface PokedexMetadata extends Document {
|
|
||||||
lastModified: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pokedexMetadataSchema = new Schema<PokedexMetadata>({
|
|
||||||
lastModified: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<PokedexMetadata>('PokedexMetadata', pokedexMetadataSchema);
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
// Supabase-based RegionGameMapping interface
|
||||||
|
export interface RegionGameMapping {
|
||||||
export interface RegionGameMapping extends Document {
|
id?: number;
|
||||||
region: string;
|
region: string;
|
||||||
games: string[];
|
game: string; // Note: Different from MongoDB version which had 'games' array
|
||||||
}
|
}
|
||||||
|
|
||||||
const regionGameMappingSchema = new Schema<RegionGameMapping>({
|
// Database record type for Supabase
|
||||||
region: String,
|
export interface RegionGameMappingDB {
|
||||||
games: Array<string>
|
id: number;
|
||||||
});
|
region: string;
|
||||||
|
game: string;
|
||||||
export default mongoose.model<RegionGameMapping>('RegionGameMapping', regionGameMappingSchema);
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import type { PokedexEntry } from './PokedexEntry';
|
||||||
|
|
||||||
|
export interface SharedCatchStatus {
|
||||||
|
pokemonId: string;
|
||||||
|
caught: boolean;
|
||||||
|
haveToEvolve: boolean;
|
||||||
|
inHome: boolean;
|
||||||
|
hasGigantamaxed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SharedPokedexMetadata {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
isLivingDex: boolean;
|
||||||
|
isShinyDex: boolean;
|
||||||
|
isOriginDex: boolean;
|
||||||
|
isFormDex: boolean;
|
||||||
|
gameScope: string | null;
|
||||||
|
dexScopes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SharedPokedexRpcData extends SharedPokedexMetadata {
|
||||||
|
catchStatuses: SharedCatchStatus[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SharedCombinedData {
|
||||||
|
pokedexEntry: PokedexEntry;
|
||||||
|
catchRecord: SharedCatchStatus | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SharedPokedexData extends SharedPokedexMetadata {
|
||||||
|
combinedData: SharedCombinedData[];
|
||||||
|
total: number;
|
||||||
|
caught: number;
|
||||||
|
completionPercentage: number;
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
|
||||||
|
|
||||||
export interface User extends Document {
|
|
||||||
username: string;
|
|
||||||
// Add other user-related fields as needed
|
|
||||||
}
|
|
||||||
|
|
||||||
const userSchema = new Schema<User>({
|
|
||||||
username: String
|
|
||||||
// Add other user-related fields as needed
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<User>('User', userSchema);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
|
||||||
|
|
||||||
export interface UserCatchEntry extends Document {
|
|
||||||
userId: mongoose.Types.ObjectId; // Reference to the user
|
|
||||||
pokedexEntryId: mongoose.Types.ObjectId; // Reference to the pokedex entry
|
|
||||||
haveToEvolve: boolean;
|
|
||||||
caught: boolean;
|
|
||||||
inHome: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userCatchEntrySchema = new Schema<UserCatchEntry>({
|
|
||||||
userId: { type: mongoose.Types.ObjectId, ref: 'User' },
|
|
||||||
pokedexEntryId: { type: mongoose.Types.ObjectId, ref: 'PokedexEntry' },
|
|
||||||
haveToEvolve: Boolean,
|
|
||||||
caught: Boolean,
|
|
||||||
inHome: Boolean
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<UserCatchEntry>('UserCatchEntry', userCatchEntrySchema);
|
|
||||||
@@ -1,34 +1,234 @@
|
|||||||
import CatchRecordModel, { type CatchRecord } from '$lib/models/CatchRecord';
|
import type { CatchRecord, CatchRecordDB } from '$lib/models/CatchRecord';
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
type PartialExcept<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||||
|
|
||||||
class CatchRecordRepository {
|
class CatchRecordRepository {
|
||||||
|
constructor(
|
||||||
|
private supabase: SupabaseClient,
|
||||||
|
private userId: string,
|
||||||
|
private pokedexId: string
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// Transform Supabase data to frontend format (minimal transformation)
|
||||||
|
private transformCatchRecord(record: CatchRecordDB): CatchRecord {
|
||||||
|
return {
|
||||||
|
_id: record.id,
|
||||||
|
userId: record.userId,
|
||||||
|
pokemonId: record.pokemonId.toString(),
|
||||||
|
pokedexId: record.pokedexId,
|
||||||
|
haveToEvolve: record.haveToEvolve,
|
||||||
|
caught: record.caught,
|
||||||
|
inHome: record.inHome,
|
||||||
|
hasGigantamaxed: record.hasGigantamaxed,
|
||||||
|
personalNotes: record.personalNotes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform frontend data to database format (minimal transformation)
|
||||||
|
private transformToDatabase(data: Partial<CatchRecord>): Partial<CatchRecordDB> {
|
||||||
|
const dbData: Partial<CatchRecordDB> = {};
|
||||||
|
if (data.pokemonId !== undefined && data.pokemonId !== null) {
|
||||||
|
const numericId = Number(data.pokemonId);
|
||||||
|
if (isNaN(numericId)) {
|
||||||
|
throw new Error(`Invalid pokemonId: ${data.pokemonId}`);
|
||||||
|
}
|
||||||
|
dbData.pokemonId = 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;
|
||||||
|
if (data.hasGigantamaxed !== undefined) dbData.hasGigantamaxed = data.hasGigantamaxed;
|
||||||
|
if (data.personalNotes !== undefined) dbData.personalNotes = data.personalNotes;
|
||||||
|
|
||||||
|
return dbData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk upsert catch records in a single request when possible.
|
||||||
|
*
|
||||||
|
* Requires a unique constraint matching the `onConflict` columns.
|
||||||
|
* If the database is not configured with that constraint, the caller should
|
||||||
|
* catch the error and fall back to per-record upserts.
|
||||||
|
*/
|
||||||
|
async bulkUpsert(
|
||||||
|
records: Array<PartialExcept<CatchRecord, 'pokemonId'>>
|
||||||
|
): Promise<CatchRecord[]> {
|
||||||
|
if (records.length === 0) return [];
|
||||||
|
|
||||||
|
const dbRows: Partial<CatchRecordDB>[] = records.map((r, index) => {
|
||||||
|
if (r.pokemonId === undefined || r.pokemonId === null || r.pokemonId === '') {
|
||||||
|
throw new Error(
|
||||||
|
`CatchRecordRepository.bulkUpsert: record at index ${index} is missing required field "pokemonId"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preserve repository scoping; if a caller supplies ids, they must match the repo scope.
|
||||||
|
if (r.userId !== undefined && r.userId !== this.userId) {
|
||||||
|
throw new Error(
|
||||||
|
`CatchRecordRepository.bulkUpsert: record at index ${index} has userId "${r.userId}" but repository is scoped to "${this.userId}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (r.pokedexId !== undefined && r.pokedexId !== this.pokedexId) {
|
||||||
|
throw new Error(
|
||||||
|
`CatchRecordRepository.bulkUpsert: record at index ${index} has pokedexId "${r.pokedexId}" but repository is scoped to "${this.pokedexId}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapped = this.transformToDatabase(r);
|
||||||
|
// Enforce repo scope after mapping so per-record input cannot override it.
|
||||||
|
mapped.userId = this.userId;
|
||||||
|
mapped.pokedexId = this.pokedexId;
|
||||||
|
|
||||||
|
if (mapped.pokemonId === undefined || mapped.pokemonId === null) {
|
||||||
|
throw new Error(
|
||||||
|
`CatchRecordRepository.bulkUpsert: record at index ${index} produced no pokemonId after transform`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.upsert(dbRows, {
|
||||||
|
onConflict: '"userId","pokedexId","pokemonId"'
|
||||||
|
})
|
||||||
|
.select();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Supabase error bulk upserting catch records:', error);
|
||||||
|
throw new Error(`Failed to bulk upsert catch records: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result ?? []).map((row) => this.transformCatchRecord(row));
|
||||||
|
}
|
||||||
|
|
||||||
async findById(id: string): Promise<CatchRecord | null> {
|
async findById(id: string): Promise<CatchRecord | null> {
|
||||||
return CatchRecordModel.findById(id).exec();
|
const { data, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', id)
|
||||||
|
.eq('userId', this.userId)
|
||||||
|
.eq('pokedexId', this.pokedexId)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !data) return null;
|
||||||
|
return this.transformCatchRecord(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<CatchRecord[]> {
|
async findAll(): Promise<CatchRecord[]> {
|
||||||
return CatchRecordModel.find().exec();
|
const { data, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.select('*')
|
||||||
|
.eq('userId', this.userId)
|
||||||
|
.eq('pokedexId', this.pokedexId);
|
||||||
|
|
||||||
|
if (error || !data) return [];
|
||||||
|
return data.map((record) => this.transformCatchRecord(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByUserId(userId: string): Promise<CatchRecord[]> {
|
||||||
|
const { data, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.select('*')
|
||||||
|
.eq('userId', userId)
|
||||||
|
.eq('pokedexId', this.pokedexId);
|
||||||
|
|
||||||
|
if (error || !data) return [];
|
||||||
|
return data.map((record) => this.transformCatchRecord(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: Partial<CatchRecord>): Promise<CatchRecord> {
|
async create(data: Partial<CatchRecord>): Promise<CatchRecord> {
|
||||||
return CatchRecordModel.create(data);
|
const dbData = {
|
||||||
|
userId: this.userId,
|
||||||
|
pokedexId: this.pokedexId,
|
||||||
|
...this.transformToDatabase(data)
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.insert(dbData)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Supabase error creating catch record:', error);
|
||||||
|
throw new Error(`Failed to create catch record: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('Failed to create catch record: No result returned');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.transformCatchRecord(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
async update(id: string, data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
||||||
return CatchRecordModel.findByIdAndUpdate(id, data, { new: true }).exec();
|
const dbData = this.transformToDatabase(data);
|
||||||
|
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.update(dbData)
|
||||||
|
.eq('id', id)
|
||||||
|
.eq('userId', this.userId)
|
||||||
|
.eq('pokedexId', this.pokedexId)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !result) return null;
|
||||||
|
return this.transformCatchRecord(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await CatchRecordModel.findByIdAndDelete(id).exec();
|
const { error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.delete()
|
||||||
|
.eq('id', id)
|
||||||
|
.eq('userId', this.userId)
|
||||||
|
.eq('pokedexId', this.pokedexId);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error deleting catch record:', error);
|
||||||
|
throw new Error(`Failed to delete catch record: ${error.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async upsert(data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
async upsert(data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
||||||
if (data._id) {
|
if (data._id) {
|
||||||
// Update existing record
|
return this.update(data._id, data);
|
||||||
return CatchRecordModel.findByIdAndUpdate(data._id, data, { new: true }).exec();
|
} else if (data.pokemonId) {
|
||||||
} else {
|
// Try to find existing record for this user, pokemon, and pokedex
|
||||||
// Create new record
|
const existing = await this.findByUserAndPokemon(this.userId, data.pokemonId, this.pokedexId);
|
||||||
return CatchRecordModel.create(data);
|
if (existing) {
|
||||||
|
return this.update(existing._id, data);
|
||||||
|
} else {
|
||||||
|
return this.create(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return this.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByUserAndPokemon(
|
||||||
|
userId: string,
|
||||||
|
pokemonId: string,
|
||||||
|
pokedexId: string
|
||||||
|
): Promise<CatchRecord | null> {
|
||||||
|
const numericId = Number(pokemonId);
|
||||||
|
if (isNaN(numericId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { data, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.select('*')
|
||||||
|
.eq('userId', userId)
|
||||||
|
.eq('pokemonId', numericId)
|
||||||
|
.eq('pokedexId', pokedexId)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !data) return null;
|
||||||
|
return this.transformCatchRecord(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +1,502 @@
|
|||||||
import PokedexEntryModel, { type PokedexEntry } from '$lib/models/PokedexEntry';
|
import { type PokedexEntry, type PokedexEntryDB } from '$lib/models/PokedexEntry';
|
||||||
import CatchRecordModel, { type CatchRecord } from '$lib/models/CatchRecord';
|
import { type CatchRecord, type CatchRecordDB } from '$lib/models/CatchRecord';
|
||||||
import { CombinedData } from '$lib/models/CombinedData';
|
import { type CombinedData } from '$lib/models/CombinedData';
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
// Raw row from game_pokedex_entry_details includes extra ordering columns not in PokedexEntryDB.
|
||||||
|
type RawDexEntry = PokedexEntryDB & {
|
||||||
|
dexNumber: number;
|
||||||
|
dexSortOrder: number;
|
||||||
|
formSortBucket: number;
|
||||||
|
formSortRegionOrder: number;
|
||||||
|
formSortRegionalSub: number;
|
||||||
|
formSortLabel: string;
|
||||||
|
unownSortOrder: number;
|
||||||
|
};
|
||||||
|
|
||||||
class CombinedDataRepository {
|
class CombinedDataRepository {
|
||||||
async findAllCombinedData(
|
private static readonly MAX_ROWS_PER_REQUEST = 1000;
|
||||||
enableForms: boolean = true,
|
|
||||||
region: string = '',
|
constructor(
|
||||||
game: string = ''
|
private supabase: SupabaseClient,
|
||||||
): Promise<CombinedData[]> {
|
private userId: string | null,
|
||||||
const pipeline: any[] = [
|
private pokedexId: string | null
|
||||||
{
|
) {}
|
||||||
$lookup: {
|
|
||||||
from: 'catchrecords',
|
// Transform Supabase data to match frontend expectations (minimal transformation)
|
||||||
localField: '_id',
|
private transformPokedexEntry(entry: PokedexEntryDB): PokedexEntry {
|
||||||
foreignField: 'pokedexEntryId',
|
return {
|
||||||
as: 'catchRecord'
|
_id: entry.id.toString(),
|
||||||
}
|
pokedexNumber: entry.pokedexNumber,
|
||||||
},
|
pokemon: entry.pokemon,
|
||||||
{
|
form: entry.form || '',
|
||||||
$unwind: {
|
spriteKey: entry.spriteKey || '',
|
||||||
path: '$catchRecord',
|
canGigantamax: entry.canGigantamax,
|
||||||
preserveNullAndEmptyArrays: true
|
regionToCatchIn: entry.regionToCatchIn || '',
|
||||||
}
|
gamesToCatchIn: entry.gamesToCatchIn || [],
|
||||||
},
|
regionToEvolveIn: entry.regionToEvolveIn || '',
|
||||||
{
|
evolutionInformation: entry.evolutionInformation || '',
|
||||||
$sort: {
|
catchInformation: entry.catchInformation || [],
|
||||||
'boxPlacementForms.box': 1,
|
notes: entry.notes || ''
|
||||||
'boxPlacementForms.row': 1,
|
// Note: Regional dex numbers are stored in separate regional_dex_numbers table
|
||||||
'boxPlacementForms.column': 1
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
];
|
private transformCatchRecord(record: CatchRecordDB): CatchRecord {
|
||||||
|
return {
|
||||||
|
_id: record.id,
|
||||||
|
userId: record.userId,
|
||||||
|
pokemonId: record.pokemonId.toString(),
|
||||||
|
pokedexId: record.pokedexId,
|
||||||
|
haveToEvolve: record.haveToEvolve,
|
||||||
|
caught: record.caught,
|
||||||
|
inHome: record.inHome,
|
||||||
|
hasGigantamaxed: record.hasGigantamaxed,
|
||||||
|
personalNotes: record.personalNotes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildEntriesQuery(enableForms: boolean, region: string, game: string) {
|
||||||
|
let query = this.supabase.from('pokedex_entries').select('*');
|
||||||
|
|
||||||
if (!enableForms) {
|
if (!enableForms) {
|
||||||
pipeline.unshift({
|
// Filter to base forms only. Gendered species (form='male') and Unown ('A') are
|
||||||
$match: {
|
// also flagged isDefaultForm in the pokemon table, so this single check covers them.
|
||||||
'boxPlacement.box': { $ne: null }
|
query = query.eq('isDefaultForm', true);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.length > 0) {
|
if (region) {
|
||||||
pipeline.unshift({
|
query = query.eq('regionToCatchIn', region);
|
||||||
$match: {
|
|
||||||
regionToCatchIn: region
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.length > 0) {
|
if (game) {
|
||||||
pipeline.unshift({
|
query = query.contains('gamesToCatchIn', [game]);
|
||||||
$match: {
|
|
||||||
gamesToCatchIn: { $in: [game] }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const combinedData: CombinedData[] = await PokedexEntryModel.aggregate(pipeline).exec();
|
// Stable ordering from the database: national dex, Unown order, base/female/temporal, then form label.
|
||||||
|
query = query
|
||||||
|
.order('pokedexNumber', { ascending: true })
|
||||||
|
.order('unownSortOrder', { ascending: true })
|
||||||
|
.order('formSortBucket', { ascending: true })
|
||||||
|
.order('formSortRegionOrder', { ascending: true })
|
||||||
|
.order('formSortRegionalSub', { ascending: true })
|
||||||
|
.order('formSortLabel', { ascending: true });
|
||||||
|
|
||||||
// Filter out entries with no catch records
|
return query;
|
||||||
const filteredData = combinedData.filter((data) => data.catchRecord);
|
}
|
||||||
|
|
||||||
return filteredData.map((data) => ({
|
private buildDexEntriesQuery(dexScopes: string[], enableForms: boolean, region: string) {
|
||||||
pokedexEntry: data as PokedexEntry,
|
let query = this.supabase.from('game_pokedex_entry_details').select('*').in('dexId', dexScopes);
|
||||||
catchRecord: data.catchRecord as CatchRecord
|
|
||||||
}));
|
if (!enableForms) {
|
||||||
|
// Filter to base forms only. Gendered species (form='male') and Unown ('A') are
|
||||||
|
// also flagged isDefaultForm in the pokemon table, so this single check covers them.
|
||||||
|
query = query.eq('isDefaultForm', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region) {
|
||||||
|
query = query.eq('regionToCatchIn', region);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stable ordering: dex order, dex number, Unown order, base/female/temporal, then form label.
|
||||||
|
query = query
|
||||||
|
.order('dexSortOrder', { ascending: true })
|
||||||
|
.order('dexNumber', { ascending: true })
|
||||||
|
.order('unownSortOrder', { ascending: true })
|
||||||
|
.order('formSortBucket', { ascending: true })
|
||||||
|
.order('formSortRegionOrder', { ascending: true })
|
||||||
|
.order('formSortRegionalSub', { ascending: true })
|
||||||
|
.order('formSortLabel', { ascending: true });
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all named-form entries for a game from pokedex_entries, excluding already-seen IDs.
|
||||||
|
// game_pokedex_entries is seeded from form IS NULL rows, so named forms must be supplemented
|
||||||
|
// from here. Deliberately keyed on `form`, NOT isDefaultForm: a default form with a name
|
||||||
|
// (e.g. Rotom "Lightbulb") is usually absent from the game dex tables, so filtering on
|
||||||
|
// isDefaultForm would drop it from game-scoped form dexes entirely. excludeIds already
|
||||||
|
// dedupes anything the dex table does list.
|
||||||
|
private async fetchFormsForGame(
|
||||||
|
game: string,
|
||||||
|
region: string,
|
||||||
|
excludeIds: Set<number>
|
||||||
|
): Promise<PokedexEntryDB[]> {
|
||||||
|
const allForms: PokedexEntryDB[] = [];
|
||||||
|
let start = 0;
|
||||||
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const end = start + maxRows - 1;
|
||||||
|
let query = this.supabase.from('pokedex_entries').select('*').not('form', 'is', null);
|
||||||
|
|
||||||
|
if (game) {
|
||||||
|
query = query.contains('gamesToCatchIn', [game]);
|
||||||
|
}
|
||||||
|
if (region) {
|
||||||
|
query = query.eq('regionToCatchIn', region);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await query.order('id', { ascending: true }).range(start, end);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error fetching forms for game:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || data.length === 0) break;
|
||||||
|
|
||||||
|
allForms.push(...(data as PokedexEntryDB[]).filter((e) => !excludeIds.has(e.id)));
|
||||||
|
|
||||||
|
if (data.length < maxRows) break;
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allForms;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchAllDexEntries(
|
||||||
|
dexScopes: string[],
|
||||||
|
enableForms: boolean,
|
||||||
|
region: string,
|
||||||
|
game: string = ''
|
||||||
|
): Promise<PokedexEntryDB[]> {
|
||||||
|
if (dexScopes.length === 0) return [];
|
||||||
|
|
||||||
|
const entries: RawDexEntry[] = [];
|
||||||
|
let start = 0;
|
||||||
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const end = start + maxRows - 1;
|
||||||
|
const { data, error } = await this.buildDexEntriesQuery(dexScopes, enableForms, region).range(
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error finding dex-scoped combined data:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.push(...(data as RawDexEntry[]));
|
||||||
|
|
||||||
|
if (data.length < maxRows) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When forms are enabled, game_pokedex_entries only has base forms, so supplement from pokedex_entries.
|
||||||
|
if (!enableForms || !game) {
|
||||||
|
return entries as PokedexEntryDB[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build lookup: pokedexNumber → dex position (from the raw game dex data)
|
||||||
|
const dexInfoByPokedexNumber = new Map<number, { dexNumber: number; dexSortOrder: number }>();
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!dexInfoByPokedexNumber.has(entry.pokedexNumber)) {
|
||||||
|
dexInfoByPokedexNumber.set(entry.pokedexNumber, {
|
||||||
|
dexNumber: entry.dexNumber ?? 0,
|
||||||
|
dexSortOrder: entry.dexSortOrder ?? 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch forms not already covered by game_pokedex_entries
|
||||||
|
const existingIds = new Set(entries.map((e) => e.id));
|
||||||
|
const formEntries = await this.fetchFormsForGame(game, region, existingIds);
|
||||||
|
|
||||||
|
if (formEntries.length === 0) {
|
||||||
|
return entries as PokedexEntryDB[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge and sort: dex entries first (they are the canonical base form for the regional dex),
|
||||||
|
// then supplemented forms. Within each group, sort by form fields.
|
||||||
|
type TaggedEntry = RawDexEntry & { _isDexEntry: boolean };
|
||||||
|
const merged: TaggedEntry[] = [
|
||||||
|
...entries.map((e) => ({ ...e, _isDexEntry: true })),
|
||||||
|
...(formEntries as RawDexEntry[]).map((e) => ({ ...e, _isDexEntry: false }))
|
||||||
|
];
|
||||||
|
|
||||||
|
merged.sort((a, b) => {
|
||||||
|
const aDex = dexInfoByPokedexNumber.get(a.pokedexNumber) ?? {
|
||||||
|
dexNumber: 9999,
|
||||||
|
dexSortOrder: 9999
|
||||||
|
};
|
||||||
|
const bDex = dexInfoByPokedexNumber.get(b.pokedexNumber) ?? {
|
||||||
|
dexNumber: 9999,
|
||||||
|
dexSortOrder: 9999
|
||||||
|
};
|
||||||
|
if (aDex.dexSortOrder !== bDex.dexSortOrder) return aDex.dexSortOrder - bDex.dexSortOrder;
|
||||||
|
if (aDex.dexNumber !== bDex.dexNumber) return aDex.dexNumber - bDex.dexNumber;
|
||||||
|
if ((a.unownSortOrder ?? 0) !== (b.unownSortOrder ?? 0))
|
||||||
|
return (a.unownSortOrder ?? 0) - (b.unownSortOrder ?? 0);
|
||||||
|
// Dex entries (canonical regional base form) sort before supplemented forms.
|
||||||
|
if (a._isDexEntry !== b._isDexEntry) return a._isDexEntry ? -1 : 1;
|
||||||
|
if ((a.formSortBucket ?? 0) !== (b.formSortBucket ?? 0))
|
||||||
|
return (a.formSortBucket ?? 0) - (b.formSortBucket ?? 0);
|
||||||
|
if ((a.formSortRegionOrder ?? 0) !== (b.formSortRegionOrder ?? 0))
|
||||||
|
return (a.formSortRegionOrder ?? 0) - (b.formSortRegionOrder ?? 0);
|
||||||
|
if ((a.formSortRegionalSub ?? 0) !== (b.formSortRegionalSub ?? 0))
|
||||||
|
return (a.formSortRegionalSub ?? 0) - (b.formSortRegionalSub ?? 0);
|
||||||
|
return (a.formSortLabel ?? '').localeCompare(b.formSortLabel ?? '');
|
||||||
|
});
|
||||||
|
|
||||||
|
return merged as PokedexEntryDB[];
|
||||||
|
}
|
||||||
|
|
||||||
|
private dedupeEntries(entries: PokedexEntryDB[]): PokedexEntryDB[] {
|
||||||
|
const seen = new Set<number>();
|
||||||
|
const deduped: PokedexEntryDB[] = [];
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (seen.has(entry.id)) continue;
|
||||||
|
seen.add(entry.id);
|
||||||
|
deduped.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deduped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchEntriesByRange(
|
||||||
|
from: number,
|
||||||
|
to: number,
|
||||||
|
enableForms: boolean,
|
||||||
|
region: string,
|
||||||
|
game: string
|
||||||
|
): Promise<PokedexEntryDB[]> {
|
||||||
|
const entries: PokedexEntryDB[] = [];
|
||||||
|
let start = from;
|
||||||
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
|
while (start <= to) {
|
||||||
|
const end = Math.min(to, start + maxRows - 1);
|
||||||
|
const { data, error } = await this.buildEntriesQuery(enableForms, region, game).range(
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error finding paginated combined data:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.push(...data);
|
||||||
|
|
||||||
|
if (data.length < end - start + 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchAllEntries(
|
||||||
|
enableForms: boolean,
|
||||||
|
region: string,
|
||||||
|
game: string
|
||||||
|
): Promise<PokedexEntryDB[]> {
|
||||||
|
const entries: PokedexEntryDB[] = [];
|
||||||
|
let start = 0;
|
||||||
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const end = start + maxRows - 1;
|
||||||
|
const { data, error } = await this.buildEntriesQuery(enableForms, region, game).range(
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error finding combined data:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.push(...data);
|
||||||
|
|
||||||
|
if (data.length < maxRows) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchCatchRecords(entryIds: number[], userId: string): Promise<CatchRecordDB[]> {
|
||||||
|
if (!this.pokedexId) return [];
|
||||||
|
if (entryIds.length === 0) return [];
|
||||||
|
|
||||||
|
const records: CatchRecordDB[] = [];
|
||||||
|
const chunkSize = 1000;
|
||||||
|
|
||||||
|
for (let i = 0; i < entryIds.length; i += chunkSize) {
|
||||||
|
const chunk = entryIds.slice(i, i + chunkSize);
|
||||||
|
const { data, error } = await this.supabase
|
||||||
|
.from('catch_records')
|
||||||
|
.select('*')
|
||||||
|
.eq('userId', userId)
|
||||||
|
.eq('pokedexId', this.pokedexId)
|
||||||
|
.in('pokemonId', chunk);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error loading catch records:', error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
records.push(...data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAllCombinedData(
|
||||||
|
userId: string,
|
||||||
|
enableForms: boolean = true,
|
||||||
|
region: string = '',
|
||||||
|
game: string = '',
|
||||||
|
dexScopes: string[] = []
|
||||||
|
): Promise<CombinedData[]> {
|
||||||
|
const entries =
|
||||||
|
dexScopes.length > 0
|
||||||
|
? this.dedupeEntries(await this.fetchAllDexEntries(dexScopes, enableForms, region, game))
|
||||||
|
: await this.fetchAllEntries(enableForms, region, game);
|
||||||
|
|
||||||
|
if (!entries || entries.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get catch records for all entries
|
||||||
|
let catchRecords: CatchRecordDB[] = [];
|
||||||
|
if (userId && this.pokedexId) {
|
||||||
|
const entryIds = entries.map((entry) => entry.id);
|
||||||
|
catchRecords = await this.fetchCatchRecords(entryIds, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the data exactly like master branch
|
||||||
|
const combinedData = entries.map((entry) => {
|
||||||
|
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
|
||||||
|
|
||||||
|
const transformedEntry = this.transformPokedexEntry(entry);
|
||||||
|
const transformedCatchRecord = userCatchRecord
|
||||||
|
? this.transformCatchRecord(userCatchRecord)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
pokedexEntry: transformedEntry,
|
||||||
|
catchRecord: transformedCatchRecord
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Box placement is calculated dynamically on the frontend based on the current sort order.
|
||||||
|
return combinedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findCombinedData(
|
async findCombinedData(
|
||||||
|
userId: string,
|
||||||
page: number = 1,
|
page: number = 1,
|
||||||
limit: number = 20,
|
limit: number = 20,
|
||||||
enableForms: boolean = true,
|
enableForms: boolean = true,
|
||||||
region: string = '',
|
region: string = '',
|
||||||
game: string = ''
|
game: string = '',
|
||||||
|
dexScopes: string[] = []
|
||||||
): Promise<CombinedData[]> {
|
): Promise<CombinedData[]> {
|
||||||
const skip = (page - 1) * limit;
|
const from = (page - 1) * limit;
|
||||||
const pipeline: any[] = [
|
const to = from + limit - 1;
|
||||||
{
|
const entries =
|
||||||
$lookup: {
|
dexScopes.length > 0
|
||||||
from: 'catchrecords',
|
? this.dedupeEntries(
|
||||||
localField: '_id',
|
await this.fetchAllDexEntries(dexScopes, enableForms, region, game)
|
||||||
foreignField: 'pokedexEntryId',
|
).slice(from, to + 1)
|
||||||
as: 'catchRecord'
|
: await this.fetchEntriesByRange(from, to, enableForms, region, game);
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$unwind: {
|
|
||||||
path: '$catchRecord',
|
|
||||||
preserveNullAndEmptyArrays: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$sort: {
|
|
||||||
'boxPlacementForms.box': 1,
|
|
||||||
'boxPlacementForms.row': 1,
|
|
||||||
'boxPlacementForms.column': 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ $skip: skip },
|
|
||||||
{ $limit: limit }
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!enableForms) {
|
if (!entries || entries.length === 0) {
|
||||||
pipeline.unshift({
|
return [];
|
||||||
$match: {
|
|
||||||
'boxPlacement.box': { $ne: null }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.length > 0) {
|
// Get catch records for these entries
|
||||||
pipeline.unshift({
|
let catchRecords: CatchRecordDB[] = [];
|
||||||
$match: {
|
if (userId && this.pokedexId) {
|
||||||
regionToCatchIn: region
|
const entryIds = entries.map((entry) => entry.id);
|
||||||
}
|
catchRecords = await this.fetchCatchRecords(entryIds, userId);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.length > 0) {
|
// Combine the data exactly like master branch
|
||||||
pipeline.unshift({
|
const combinedData = entries.map((entry) => {
|
||||||
$match: {
|
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
|
||||||
gamesToCatchIn: { $in: [game] }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedData: CombinedData[] = await PokedexEntryModel.aggregate(pipeline).exec();
|
const transformedEntry = this.transformPokedexEntry(entry);
|
||||||
|
const transformedCatchRecord = userCatchRecord
|
||||||
|
? this.transformCatchRecord(userCatchRecord)
|
||||||
|
: null;
|
||||||
|
|
||||||
// Filter out entries with no catch records
|
return {
|
||||||
const filteredData = combinedData.filter((data) => data.catchRecord);
|
pokedexEntry: transformedEntry,
|
||||||
|
catchRecord: transformedCatchRecord
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return filteredData.map((data) => ({
|
// Box placement is calculated dynamically on the frontend based on the current sort order.
|
||||||
pokedexEntry: data as PokedexEntry,
|
return combinedData;
|
||||||
catchRecord: data.catchRecord as CatchRecord
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async countCombinedData(enableForms: boolean, region: string, game: string): Promise<number> {
|
async countCombinedData(
|
||||||
const filter: any = {};
|
enableForms: boolean,
|
||||||
|
region: string,
|
||||||
|
game: string,
|
||||||
|
dexScopes: string[] = []
|
||||||
|
): Promise<number> {
|
||||||
|
if (dexScopes.length > 0) {
|
||||||
|
const entries = this.dedupeEntries(
|
||||||
|
await this.fetchAllDexEntries(dexScopes, enableForms, region, game)
|
||||||
|
);
|
||||||
|
return entries.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = this.supabase.from('pokedex_entries').select('id', { count: 'exact', head: true });
|
||||||
|
|
||||||
|
// Apply same filters as in findCombinedData
|
||||||
if (!enableForms) {
|
if (!enableForms) {
|
||||||
filter['boxPlacement.box'] = { $ne: null };
|
// Filter to base forms only. Gendered species (form='male') and Unown ('A') are
|
||||||
|
// also flagged isDefaultForm in the pokemon table, so this single check covers them.
|
||||||
|
query = query.eq('isDefaultForm', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.length > 0) {
|
if (region) {
|
||||||
filter['regionToCatchIn'] = region;
|
query = query.eq('regionToCatchIn', region);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.length > 0) {
|
if (game) {
|
||||||
filter['gamesToCatchIn'] = { $in: [game] };
|
query = query.contains('gamesToCatchIn', [game]);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const { count, error } = await query;
|
||||||
const count = await PokedexEntryModel.countDocuments(filter).exec();
|
|
||||||
return count;
|
if (error) {
|
||||||
} catch (error) {
|
console.error('Error counting combined data:', error);
|
||||||
console.error('Error counting documents:', error);
|
return 0;
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return count || 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,143 @@
|
|||||||
import PokedexEntryModel, { type PokedexEntry } from '$lib/models/PokedexEntry';
|
import {
|
||||||
|
type CatchInformationItem,
|
||||||
|
type PokedexEntry,
|
||||||
|
type PokedexEntryDB
|
||||||
|
} from '$lib/models/PokedexEntry';
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
class PokedexEntryRepository {
|
class PokedexEntryRepository {
|
||||||
|
constructor(private supabase: SupabaseClient) {}
|
||||||
|
|
||||||
|
private parseCatchInformation(values: string[] | null): Array<string | CatchInformationItem> {
|
||||||
|
if (!values) return [];
|
||||||
|
return values.map((value) => {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed.startsWith('{')) return value;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed) as Partial<CatchInformationItem>;
|
||||||
|
if (
|
||||||
|
parsed &&
|
||||||
|
typeof parsed.game === 'string' &&
|
||||||
|
typeof parsed.location === 'string' &&
|
||||||
|
typeof parsed.notes === 'string'
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
game: parsed.game,
|
||||||
|
location: parsed.location,
|
||||||
|
notes: parsed.notes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeCatchInformation(values: Array<string | CatchInformationItem>): string[] {
|
||||||
|
return values.map((value) => (typeof value === 'string' ? value : JSON.stringify(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform Supabase data to match frontend expectations (minimal transformation)
|
||||||
|
private transformPokedexEntry(entry: PokedexEntryDB): PokedexEntry {
|
||||||
|
return {
|
||||||
|
_id: entry.id.toString(),
|
||||||
|
pokedexNumber: entry.pokedexNumber,
|
||||||
|
pokemon: entry.pokemon,
|
||||||
|
form: entry.form || '',
|
||||||
|
spriteKey: entry.spriteKey || '',
|
||||||
|
canGigantamax: entry.canGigantamax,
|
||||||
|
regionToCatchIn: entry.regionToCatchIn || '',
|
||||||
|
gamesToCatchIn: entry.gamesToCatchIn || [],
|
||||||
|
regionToEvolveIn: entry.regionToEvolveIn || '',
|
||||||
|
evolutionInformation: entry.evolutionInformation || '',
|
||||||
|
catchInformation: this.parseCatchInformation(entry.catchInformation),
|
||||||
|
notes: entry.notes || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async findById(id: string): Promise<PokedexEntry | null> {
|
async findById(id: string): Promise<PokedexEntry | null> {
|
||||||
return PokedexEntryModel.findById(id).exec();
|
const { data, error } = await this.supabase
|
||||||
|
.from('pokedex_entries')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !data) return null;
|
||||||
|
return this.transformPokedexEntry(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<PokedexEntry[]> {
|
async findAll(): Promise<PokedexEntry[]> {
|
||||||
return PokedexEntryModel.find().exec();
|
const { data, error } = await this.supabase
|
||||||
|
.from('pokedex_entries')
|
||||||
|
.select('*')
|
||||||
|
.order('pokedexNumber', { ascending: true });
|
||||||
|
|
||||||
|
if (error || !data) return [];
|
||||||
|
return data.map((entry) => this.transformPokedexEntry(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: Partial<PokedexEntry>): Promise<PokedexEntry> {
|
async create(data: Partial<PokedexEntry>): Promise<PokedexEntry> {
|
||||||
return PokedexEntryModel.create(data);
|
// Transform to database format (minimal transformation needed)
|
||||||
|
const dbData: Partial<PokedexEntryDB> = {};
|
||||||
|
if (data.pokedexNumber !== undefined) dbData.pokedexNumber = data.pokedexNumber;
|
||||||
|
if (data.pokemon !== undefined) dbData.pokemon = data.pokemon;
|
||||||
|
if (data.form !== undefined) dbData.form = data.form;
|
||||||
|
if (data.spriteKey !== undefined) dbData.spriteKey = data.spriteKey;
|
||||||
|
if (data.canGigantamax !== undefined) dbData.canGigantamax = data.canGigantamax;
|
||||||
|
if (data.regionToCatchIn !== undefined) dbData.regionToCatchIn = data.regionToCatchIn;
|
||||||
|
if (data.gamesToCatchIn !== undefined) dbData.gamesToCatchIn = data.gamesToCatchIn;
|
||||||
|
if (data.regionToEvolveIn !== undefined) dbData.regionToEvolveIn = data.regionToEvolveIn;
|
||||||
|
if (data.evolutionInformation !== undefined)
|
||||||
|
dbData.evolutionInformation = data.evolutionInformation;
|
||||||
|
if (data.catchInformation !== undefined) {
|
||||||
|
dbData.catchInformation = this.serializeCatchInformation(data.catchInformation);
|
||||||
|
}
|
||||||
|
if (data.notes !== undefined) dbData.notes = data.notes;
|
||||||
|
// Box placement is calculated dynamically - not stored in database
|
||||||
|
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('pokedex_entries')
|
||||||
|
.insert(dbData)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !result) throw new Error('Failed to create pokedex entry');
|
||||||
|
return this.transformPokedexEntry(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: Partial<PokedexEntry>): Promise<PokedexEntry | null> {
|
async update(id: string, data: Partial<PokedexEntry>): Promise<PokedexEntry | null> {
|
||||||
return PokedexEntryModel.findByIdAndUpdate(id, data, { new: true }).exec();
|
// Transform to database format (minimal transformation needed)
|
||||||
|
const dbData: Partial<PokedexEntryDB> = {};
|
||||||
|
if (data.pokedexNumber !== undefined) dbData.pokedexNumber = data.pokedexNumber;
|
||||||
|
if (data.pokemon !== undefined) dbData.pokemon = data.pokemon;
|
||||||
|
if (data.form !== undefined) dbData.form = data.form;
|
||||||
|
if (data.spriteKey !== undefined) dbData.spriteKey = data.spriteKey;
|
||||||
|
if (data.canGigantamax !== undefined) dbData.canGigantamax = data.canGigantamax;
|
||||||
|
if (data.regionToCatchIn !== undefined) dbData.regionToCatchIn = data.regionToCatchIn;
|
||||||
|
if (data.gamesToCatchIn !== undefined) dbData.gamesToCatchIn = data.gamesToCatchIn;
|
||||||
|
if (data.regionToEvolveIn !== undefined) dbData.regionToEvolveIn = data.regionToEvolveIn;
|
||||||
|
if (data.evolutionInformation !== undefined)
|
||||||
|
dbData.evolutionInformation = data.evolutionInformation;
|
||||||
|
if (data.catchInformation !== undefined) {
|
||||||
|
dbData.catchInformation = this.serializeCatchInformation(data.catchInformation);
|
||||||
|
}
|
||||||
|
if (data.notes !== undefined) dbData.notes = data.notes;
|
||||||
|
// Box placement is calculated dynamically - not stored in database
|
||||||
|
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('pokedex_entries')
|
||||||
|
.update(dbData)
|
||||||
|
.eq('id', id)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !result) return null;
|
||||||
|
return this.transformPokedexEntry(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await PokedexEntryModel.findByIdAndDelete(id).exec();
|
await this.supabase.from('pokedex_entries').delete().eq('id', id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import type {
|
||||||
|
PokedexExportIntegration,
|
||||||
|
PokedexExportIntegrationDB
|
||||||
|
} from '$lib/models/PokedexExportIntegration';
|
||||||
|
|
||||||
|
// `from()` returns a table builder; only `select()` yields the filter builder that `eq`/`is`
|
||||||
|
// live on. Typing the scope helper with the table builder made `data` untyped downstream.
|
||||||
|
type IntegrationQuery = ReturnType<ReturnType<SupabaseClient['from']>['select']>;
|
||||||
|
|
||||||
|
class PokedexExportIntegrationRepository {
|
||||||
|
constructor(
|
||||||
|
private supabase: SupabaseClient,
|
||||||
|
private userId: string,
|
||||||
|
private pokedexId: string | null
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private transform(db: PokedexExportIntegrationDB): PokedexExportIntegration {
|
||||||
|
return {
|
||||||
|
_id: db.id,
|
||||||
|
userId: db.userId,
|
||||||
|
pokedexId: db.pokedexId,
|
||||||
|
provider: db.provider,
|
||||||
|
enabled: db.enabled,
|
||||||
|
fileName: db.fileName,
|
||||||
|
folderId: db.folderId,
|
||||||
|
path: db.path,
|
||||||
|
accessToken: db.accessToken,
|
||||||
|
refreshToken: db.refreshToken,
|
||||||
|
accessTokenExpiresAt: db.accessTokenExpiresAt,
|
||||||
|
metadata: db.metadata,
|
||||||
|
lastExportedAt: db.lastExportedAt,
|
||||||
|
lastError: db.lastError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private baseQuery() {
|
||||||
|
return this.supabase.from('pokedex_export_integrations').select('*').eq('userId', this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addPokedexScope(query: IntegrationQuery): IntegrationQuery {
|
||||||
|
if (this.pokedexId) {
|
||||||
|
return query.eq('pokedexId', this.pokedexId);
|
||||||
|
}
|
||||||
|
return query.is('pokedexId', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listEnabled(): Promise<PokedexExportIntegration[]> {
|
||||||
|
const { data, error } = await this.addPokedexScope(this.baseQuery()).eq('enabled', true);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to load export integrations:', error);
|
||||||
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
|
}
|
||||||
|
if (!data) return [];
|
||||||
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
async listAll(): Promise<PokedexExportIntegration[]> {
|
||||||
|
const { data, error } = await this.addPokedexScope(this.baseQuery());
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to load export integrations:', error);
|
||||||
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
|
}
|
||||||
|
if (!data) return [];
|
||||||
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsert(
|
||||||
|
data: Partial<PokedexExportIntegrationDB> & Pick<PokedexExportIntegrationDB, 'provider'>
|
||||||
|
): Promise<PokedexExportIntegration> {
|
||||||
|
const payload: Partial<PokedexExportIntegrationDB> = {
|
||||||
|
userId: this.userId,
|
||||||
|
pokedexId: null,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
const { data: result, error } = await this.supabase
|
||||||
|
.from('pokedex_export_integrations')
|
||||||
|
.upsert(payload, {
|
||||||
|
onConflict: 'userId,provider'
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to upsert export integration:', error);
|
||||||
|
throw new Error(`Failed to save export integration: ${error.message}`);
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('Failed to save export integration: No result returned');
|
||||||
|
}
|
||||||
|
return this.transform(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listEnabledForPokedexOrUser(): Promise<PokedexExportIntegration[]> {
|
||||||
|
const base = this.supabase
|
||||||
|
.from('pokedex_export_integrations')
|
||||||
|
.select('*')
|
||||||
|
.eq('userId', this.userId)
|
||||||
|
.eq('enabled', true);
|
||||||
|
const query = this.pokedexId
|
||||||
|
? base.or(`pokedexId.eq.${this.pokedexId},pokedexId.is.null`)
|
||||||
|
: base.is('pokedexId', null);
|
||||||
|
const { data, error } = await query;
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to load export integrations:', error);
|
||||||
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
|
}
|
||||||
|
if (!data) return [];
|
||||||
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateTokens(
|
||||||
|
id: string,
|
||||||
|
patch: {
|
||||||
|
accessToken?: string;
|
||||||
|
refreshToken?: string | null;
|
||||||
|
accessTokenExpiresAt?: string | null;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const query = this.supabase
|
||||||
|
.from('pokedex_export_integrations')
|
||||||
|
.update(patch)
|
||||||
|
.eq('id', id)
|
||||||
|
.eq('userId', this.userId);
|
||||||
|
const { error } = this.pokedexId
|
||||||
|
? await query.eq('pokedexId', this.pokedexId)
|
||||||
|
: await query.is('pokedexId', null);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to update export integration tokens:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateExportStatus(
|
||||||
|
id: string,
|
||||||
|
patch: {
|
||||||
|
lastExportedAt?: string | null;
|
||||||
|
lastError?: string | null;
|
||||||
|
metadata?: Record<string, unknown> | null;
|
||||||
|
folderId?: string | null;
|
||||||
|
path?: string | null;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const query = this.supabase
|
||||||
|
.from('pokedex_export_integrations')
|
||||||
|
.update(patch)
|
||||||
|
.eq('id', id)
|
||||||
|
.eq('userId', this.userId);
|
||||||
|
const { error } = this.pokedexId
|
||||||
|
? await query.eq('pokedexId', this.pokedexId)
|
||||||
|
: await query.is('pokedexId', null);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Failed to update export integration status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PokedexExportIntegrationRepository;
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
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, dexScopes: string[] = []): Pokedex {
|
||||||
|
return {
|
||||||
|
_id: db.id,
|
||||||
|
shareToken: db.shareToken,
|
||||||
|
userId: db.userId,
|
||||||
|
name: db.name,
|
||||||
|
description: db.description || '',
|
||||||
|
isLivingDex: db.isLivingDex,
|
||||||
|
isShinyDex: db.isShinyDex,
|
||||||
|
isOriginDex: db.isOriginDex,
|
||||||
|
isFormDex: db.isFormDex,
|
||||||
|
gameScope: db.gameScope,
|
||||||
|
dexScopes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchDexScopesMap(pokedexIds: string[]): Promise<Map<string, string[]>> {
|
||||||
|
if (pokedexIds.length === 0) return new Map();
|
||||||
|
const { data, error } = await this.supabase
|
||||||
|
.from('pokedex_dex_scopes')
|
||||||
|
.select('pokedexId, dexId')
|
||||||
|
.in('pokedexId', pokedexIds);
|
||||||
|
|
||||||
|
if (error || !data) return new Map();
|
||||||
|
|
||||||
|
const map = new Map<string, string[]>();
|
||||||
|
for (const row of data) {
|
||||||
|
if (!map.has(row.pokedexId)) map.set(row.pokedexId, []);
|
||||||
|
map.get(row.pokedexId)?.push(row.dexId);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
const dexScopesMap = await this.fetchDexScopesMap([data.id]);
|
||||||
|
return this.transform(data, dexScopesMap.get(data.id) || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 [];
|
||||||
|
const dexScopesMap = await this.fetchDexScopesMap(data.map((d) => d.id));
|
||||||
|
return data.map((d) => this.transform(d, dexScopesMap.get(d.id) || []));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
// Preserve the Supabase error code so the API layer can map to a user-friendly response.
|
||||||
|
throw Object.assign(new Error(`Failed to create pokedex: ${error.message}`), {
|
||||||
|
code: error.code
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
console.error('Error updating pokedex:', error);
|
||||||
|
throw Object.assign(new Error(`Failed to update pokedex: ${error.message}`), {
|
||||||
|
code: error.code
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!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;
|
||||||
@@ -1,24 +1,38 @@
|
|||||||
import RegionGameMappingModel, { type RegionGameMapping } from '$lib/models/RegionGameMapping';
|
import type { RegionGameMapping } from '$lib/models/RegionGameMapping';
|
||||||
|
|
||||||
class RegionGameMappingRepository {
|
class RegionGameMappingRepository {
|
||||||
async findById(id: string): Promise<RegionGameMapping | null> {
|
async findById(id: string): Promise<RegionGameMapping | null> {
|
||||||
return RegionGameMappingModel.findById(id).exec();
|
void id;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<RegionGameMapping[]> {
|
async findAll(): Promise<RegionGameMapping[]> {
|
||||||
return RegionGameMappingModel.find().exec();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: Partial<RegionGameMapping>): Promise<RegionGameMapping> {
|
async create(data: Partial<RegionGameMapping>): Promise<RegionGameMapping | null> {
|
||||||
return RegionGameMappingModel.create(data);
|
if (!data.region || !data.game) return null;
|
||||||
|
return {
|
||||||
|
id: data.id,
|
||||||
|
region: data.region,
|
||||||
|
game: data.game
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: Partial<RegionGameMapping>): Promise<RegionGameMapping | null> {
|
async update(id: string, data: Partial<RegionGameMapping>): Promise<RegionGameMapping | null> {
|
||||||
return RegionGameMappingModel.findByIdAndUpdate(id, data, { new: true }).exec();
|
if (!data.region || !data.game) return null;
|
||||||
|
const normalizedId = Number(id);
|
||||||
|
const resolvedId = Number.isFinite(normalizedId) ? normalizedId : data.id;
|
||||||
|
if (typeof resolvedId !== 'number' || !Number.isFinite(resolvedId)) return null;
|
||||||
|
return {
|
||||||
|
id: resolvedId,
|
||||||
|
region: data.region,
|
||||||
|
game: data.game
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await RegionGameMappingModel.findByIdAndDelete(id).exec();
|
void id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import type { Pokedex } from '$lib/models/Pokedex';
|
||||||
|
|
||||||
|
type GameDexRow = {
|
||||||
|
id: string;
|
||||||
|
displayName: string;
|
||||||
|
sortOrder: number;
|
||||||
|
isDlc: boolean;
|
||||||
|
parentDexId: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GameSummary = {
|
||||||
|
displayName: string;
|
||||||
|
releaseYear: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GameDexRowWithGame = GameDexRow & {
|
||||||
|
games: GameSummary | GameSummary[] | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getGameIdForScope(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
gameScope: string
|
||||||
|
): Promise<string | null> {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('id')
|
||||||
|
.eq('displayName', gameScope)
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
if (error || !data) return null;
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveDexScopes(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
pokedex: Pokedex
|
||||||
|
): Promise<string[]> {
|
||||||
|
if (!pokedex.gameScope) return [];
|
||||||
|
if (pokedex.dexScopes && pokedex.dexScopes.length > 0) {
|
||||||
|
return pokedex.dexScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gameId = await getGameIdForScope(supabase, pokedex.gameScope);
|
||||||
|
if (!gameId) return [];
|
||||||
|
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('game_dexes')
|
||||||
|
.select('id')
|
||||||
|
.eq('gameId', gameId)
|
||||||
|
.order('sortOrder', { ascending: true })
|
||||||
|
.order('displayName', { ascending: true });
|
||||||
|
|
||||||
|
if (error || !data) return [];
|
||||||
|
return data.map((row) => row.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPokedexDexScopes(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
pokedexId: string,
|
||||||
|
dexIds: string[]
|
||||||
|
): Promise<void> {
|
||||||
|
const uniqueDexIds = Array.from(new Set(dexIds));
|
||||||
|
|
||||||
|
const { error: deleteError } = await supabase
|
||||||
|
.from('pokedex_dex_scopes')
|
||||||
|
.delete()
|
||||||
|
.eq('pokedexId', pokedexId);
|
||||||
|
|
||||||
|
if (deleteError) {
|
||||||
|
throw new Error(`Failed to clear dex scopes: ${deleteError.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniqueDexIds.length === 0) return;
|
||||||
|
|
||||||
|
const rows = uniqueDexIds.map((dexId) => ({
|
||||||
|
pokedexId,
|
||||||
|
dexId
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { error: insertError } = await supabase.from('pokedex_dex_scopes').insert(rows);
|
||||||
|
if (insertError) {
|
||||||
|
throw new Error(`Failed to save dex scopes: ${insertError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listGameDexes(supabase: SupabaseClient): Promise<{
|
||||||
|
gameDexes: Record<string, GameDexRow[]>;
|
||||||
|
gameOrder: string[];
|
||||||
|
games: { displayName: string; releaseYear: number }[];
|
||||||
|
}> {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('game_dexes')
|
||||||
|
.select('id, displayName, sortOrder, isDlc, parentDexId, games!inner(displayName, releaseYear)')
|
||||||
|
.order('sortOrder', { ascending: true })
|
||||||
|
.order('displayName', { ascending: true });
|
||||||
|
|
||||||
|
if (error || !data) return { gameDexes: {}, gameOrder: [], games: [] };
|
||||||
|
|
||||||
|
const grouped: Record<string, GameDexRow[]> = {};
|
||||||
|
const releaseYears: Record<string, number> = {};
|
||||||
|
const rows = data as GameDexRowWithGame[];
|
||||||
|
for (const row of rows) {
|
||||||
|
const game = Array.isArray(row.games) ? row.games[0] : row.games;
|
||||||
|
if (!game) continue;
|
||||||
|
const gameName = game.displayName;
|
||||||
|
if (!grouped[gameName]) grouped[gameName] = [];
|
||||||
|
if (releaseYears[gameName] == null) {
|
||||||
|
releaseYears[gameName] = game.releaseYear;
|
||||||
|
}
|
||||||
|
grouped[gameName].push({
|
||||||
|
id: row.id,
|
||||||
|
displayName: row.displayName,
|
||||||
|
sortOrder: row.sortOrder,
|
||||||
|
isDlc: row.isDlc,
|
||||||
|
parentDexId: row.parentDexId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const games = Object.keys(grouped)
|
||||||
|
.map((name) => ({
|
||||||
|
displayName: name,
|
||||||
|
releaseYear: releaseYears[name] ?? 0
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.releaseYear !== b.releaseYear) return b.releaseYear - a.releaseYear;
|
||||||
|
return a.displayName.localeCompare(b.displayName);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gameOrder = games.map((game) => game.displayName);
|
||||||
|
|
||||||
|
return { gameDexes: grouped, gameOrder, games };
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import type { CombinedData } from '$lib/models/CombinedData';
|
||||||
|
|
||||||
|
export function csvEscape(value: unknown): string {
|
||||||
|
if (value === null || value === undefined) return '';
|
||||||
|
const str = String(value);
|
||||||
|
if (/[",\n\r]/.test(str)) return `"${str.replace(/"/g, '""')}"`;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sanitizeFileName(name: string, fallback: string): string {
|
||||||
|
const trimmed = name.trim();
|
||||||
|
const safe = trimmed.replace(/[\\/:*?"<>|]+/g, '-');
|
||||||
|
if (!safe) return fallback;
|
||||||
|
return safe.endsWith('.csv') ? safe : `${safe}.csv`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildCsv(combinedData: CombinedData[]): string {
|
||||||
|
const headers = [
|
||||||
|
'pokemonId',
|
||||||
|
'pokedexNumber',
|
||||||
|
'pokemon',
|
||||||
|
'form',
|
||||||
|
'caught',
|
||||||
|
'haveToEvolve',
|
||||||
|
'inHome',
|
||||||
|
'personalNotes'
|
||||||
|
];
|
||||||
|
const lines = [headers.map(csvEscape).join(',')];
|
||||||
|
|
||||||
|
for (const row of combinedData) {
|
||||||
|
const entry = row.pokedexEntry;
|
||||||
|
const catchRecord = row.catchRecord ?? {
|
||||||
|
caught: false,
|
||||||
|
haveToEvolve: false,
|
||||||
|
inHome: false,
|
||||||
|
personalNotes: ''
|
||||||
|
};
|
||||||
|
lines.push(
|
||||||
|
[
|
||||||
|
entry._id,
|
||||||
|
entry.pokedexNumber,
|
||||||
|
entry.pokemon,
|
||||||
|
entry.form || '',
|
||||||
|
catchRecord.caught,
|
||||||
|
catchRecord.haveToEvolve,
|
||||||
|
catchRecord.inHome,
|
||||||
|
catchRecord.personalNotes || ''
|
||||||
|
]
|
||||||
|
.map(csvEscape)
|
||||||
|
.join(',')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join('\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldRefreshToken(expiresAt: string | null): boolean {
|
||||||
|
if (!expiresAt) return false;
|
||||||
|
const expiry = new Date(expiresAt).getTime();
|
||||||
|
if (!Number.isFinite(expiry)) return false;
|
||||||
|
return expiry - Date.now() < 60_000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import type { Pokedex } from '$lib/models/Pokedex';
|
||||||
|
import type {
|
||||||
|
ExportProvider,
|
||||||
|
PokedexExportIntegration
|
||||||
|
} from '$lib/models/PokedexExportIntegration';
|
||||||
|
import PokedexRepository from '$lib/repositories/PokedexRepository';
|
||||||
|
import CombinedDataRepository from '$lib/repositories/CombinedDataRepository';
|
||||||
|
import PokedexExportIntegrationRepository from '$lib/repositories/PokedexExportIntegrationRepository';
|
||||||
|
import { resolveDexScopes } from '$lib/services/PokedexDexScopeService';
|
||||||
|
import { getEnv } from '$lib/utils/env';
|
||||||
|
import { getProviderEndpoints } from '$lib/services/providerEndpoints';
|
||||||
|
import {
|
||||||
|
buildCsv,
|
||||||
|
sanitizeFileName,
|
||||||
|
shouldRefreshToken
|
||||||
|
} from '$lib/services/PokedexExportFormatting';
|
||||||
|
|
||||||
|
type ExportFailure = {
|
||||||
|
integrationId: string;
|
||||||
|
provider: ExportProvider;
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PokedexExportResult = {
|
||||||
|
attempted: number;
|
||||||
|
succeeded: number;
|
||||||
|
failed: ExportFailure[];
|
||||||
|
};
|
||||||
|
|
||||||
|
async function refreshGoogleToken(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository
|
||||||
|
): Promise<PokedexExportIntegration> {
|
||||||
|
if (!integration.refreshToken) {
|
||||||
|
throw new Error('Missing Google refresh token');
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = getEnv();
|
||||||
|
const clientId = env.GOOGLE_OAUTH_CLIENT_ID;
|
||||||
|
const clientSecret = env.GOOGLE_OAUTH_CLIENT_SECRET;
|
||||||
|
if (!clientId || !clientSecret) {
|
||||||
|
throw new Error('Missing Google OAuth client credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret,
|
||||||
|
refresh_token: integration.refreshToken,
|
||||||
|
grant_type: 'refresh_token'
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(getProviderEndpoints().google.token, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: params.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Google token refresh failed: ${response.status} ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as {
|
||||||
|
access_token: string;
|
||||||
|
expires_in?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const expiresAt = data.expires_in
|
||||||
|
? new Date(Date.now() + data.expires_in * 1000).toISOString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await repo.updateTokens(integration._id, {
|
||||||
|
accessToken: data.access_token,
|
||||||
|
accessTokenExpiresAt: expiresAt
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...integration,
|
||||||
|
accessToken: data.access_token,
|
||||||
|
accessTokenExpiresAt: expiresAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshDropboxToken(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository
|
||||||
|
): Promise<PokedexExportIntegration> {
|
||||||
|
if (!integration.refreshToken) {
|
||||||
|
throw new Error('Missing Dropbox refresh token');
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = getEnv();
|
||||||
|
const clientId = env.DROPBOX_OAUTH_CLIENT_ID;
|
||||||
|
const clientSecret = env.DROPBOX_OAUTH_CLIENT_SECRET;
|
||||||
|
if (!clientId || !clientSecret) {
|
||||||
|
throw new Error('Missing Dropbox OAuth client credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret,
|
||||||
|
refresh_token: integration.refreshToken,
|
||||||
|
grant_type: 'refresh_token'
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(getProviderEndpoints().dropbox.token, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: params.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Dropbox token refresh failed: ${response.status} ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as {
|
||||||
|
access_token: string;
|
||||||
|
expires_in?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const expiresAt = data.expires_in
|
||||||
|
? new Date(Date.now() + data.expires_in * 1000).toISOString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await repo.updateTokens(integration._id, {
|
||||||
|
accessToken: data.access_token,
|
||||||
|
accessTokenExpiresAt: expiresAt
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...integration,
|
||||||
|
accessToken: data.access_token,
|
||||||
|
accessTokenExpiresAt: expiresAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureAccessToken(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository
|
||||||
|
): Promise<PokedexExportIntegration> {
|
||||||
|
if (!shouldRefreshToken(integration.accessTokenExpiresAt)) {
|
||||||
|
return integration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.provider === 'google_drive') {
|
||||||
|
return await refreshGoogleToken(integration, repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.provider === 'dropbox') {
|
||||||
|
return await refreshDropboxToken(integration, repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return integration;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoogleDriveMetadata = {
|
||||||
|
files?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getGoogleFileId(
|
||||||
|
metadata: Record<string, unknown> | null,
|
||||||
|
pokedexId: string
|
||||||
|
): string | null {
|
||||||
|
const data = metadata as GoogleDriveMetadata | null;
|
||||||
|
const fileId = data?.files?.[pokedexId];
|
||||||
|
return typeof fileId === 'string' && fileId ? fileId : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withGoogleFileId(
|
||||||
|
metadata: Record<string, unknown> | null,
|
||||||
|
pokedexId: string,
|
||||||
|
fileId: string
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const data = (metadata as GoogleDriveMetadata | null) ?? {};
|
||||||
|
const files = { ...(data.files ?? {}) };
|
||||||
|
files[pokedexId] = fileId;
|
||||||
|
return { ...data, files };
|
||||||
|
}
|
||||||
|
|
||||||
|
function withoutGoogleFileId(
|
||||||
|
metadata: Record<string, unknown> | null,
|
||||||
|
pokedexId: string
|
||||||
|
): Record<string, unknown> | null {
|
||||||
|
const data = (metadata as GoogleDriveMetadata | null) ?? {};
|
||||||
|
if (!data.files || !data.files[pokedexId]) return metadata;
|
||||||
|
const files = { ...data.files };
|
||||||
|
delete files[pokedexId];
|
||||||
|
const next = { ...data, files };
|
||||||
|
if (Object.keys(files).length === 0) {
|
||||||
|
delete (next as GoogleDriveMetadata).files;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadToGoogleDrive(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository,
|
||||||
|
pokedex: Pokedex,
|
||||||
|
csv: string
|
||||||
|
): Promise<void> {
|
||||||
|
const refreshed = await ensureAccessToken(integration, repo);
|
||||||
|
const fileName = sanitizeFileName(pokedex.name, 'pokedex.csv');
|
||||||
|
|
||||||
|
let folderId = refreshed.folderId?.trim() ?? '';
|
||||||
|
if (!folderId) {
|
||||||
|
try {
|
||||||
|
const folderResponse = await fetch(
|
||||||
|
`${getProviderEndpoints().google.driveApi}/files?` +
|
||||||
|
new URLSearchParams({
|
||||||
|
q: "name='Living Dex Tracker' and mimeType='application/vnd.google-apps.folder' and trashed=false",
|
||||||
|
fields: 'files(id,name)',
|
||||||
|
pageSize: '1'
|
||||||
|
}).toString(),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${refreshed.accessToken}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (folderResponse.ok) {
|
||||||
|
const folderData = (await folderResponse.json()) as { files?: Array<{ id: string }> };
|
||||||
|
folderId = folderData.files?.[0]?.id ?? '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to lookup Google Drive folder, using root:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!folderId) {
|
||||||
|
try {
|
||||||
|
const createResponse = await fetch(`${getProviderEndpoints().google.driveApi}/files`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${refreshed.accessToken}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: 'Living Dex Tracker',
|
||||||
|
mimeType: 'application/vnd.google-apps.folder'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (createResponse.ok) {
|
||||||
|
const created = (await createResponse.json()) as { id?: string };
|
||||||
|
folderId = created.id ?? '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to create Google Drive folder, using root:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingFileId = getGoogleFileId(refreshed.metadata, pokedex._id);
|
||||||
|
let currentFileId = existingFileId;
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt < 2; attempt++) {
|
||||||
|
const metadata: Record<string, unknown> = {
|
||||||
|
name: fileName,
|
||||||
|
mimeType: 'text/csv'
|
||||||
|
};
|
||||||
|
if (folderId && !currentFileId) {
|
||||||
|
metadata.parents = [folderId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const boundary = `boundary_${randomUUID()}`;
|
||||||
|
const body = [
|
||||||
|
`--${boundary}`,
|
||||||
|
'Content-Type: application/json; charset=UTF-8',
|
||||||
|
'',
|
||||||
|
JSON.stringify(metadata),
|
||||||
|
`--${boundary}`,
|
||||||
|
'Content-Type: text/csv',
|
||||||
|
'',
|
||||||
|
csv,
|
||||||
|
`--${boundary}--`
|
||||||
|
].join('\r\n');
|
||||||
|
|
||||||
|
const url = currentFileId
|
||||||
|
? `${getProviderEndpoints().google.driveUpload}/files/${currentFileId}?uploadType=multipart`
|
||||||
|
: `${getProviderEndpoints().google.driveUpload}/files?uploadType=multipart`;
|
||||||
|
const method = currentFileId ? 'PATCH' : 'POST';
|
||||||
|
const uploadUrl =
|
||||||
|
currentFileId && folderId ? `${url}&addParents=${encodeURIComponent(folderId)}` : url;
|
||||||
|
|
||||||
|
const response = await fetch(uploadUrl, {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${refreshed.accessToken}`,
|
||||||
|
'Content-Type': `multipart/related; boundary=${boundary}`
|
||||||
|
},
|
||||||
|
body
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
if (currentFileId && response.status === 404 && attempt === 0) {
|
||||||
|
const nextMetadata = withoutGoogleFileId(refreshed.metadata, pokedex._id);
|
||||||
|
await repo.updateExportStatus(refreshed._id, { metadata: nextMetadata });
|
||||||
|
currentFileId = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new Error(`Google Drive upload failed: ${response.status} ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as { id?: string };
|
||||||
|
if (data.id && folderId) {
|
||||||
|
await repo.updateExportStatus(refreshed._id, {
|
||||||
|
folderId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFileId && data.id) {
|
||||||
|
const nextMetadata = withGoogleFileId(refreshed.metadata, pokedex._id, data.id);
|
||||||
|
await repo.updateExportStatus(refreshed._id, { metadata: nextMetadata });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadToDropbox(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository,
|
||||||
|
pokedex: Pokedex,
|
||||||
|
csv: string
|
||||||
|
): Promise<void> {
|
||||||
|
const refreshed = await ensureAccessToken(integration, repo);
|
||||||
|
const fileName = sanitizeFileName(pokedex.name, 'pokedex.csv');
|
||||||
|
const trimmedPath = refreshed.path?.trim() ?? '';
|
||||||
|
let targetPath = trimmedPath;
|
||||||
|
if (!targetPath) {
|
||||||
|
targetPath = `/${fileName}`;
|
||||||
|
} else if (targetPath.endsWith('/')) {
|
||||||
|
targetPath = `${targetPath}${fileName}`;
|
||||||
|
} else if (!targetPath.toLowerCase().endsWith('.csv')) {
|
||||||
|
targetPath = `${targetPath}/${fileName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(getProviderEndpoints().dropbox.upload, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${refreshed.accessToken}`,
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
'Dropbox-API-Arg': JSON.stringify({
|
||||||
|
path: targetPath,
|
||||||
|
mode: 'overwrite',
|
||||||
|
mute: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
body: csv
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text();
|
||||||
|
throw new Error(`Dropbox upload failed: ${response.status} ${text}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportToIntegration(
|
||||||
|
integration: PokedexExportIntegration,
|
||||||
|
repo: PokedexExportIntegrationRepository,
|
||||||
|
pokedex: Pokedex,
|
||||||
|
csv: string
|
||||||
|
): Promise<void> {
|
||||||
|
if (integration.provider === 'google_drive') {
|
||||||
|
await uploadToGoogleDrive(integration, repo, pokedex, csv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (integration.provider === 'dropbox') {
|
||||||
|
await uploadToDropbox(integration, repo, pokedex, csv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exportPokedexIfConfigured(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
userId: string,
|
||||||
|
pokedexId: string
|
||||||
|
): Promise<PokedexExportResult> {
|
||||||
|
const pokedexRepo = new PokedexRepository(supabase, userId);
|
||||||
|
const pokedex = await pokedexRepo.findById(pokedexId);
|
||||||
|
if (!pokedex) {
|
||||||
|
return { attempted: 0, succeeded: 0, failed: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const integrationRepo = new PokedexExportIntegrationRepository(supabase, userId, pokedexId);
|
||||||
|
const integrations = await integrationRepo.listEnabledForPokedexOrUser();
|
||||||
|
if (integrations.length === 0) {
|
||||||
|
return { attempted: 0, succeeded: 0, failed: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const dexScopes = await resolveDexScopes(supabase, pokedex);
|
||||||
|
const combinedRepo = new CombinedDataRepository(supabase, userId, pokedexId);
|
||||||
|
const combinedData = await combinedRepo.findAllCombinedData(
|
||||||
|
userId,
|
||||||
|
!!pokedex.isFormDex,
|
||||||
|
'',
|
||||||
|
pokedex.gameScope || '',
|
||||||
|
dexScopes
|
||||||
|
);
|
||||||
|
const csv = buildCsv(combinedData);
|
||||||
|
|
||||||
|
const failures: ExportFailure[] = [];
|
||||||
|
let successes = 0;
|
||||||
|
|
||||||
|
for (const integration of integrations) {
|
||||||
|
const scopedRepo = new PokedexExportIntegrationRepository(
|
||||||
|
supabase,
|
||||||
|
userId,
|
||||||
|
integration.pokedexId ?? null
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await exportToIntegration(integration, scopedRepo, pokedex, csv);
|
||||||
|
await scopedRepo.updateExportStatus(integration._id, {
|
||||||
|
lastExportedAt: new Date().toISOString(),
|
||||||
|
lastError: null
|
||||||
|
});
|
||||||
|
successes++;
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
failures.push({
|
||||||
|
integrationId: integration._id,
|
||||||
|
provider: integration.provider,
|
||||||
|
error: message
|
||||||
|
});
|
||||||
|
await scopedRepo.updateExportStatus(integration._id, {
|
||||||
|
lastError: message
|
||||||
|
});
|
||||||
|
console.error('Failed to export pokedex:', integration.provider, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
attempted: integrations.length,
|
||||||
|
succeeded: successes,
|
||||||
|
failed: failures
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
import type { Pokedex } from '$lib/models/Pokedex';
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import { resolveDexScopes } from '$lib/services/PokedexDexScopeService';
|
||||||
|
|
||||||
|
// PostgREST caps every response at `max_rows` (1000, see supabase/config.toml) and truncates
|
||||||
|
// SILENTLY rather than erroring, so any query here that can return more must page. All three
|
||||||
|
// can: game_pokedex_entries holds 11k+ rows, the base-form set is 1025 and a form dex 1390.
|
||||||
|
const MAX_ROWS_PER_REQUEST = 1000;
|
||||||
|
// Keeps `.in()` lists well clear of URL length limits.
|
||||||
|
const ID_CHUNK_SIZE = 500;
|
||||||
|
|
||||||
|
type PageResult = { data: unknown[] | null; error: { message: string } | null };
|
||||||
|
|
||||||
|
/** Repeatedly fetch `fetchPage` ranges until a short page signals the end. */
|
||||||
|
async function fetchAllRows<T>(
|
||||||
|
fetchPage: (from: number, to: number) => PromiseLike<PageResult>,
|
||||||
|
context: string
|
||||||
|
): Promise<T[]> {
|
||||||
|
const rows: T[] = [];
|
||||||
|
|
||||||
|
for (let from = 0; ; from += MAX_ROWS_PER_REQUEST) {
|
||||||
|
const { data, error } = await fetchPage(from, from + MAX_ROWS_PER_REQUEST - 1);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error ${context}:`, error);
|
||||||
|
throw new Error(`Failed to ${context}: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data?.length) break;
|
||||||
|
rows.push(...(data as T[]));
|
||||||
|
if (data.length < MAX_ROWS_PER_REQUEST) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chunk<T>(values: T[], size: number): T[][] {
|
||||||
|
const chunks: T[][] = [];
|
||||||
|
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate expected pokedex entries based on pokedex configuration
|
||||||
|
* Applies the same filtering logic as CombinedDataRepository
|
||||||
|
* @throws Error if the Supabase query fails
|
||||||
|
*/
|
||||||
|
export async function calculateExpectedEntries(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
pokedex: Pokedex
|
||||||
|
): Promise<number[]> {
|
||||||
|
const dexScopes = await resolveDexScopes(supabase, pokedex);
|
||||||
|
|
||||||
|
if (dexScopes.length > 0) {
|
||||||
|
const dexEntries = await fetchAllRows<{ pokemonId: number }>(
|
||||||
|
(from, to) =>
|
||||||
|
supabase
|
||||||
|
.from('game_pokedex_entries')
|
||||||
|
.select('pokemonId')
|
||||||
|
.in('dexId', dexScopes)
|
||||||
|
// (dexId, pokemonId) is the primary key, so this orders pages deterministically.
|
||||||
|
.order('dexId', { ascending: true })
|
||||||
|
.order('pokemonId', { ascending: true })
|
||||||
|
.range(from, to),
|
||||||
|
'calculate dex-scoped entries'
|
||||||
|
);
|
||||||
|
|
||||||
|
const uniqueIds = Array.from(new Set(dexEntries.map((row) => row.pokemonId)));
|
||||||
|
if (uniqueIds.length === 0) return [];
|
||||||
|
|
||||||
|
if (!pokedex.isFormDex) {
|
||||||
|
const entries: { id: number; isDefaultForm: boolean }[] = [];
|
||||||
|
|
||||||
|
for (const ids of chunk(uniqueIds, ID_CHUNK_SIZE)) {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('pokedex_entries')
|
||||||
|
.select('id, isDefaultForm')
|
||||||
|
.in('id', ids)
|
||||||
|
.returns<{ id: number; isDefaultForm: boolean }[]>();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error filtering base forms for dex scopes:', error);
|
||||||
|
throw new Error(`Failed to filter base forms: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) entries.push(...data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gendered species (form='male') and Unown ('A') are also flagged isDefaultForm
|
||||||
|
// in the pokemon table, so this single check covers them.
|
||||||
|
return entries.filter((entry) => entry.isDefaultForm).map((entry) => entry.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the region up front so each page can be built from scratch below. A PostgREST
|
||||||
|
// builder is single-use, so it must be rebuilt per page rather than reused.
|
||||||
|
let region: string | null = null;
|
||||||
|
if (pokedex.gameScope) {
|
||||||
|
// Determine region from gameScope using games table.
|
||||||
|
// We keep games.region as a denormalized convenience column.
|
||||||
|
const { data: regionData, error: regionError } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('region')
|
||||||
|
.eq('displayName', pokedex.gameScope)
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
if (regionError) {
|
||||||
|
console.error('Error looking up region for gameScope:', pokedex.gameScope, regionError);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to lookup region for gameScope "${pokedex.gameScope}": ${regionError.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
region = regionData?.region ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildQuery = () => {
|
||||||
|
let query = supabase.from('pokedex_entries').select('id');
|
||||||
|
|
||||||
|
// Apply form filter: if isFormDex is false, only include base forms. Gendered species
|
||||||
|
// (form='male') and Unown ('A') are also flagged isDefaultForm, so this covers them too.
|
||||||
|
if (!pokedex.isFormDex) {
|
||||||
|
query = query.eq('isDefaultForm', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply region filter: if gameScope is specified, filter by region
|
||||||
|
if (region) {
|
||||||
|
query = query.eq('regionToCatchIn', region);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply game filter: if gameScope is specified, filter by gamesToCatchIn array
|
||||||
|
if (pokedex.gameScope) {
|
||||||
|
query = query.contains('gamesToCatchIn', [pokedex.gameScope]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
};
|
||||||
|
|
||||||
|
const entries = await fetchAllRows<{ id: number }>(
|
||||||
|
(from, to) => buildQuery().order('id', { ascending: true }).range(from, to),
|
||||||
|
'calculate expected entries'
|
||||||
|
);
|
||||||
|
|
||||||
|
return entries.map((entry) => entry.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate pokedex_entries_mapping table for a pokedex
|
||||||
|
* Uses chunked upsert to avoid UNIQUE constraint violations and request size limits
|
||||||
|
* @throws Error if the Supabase upsert fails
|
||||||
|
*/
|
||||||
|
export async function populatePokedexMappings(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
pokedexId: string,
|
||||||
|
pokedex: Pokedex
|
||||||
|
): Promise<void> {
|
||||||
|
const expectedEntryIds = await calculateExpectedEntries(supabase, pokedex);
|
||||||
|
|
||||||
|
if (expectedEntryIds.length === 0) {
|
||||||
|
console.warn(`No expected entries calculated for pokedex ${pokedexId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New schema writes to pokedex_pokemon_mapping; column is pokemonId
|
||||||
|
const mappings = expectedEntryIds.map((pokemonId) => ({
|
||||||
|
pokedexId,
|
||||||
|
pokemonId
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Process in chunks to avoid request size limits
|
||||||
|
const CHUNK_SIZE = 500;
|
||||||
|
for (let i = 0; i < mappings.length; i += CHUNK_SIZE) {
|
||||||
|
const chunk = mappings.slice(i, i + CHUNK_SIZE);
|
||||||
|
const { error } = await supabase.from('pokedex_pokemon_mapping').upsert(chunk, {
|
||||||
|
onConflict: 'pokedexId,pokemonId',
|
||||||
|
ignoreDuplicates: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error populating pokedex mappings:', error);
|
||||||
|
throw new Error(`Failed to populate pokedex mappings: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculate pokedex_entries_mapping table for a pokedex (used on update)
|
||||||
|
* Uses a single atomic RPC call to prevent orphaned pokedexes
|
||||||
|
* @throws Error if the Supabase RPC call fails
|
||||||
|
*/
|
||||||
|
export async function recalculatePokedexMappings(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
pokedexId: string,
|
||||||
|
pokedex: Pokedex
|
||||||
|
): Promise<void> {
|
||||||
|
const expectedEntryIds = await calculateExpectedEntries(supabase, pokedex);
|
||||||
|
|
||||||
|
if (expectedEntryIds.length === 0) {
|
||||||
|
console.warn(`No expected entries calculated for pokedex ${pokedexId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the atomic RPC function to delete old mappings and insert new ones
|
||||||
|
const { error } = await supabase.rpc('recalculate_pokedex_mappings', {
|
||||||
|
p_pokedex_id: pokedexId,
|
||||||
|
p_entry_ids: expectedEntryIds
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error recalculating pokedex mappings:', error);
|
||||||
|
throw new Error(`Failed to recalculate pokedex mappings: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import sharp from 'sharp';
|
||||||
|
import type { SharedPokedexData } from '$lib/models/SharedPokedex';
|
||||||
|
|
||||||
|
export const SHARE_PREVIEW_WIDTH = 1200;
|
||||||
|
export const SHARE_PREVIEW_HEIGHT = 630;
|
||||||
|
|
||||||
|
export function escapeXml(value: string): string {
|
||||||
|
return value
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
|
.replaceAll('"', '"')
|
||||||
|
.replaceAll("'", ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function truncatePreviewText(value: string, maximumLength: number): string {
|
||||||
|
const normalized = value.replace(/\s+/g, ' ').trim();
|
||||||
|
if (normalized.length <= maximumLength) return normalized;
|
||||||
|
return `${normalized.slice(0, Math.max(0, maximumLength - 1)).trimEnd()}…`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewBadges(shared: SharedPokedexData): string[] {
|
||||||
|
return [
|
||||||
|
shared.isLivingDex && 'Living',
|
||||||
|
shared.isShinyDex && 'Shiny',
|
||||||
|
shared.isOriginDex && 'Origin',
|
||||||
|
shared.isFormDex && 'Form',
|
||||||
|
shared.gameScope || 'All Games'
|
||||||
|
].filter((value): value is string => Boolean(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSharePreviewSvg(shared: SharedPokedexData): string {
|
||||||
|
const name = escapeXml(truncatePreviewText(shared.name, 48));
|
||||||
|
const description = escapeXml(truncatePreviewText(shared.description, 92));
|
||||||
|
const badges = previewBadges(shared).slice(0, 5);
|
||||||
|
const badgeMarkup = badges
|
||||||
|
.map((badge, index) => {
|
||||||
|
const label = escapeXml(truncatePreviewText(badge, 22));
|
||||||
|
const width = Math.max(112, Math.min(220, 44 + badge.length * 15));
|
||||||
|
const previousWidth = badges
|
||||||
|
.slice(0, index)
|
||||||
|
.reduce((sum, value) => sum + Math.max(112, Math.min(220, 44 + value.length * 15)) + 16, 0);
|
||||||
|
return `<g transform="translate(${76 + previousWidth} 270)">
|
||||||
|
<rect width="${width}" height="52" rx="26" fill="#fee2e2" />
|
||||||
|
<text x="${width / 2}" y="34" text-anchor="middle" class="badge">${label}</text>
|
||||||
|
</g>`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
const progressWidth = Math.round(
|
||||||
|
(870 * Math.min(100, Math.max(0, shared.completionPercentage))) / 100
|
||||||
|
);
|
||||||
|
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${SHARE_PREVIEW_WIDTH}" height="${SHARE_PREVIEW_HEIGHT}" viewBox="0 0 ${SHARE_PREVIEW_WIDTH} ${SHARE_PREVIEW_HEIGHT}">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#7f1d1d" />
|
||||||
|
<stop offset="1" stop-color="#dc2626" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<style>
|
||||||
|
.title { font: 700 64px system-ui, -apple-system, sans-serif; fill: #fff; }
|
||||||
|
.description { font: 400 27px system-ui, -apple-system, sans-serif; fill: #fecaca; }
|
||||||
|
.badge { font: 650 22px system-ui, -apple-system, sans-serif; fill: #991b1b; }
|
||||||
|
.progress { font: 750 52px system-ui, -apple-system, sans-serif; fill: #fff; }
|
||||||
|
.percent { font: 800 82px system-ui, -apple-system, sans-serif; fill: #fff; }
|
||||||
|
.brand { font: 650 24px system-ui, -apple-system, sans-serif; fill: #fecaca; letter-spacing: 1px; }
|
||||||
|
</style>
|
||||||
|
<rect width="1200" height="630" fill="url(#background)" />
|
||||||
|
<circle cx="1070" cy="90" r="190" fill="#fff" opacity=".08" />
|
||||||
|
<circle cx="1070" cy="90" r="62" fill="none" stroke="#fff" stroke-width="26" opacity=".16" />
|
||||||
|
<path d="M880 90h380" stroke="#fff" stroke-width="26" opacity=".16" />
|
||||||
|
<text x="76" y="118" class="brand">LIVING DEX TRACKER</text>
|
||||||
|
<text x="76" y="205" class="title">${name}</text>
|
||||||
|
${description ? `<text x="76" y="246" class="description">${description}</text>` : ''}
|
||||||
|
${badgeMarkup}
|
||||||
|
<text x="76" y="425" class="progress">${shared.caught} of ${shared.total} Pokémon caught</text>
|
||||||
|
<text x="1090" y="425" text-anchor="end" class="percent">${shared.completionPercentage}%</text>
|
||||||
|
<rect x="76" y="472" width="870" height="28" rx="14" fill="#450a0a" opacity=".65" />
|
||||||
|
<rect x="76" y="472" width="${progressWidth}" height="28" rx="14" fill="#fff" />
|
||||||
|
<text x="76" y="574" class="brand">pokedex.jcreek.co.uk</text>
|
||||||
|
</svg>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renderSharePreview(shared: SharedPokedexData): Promise<Buffer> {
|
||||||
|
return sharp(Buffer.from(buildSharePreviewSvg(shared)))
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import CombinedDataRepository from '$lib/repositories/CombinedDataRepository';
|
||||||
|
import type {
|
||||||
|
SharedCatchStatus,
|
||||||
|
SharedPokedexData,
|
||||||
|
SharedPokedexRpcData
|
||||||
|
} from '$lib/models/SharedPokedex';
|
||||||
|
|
||||||
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||||
|
|
||||||
|
export function isShareToken(value: string): boolean {
|
||||||
|
return UUID_PATTERN.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateSharedProgress(statuses: SharedCatchStatus[], total: number) {
|
||||||
|
const caught = statuses.reduce(
|
||||||
|
(sum, status) => sum + (status.caught || status.haveToEvolve ? 1 : 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
caught,
|
||||||
|
completionPercentage: total === 0 ? 0 : Math.round((caught / total) * 100)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadSharedPokedex(
|
||||||
|
supabase: SupabaseClient,
|
||||||
|
shareToken: string
|
||||||
|
): Promise<SharedPokedexData | null> {
|
||||||
|
if (!isShareToken(shareToken)) return null;
|
||||||
|
|
||||||
|
const { data, error } = await supabase.rpc('get_shared_pokedex', {
|
||||||
|
p_share_token: shareToken
|
||||||
|
});
|
||||||
|
if (error || !data) return null;
|
||||||
|
|
||||||
|
const shared = data as SharedPokedexRpcData;
|
||||||
|
const repo = new CombinedDataRepository(supabase, null, null);
|
||||||
|
const entries = await repo.findAllCombinedData(
|
||||||
|
'',
|
||||||
|
shared.isFormDex,
|
||||||
|
'',
|
||||||
|
shared.gameScope || '',
|
||||||
|
shared.dexScopes
|
||||||
|
);
|
||||||
|
const statuses = new Map(shared.catchStatuses.map((status) => [status.pokemonId, status]));
|
||||||
|
const combinedData = entries.map(({ pokedexEntry }) => ({
|
||||||
|
pokedexEntry,
|
||||||
|
catchRecord: statuses.get(pokedexEntry._id) ?? null
|
||||||
|
}));
|
||||||
|
const visibleStatuses = combinedData.flatMap(({ catchRecord }) =>
|
||||||
|
catchRecord ? [catchRecord] : []
|
||||||
|
);
|
||||||
|
const progress = calculateSharedProgress(visibleStatuses, combinedData.length);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: shared.name,
|
||||||
|
description: shared.description,
|
||||||
|
isLivingDex: shared.isLivingDex,
|
||||||
|
isShinyDex: shared.isShinyDex,
|
||||||
|
isOriginDex: shared.isOriginDex,
|
||||||
|
isFormDex: shared.isFormDex,
|
||||||
|
gameScope: shared.gameScope,
|
||||||
|
dexScopes: shared.dexScopes,
|
||||||
|
combinedData,
|
||||||
|
total: combinedData.length,
|
||||||
|
...progress
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { getEnv } from '$lib/utils/env';
|
||||||
|
|
||||||
|
export type ProviderEndpoints = {
|
||||||
|
google: { authorize: string; token: string; driveApi: string; driveUpload: string };
|
||||||
|
dropbox: { authorize: string; token: string; upload: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULTS: ProviderEndpoints = {
|
||||||
|
google: {
|
||||||
|
authorize: 'https://accounts.google.com/o/oauth2/v2/auth',
|
||||||
|
token: 'https://oauth2.googleapis.com/token',
|
||||||
|
driveApi: 'https://www.googleapis.com/drive/v3',
|
||||||
|
driveUpload: 'https://www.googleapis.com/upload/drive/v3'
|
||||||
|
},
|
||||||
|
dropbox: {
|
||||||
|
authorize: 'https://www.dropbox.com/oauth2/authorize',
|
||||||
|
token: 'https://api.dropbox.com/oauth2/token',
|
||||||
|
upload: 'https://content.dropboxapi.com/2/files/upload'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '[::1]']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These endpoints receive the OAuth client secret and the user's refresh token, so an override
|
||||||
|
* is only ever a local test seam - never a deployment knob. The guards require the explicit
|
||||||
|
* override flag, the BDD service-role context, and a loopback test stack. Each override must also
|
||||||
|
* be loopback, so a single injected variable cannot redirect credentials to an arbitrary host.
|
||||||
|
* Values are read at runtime from `$env/dynamic/private`.
|
||||||
|
*/
|
||||||
|
function isLocalOverride(value: string): boolean {
|
||||||
|
try {
|
||||||
|
const url = new URL(value);
|
||||||
|
return (
|
||||||
|
(url.protocol === 'http:' || url.protocol === 'https:') && LOOPBACK_HOSTS.has(url.hostname)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveProviderEndpoints(
|
||||||
|
env: Record<string, string | undefined>
|
||||||
|
): ProviderEndpoints {
|
||||||
|
const overridesAllowed =
|
||||||
|
env.ALLOW_PROVIDER_ENDPOINT_OVERRIDES === 'true' &&
|
||||||
|
!!env.E2E_SERVICE_ROLE_KEY &&
|
||||||
|
!!env.TEST_SUPABASE_URL &&
|
||||||
|
isLocalOverride(env.TEST_SUPABASE_URL);
|
||||||
|
const pick = (override: string | undefined, fallback: string) =>
|
||||||
|
overridesAllowed && override && isLocalOverride(override) ? override : fallback;
|
||||||
|
|
||||||
|
return {
|
||||||
|
google: {
|
||||||
|
authorize: pick(env.GOOGLE_OAUTH_AUTHORIZE_URL, DEFAULTS.google.authorize),
|
||||||
|
token: pick(env.GOOGLE_OAUTH_TOKEN_URL, DEFAULTS.google.token),
|
||||||
|
driveApi: pick(env.GOOGLE_DRIVE_API_URL, DEFAULTS.google.driveApi),
|
||||||
|
driveUpload: pick(env.GOOGLE_DRIVE_UPLOAD_URL, DEFAULTS.google.driveUpload)
|
||||||
|
},
|
||||||
|
dropbox: {
|
||||||
|
authorize: pick(env.DROPBOX_OAUTH_AUTHORIZE_URL, DEFAULTS.dropbox.authorize),
|
||||||
|
token: pick(env.DROPBOX_OAUTH_TOKEN_URL, DEFAULTS.dropbox.token),
|
||||||
|
upload: pick(env.DROPBOX_UPLOAD_URL, DEFAULTS.dropbox.upload)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProviderEndpoints(): ProviderEndpoints {
|
||||||
|
return resolveProviderEndpoints(getEnv());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PROVIDER_ENDPOINT_DEFAULTS = DEFAULTS;
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER } from '$env/static/public';
|
||||||
|
import type { OfflineSnapshot } from '$lib/models/OfflineSnapshot';
|
||||||
|
import { resolveSpriteUrl, spriteRoot } from '$lib/utils/spriteUrl';
|
||||||
|
|
||||||
|
export type OfflineSyncStatus = {
|
||||||
|
state: 'idle' | 'syncing' | 'ready' | 'error';
|
||||||
|
generatedAt: string | null;
|
||||||
|
message: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ArtworkDownloadStatus = {
|
||||||
|
// unknown: not checked yet; missing: some sprites aren't saved; done: every sprite is saved.
|
||||||
|
state: 'unknown' | 'missing' | 'downloading' | 'done' | 'error';
|
||||||
|
missingBytes: number | null;
|
||||||
|
message: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const offlineSyncStatus = writable<OfflineSyncStatus>({
|
||||||
|
state: 'idle',
|
||||||
|
generatedAt: null,
|
||||||
|
message: null
|
||||||
|
});
|
||||||
|
|
||||||
|
export const artworkDownloadStatus = writable<ArtworkDownloadStatus>({
|
||||||
|
state: 'unknown',
|
||||||
|
missingBytes: null,
|
||||||
|
message: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const SYNC_EVENT = 'livingdex:offline-sync';
|
||||||
|
const OFFLINE_CACHE_PREFIX = 'livingdex-offline-';
|
||||||
|
const OFFLINE_META_CACHE = `${OFFLINE_CACHE_PREFIX}meta-v1`;
|
||||||
|
const OFFLINE_META_URL = '/__offline/current';
|
||||||
|
// Must match OFFLINE_META_FORMAT in static/offline-worker.js. Older copies are always re-synced so
|
||||||
|
// the worker can migrate them (e.g. drop the full-size artwork cache).
|
||||||
|
const OFFLINE_META_FORMAT = 2;
|
||||||
|
// A page load reuses an offline copy this recent instead of downloading the whole collection again.
|
||||||
|
// Changes made in the app request a sync explicitly, so this only delays picking up edits made on
|
||||||
|
// another device.
|
||||||
|
const SNAPSHOT_FRESH_MS = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
type OfflineMeta = { userId: string; generatedAt?: string; format?: number };
|
||||||
|
|
||||||
|
async function workerMessage(
|
||||||
|
message: unknown,
|
||||||
|
timeoutMs = 120_000,
|
||||||
|
waitForReady = true
|
||||||
|
): Promise<Record<string, unknown>> {
|
||||||
|
if (!('serviceWorker' in navigator)) throw new Error('Service workers are unavailable');
|
||||||
|
const registration = waitForReady
|
||||||
|
? await navigator.serviceWorker.ready
|
||||||
|
: await navigator.serviceWorker.getRegistration();
|
||||||
|
if (!registration?.active) throw new Error('Offline worker is not active');
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
const timeout = window.setTimeout(
|
||||||
|
() => reject(new Error('Offline worker timed out')),
|
||||||
|
timeoutMs
|
||||||
|
);
|
||||||
|
channel.port1.onmessage = (event) => {
|
||||||
|
window.clearTimeout(timeout);
|
||||||
|
const result = event.data as Record<string, unknown>;
|
||||||
|
if (result?.ok) resolve(result);
|
||||||
|
else reject(new Error(String(result?.error ?? 'Offline worker failed')));
|
||||||
|
};
|
||||||
|
registration.active?.postMessage(message, [channel.port2]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requestOfflineSync(): void {
|
||||||
|
if (typeof window !== 'undefined') window.dispatchEvent(new Event(SYNC_EVENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readOfflineMeta(): Promise<OfflineMeta | null> {
|
||||||
|
if (!('caches' in window)) return null;
|
||||||
|
if (!(await caches.keys()).includes(OFFLINE_META_CACHE)) return null;
|
||||||
|
const response = await (await caches.open(OFFLINE_META_CACHE)).match(OFFLINE_META_URL);
|
||||||
|
const meta = await response?.json().catch(() => null);
|
||||||
|
return typeof meta?.userId === 'string' ? meta : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteOfflineCaches(): Promise<void> {
|
||||||
|
if (typeof window === 'undefined' || !('caches' in window)) return;
|
||||||
|
const names = await caches.keys();
|
||||||
|
await Promise.all(
|
||||||
|
names.filter((name) => name.startsWith(OFFLINE_CACHE_PREFIX)).map((name) => caches.delete(name))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function claimOfflineData(userId: string): Promise<void> {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
if ('caches' in window && (await caches.keys()).includes(OFFLINE_META_CACHE)) {
|
||||||
|
const meta = await readOfflineMeta();
|
||||||
|
if (meta?.userId !== userId) await deleteOfflineCaches();
|
||||||
|
}
|
||||||
|
if ('serviceWorker' in navigator && (await navigator.serviceWorker.getRegistration())?.active) {
|
||||||
|
await workerMessage({ type: 'CLAIM_OFFLINE_USER', userId }, 10_000, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearOfflineData(): Promise<void> {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
await deleteOfflineCaches();
|
||||||
|
if ('serviceWorker' in navigator && (await navigator.serviceWorker.getRegistration())?.active) {
|
||||||
|
await workerMessage({ type: 'CLEAR_OFFLINE_DATA' }, 10_000, false);
|
||||||
|
}
|
||||||
|
// Sprites are kept across sign-out (they aren't account data), so the artwork status stays valid.
|
||||||
|
offlineSyncStatus.set({ state: 'idle', generatedAt: null, message: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentSpriteRoot(): string {
|
||||||
|
return spriteRoot(PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyArtworkResult(result: Record<string, unknown>): void {
|
||||||
|
const missing = Number(result.missing ?? 0);
|
||||||
|
const failed = Number(result.failedArtwork ?? 0);
|
||||||
|
if (failed > 0) {
|
||||||
|
artworkDownloadStatus.set({
|
||||||
|
state: 'error',
|
||||||
|
missingBytes: Number(result.missingBytes ?? 0),
|
||||||
|
message: `${failed} artwork files could not be saved`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
artworkDownloadStatus.set({
|
||||||
|
state: missing > 0 ? 'missing' : 'done',
|
||||||
|
missingBytes: Number(result.missingBytes ?? 0),
|
||||||
|
message: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks whether every sprite (all forms, shiny and female) is already saved on this device. */
|
||||||
|
export async function checkArtworkStatus(): Promise<void> {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
try {
|
||||||
|
applyArtworkResult(
|
||||||
|
await workerMessage({ type: 'ARTWORK_STATUS', spriteRoot: currentSpriteRoot() }, 30_000)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
// Leave the link hidden rather than offering a download that can't be checked.
|
||||||
|
console.error('Unable to check saved artwork', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artwork is normally cached as it is viewed. This saves every sprite that exists - all forms,
|
||||||
|
* shiny and female variants, not just the saved dexes - skipping any that are already cached.
|
||||||
|
*/
|
||||||
|
export async function downloadAllArtwork(): Promise<void> {
|
||||||
|
if (typeof window === 'undefined' || !navigator.onLine) return;
|
||||||
|
artworkDownloadStatus.update((status) => ({ ...status, state: 'downloading', message: null }));
|
||||||
|
try {
|
||||||
|
// Generous timeout: the worker fetches each missing sprite with its own 15s limit.
|
||||||
|
applyArtworkResult(
|
||||||
|
await workerMessage(
|
||||||
|
{ type: 'CACHE_ALL_ARTWORK', spriteRoot: currentSpriteRoot() },
|
||||||
|
30 * 60 * 1000
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
artworkDownloadStatus.update((status) => ({
|
||||||
|
...status,
|
||||||
|
state: 'error',
|
||||||
|
message: error instanceof Error ? error.message : String(error)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startOfflineSync(getUserId: () => string | null): () => void {
|
||||||
|
let timer: number | null = null;
|
||||||
|
let stopped = false;
|
||||||
|
let running = false;
|
||||||
|
let rerun = false;
|
||||||
|
let lastGeneratedAt: string | null = null;
|
||||||
|
|
||||||
|
const synchronize = async (reuseFreshCopy: boolean) => {
|
||||||
|
if (stopped || !navigator.onLine) return;
|
||||||
|
if (running) {
|
||||||
|
rerun = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userId = getUserId();
|
||||||
|
if (!userId || !('serviceWorker' in navigator)) return;
|
||||||
|
running = true;
|
||||||
|
try {
|
||||||
|
await claimOfflineData(userId);
|
||||||
|
if (reuseFreshCopy) {
|
||||||
|
const meta = await readOfflineMeta();
|
||||||
|
const age = meta?.generatedAt ? Date.now() - Date.parse(meta.generatedAt) : Infinity;
|
||||||
|
if (
|
||||||
|
meta?.userId === userId &&
|
||||||
|
meta.format === OFFLINE_META_FORMAT &&
|
||||||
|
age >= 0 &&
|
||||||
|
age < SNAPSHOT_FRESH_MS
|
||||||
|
) {
|
||||||
|
lastGeneratedAt = meta.generatedAt ?? null;
|
||||||
|
offlineSyncStatus.set({ state: 'ready', generatedAt: lastGeneratedAt, message: null });
|
||||||
|
void checkArtworkStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offlineSyncStatus.set({ state: 'syncing', generatedAt: null, message: null });
|
||||||
|
const response = await fetch('/api/offline-snapshot', {
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { Accept: 'application/json' }
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Snapshot request failed (${response.status})`);
|
||||||
|
const snapshot = (await response.json()) as OfflineSnapshot;
|
||||||
|
if (snapshot.userId !== userId) throw new Error('Snapshot owner did not match the session');
|
||||||
|
const useLocal = PUBLIC_USE_LOCAL_POKEMON_SPRITE_FOLDER === 'true';
|
||||||
|
// The worker derives which artwork belongs to the collection from these URLs, and the offline
|
||||||
|
// viewer renders them.
|
||||||
|
for (const { pokedex, entries } of snapshot.pokedexes) {
|
||||||
|
for (const { pokedexEntry } of entries) {
|
||||||
|
(pokedexEntry as typeof pokedexEntry & { offlineSpriteUrl: string }).offlineSpriteUrl =
|
||||||
|
resolveSpriteUrl(pokedexEntry, pokedex.isShinyDex, useLocal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getUserId() !== userId) throw new Error('Session changed during offline synchronization');
|
||||||
|
await workerMessage({ type: 'SYNC_OFFLINE_SNAPSHOT', snapshot });
|
||||||
|
offlineSyncStatus.set({ state: 'ready', generatedAt: snapshot.generatedAt, message: null });
|
||||||
|
lastGeneratedAt = snapshot.generatedAt;
|
||||||
|
void checkArtworkStatus();
|
||||||
|
} catch (error) {
|
||||||
|
offlineSyncStatus.set({
|
||||||
|
state: 'error',
|
||||||
|
generatedAt: lastGeneratedAt,
|
||||||
|
message: error instanceof Error ? error.message : String(error)
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
running = false;
|
||||||
|
if (rerun && !stopped) {
|
||||||
|
rerun = false;
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleSync = (reuseFreshCopy: boolean) => {
|
||||||
|
if (timer !== null) window.clearTimeout(timer);
|
||||||
|
timer = window.setTimeout(() => {
|
||||||
|
timer = null;
|
||||||
|
void synchronize(reuseFreshCopy);
|
||||||
|
}, 1_000);
|
||||||
|
};
|
||||||
|
// Explicit requests (edits, Retry, a new sign-in) and reconnecting always fetch a new copy.
|
||||||
|
const schedule = () => scheduleSync(false);
|
||||||
|
|
||||||
|
// Best effort: ask the browser not to evict the offline artwork cache under storage pressure.
|
||||||
|
void navigator.storage?.persist?.().catch(() => undefined);
|
||||||
|
window.addEventListener(SYNC_EVENT, schedule);
|
||||||
|
window.addEventListener('online', schedule);
|
||||||
|
scheduleSync(true);
|
||||||
|
return () => {
|
||||||
|
stopped = true;
|
||||||
|
if (timer !== null) window.clearTimeout(timer);
|
||||||
|
window.removeEventListener(SYNC_EVENT, schedule);
|
||||||
|
window.removeEventListener('online', schedule);
|
||||||
|
};
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user