@vatvaghool/create-ipl-dashboard 0.1.20 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,13 +4,16 @@ Scaffold a full-featured IPL fantasy cricket dashboard in seconds.
4
4
 
5
5
  ```bash
6
6
  npx @vatvaghool/create-ipl-dashboard my-league
7
+ cd my-league
8
+ # Edit .env with your MongoDB URI, league URL and POST secret
9
+ npm run dev:simple
7
10
  ```
8
11
 
9
- Follow the prompts to enter your MongoDB URI, fantasy league URL, and league name — you get a ready-to-run Next.js dashboard with standings charts, performance trackers, AI roasts, and more.
12
+ Open http://localhost:3000
10
13
 
11
14
  ## Usage
12
15
 
13
- ```bash
16
+ ```
14
17
  npx @vatvaghool/create-ipl-dashboard [project-name] [options]
15
18
  ```
16
19
 
@@ -19,149 +22,61 @@ npx @vatvaghool/create-ipl-dashboard [project-name] [options]
19
22
  | Flag | Description |
20
23
  |------|-------------|
21
24
  | `--help`, `-h` | Show help message |
22
- | `--scrape` | Auto-detect team names from the league URL |
23
25
  | `--skip-install` | Skip `npm install` (useful for testing) |
24
26
 
25
- ### Interactive prompts
27
+ ### Prompts
26
28
 
27
29
  | Prompt | Description |
28
30
  |--------|-------------|
29
31
  | Project name | Directory to scaffold into |
30
- | MongoDB URI | Your MongoDB connection string (press Enter for default) |
31
- | League URL | The fantasy.iplt20.com league page URL |
32
32
  | League name | Used as MongoDB collection name |
33
- | Team names | Comma-separated with optional owner in parens, e.g. `Team A (Alice), Team B (Bob)` (press Enter for defaults) |
34
-
35
- Use `--scrape` to auto-detect from the league URL, or press Enter at the team names prompt for default placeholder teams. You can always edit `app/data/teams.ts` later.
36
-
37
- ## Screenshots
38
-
39
- ![](https://files.catbox.moe/a83xlk.png)
40
-
41
- *Dashboard overview — full page*
42
-
43
- ![](https://files.catbox.moe/hl0kxd.png)
44
-
45
- *Performance Tracker — Recharts line chart*
46
-
47
- ![](https://files.catbox.moe/k4sw53.png)
48
-
49
- *Captain Board — captain/vice-captain picks*
33
+ | Team names | Comma-separated with optional owner in parens e.g. `Team A (Alice), Team B (Bob)` (Enter for 8 default teams) |
50
34
 
51
- ![](https://files.catbox.moe/vt5i8w.png)
35
+ ### Setup
52
36
 
53
- *Ledger Table standings with rank shifts and efficiency*
37
+ After scaffolding, edit `.env` and fill in:
54
38
 
55
- ![](https://files.catbox.moe/tuvkjo.png)
56
-
57
- *Match Scrubber — interactive timeline scrubber*
58
-
59
- ![](https://files.catbox.moe/5nfi5i.png)
60
-
61
- *AI Roast Corner — generated commentary*
62
-
63
- ## What you get
64
-
65
- A full Next.js 16 project with:
66
-
67
- - **Leaderboard dashboard** — live standings with rank movements and point gaps
68
- - **Performance charts** — Recharts line charts tracking every team's trajectory
69
- - **Captain board** — captain/vice-captain picks per team
70
- - **Match scrubber** — interactive timeline through match history
71
- - **AI roasting** — generated commentary in multiple languages
72
- - **Stock ticker** — fantasy stocks with sparklines
73
- - **Live updates** — bookmarklet or Playwright scraper for live sync
74
- - **MongoDB persistence** — auto-configured database connection with collection per league
39
+ ```
40
+ MONGODB_URI=mongodb+srv://...
41
+ IPL_LEAGUE_URL=https://fantasy.iplt20.com/classic/league/view/...
42
+ IPL_POST_SECRET=your-secret
43
+ ```
75
44
 
76
- ## Quick start after scaffold
45
+ Then seed data and start syncing:
77
46
 
78
47
  ```bash
