Personal poker tracker: Go + React + PostgreSQL PWA for tracking home sessions and poker trips with AI-powered schedule research. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
593 lines
21 KiB
Markdown
593 lines
21 KiB
Markdown
# PokerTrip — Personal Poker Tracker Design Spec
|
|
|
|
## Overview
|
|
|
|
A personal poker tracker PWA for a tournament player based in Copenhagen. Tracks ad-hoc home sessions across local venues and structured poker trips abroad, each with isolated budgets. Mobile-first "cockpit" for fast input at the table, desktop "command center" for analysis and trip planning. Includes an AI-powered trip research agent that builds tournament schedules from web searches, URLs, and uploaded images (venue flyers, Facebook posts, etc.).
|
|
|
|
Designed as a personal tool with architectural choices that support a future SaaS launch (multi-user, shared tournament knowledge base, tiered AI access).
|
|
|
|
## User Context
|
|
|
|
- Danish developer in Copenhagen, plays tournaments ad-hoc (2 jobs + family = no fixed schedule)
|
|
- Travels for poker a few times a year (upcoming Manila trip May 2026) — trips are where structured planning and budget tracking matter
|
|
- Runs Proxmox on Hetzner with Nginx Proxy Manager, PBS with off-site backup
|
|
- Prefers Catppuccin Mocha theming, JetBrains Mono font
|
|
- Does not code — Claude builds everything
|
|
|
|
## Core Concepts
|
|
|
|
### Home vs. Trip
|
|
|
|
Everything is either **Home** or a **Trip**.
|
|
|
|
- **Home**: Your default poker life. Ad-hoc sessions at local venues in Denmark. No fixed schedule. One ongoing budget/P&L. Rich filtering by venue(s), buyin range, date range, game type.
|
|
- **Trip**: A planned poker trip with its own dates, location, budget, and tournament schedule. Budget is soft — you set an intended amount but can exceed it. Trips have a lifecycle: planning → active → completed.
|
|
|
|
### Three Layers of Tournament Data
|
|
|
|
1. **Global Events** (public, system-level)
|
|
- Scraped/curated nightly from Hendon Mob, WPT, WSOP, PokerStars Live, APT, etc.
|
|
- Series-level data: "EPT Barcelona, July 15-28"
|
|
- Background River job, builds a baseline calendar of the poker world
|
|
- Available to all users as a browsable calendar
|
|
|
|
2. **Location Knowledge** (community-contributed, anonymized)
|
|
- When a user runs AI research for a location/date range and approves results, the venue and schedule data enriches a shared pool
|
|
- No attribution — can't see who contributed, just that data exists
|
|
- Another user researching the same location gets this as context fed into their own research run (not free access — you must run your own research to benefit)
|
|
|
|
3. **My Trips** (private, per-user)
|
|
- Your trip: name, dates, budget, which events you're registered for, your results
|
|
- Completely invisible to others unless explicitly shared via a friends feature (SaaS)
|
|
|
|
### Venue Profiles (the "app learns" feature)
|
|
|
|
Users set up venue profiles: "Casino Copenhagen → Thursday → NLH, 500 DKK, 50K GTD, 10K starting stack". When logging a session at that venue on that day, the app pre-fills these defaults. User can always override. Pattern-based auto-detection from history is a future enhancement — manual profiles first.
|
|
|
|
### Tournament Snapshots
|
|
|
|
During an active session, you can take a photo of the tournament screen (the TV showing clock/blinds/players). A cheap vision model (e.g. Gemini Flash, Haiku) extracts: level, blinds, ante, players remaining, average stack, total chips, next break, prize pool. You manually enter your own stack size. This creates a timestamped snapshot enabling:
|
|
|
|
- Stack trajectory charts per session (your stack in BB over time)
|
|
- Your stack vs. average stack overlay
|
|
- "At what BB count do I tend to bust?" analysis
|
|
- Smarter re-entry tilt guard (uses live data from last snapshot)
|
|
|
|
### Re-entry Tilt Guard
|
|
|
|
When tapping "Re-entry", the app calculates and displays: how many BBs you'll get, total invested so far, break-even cash amount needed. Forces a moment of rational thought before emotional re-entry decisions. Uses blind structure from venue profiles or trip schedule data, enhanced by snapshot data if available.
|
|
|
|
## Data Model
|
|
|
|
### Users
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| email | TEXT | unique |
|
|
| password_hash | TEXT | bcrypt |
|
|
| display_name | TEXT | |
|
|
| settings | JSONB | preferred currency, AI config, snapshot model, etc. |
|
|
| created_at | TIMESTAMPTZ | |
|
|
| updated_at | TIMESTAMPTZ | |
|
|
|
|
### Venues
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| name | TEXT | |
|
|
| short_name | TEXT | |
|
|
| city | TEXT | |
|
|
| country | TEXT | |
|
|
| area | TEXT | neighborhood/district |
|
|
| type | TEXT | tournament / cash / both |
|
|
| notes | TEXT | |
|
|
| color | TEXT | hex color for UI |
|
|
| visibility | TEXT | public / private |
|
|
| created_by_user_id | UUID | FK → users |
|
|
| created_at | TIMESTAMPTZ | |
|
|
|
|
### Venue Profiles (per-user defaults)
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| user_id | UUID | FK → users |
|
|
| venue_id | UUID | FK → venues |
|
|
| day_of_week | INT | 0=Sunday, 1=Monday, etc. (nullable for "any day" default) |
|
|
| default_game_type | TEXT | NLH, PLO, etc. |
|
|
| default_buyin | INT | in minor currency unit |
|
|
| default_currency | TEXT | DKK, PHP, USD, EUR, etc. |
|
|
| default_gtd | INT | nullable |
|
|
| starting_stack | INT | chips |
|
|
| blind_structure | JSONB | array of {level, small, big, ante, duration_min} |
|
|
| notes | TEXT | |
|
|
|
|
### Trips
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| user_id | UUID | FK → users |
|
|
| name | TEXT | "Manila May 2026" |
|
|
| location | TEXT | "Manila" |
|
|
| country | TEXT | "Philippines" |
|
|
| start_date | DATE | |
|
|
| end_date | DATE | |
|
|
| planned_budget | INT | soft ceiling |
|
|
| currency | TEXT | |
|
|
| status | TEXT | planning / active / completed |
|
|
| created_at | TIMESTAMPTZ | |
|
|
| updated_at | TIMESTAMPTZ | |
|
|
|
|
### Global Events (scraped)
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| name | TEXT | "EPT Barcelona" |
|
|
| series | TEXT | EPT, WPT, WSOP, etc. |
|
|
| location | TEXT | |
|
|
| country | TEXT | |
|
|
| start_date | DATE | |
|
|
| end_date | DATE | |
|
|
| source_url | TEXT | where it was scraped from |
|
|
| details | JSONB | flexible extra data |
|
|
| scraped_at | TIMESTAMPTZ | |
|
|
|
|
### Location Schedules (community-contributed, anonymized)
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| location_key | TEXT | e.g. "manila-2026-05" for dedup/lookup |
|
|
| venue_id | UUID | FK → venues |
|
|
| date | DATE | |
|
|
| time | TIME | |
|
|
| event_code | TEXT | "#29A" etc. |
|
|
| name | TEXT | |
|
|
| buyin | INT | |
|
|
| currency | TEXT | |
|
|
| guarantee | INT | nullable |
|
|
| format | TEXT | NLH, PLO, NLH PKO, etc. |
|
|
| notes | TEXT | |
|
|
| multi_day | BOOLEAN | |
|
|
| multi_day_info | TEXT | "Day 2: May 9, Final: May 10" |
|
|
| starting_stack | INT | nullable |
|
|
| blind_structure | JSONB | nullable |
|
|
| source | TEXT | "ai_research" / "manual" / "scraped" |
|
|
| created_at | TIMESTAMPTZ | |
|
|
|
|
### Sessions (the core table — every tournament/cash game played)
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| user_id | UUID | FK → users |
|
|
| venue_id | UUID | FK → venues |
|
|
| trip_id | UUID | FK → trips, nullable (null = Home) |
|
|
| schedule_entry_id | UUID | FK → location_schedules, nullable |
|
|
| date | DATE | |
|
|
| start_time | TIMESTAMPTZ | |
|
|
| end_time | TIMESTAMPTZ | nullable (set when session ends) |
|
|
| game_type | TEXT | NLH, PLO, etc. |
|
|
| session_type | TEXT | tournament / cash |
|
|
| buyin | INT | |
|
|
| currency | TEXT | |
|
|
| payout | INT | nullable |
|
|
| finish_position | INT | nullable |
|
|
| field_size | INT | nullable |
|
|
| status | TEXT | registered / playing / busted / cashed / day2 / skipped |
|
|
| late_reg | BOOLEAN | default false |
|
|
| entry_level | INT | nullable — what level you joined at |
|
|
| entry_blinds | TEXT | nullable — e.g. "100/200" |
|
|
| notes | TEXT | |
|
|
| created_at | TIMESTAMPTZ | |
|
|
| updated_at | TIMESTAMPTZ | |
|
|
|
|
### Session Re-entries
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| session_id | UUID | FK → sessions |
|
|
| timestamp | TIMESTAMPTZ | when the re-entry happened |
|
|
| level | INT | nullable |
|
|
| blinds | TEXT | nullable — e.g. "200/400" |
|
|
| stack_received | INT | nullable — chips received |
|
|
| cost | INT | re-entry cost (may differ from original buyin) |
|
|
|
|
### Session Snapshots
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| session_id | UUID | FK → sessions |
|
|
| timestamp | TIMESTAMPTZ | |
|
|
| my_stack | INT | user-entered |
|
|
| level | INT | extracted from image |
|
|
| blinds_small | INT | extracted |
|
|
| blinds_big | INT | extracted |
|
|
| ante | INT | nullable, extracted |
|
|
| players_remaining | INT | nullable, extracted |
|
|
| average_stack | INT | nullable, extracted |
|
|
| total_chips | BIGINT | nullable, extracted |
|
|
| next_break | TEXT | nullable, extracted |
|
|
| prize_pool | INT | nullable, extracted |
|
|
| bb_count | NUMERIC | calculated: my_stack / blinds_big |
|
|
| image_url | TEXT | path to stored image |
|
|
|
|
### Research Jobs
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| user_id | UUID | FK → users |
|
|
| trip_id | UUID | FK → trips |
|
|
| status | TEXT | pending / running / completed / failed |
|
|
| prompt | TEXT | user's research request |
|
|
| model | TEXT | model ID used |
|
|
| provider_endpoint | TEXT | OpenRouter/Requesty URL |
|
|
| attachments | JSONB | array of {type: "image"/"url", value: "..."} |
|
|
| results | JSONB | structured venue + schedule data proposed by AI |
|
|
| conversation | JSONB | full tool-use conversation for refinement context |
|
|
| created_at | TIMESTAMPTZ | |
|
|
| updated_at | TIMESTAMPTZ | |
|
|
|
|
### Uploads
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| id | UUID | PK |
|
|
| user_id | UUID | FK → users |
|
|
| path | TEXT | filesystem path |
|
|
| content_type | TEXT | image/jpeg, image/png, etc. |
|
|
| size_bytes | BIGINT | |
|
|
| purpose | TEXT | research / snapshot |
|
|
| created_at | TIMESTAMPTZ | |
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Phone/Desktop (React PWA)
|
|
├── Optimistic local state (in-memory + localStorage cache)
|
|
├── Background sync → API
|
|
└── SSE connection for real-time updates (research progress, etc.)
|
|
│
|
|
▼
|
|
Go API (single binary, embeds React frontend)
|
|
├── Chi router
|
|
├── Auth middleware (JWT + refresh tokens)
|
|
├── REST endpoints (/api/v1/*)
|
|
├── SSE endpoint for live updates
|
|
├── River job queue (in-process, Postgres-backed)
|
|
│ ├── Sync/write jobs
|
|
│ ├── AI research jobs (search → fetch → structure)
|
|
│ ├── Snapshot OCR jobs
|
|
│ └── Nightly global events scraper
|
|
└── SearXNG client (for AI web search tool-use)
|
|
│
|
|
▼
|
|
Postgres (10.5.0.109)
|
|
├── Application data
|
|
└── River job queue tables
|
|
|
|
SearXNG (LXC on Proxmox)
|
|
└── Metasearch engine, self-hosted, no API keys
|
|
```
|
|
|
|
### Write Path (message queue pattern)
|
|
|
|
Every mutation from the client follows fire-and-forget:
|
|
|
|
1. Client makes optimistic UI update (instant)
|
|
2. `POST`/`PATCH` to API → returns `202 Accepted` immediately
|
|
3. River job processes the actual DB write
|
|
4. If failure, client resyncs on next pull
|
|
5. SSE pushes confirmations for critical operations
|
|
|
|
The UI is always ahead of the database. No lag, ever.
|
|
|
|
## API Design
|
|
|
|
All endpoints versioned under `/api/v1/`. All write endpoints return `202 Accepted`. All list endpoints support cursor pagination + query param filters.
|
|
|
|
```
|
|
/api/v1/auth
|
|
POST /register
|
|
POST /login → JWT access token + refresh token (httpOnly cookie)
|
|
POST /refresh → rotate tokens
|
|
|
|
/api/v1/me
|
|
GET / → user profile + settings
|
|
PATCH / → update settings
|
|
|
|
/api/v1/venues
|
|
GET / → list (public + user's private)
|
|
POST /
|
|
PATCH /:id
|
|
|
|
/api/v1/venue-profiles
|
|
GET / → user's venue defaults
|
|
PUT /:venue_id/:dow → set/update default
|
|
|
|
/api/v1/sessions
|
|
GET / → filterable: venue, trip, date range, buyin range, game type, status
|
|
POST /
|
|
PATCH /:id → status changes, payout, notes, end time
|
|
POST /:id/reentry → add re-entry (triggers tilt guard response)
|
|
POST /:id/snapshot → upload tournament screen photo + stack size
|
|
|
|
/api/v1/trips
|
|
GET /
|
|
POST /
|
|
PATCH /:id
|
|
GET /:id/schedule → trip's tournament schedule
|
|
|
|
/api/v1/schedule
|
|
GET / → location schedules (query by location_key, date range)
|
|
POST / → manually add schedule entry
|
|
|
|
/api/v1/research
|
|
POST / → start AI research job
|
|
GET /:id → job status + results
|
|
POST /:id/refine → iterate with additional context/images
|
|
POST /:id/approve → accept results → writes to venues + location_schedules
|
|
|
|
/api/v1/global-events
|
|
GET / → browse scraped series (filterable by date, location)
|
|
|
|
/api/v1/events
|
|
GET /stream → SSE (research progress, sync confirmations)
|
|
|
|
/api/v1/settings/models
|
|
GET / → quick picks + full model list from provider API
|
|
```
|
|
|
|
## AI Research System
|
|
|
|
### Research Flow
|
|
|
|
1. User creates a trip, hits "Research"
|
|
2. Chat-like interface: text prompt + image uploads + URL links + model picker
|
|
3. `POST /research` → River job queued
|
|
4. Go backend calls chosen model via OpenRouter/Requesty (OpenAI-compatible endpoint)
|
|
5. Model receives system prompt + tools:
|
|
- `search(query)` → hits self-hosted SearXNG
|
|
- `fetch_url(url)` → fetches and extracts page content
|
|
- `analyze_image(image)` → model's own vision capability on uploaded images
|
|
- `create_venue(data)` → proposes a new venue
|
|
- `create_schedule_entry(data)` → proposes a tournament entry
|
|
6. SSE streams progress to browser ("Searching... Found venue... Extracting schedule...")
|
|
7. Results presented as proposed schedule cards with Accept/Edit/Remove per entry
|
|
8. User can refine ("also check 9D Poker Club") → `POST /research/:id/refine`
|
|
9. User approves → venues (public) + schedule entries written to DB
|
|
|
|
### Shared Knowledge Context
|
|
|
|
Before starting research, system checks for existing location knowledge for the target location + date range. If found, it's provided as context to the AI ("We already know about these venues and events — build on this"). This requires the user to run their own research — no free access to the knowledge base without contributing.
|
|
|
|
### Snapshot OCR Flow
|
|
|
|
1. During active session, tap camera button
|
|
2. Snap tournament screen → upload image
|
|
3. `POST /sessions/:id/snapshot` with image + user's stack size
|
|
4. River job sends image to snapshot model (cheap/fast vision model, configured separately in settings)
|
|
5. Model extracts: level, blinds, ante, players, average stack, etc.
|
|
6. Returns pre-filled fields → user confirms/corrects → saved
|
|
|
|
### Model Management (Settings UI)
|
|
|
|
```
|
|
AI Settings:
|
|
Provider: [OpenRouter ▾] (OpenRouter / Requesty / Custom)
|
|
Endpoint URL: [https://openrouter.ai/api/v1]
|
|
API Key: [••••••••••••]
|
|
|
|
Research Model: Quick picks: Sonnet, Opus, GPT-4o, Gemini Pro, Grok
|
|
[🔍 Search all models...]
|
|
|
|
Snapshot Model: Quick picks: Haiku, Gemini Flash, GPT-4o-mini
|
|
[🔍 Search all models...]
|
|
|
|
🖼️ Vision support indicator per model
|
|
```
|
|
|
|
Quick picks populated from provider's `/models` endpoint, filtered to top models from Anthropic, OpenAI, Google, xAI. Fuzzy search for the full list.
|
|
|
|
## Frontend
|
|
|
|
### Shared Principles
|
|
- Catppuccin Mocha color palette throughout
|
|
- JetBrains Mono / Fira Code font
|
|
- Dark mode only
|
|
- PWA: manifest.json + service worker (Workbox) for offline caching
|
|
- Optimistic UI: every action feels instant
|
|
|
|
### Mobile Cockpit (< 768px)
|
|
|
|
The in-the-moment tool. Speed and touch-friendliness above all.
|
|
|
|
**Top toggle: Home | Trip** (Trip only visible when an active trip exists)
|
|
|
|
**Home mode:**
|
|
- "Log a session" prominent button
|
|
- Today's venue suggestion (from venue profiles for this day of week)
|
|
- Last 3-5 sessions as quick history
|
|
- Tap "Log" → pick venue (favorites first, search others) → pre-filled from venue profile → confirm → live session
|
|
- Active session: timer, big thumb-friendly action buttons (Rebuy, Add-on, Bust, Cash, Made Day 2, Snapshot)
|
|
- Re-entry tilt guard modal before confirming re-entry
|
|
- "Cash" → input payout + finish position → end session
|
|
|
|
**Trip mode:**
|
|
- Today's schedule (card-style, like Manila JSX)
|
|
- Filter by venue
|
|
- Status badges and action buttons per tournament
|
|
- Budget bar: spent vs. planned, current P&L
|
|
- Swipe between days
|
|
|
|
**Session entry captures:**
|
|
- Venue, date, game type, buyin, currency
|
|
- Start time (auto), end time (when you tap "done")
|
|
- Late registration flag + entry level/blinds (optional)
|
|
- Re-entries (tracked individually with timestamp, level, blinds, cost)
|
|
- Payout, finish position, field size (on completion)
|
|
- Notes
|
|
- Snapshots (photos of tournament screen + your stack)
|
|
|
|
### Desktop Dashboard (≥ 768px)
|
|
|
|
The analysis and planning tool.
|
|
|
|
**Sidebar navigation:** Home, Trips, Stats, Venues, Settings
|
|
|
|
**Home view:**
|
|
- Session history table with powerful filtering: date range, venue(s), buyin range, game type, result
|
|
- Summary stats bar: total sessions, P&L, ROI, avg buyin, avg cash, ITM%
|
|
- Filters update everything live
|
|
|
|
**Trip view:**
|
|
- Trip list → drill into trip → full schedule explorer
|
|
- Side panel: budget tracker (planned vs. actual), session results, daily breakdown
|
|
- Research tab: start/refine AI research, review proposed schedules, approve
|
|
|
|
**Stats view:**
|
|
- P&L over time (line chart)
|
|
- By venue (bar chart)
|
|
- By game type, by buyin range
|
|
- Home vs. Trips breakdown
|
|
- Monthly/quarterly/yearly views
|
|
- Late reg vs. on-time performance comparison
|
|
- Stack trajectory charts (from snapshots)
|
|
- Re-entry analysis (ROI with vs. without re-entries)
|
|
- All filterable with same filter system
|
|
|
|
**Venues view:**
|
|
- Manage venues (public/private)
|
|
- Set up venue profiles (day-of-week defaults, blind structures, starting stacks)
|
|
|
|
**Settings view:**
|
|
- Profile (email, display name, preferred currency)
|
|
- AI configuration (provider, API key, research model, snapshot model)
|
|
- Model picker with quick picks + fuzzy search
|
|
|
|
## Auth & Multi-tenancy
|
|
|
|
### Current (personal tool)
|
|
- Email + password registration (bcrypt)
|
|
- JWT access token (15 min) + refresh token (30 days, httpOnly cookie)
|
|
- All queries scoped by user_id
|
|
- No email verification (single user)
|
|
|
|
### Data Visibility Rules
|
|
|
|
| Data | Visibility |
|
|
|------|-----------|
|
|
| Global events | Everyone |
|
|
| Location schedules | Users who have run research (give-to-get) |
|
|
| Public venues | Everyone |
|
|
| Private venues | Creator only (redacted as "Private Venue" in any shared context) |
|
|
| Trips | Owner only |
|
|
| Sessions & results | Owner only |
|
|
| Budgets | Owner only |
|
|
| Snapshots | Owner only |
|
|
| Venue profiles | Owner only |
|
|
| Research jobs | Owner only (approved venues/schedules become shared) |
|
|
|
|
## Tech Stack
|
|
|
|
### Backend
|
|
- **Go** (latest stable)
|
|
- **chi** — lightweight router
|
|
- **pgx** — Postgres driver
|
|
- **sqlc** — type-safe SQL → Go codegen
|
|
- **goose** — migrations
|
|
- **river** — Postgres-backed job queue (no Redis)
|
|
- **golang-jwt/jwt/v5** — JWT
|
|
- **golang.org/x/crypto/bcrypt** — password hashing
|
|
- **embed** — React frontend baked into binary
|
|
|
|
### Frontend
|
|
- **React** + **TypeScript** (Vite)
|
|
- **Tailwind CSS** (Catppuccin Mocha palette)
|
|
- **Framer Motion** (animations)
|
|
- **Recharts** (stats/charts)
|
|
- **Workbox** (PWA/service worker)
|
|
- **Zustand** or **Jotai** (lightweight state)
|
|
|
|
### Infrastructure
|
|
- **Postgres** (10.5.0.109, database: pokertrip)
|
|
- **SearXNG** (self-hosted LXC on Proxmox)
|
|
- **Nginx Proxy Manager** (existing, SSL termination)
|
|
|
|
## Deployment
|
|
|
|
Single Go binary embeds the built React frontend. One process, one port.
|
|
|
|
```
|
|
poker.georgsen.dk
|
|
→ Nginx Proxy Manager (SSL)
|
|
→ Go binary :8080
|
|
├── / → embedded React SPA
|
|
├── /api/v1/* → API
|
|
└── /api/v1/events/stream → SSE
|
|
```
|
|
|
|
Environment config via `.env` file (gitignored). See `.env.example` for template.
|
|
|
|
Dev experience:
|
|
- `make dev` — Go hot reload (Air) + Vite dev server with proxy
|
|
- `make build` — builds React, embeds into Go binary
|
|
- `make migrate` — runs Postgres migrations (goose)
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
pokertrip/
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go
|
|
├── internal/
|
|
│ ├── api/
|
|
│ │ ├── auth.go
|
|
│ │ ├── sessions.go
|
|
│ │ ├── trips.go
|
|
│ │ ├── venues.go
|
|
│ │ ├── research.go
|
|
│ │ ├── snapshots.go
|
|
│ │ └── middleware.go
|
|
│ ├── db/
|
|
│ │ ├── migrations/
|
|
│ │ ├── queries/
|
|
│ │ └── sqlc.yaml
|
|
│ ├── jobs/
|
|
│ │ ├── research.go
|
|
│ │ ├── scraper.go
|
|
│ │ └── snapshot_ocr.go
|
|
│ ├── ai/
|
|
│ │ ├── client.go
|
|
│ │ ├── tools.go
|
|
│ │ └── models.go
|
|
│ ├── storage/
|
|
│ │ └── local.go
|
|
│ └── config/
|
|
│ └── config.go
|
|
├── frontend/
|
|
│ ├── src/
|
|
│ │ ├── components/
|
|
│ │ │ ├── mobile/
|
|
│ │ │ ├── desktop/
|
|
│ │ │ └── shared/
|
|
│ │ ├── hooks/
|
|
│ │ ├── stores/
|
|
│ │ ├── api/
|
|
│ │ ├── App.tsx
|
|
│ │ └── main.tsx
|
|
│ ├── public/
|
|
│ │ ├── manifest.json
|
|
│ │ └── sw.js
|
|
│ ├── index.html
|
|
│ ├── vite.config.ts
|
|
│ ├── tailwind.config.ts
|
|
│ └── package.json
|
|
├── docs/
|
|
│ ├── manila-poker-tracker.jsx ← reference JSX for Manila trip UI
|
|
│ ├── saas-considerations.md
|
|
│ └── 2026-03-18-pokertrip-design.md ← this file
|
|
├── .env
|
|
├── .env.example
|
|
├── .gitignore
|
|
├── Makefile
|
|
├── Dockerfile
|
|
└── go.mod
|
|
```
|