A Next.js web application for browsing, exploring, and proposing changes to OLManager game data. Built with Next.js 16, React 19, Tailwind CSS 4, and TypeScript.
- Browse 43 competitions, 345+ teams, 1,600+ players, and staff across all LoL leagues
- Search and filter with text search, column filters (position, tier, nationality, competition), and multi-column sort
- Explore player attributes, OVR ratings, natural vs current positions, market values, and contracts
- Propose changes via typed, validated proposal forms with pre-filled current data
- Review diffs deterministically computed between current state and proposed changes
- Dark mode toggle with system preference detection and localStorage persistence
# Install dependencies
npm install
# Start development server
npm run dev
# Open http://localhost:3000Sign-in uses Discord OAuth via Auth.js. Contributors log in with their Discord account to identify themselves. A separate GitHub App handles bot actions (creating issues and PRs in the data repository).
To enable Discord auth locally:
- Create a Discord Application at https://discord.com/developers/applications
- Redirect URL:
http://localhost:3000/api/auth/callback/discord
- Redirect URL:
- Copy
.env.local.exampleto.env.localand fill in:AUTH_DISCORD_ID=your_client_id AUTH_DISCORD_SECRET=your_client_secret
- The
AUTH_SECRETis auto-generated. Regenerate withopenssl rand -base64 32. - Restart the dev server — the Topbar will show "Sign in with Discord".
Without Discord credentials, the app works fully for local data exploration. Auth is only needed for the upcoming proposal persistence / GitHub integration.
| Command | Description |
|---|---|
npm run dev |
Start development server with Turbopack |
npm run build |
Production build |
npm start |
Start production server |
npm run test:run |
Run all tests (vitest) |
npm run lint |
Run ESLint |
Game data is stored in the olmanager-data repository and linked as a git submodule at src/data/. To update data after cloning:
git submodule update --init --remoteEach competition manifest declares its teams_file and players_file for automatic association. Data is loaded at build time via fs + React cache().
The app follows a clean layered architecture:
src/
├── app/ # Next.js App Router pages and layouts
│ ├── (data)/ # Data explorer routes (/data/*)
│ └── (proposals)/ # Proposal workbench routes (/proposals/*)
├── components/ # UI primitives (DataTable, Badge, Button, Select, etc.)
│ └── layout/ # Shell, Sidebar, Topbar, ThemeToggle
├── domain/proposals/ # Domain logic (types, validation, diff, review state)
├── features/proposals/ # Proposal UI (forms, routes, session store)
├── lib/ # Shared utilities
│ ├── data/ # Data accessors (competitions, teams, players, etc.)
│ └── olmanager/ # Data loader layer (fs-based JSON reader, types, rating)
└── types/ # TypeScript declaration files
Game data lives in a separate repository — OpenLeagueManager/olmanager-data — linked as a git submodule at src/data/. This separation means:
- Data changes don't redeploy the app — the app is deployed independently
- Data has its own audit trail —
git login the data repo shows every change - OLManager consumes the same data — the game can link the same submodule
- Permissions are separate — data maintainers don't need app code access
To clone with data:
git clone --recurse-submodules git@github.com:OpenLeagueManager/olmanager-data-manager.git- Server Components handle data fetching. Client Components handle interactivity (DataTable, forms, theme toggle). Pre-computed data is passed across the boundary as serializable props.
- DataTable supports text search, column filters (dropdowns auto-extracted from data), and three-state sort (asc → desc → none).
- Proposal validation uses a typed validator factory that closes over game data for domain-specific checks (entity existence, attribute ranges, role validation).
- Theme uses Tailwind CSS 4 with
@custom-variant darkandnext-themesfor the toggle.
| Type | Description |
|---|---|
| AddPlayer | Create a new player record |
| EditPlayer | Modify an existing player's profile or attributes |
| TransferPlayer | Move a player between teams |
| ReleasePlayer | Record a player release with reason and severance |
| AddStaff | Create a new staff member |
| EditStaff | Modify a staff member's role, wage, or attributes |
| ReleaseStaff | Record a staff release |
| EditTeam | Modify team budget, focus, or identity |
| RemoveTeam | Record a team removal |
| EditCompetition | Modify competition name, logo, tier, or active state |
| RemoveCompetition | Record a competition removal |
| AddSocialAccount | Create a social media account |
| EditSocialTemplate | Modify a social post template |
| AddNewsTemplate | Create a news storyline template |
All proposals are validated against the current game data. Diffs are computed deterministically between the current state and the proposed changes.
This is a local data exploration and proposal drafting tool. The following are intentionally not implemented:
- No authentication — Discord OAuth and role checks are not wired
- No persistence — proposals live in
sessionStorage(browser tab only, cleared on close) - No backend — no database, no API, no GitHub integration
- No production workflow — review decisions are stubs; approving a proposal does not write data back
The MVP boundary is clearly marked in the UI. This app is designed for maintainers to draft, validate, and review data changes locally before manually applying them to the upstream data repository.
- Add or update JSON files in
src/data/following the existing schema - If adding a new competition, create
src/data/competitions/<id>/manifest.jsonwithteams_fileandplayers_filefields - Rebuild:
npm run build - The data explorer will automatically pick up new files
- Framework: Next.js 16.2 (App Router, Turbopack)
- UI: React 19, Tailwind CSS 4
- Language: TypeScript
- Testing: Vitest + Testing Library
- Theme: next-themes (class strategy)
- Data: Local JSON files loaded via
fs+ Reactcache()