79
- cd my-league
80
-
81
- # Start the dev server
82
- npm run dev:simple
48
+ npm run seed:api # Seed match data into MongoDB
49
+ npm run seed:league # Seed league metadata
50
+ npm run capture:ipl-auth # One-time Playwright login
51
+ npm run sync:ipl:watch # Start live sync
83
52
  ```
84
53
 
85
- Open http://localhost:3000 to see your dashboard.
86
-
87
- ### Available commands
54
+ ## Commands
88
55
 
89
56
  | Command | Description |
90
57
  |---------|-------------|
91
- | `npm run dev` | Dev server with welcome splash |
92
- | `npm run dev:simple` | Dev server (simple, no splash) |
58
+ | `npm run dev:simple` | Start dev server |
93
59
  | `npm run build` | Production build |
94
- | `npm start` | Production server |
95
60
  | `npm run capture:ipl-auth` | Capture Playwright login state (one-time) |
96
61
  | `npm run sync:ipl` | Scrape live leaderboard snapshot |
97
- | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode (polls every 2 min) |
98
- | `npm run sync:ipl:transfers-daily` | Scrape transfer/booster data |
99
- | `npm run sync:cloud` | Run both leaderboard + transfer sync (for cloud jobs) |
100
- | `npm run seed:league` | Seed league metadata into storage |
101
- | `npm run seed:mongodb` | Seed initial raw user data from `data.ts` |
102
- | `npm run seed:mongodb:reset` | Reset and re-seed MongoDB data |
103
- | `npm run verify:production` | Verify production setup |
104
- | `npm run monitor:ops` | Check ops health status |
105
- | `npm run test` | Run test suite |
62
+ | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode |
63
+ | `npm run sync:cloud` | Run all scrapers |
64
+ | `npm run seed:api` | POST /api/ops/seed seed match data into MongoDB |
65
+ | `npm run seed:league` | Seed league metadata into MongoDB |
66
+ | `npm run test` | Run tests |
106
67
  | `npm run lint` | Run linter |
107
68
 
108
- ## How it works
69
+ ## API
109
70
 
110
- The CLI:
71
+ | Route | Method | Description |
72
+ |-------|--------|-------------|
73
+ | `/api/ipl` | GET | Dashboard payload |
74
+ | `/api/ipl` | POST | Ingest leaderboard snapshot |
75
+ | `/api/ipl/transfers` | GET/POST | Transfer/booster data |
76
+ | `/api/ipl/upcoming-matches` | GET | Upcoming match schedule |
77
+ | `/api/ops/status` | GET | Health check |
78
+ | `/api/ops/seed` | POST | Seed initial match data into MongoDB |
111
79
 
112
- 1. Copies a pre-built Next.js app template
113
- 2. Writes your `.env` with the `MONGODB_URI`, `COLLECTION_NAME`, league URL, and league name
114
- 3. Generates `app/data/teams.ts` with your team roster (if `--scrape` was used)
115
- 4. Generates `app/data/league.ts` with league metadata
116
- 5. Creates a placeholder `app/data/match-points.ts` (auto-populates as you sync)
117
- 6. Installs dependencies
118
- 7. Runs `seed:league` to create a **document in your specified collection** in the pre-configured database, storing league metadata (name, URL, teams, timestamps)
119
-
120
- Each league gets its own collection — run `create-ipl-dashboard` again with a different league name to add another league.
121
-
122
- The template includes all dashboard components, API endpoints, scrapers, and tests from the [ipl-dashboard](https://github.com/anomalyco/ipl-dashboard) project.
123
-
124
- ---
125
-
126
- ## Next Steps
127
-
128
- ### Adding more leagues
129
-
130
- ```bash
131
- npx @vatvaghool/create-ipl-dashboard another-league
132
- ```
133
-
134
- Provide a different league name and the new league will be stored in its own collection — data stays fully isolated.
135
-
136
- ### Viewing seeded data
137
-
138
- Connect with any MongoDB client. Each league appears as a separate collection with `type: "league"`.
139
-
140
- ### Production deployment
141
-
142
- ```bash
143
- cd my-league
144
- npm run build
145
- npx vercel --prod
146
- ```
147
-
148
- Set `IPL_POST_SECRET` in your Vercel dashboard. Storage credentials are already populated in the scaffolded `.env`.
149
-
150
- ### Data sync workflow
151
-
152
- ```bash
153
- # 1. Capture browser login (one-time)
154
- npm run capture:ipl-auth
155
-
156
- # 2. Scrape leaderboard (manual or watch mode)
157
- npm run sync:ipl
158
- npm run sync:ipl:watch # auto-poll every 2 min
159
-
160
- # 3. Scrape transfer/booster data
161
- npm run sync:ipl:transfers-daily
162
-
163
- # 4. Run all scrapers (for cloud jobs)
164
- npm run sync:cloud
165
- ```
80
+ ## Project
166
81
 
167
- See the [Available commands](#quick-start-after-scaffold) table above for all options.
82
+ A full Next.js 16 project with dashboard components, API endpoints, scrapers, and tests.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vatvaghool/create-ipl-dashboard",
3
- "version": "0.1.20",
3
+ "version": "0.1.23",
4
4
  "description": "Scaffold an IPL fantasy cricket dashboard project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,8 +8,7 @@
8
8
  },
9
9
  "files": [
10
10
  "src",
11
- "template",
12
- "screenshots"
11
+ "template"
13
12
  ],
14
13
  "publishConfig": {
15
14
  "access": "public"
package/src/index.mjs CHANGED
@@ -3,9 +3,8 @@
3
3
  import { join } from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
  import { mkdir } from "node:fs/promises";
6
- import { getProjectName, getMongoUri, getLeagueUrl, getLeagueName, getTeams, close } from "./prompts.mjs";
6
+ import { getProjectName, getLeagueName, getTeams, close } from "./prompts.mjs";
7
7
  import { scaffoldProject } from "./scaffold.mjs";
8
- import { scrapeTeamsFromUrl } from "./scraper.mjs";
9
8
 
10
9
  function printHelp(exitCode) {
11
10
  console.log(`
