Quality / test (push) Canceled after 0s
Commit the working vertical slices for inventory image processing and the Sale workflow, including Sale history, audited voiding, multi-item persistence, admin UI, migrations, documentation, and tests.
148 lines
6.4 KiB
Markdown
148 lines
6.4 KiB
Markdown
# Pokémon Inventory & Sales Reference Catalog
|
|
|
|
A personal Pokémon inventory and sales reference catalog for documenting owned Pokémon products, comparing market pricing from supported providers, and presenting public asking prices.
|
|
|
|
## Scope
|
|
|
|
This is a catalog and sales-reference application, not an e-commerce platform.
|
|
|
|
The application supports:
|
|
- Pokémon trading cards
|
|
- Pokémon merchandise
|
|
- Sealed products
|
|
- Accessories
|
|
- Pokémon Worlds / Pokémon Center merchandise
|
|
- Other Pokémon-related products
|
|
|
|
The public site communicates what is available, what the market suggests it is worth, and what the seller is asking. Transactions occur outside the application.
|
|
|
|
## Core domain model
|
|
|
|
`Product → Inventory Item → Listing → Sale`
|
|
|
|
- **Product:** what an item is.
|
|
- **Inventory Item:** an individual physical copy owned by the seller.
|
|
- **Listing:** the current public offer for an inventory item.
|
|
- **Sale:** a manual record of an external transaction.
|
|
|
|
Products remain in the database after inventory is sold.
|
|
|
|
## Technology
|
|
|
|
- Vue 3 + Vite
|
|
- Modern JavaScript / latest ECMAScript
|
|
- Vue Router
|
|
- Pinia where justified
|
|
- Node.js + Fastify
|
|
- PostgreSQL + Drizzle ORM
|
|
- Redis + BullMQ
|
|
- REST API under `/api/v1`
|
|
- Docker + Docker Compose
|
|
- GitHub Actions
|
|
- PostgreSQL-backed search behind a `SearchService`
|
|
- Pluggable image storage and pricing-provider adapters
|
|
- Automated unit, integration, component, E2E, and accessibility tests
|
|
- WCAG 2.2 AA target
|
|
|
|
TypeScript is not used unless a documented technical justification establishes a concrete benefit that cannot reasonably be achieved with modern JavaScript.
|
|
|
|
## Repository layout
|
|
|
|
- `apps/web` — Vue frontend
|
|
- `apps/api` — Fastify API
|
|
- `apps/worker` — asynchronous jobs
|
|
- `packages/*` — genuinely shared contracts, validation, and safe utilities
|
|
- `database` — migrations and seeds
|
|
- `docs` — source-of-truth project documentation
|
|
- `.github` — Copilot instructions and CI
|
|
|
|
Read `AGENTS.md` and `.github/copilot-instructions.md` before implementing features.
|
|
|
|
## Implementation Status
|
|
|
|
Start future work from [docs/IMPLEMENTATION-STATUS.md](docs/IMPLEMENTATION-STATUS.md). It records the completed foundation, the next bounded milestone, the overall roadmap, and verification commands.
|
|
|
|
## Database Development
|
|
|
|
1. Copy `.env.example` to `.env` and replace `change-me` with a local-only password in both connection URLs.
|
|
2. Run `npm install`, `npm run db:up`, and `npm run db:migrate` to prepare the development database and Redis.
|
|
3. Create and migrate the isolated `pokemon_inventory_test` database before running integration tests.
|
|
4. Run `TEST_DATABASE_URL='postgresql://...' TEST_REDIS_URL='redis://...' npm run test:integration`.
|
|
|
|
Integration tests reject database names not ending in `_test` and truncate their tables before each test. The Redis limiter test uses a unique temporary key prefix and does not modify application session data.
|
|
|
|
## Local Development
|
|
|
|
After creating `.env`, set the required local SMTP and MFA values, then start the complete development stack from the repository root with:
|
|
|
|
```env
|
|
SMTP_URL=smtp://localhost:1025
|
|
SMTP_FROM=no-reply@example.test
|
|
MFA_ENCRYPTION_KEY=<output from: openssl rand -base64 32>
|
|
```
|
|
|
|
The MFA key must remain stable while enrolled MFA methods exist. Keep it in local secret storage and do not commit it.
|
|
|
|
```sh
|
|
npm run dev
|
|
```
|
|
|
|
This command starts healthy PostgreSQL and Redis containers, applies migrations, and runs the Fastify API, durable image worker, and Vue/Vite web app together. Open `http://localhost:5173/` in a browser. The Vite server proxies `/api` requests to the API on port `3000`.
|
|
|
|
The command does not provision an administrator automatically. Run `npm run provision:admin` once after setting the initial administrator variables in `.env` when admin access is needed. Stop the application processes with `Ctrl+C`; stop the containers separately with `npm run db:down` when they are no longer needed.
|
|
|
|
For a disposable local database reset, use:
|
|
|
|
```sh
|
|
npm run db:reset
|
|
npm run provision:admin
|
|
```
|
|
|
|
Or combine both operations with:
|
|
|
|
```sh
|
|
npm run db:reset:admin
|
|
```
|
|
|
|
`db:reset` removes the local PostgreSQL and Redis volumes, recreates healthy containers, and synchronizes the current Drizzle schema with `drizzle-kit push --force`. It is destructive and local-development-only. It does not modify `.env`.
|
|
|
|
## Visual Development
|
|
|
|
Run Storybook to inspect the authoritative design tokens and mock visual fixtures without requiring API data:
|
|
|
|
```sh
|
|
npm run storybook
|
|
```
|
|
|
|
Open `http://localhost:6006/`. Use the Storybook theme toolbar to switch between light and dark themes. The accessibility addon is enabled for component review. The fixture showcase is intentionally mock-only and is not part of the production router.
|
|
|
|
Seed representative API-backed catalog data for browser testing with:
|
|
|
|
```sh
|
|
npm run seed:demo
|
|
```
|
|
|
|
The seed is non-destructive and exits without changes when the demo catalog already exists.
|
|
|
|
## Browser Workflow Tests
|
|
|
|
With `npm run dev` running, configured local admin credentials, and demo lookup data available, run the authenticated browser workflow with:
|
|
|
|
```sh
|
|
npm run test:e2e
|
|
```
|
|
|
|
The workflow covers login, admin overview loading, product creation, physical inventory creation, and navigation to image management. It skips when `INITIAL_ADMIN_EMAIL` or `INITIAL_ADMIN_PASSWORD` is not configured.
|
|
|
|
## Initial Administrator
|
|
|
|
After the development database is migrated, set `INITIAL_ADMIN_EMAIL`, `INITIAL_ADMIN_PASSWORD`, and `INITIAL_SELLER_NAME` in your local `.env`, then run `npm run provision:admin` once. The command creates the only initial administrator and linked Seller; it refuses to run after any User exists.
|
|
|
|
## Password Recovery
|
|
|
|
Set `SMTP_URL`, `SMTP_FROM`, and `PASSWORD_RESET_URL` in the local `.env` before starting the API. For local development with an SMTP service listening on port `1025`, use `SMTP_URL=smtp://localhost:1025` and `SMTP_FROM=no-reply@example.test`. Password reset tokens are delivered only through SMTP, expire after one hour, are stored as hashes, and are single use.
|
|
|
|
## MFA Secret Protection
|
|
|
|
Set `MFA_ENCRYPTION_KEY` to a base64-encoded 32-byte key before starting the API. Generate one with `openssl rand -base64 32`, then paste the output after `MFA_ENCRYPTION_KEY=` in `.env`. A command that only runs `printf` displays the value but does not update `.env`. Keep the key in secret management and retain it for as long as enrolled MFA methods must remain readable.
|