@@ -19,11 +18,9 @@ function printHelp(exitCode) {
19
18
  Options:
20
19
  --help, -h Show this help message
21
20
  --skip-install Skip npm install after scaffolding
22
- --scrape Auto-detect team names from the league URL
23
21
 
24
22
  Examples:
25
23
  npx create-ipl-dashboard my-league
26
- npx create-ipl-dashboard my-league --scrape
27
24
  npx create-ipl-dashboard --skip-install
28
25
  `);
29
26
  process.exit(exitCode);
@@ -31,12 +28,11 @@ function printHelp(exitCode) {
31
28
 
32
29
  async function main() {
33
30
  const args = process.argv.slice(2);
34
- const flags = { scrape: false, skipInstall: false, help: false };
31
+ const flags = { skipInstall: false, help: false };
35
32
  const positional = [];
36
33
 
37
34
  for (const arg of args) {
38
35
  if (arg === "--help" || arg === "-h") flags.help = true;
39
- else if (arg === "--scrape") flags.scrape = true;
40
36
  else if (arg === "--skip-install") flags.skipInstall = true;
41
37
  else if (!arg.startsWith("--")) positional.push(arg);
42
38
  else { console.error(`Unknown flag: ${arg}`); printHelp(1); }
@@ -59,22 +55,9 @@ async function main() {
59
55
 
60
56
  await mkdir(projectPath, { recursive: true });
61
57
 
62
- const mongoUri = await getMongoUri();
63
- const leagueUrl = await getLeagueUrl();
64
58
  const leagueName = await getLeagueName();
65
59
 
66
- let teams;
67
- if (flags.scrape && leagueUrl) {
68
- console.log(" Attempting to scrape team names from league URL...");
69
- teams = await scrapeTeamsFromUrl(leagueUrl);
70
- if (teams && teams.length > 0) {
71
- console.log(` Found ${teams.length} team(s): ${teams.map((t) => t.name).join(", ")}`);
72
- }
73
- }
74
-
75
- if (!teams) {
76
- teams = await getTeams();
77
- }
60
+ let teams = await getTeams();
78
61
 
79
62
  if (!teams || teams.length === 0) {
80
63
  teams = [
@@ -91,18 +74,14 @@ async function main() {
91
74
 
92
75
  close();
93
76
 
94
- await scaffoldProject(projectPath, { mongoUri, leagueUrl, leagueName, teams, skipInstall: flags.skipInstall });
77
+ await scaffoldProject(projectPath, { leagueName, teams, skipInstall: flags.skipInstall });
95
78
 
96
79
  console.log("");
97
80
  console.log(" Next steps:");
98
81
  console.log(` cd ${projectName}`);
82
+ console.log(" Edit .env — set MONGODB_URI, IPL_LEAGUE_URL and IPL_POST_SECRET");
99
83
  console.log(" npm run dev:simple");
100
84
  console.log("");
101
- console.log(" For live data sync, capture auth state:");
102
- console.log(` cd ${projectName}`);
103
- console.log(" npm run capture:ipl-auth");
104
- console.log(" npm run sync:ipl");
105
- console.log("");
106
85
  }
107
86
 
108
87
  main().catch((err) => {
package/src/prompts.mjs CHANGED
@@ -33,25 +33,6 @@ export async function getProjectName(args) {
33
33
  return (await prompt("Project name: ")).trim() || "ipl-dashboard";
34
34
  }
35
35
 
36
- const DEFAULT_MONGODB_URI = "mongodb+srv://admin:admin@cricket.ptjjrub.mongodb.net/?appName=cricket";
37
-
38
- export async function getMongoUri() {
39
- if (!isTTY) {
40
- await consumeAllLines();
41
- return (pipedLines[promptIndex++] || DEFAULT_MONGODB_URI || "").trim();
42
- }
43
- const answer = (await prompt("MongoDB URI (press Enter for default): ")).trim();
44
- return answer || DEFAULT_MONGODB_URI || "";
45
- }
46
-
47
- export async function getLeagueUrl() {
48
- if (!isTTY) {
49
- await consumeAllLines();
50
- return (pipedLines[promptIndex++] || "").trim();
51
- }
52
- return (await prompt("IPL fantasy league URL: ")).trim();
53
- }
54
-
55
36
  export async function getLeagueName() {
56
37
  if (!isTTY) {
57
38
  await consumeAllLines();
package/src/scaffold.mjs CHANGED
@@ -10,7 +10,7 @@ const TEMPLATE_DIR = join(PACKAGE_ROOT, "template");
10
10
 
11
11
  export async function scaffoldProject(
12
12
  projectPath,
13
- { mongoUri, leagueUrl, leagueName, teams, skipInstall = false },
13
+ { leagueName, teams, skipInstall = false },
14
14
  ) {
15
15
  console.log(`\nCreating project at ${projectPath}...`);
16
16
 
@@ -33,13 +33,9 @@ export async function scaffoldProject(
33
33
  } catch {}
34
34
  }
35
35
 
36
- await writeEnvFile(projectPath, {
37
- mongoUri,
38
- leagueUrl,
39
- leagueName,
40
- });
36
+ await writeEnvFile(projectPath, { leagueName });
41
37
  await writeTeamData(projectPath, teams || []);
42
- await writeLeagueData(projectPath, { leagueName, leagueUrl, teams: teams || [] });
38
+ await writeLeagueData(projectPath, { leagueName, teams: teams || [] });
43
39
  await writeMatchPointsPlaceholder(projectPath);
44
40
  await updatePackageJson(projectPath);
45
41
 
@@ -56,16 +52,11 @@ export async function scaffoldProject(
56
52
  console.log(" Running npm install...");
57
53
  execSync("npm install", { cwd: projectPath, stdio: "inherit" });
58
54
 
59
- if (leagueName) {
60
- console.log(" Seeding league metadata...");
61
- try {
62
- execSync(`npm run seed:league -- "${leagueName}" "${leagueName}"`, {
63
- cwd: projectPath,
64
- stdio: "inherit",
65
- });
66
- } catch {
67
- console.log(" League seed skipped (storage may not be reachable yet)");
68
- }
55
+ console.log(" Installing Playwright browser...");
56
+ try {
57
+ execSync("npx playwright install chromium", { cwd: projectPath, stdio: "inherit" });
58
+ } catch {
59
+ console.log(" Playwright install skipped (run manually: npx playwright install chromium)");
69
60
  }
70
61
  } else {
71
62
  console.log(
@@ -76,18 +67,21 @@ export async function scaffoldProject(
76
67
  console.log("\n Done! Your project is ready at:", projectPath);
77
68
  }
78
69
 
79
- async function writeEnvFile(
80
- projectPath,
81
- { mongoUri, leagueUrl, leagueName },
82
- ) {
70
+ async function writeEnvFile(projectPath, { leagueName }) {
83
71
  const envPath = join(projectPath, ".env");
84
72
  const lines = [
85
73
  `COLLECTION_NAME=${leagueName || "my_league"}`,
86
- `MONGODB_URI=${mongoUri || ""}`,
87
- `IPL_LEAGUE_URL=${leagueUrl || ""}`,
74
+ `MONGODB_URI=`,
75
+ `IPL_LEAGUE_URL=`,
88
76
  `IPL_LEAGUE_NAME=${leagueName || ""}`,
89
77
  `IPL_POST_SECRET=`,
90
78
  ``,
79
+ `# Replace the values above, then run:`,
80
+ `# npm run seed:api (seed match data into MongoDB)`,
81
+ `# npm run seed:league (seed league metadata)`,
82
+ `# npm run capture:ipl-auth (one-time login)`,
83
+ `# npm run sync:ipl (scrape live leaderboard)`,
84
+ ``,
91
85
  `IPL_API_BASE_URL=http://localhost:3000`,
92
86
  `IPL_API_LOG=1`,
93
87
  `IPL_API_LOG_PAYLOAD=0`,
@@ -137,7 +131,7 @@ for (const team of TEAMS) {
137
131
  await writeFile(join(dir, "teams.ts"), content);
138
132
  }
139
133
 
140
- async function writeLeagueData(projectPath, { leagueName, leagueUrl, teams }) {
134
+ async function writeLeagueData(projectPath, { leagueName, teams }) {
141
135
  const dir = join(projectPath, "app/data");
142
136
  await mkdir(dir, { recursive: true });
143
137
 
@@ -153,7 +147,7 @@ async function writeLeagueData(projectPath, { leagueName, leagueUrl, teams }) {
153
147
 
154
148
  export const leagueInfo: LeagueInfo = {
155
149
  name: "${leagueName || ""}",
156
- leagueUrl: "${leagueUrl || ""}",
150
+ leagueUrl: "",
157
151
  teams: [
158
152
  ${teamEntries},
159
153
  ],
@@ -188,6 +182,7 @@ async function updatePackageJson(projectPath) {
188
182
  pkg.private = false;
189
183
  delete pkg.scripts?.["generate:template"];
190
184
  pkg.scripts["seed:league"] = "node scripts/seed-league.mjs";
185
+ pkg.scripts["seed:api"] = "node --experimental-strip-types -e \"fetch('http://localhost:3000/api/ops/seed',{method:'POST'}).then(r=>r.json()).then(console.log).catch(console.error)\"";
191
186
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
192
187
  } catch {}
193
188
  }
@@ -1,226 +1,57 @@
1
1
  # IPL Fantasy Cricket Dashboard
2
2
 
3
- Private Next.js dashboard for an IPL fantasy league. Combines seeded match history with live leaderboard snapshots to show standings, match-by-match movement, captain picks, cumulative trends, and league insights in one place.
4
-
5
3
  ## Quick Start
6
4
 
7
5
  ```bash
8
- # Scaffold a new project
9
6
  npx @vatvaghool/create-ipl-dashboard my-league
10
-
11
- # Or from source:
12
- npm install
13
- ```
14
-
15
- Start the dev server:
16
-
17
- ```bash
18
- npm run dev:simple
7
+ cd my-league
8
+ # Edit .env — set MONGODB_URI, IPL_LEAGUE_URL and IPL_POST_SECRET
9
+ npm run seed:api # Seed match data into MongoDB
10
+ npm run seed:league # Seed league metadata
11
+ npm run capture:ipl-auth # One-time Playwright login
12
+ npm run sync:ipl:watch # Start live sync
19
13
  ```
20
14
 
21
15
  Open http://localhost:3000
22
16
 
23
- ### Available commands
17
+ ## Commands
24
18
 
25
19
  | Command | Description |
26
20
  |---------|-------------|
27
- | `npm run dev` | Dev server with welcome splash |
28
- | `npm run dev:simple` | Dev server (simple, no splash) |
21
+ | `npm run dev:simple` | Start dev server |
29
22
  | `npm run build` | Production build |
30
- | `npm start` | Production server |
31
23
  | `npm run capture:ipl-auth` | Capture Playwright login state (one-time) |
32
24
  | `npm run sync:ipl` | Scrape live leaderboard snapshot |
33
- | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode (polls every 2 min) |
34
- | `npm run sync:ipl:transfers-daily` | Scrape transfer/booster data |
35
- | `npm run sync:cloud` | Run both leaderboard + transfer sync (for cloud jobs) |
36
- | `npm run seed:league` | Seed league metadata into storage |
37
- | `npm run seed:mongodb` | Seed initial raw user data from `data.ts` |
38
- | `npm run seed:mongodb:reset` | Reset and re-seed MongoDB data |
39
- | `npm run verify:production` | Verify production setup |
40
- | `npm run monitor:ops` | Check ops health status |
41
- | `npm run test` | Run test suite |
25
+ | `npm run sync:ipl:watch` | Scrape leaderboard in watch mode |
26
+ | `npm run sync:cloud` | Run all scrapers |
27
+ | `npm run seed:api` | Seed match data into MongoDB |
28
+ | `npm run seed:league` | Seed league metadata |
29
+ | `npm run test` | Run tests |
42
30
  | `npm run lint` | Run linter |
43
31
 
44
- ---
45
-
46
- ## Screenshots
47
-
48
- ![](https://files.catbox.moe/a83xlk.png)
49
-
50
- *Dashboard overview — full page*
51
-
52
- ![](https://files.catbox.moe/hl0kxd.png)
53
-
54
- *Performance Tracker — Recharts line chart*
55
-
56
- ![](https://files.catbox.moe/k4sw53.png)
57
-
58
- *Captain Board — captain/vice-captain picks*
59
-
60
- ![](https://files.catbox.moe/vt5i8w.png)
61
-
62
- *Ledger Table — standings with rank shifts and efficiency*
63
-
64
- ![](https://files.catbox.moe/tuvkjo.png)
65
-
66
- *Match Scrubber — interactive timeline scrubber*
67
-
68
- ![](https://files.catbox.moe/5nfi5i.png)
69
-
70
- *AI Roast Corner — generated commentary*
71
-
72
- ---
73
-
74
- ## Components
75
-
76
- ### Dashboard Sections
77
-
78
- | Component | File | Description |
79
- |-----------|------|-------------|
80
- | **StickyMini** | `components/dashboard/StickyMini.tsx` | Compact stat card with title, value, note, and color-coded icon |
81
- | **SectionCard** | `components/dashboard/SectionCard.tsx` | Animated motion-section wrapper with title, note, accent color, and staggered entrance |
82
- | **ChartBoard** | `components/dashboard/ChartBoard.tsx` | Recharts bar chart for team data with pill tags and tooltips |
83
- | **CaptainBoard** | `components/dashboard/CaptainBoard.tsx` | All playing players with points as TeamPills badges, C/VC highlighted |
84
- | **LedgerTable** | `components/dashboard/LedgerTable.tsx` | Full standings table with colors, boosters, rank arrows, and formatted points |
85
- | **LatestBadge** | `components/dashboard/LatestBadge.tsx` | Color-coded pill badge for points (pink <200, orange 200-400, green >400) |
86
-
87
- ### Widgets
88
-
89
- | Component | File | Description |
90
- |-----------|------|-------------|
91
- | **PerformanceTracker** | `components/PerformanceTracker.tsx` | Recharts line chart tracking each team's point trajectory across match days |
92
- | **CrownBattle** | `components/CrownBattle.tsx` | Head-to-head showdown between #1 and #2 ranked teams |
93
- | **AIRoasting** | `components/AIRoasting.tsx` | AI-generated roast commentary cards cycling through hot/cold/lightning/trend types |
94
- | **FantasyStockTicker** | `components/FantasyStockTicker.tsx` | Team stocks with trend arrows, sparklines, and fluctuating values |
95
- | **TeamDNAScanner** | `components/TeamDNAScanner.tsx` | Sci-fi DNA scan visualization with moving scan-line and std deviation metrics |
96
- | **MatchRecapScroll** | `components/MatchRecapScroll.tsx` | Scroll-styled recap panel for the latest match day results |
97
- | **MatchStoryScrubber** | `components/MatchStoryScrubber.tsx` | Interactive timeline scrubber to step through match history with bar charts |
98
- | **LiveMatchTicker** | `components/LiveMatchTicker.tsx` | Live-scrolling ticker of upcoming matches with countdowns |
99
- | **ProgressGlowRings** | `components/ProgressGlowRings.tsx` | Animated SVG glowing ring progress indicators per team |
100
- | **ColorWave** | `components/ColorWave.tsx` | Animated SVG wave/chart visualizing team scores over time |
101
- | **FireworksBurst** | `components/FireworksBurst.tsx` | Particle-based fireworks celebration on milestones |
102
- | **ThemeToggle** | `components/ThemeToggle.tsx` | Dark/light mode toggle persisted to localStorage |
103
-
104
- ### UI Primitives
105
-
106
- | Component | File | Description |
107
- |-----------|------|-------------|
108
- | **TeamPills** | `components/ui/TeamPills.tsx` | Flex-wrap row of bordered pill/chip team labels |
109
- | **DashboardChartFrame** | `components/ui/DashboardChartFrame.tsx` | Responsive chart wrapper with hover-jitter effects |
110
- | **DoodleSpinner** | `components/ui/DoodleSpinner.tsx` | Hand-drawn SVG loading spinner with dotted circles |
111
-
112
- ---
113
-
114
- ## API Endpoints
32
+ ## API
115
33
 
116
34
  | Route | Method | Description |
117
35
  |-------|--------|-------------|
118
- | `/api/ipl` | GET | Full dashboard payload used by the UI |
119
- | `/api/ipl?format=snapshot` | GET | Raw normalized snapshot only |
120
- | `/api/ipl` | POST | Ingest a leaderboard snapshot |
121
- | `/api/ipl/transfers` | GET | Transfer/booster snapshot for all teams (fetched once on mount, not on poll) |
122
- | `/api/ipl/transfers` | POST | Ingest a single team transfer record |
36
+ | `/api/ipl` | GET | Dashboard payload |
37
+ | `/api/ipl` | POST | Ingest leaderboard snapshot |
38
+ | `/api/ipl/transfers` | GET/POST | Transfer/booster data |
123
39
  | `/api/ipl/upcoming-matches` | GET | Upcoming match schedule |
124
- | `/api/ops/status` | GET | Health/monitoring endpoint |
125
-
126
- ---
127
-
128
- ## Data Flow
129
-
130
- ```
131
- GET /api/ipl resolution order:
132
- 1. MongoDB raw users (if MONGODB_URI configured)
133
- 2. Fallback: local seed data (app/api/ipl/data.ts)
134
- 3. MongoDB live snapshot (if MONGODB_URI configured)
135
- 4. Fallback: local snapshot file (app/api/ipl/live-snapshot.json)
136
- ```
137
-
138
- ---
139
-
140
- ## Configuration
141
-
142
- All hardcoded values are centralized in `app/lib/config.ts` and overridable via environment variables:
143
-
144
- | Variable | Default | Description |
145
- |----------|---------|-------------|
146
- | `MONGODB_URI` | - | MongoDB connection string |
147
- | `LEAGUE_NAME` | - | League name (collection name) |
148
- | `IPL_LEAGUE_URL` | fantasy.iplt20.com/... | Fantasy league page URL |
149
- | `IPL_POST_SECRET` | - | Bearer token for POST endpoints |
150
- | `IPL_DB_NAME` | `ipl` | MongoDB database name |
151
- | `IPL_COLLECTION_NAME` | `ipl` | MongoDB collection name |
152
- | `IPL_TOTAL_TRANSFERS` | `160` | Total transfers per season |
153
- | `IPL_SYNC_INTERVAL_MS` | `120000` | Dashboard polling interval |
154
- | `IPL_DASHBOARD_STALE_MINUTES` | `20` (prod) / `180` (dev) | Staleness threshold |
155
- | `IPL_TRANSFERS_STALE_MINUTES` | `720` (prod) / `1440` (dev) | Transfer staleness threshold |
156
-
157
- ---
158
-
159
- ## Automation
160
-
161
- See the [commands table](#available-commands) above for all options.
162
-
163
- Also supports a browser bookmarklet — visit `/api/ipl/bookmarklet` while the app is running, copy the returned `javascript:` code, and save it as a bookmark. Click it on the fantasy leaderboard page to sync data.
164
-
165
- ---
40
+ | `/api/ops/status` | GET | Health check |
41
+ | `/api/ops/seed` | POST | Seed initial match data into MongoDB |
166
42
 
167
43
  ## Deployment
168
44
 
169
- ### Deploy to Vercel (recommended)
170
-
171
45
  ```bash
172
- # Build the app
173
46
  npm run build
174
-
175
- # Deploy to Vercel
176
47
  npx vercel --prod
177
48
  ```
178
49
 
179
- Set these environment variables in your Vercel project dashboard:
180
-
181
- | Variable | Required | Description |
182
- |----------|----------|-------------|
183
- | `MONGODB_URI` | Yes | MongoDB connection string |
184
- | `LEAGUE_NAME` | Yes | MongoDB collection name |
185
- | `IPL_POST_SECRET` | Yes | Bearer token protecting write endpoints |
186
- | `IPL_LEAGUE_URL` | Yes | Your fantasy league page URL |
187
- | `IPL_DASHBOARD_STALE_MINUTES` | No | Staleness threshold for health checks (default: `20`) |
188
- | `IPL_TRANSFERS_STALE_MINUTES` | No | Transfer staleness threshold (default: `720`) |
189
-
190
- After deployment:
191
-
192
- 1. Verify the app is healthy — visit `https://your-app.vercel.app/api/ops/status`
193
- 2. Run the Playwright scraper (`npm run sync:cloud`) pointed at your production URL to populate live data
194
- 3. Optionally deploy the scraper as a Cloud Run Job with `Dockerfile.sync` for automated sync every 5-10 minutes
195
-
196
- ### Without Vercel
197
-
198
- ```bash
199
- # Build
200
- npm run build
201
-
202
- # Start production server
203
- npm start
204
- ```
205
-
206
- The app runs as a standard Node.js server on `process.env.PORT` (default `3000`).
207
-
208
- ### Production requirements
209
-
210
- - **MongoDB** is required in production for POST endpoints (returns `503` without it)
211
- - **IPL_POST_SECRET** should be a strong random value set in your hosting environment
212
- - The scraper should run outside the web app (Cloud Run, GitHub Actions, cron) — never in the same process
213
-
214
- ---
50
+ Set `MONGODB_URI`, `COLLECTION_NAME`, `IPL_LEAGUE_URL`, and `IPL_POST_SECRET` in your Vercel dashboard.
215
51
 
216
52
  ## Stack
217
53
 
218
- - **Next.js** 16 App Router
219
- - **React** 19
220
- - **TypeScript**
221
- - **Tailwind CSS** 4
222
- - **Recharts** — charts
223
- - **Framer Motion** — animations
54
+ - **Next.js** 16 App Router, **React** 19, **TypeScript**, **Tailwind CSS** 4
55
+ - **Recharts**, **Framer Motion**, **Zustand**
224
56
  - **MongoDB** — data persistence
225
- - **Zustand** — client state
226
57
  - **Playwright** — scraper automation