@vatvaghool/create-ipl-dashboard 0.1.0
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 +75 -0
- package/package.json +27 -0
- package/src/generate-template.mjs +73 -0
- package/src/index.mjs +98 -0
- package/src/prompts.mjs +78 -0
- package/src/scaffold.mjs +129 -0
- package/src/scraper.mjs +79 -0
- package/template/.dockerignore +13 -0
- package/template/AGENTS.md +5 -0
- package/template/Dockerfile.sync +14 -0
- package/template/README.md +160 -0
- package/template/app/api/ipl/data.ts +24 -0
- package/template/app/api/ipl/route.ts +505 -0
- package/template/app/api/ipl/transfers/route.ts +261 -0
- package/template/app/api/ipl/transfers/transform.ts +156 -0
- package/template/app/api/ipl/transform.ts +20 -0
- package/template/app/api/ipl/upcoming-matches/route.ts +18 -0
- package/template/app/api/ops/status/route.ts +225 -0
- package/template/app/components/AIRoasting.tsx +278 -0
- package/template/app/components/ColorWave.tsx +193 -0
- package/template/app/components/CrownBattle.tsx +207 -0
- package/template/app/components/DashboardContent.tsx +377 -0
- package/template/app/components/FantasyStockTicker.tsx +192 -0
- package/template/app/components/FireworksBurst.tsx +225 -0
- package/template/app/components/LiveMatchTicker.tsx +117 -0
- package/template/app/components/MatchRecapScroll.tsx +135 -0
- package/template/app/components/MatchStoryScrubber.tsx +274 -0
- package/template/app/components/PerformanceTracker.tsx +132 -0
- package/template/app/components/ProgressGlowRings.tsx +157 -0
- package/template/app/components/TeamDNAScanner.tsx +238 -0
- package/template/app/components/ThemeToggle.tsx +74 -0
- package/template/app/components/dashboard/CaptainBoard.tsx +138 -0
- package/template/app/components/dashboard/ChartBoard.tsx +162 -0
- package/template/app/components/dashboard/LatestBadge.tsx +23 -0
- package/template/app/components/dashboard/LedgerTable.tsx +385 -0
- package/template/app/components/dashboard/SectionCard.tsx +59 -0
- package/template/app/components/dashboard/StickyMini.tsx +20 -0
- package/template/app/components/dashboard/index.ts +6 -0
- package/template/app/components/ui/DashboardChartFrame.tsx +74 -0
- package/template/app/components/ui/DoodleSpinner.tsx +15 -0
- package/template/app/components/ui/TeamPills.tsx +41 -0
- package/template/app/data/match-points.ts +3 -0
- package/template/app/data/teams.ts +32 -0
- package/template/app/globals.css +1267 -0
- package/template/app/hooks/dashboard/index.ts +1 -0
- package/template/app/hooks/dashboard/useDashboardModel.ts +25 -0
- package/template/app/hooks/dashboardCache.ts +53 -0
- package/template/app/hooks/dashboardPolling.ts +53 -0
- package/template/app/hooks/snapshotCache.ts +47 -0
- package/template/app/hooks/useDashboardData.ts +28 -0
- package/template/app/layout.tsx +75 -0
- package/template/app/lib/aiAgent.ts +444 -0
- package/template/app/lib/config.ts +29 -0
- package/template/app/lib/dashboard/index.ts +1 -0
- package/template/app/lib/dashboard/model.ts +257 -0
- package/template/app/lib/dashboardData.ts +50 -0
- package/template/app/lib/dashboardView.ts +22 -0
- package/template/app/lib/detailedData.ts +112 -0
- package/template/app/lib/matchStatus.ts +28 -0
- package/template/app/lib/matches.ts +131 -0
- package/template/app/lib/teamBadges.ts +223 -0
- package/template/app/lib/upcomingMatches.ts +154 -0
- package/template/app/lib/useDb.ts +29 -0
- package/template/app/lib/utils/diff.ts +24 -0
- package/template/app/lib/utils/getChartColor.ts +17 -0
- package/template/app/lib/utils/getStdDeviation.ts +6 -0
- package/template/app/lib/utils/time.ts +40 -0
- package/template/app/lib/utils.ts +70 -0
- package/template/app/page.tsx +15 -0
- package/template/app/store/dashboardStore.ts +85 -0
- package/template/app/types/dashboard.ts +75 -0
- package/template/app/types.ts +130 -0
- package/template/app/utils/dashboard/index.ts +72 -0
- package/template/eslint.config.mjs +18 -0
- package/template/infra/cloud-run/README.md +68 -0
- package/template/infra/cloud-run/sync-job.yaml +32 -0
- package/template/infra/cutover/README.md +84 -0
- package/template/infra/vercel/README.md +57 -0
- package/template/next.config.ts +7 -0
- package/template/package-lock.json +7330 -0
- package/template/package.json +47 -0
- package/template/packages/ipl-dashboard-utils/README.md +316 -0
- package/template/packages/ipl-dashboard-utils/package.json +34 -0
- package/template/packages/ipl-dashboard-utils/src/index.ts +22 -0
- package/template/packages/ipl-dashboard-utils/src/transform.ts +687 -0
- package/template/packages/ipl-dashboard-utils/src/types.ts +88 -0
- package/template/packages/ipl-dashboard-utils/tsconfig.build.json +17 -0
- package/template/postcss.config.mjs +7 -0
- package/template/scripts/capture-ipl-auth.mjs +54 -0
- package/template/scripts/deploy-cloud-run-sync.sh +48 -0
- package/template/scripts/deploy-cloud-scheduler.sh +42 -0
- package/template/scripts/dev-simple.js +31 -0
- package/template/scripts/dev-welcome.mjs +38 -0
- package/template/scripts/monitor-ops-status.sh +50 -0
- package/template/scripts/seed-mongodb.ts +115 -0
- package/template/scripts/sync-cloud.mjs +50 -0
- package/template/scripts/sync-ipl.mjs +238 -0
- package/template/scripts/sync-transfers-daily.mjs +175 -0
- package/template/scripts/verify-production.mjs +108 -0
- package/template/tests/coverage-gaps.test.ts +290 -0
- package/template/tests/dashboard-polling.test.ts +96 -0
- package/template/tests/detailed-data.test.ts +60 -0
- package/template/tests/ipl-transform.test.ts +590 -0
- package/template/tests/transfers-route.test.ts +109 -0
- package/template/tests/upcoming-matches.test.ts +34 -0
- package/template/tests/utils-and-cache.test.ts +267 -0
- package/template/tsconfig.json +35 -0
- package/template/vercel.json +7 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { motion } from "framer-motion";
|
|
2
|
+
import {
|
|
3
|
+
FaArrowDown,
|
|
4
|
+
FaArrowTrendUp,
|
|
5
|
+
FaCircle,
|
|
6
|
+
FaMinus,
|
|
7
|
+
FaTrophy,
|
|
8
|
+
} from "react-icons/fa6";
|
|
9
|
+
import { buildDetailedTableRows } from "../../lib/detailedData";
|
|
10
|
+
import {
|
|
11
|
+
getTeamColor,
|
|
12
|
+
getBoosterCount,
|
|
13
|
+
formatLedgerNumber,
|
|
14
|
+
} from "../../utils/dashboard";
|
|
15
|
+
import { LatestBadge } from "./LatestBadge";
|
|
16
|
+
|
|
17
|
+
interface LedgerTableProps {
|
|
18
|
+
rows: ReturnType<typeof buildDetailedTableRows>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function LedgerTable({ rows }: LedgerTableProps) {
|
|
22
|
+
if (!rows.length) {
|
|
23
|
+
return <p className="ledger-note py-6">No table rows available yet.</p>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const highestLatest = rows.reduce(
|
|
27
|
+
(max, row) => Math.max(max, row.latestPoints ?? 0),
|
|
28
|
+
0,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className="ledger-shell"
|
|
34
|
+
style={{
|
|
35
|
+
borderColor: "color-mix(in srgb, var(--accent-cyan) 30%, var(--line))",
|
|
36
|
+
background:
|
|
37
|
+
"linear-gradient(180deg, color-mix(in srgb, var(--accent-cyan) 7%, var(--panel-strong) 93%), color-mix(in srgb, var(--panel) 92%, transparent))",
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<table className="ledger-table">
|
|
41
|
+
<thead>
|
|
42
|
+
<tr>
|
|
43
|
+
<th>Rank</th>
|
|
44
|
+
<th>Team</th>
|
|
45
|
+
<th>Overall</th>
|
|
46
|
+
<th>Gap</th>
|
|
47
|
+
<th>Latest</th>
|
|
48
|
+
<th>Moves</th>
|
|
49
|
+
<th>Transfers Left</th>
|
|
50
|
+
<th>Booster</th>
|
|
51
|
+
<th>Eff</th>
|
|
52
|
+
</tr>
|
|
53
|
+
</thead>
|
|
54
|
+
<tbody>
|
|
55
|
+
{rows.map((row, index) => (
|
|
56
|
+
<TableRow
|
|
57
|
+
key={row.name}
|
|
58
|
+
row={row}
|
|
59
|
+
index={index}
|
|
60
|
+
highestLatest={highestLatest}
|
|
61
|
+
/>
|
|
62
|
+
))}
|
|
63
|
+
</tbody>
|
|
64
|
+
</table>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface TableRowProps {
|
|
70
|
+
row: ReturnType<typeof buildDetailedTableRows>[0];
|
|
71
|
+
index: number;
|
|
72
|
+
highestLatest: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function TableRow({ row, index, highestLatest }: TableRowProps) {
|
|
76
|
+
const teamColor = getTeamColor(index, 8); // Assuming 8 teams
|
|
77
|
+
const shift = getRankShift(row);
|
|
78
|
+
const shiftAmount = getShiftAmount(row);
|
|
79
|
+
const boosterCount = getBoosterCount(row.boostersUsed);
|
|
80
|
+
const isTopLatest =
|
|
81
|
+
row.latestPoints !== undefined &&
|
|
82
|
+
row.latestPoints === highestLatest &&
|
|
83
|
+
highestLatest > 0;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<motion.tr
|
|
87
|
+
initial={{ opacity: 0, x: -8 }}
|
|
88
|
+
animate={{ opacity: 1, x: 0 }}
|
|
89
|
+
transition={{ delay: index * 0.04 }}
|
|
90
|
+
className="relative"
|
|
91
|
+
style={{
|
|
92
|
+
background:
|
|
93
|
+
"color-mix(in srgb, var(--paper) 76%, var(--panel-strong) 24%)",
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<RankCell row={row} teamColor={teamColor} index={index} />
|
|
97
|
+
<TeamCell row={row} />
|
|
98
|
+
<OverallCell row={row} />
|
|
99
|
+
<GapCell row={row} />
|
|
100
|
+
<LatestCell row={row} isTop={isTopLatest} />
|
|
101
|
+
<MovesCell shift={shift} shiftAmount={shiftAmount} />
|
|
102
|
+
<TransfersCell row={row} />
|
|
103
|
+
<BoosterCell boosterCount={boosterCount} />
|
|
104
|
+
<EfficiencyCell row={row} />
|
|
105
|
+
</motion.tr>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function RankCell({
|
|
110
|
+
row,
|
|
111
|
+
teamColor,
|
|
112
|
+
index,
|
|
113
|
+
}: {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
row: any;
|
|
116
|
+
teamColor: string;
|
|
117
|
+
index: number;
|
|
118
|
+
}) {
|
|
119
|
+
return (
|
|
120
|
+
<td className="relative">
|
|
121
|
+
<motion.div
|
|
122
|
+
className="absolute inset-0 rounded-lg pointer-events-none"
|
|
123
|
+
style={{
|
|
124
|
+
background: `radial-gradient(circle at 50% 50%, color-mix(in srgb, ${teamColor} 14%, transparent) 0%, transparent 70%)`,
|
|
125
|
+
}}
|
|
126
|
+
animate={{ opacity: [0.2, 0.5, 0.2] }}
|
|
127
|
+
transition={{
|
|
128
|
+
repeat: Infinity,
|
|
129
|
+
duration: 2.5 + (index % 3),
|
|
130
|
+
ease: "easeInOut",
|
|
131
|
+
delay: index * 0.1,
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
<motion.div
|
|
135
|
+
className="absolute -inset-px rounded-lg pointer-events-none"
|
|
136
|
+
style={{
|
|
137
|
+
border: `1.5px solid ${teamColor}`,
|
|
138
|
+
opacity: 0.3,
|
|
139
|
+
}}
|
|
140
|
+
animate={{
|
|
141
|
+
opacity: [0.15, 0.45, 0.15],
|
|
142
|
+
filter: ["blur(2px)", "blur(5px)", "blur(2px)"],
|
|
143
|
+
}}
|
|
144
|
+
transition={{
|
|
145
|
+
repeat: Infinity,
|
|
146
|
+
duration: 2,
|
|
147
|
+
ease: "easeInOut",
|
|
148
|
+
delay: index * 0.1,
|
|
149
|
+
}}
|
|
150
|
+
/>
|
|
151
|
+
<span
|
|
152
|
+
className="relative z-10 inline-flex items-center gap-2 rounded-full border px-3 py-1 text-[13px] font-black"
|
|
153
|
+
style={{
|
|
154
|
+
borderColor: `color-mix(in srgb, ${teamColor} 38%, var(--line))`,
|
|
155
|
+
background: `color-mix(in srgb, ${teamColor} 16%, var(--panel-strong) 84%)`,
|
|
156
|
+
boxShadow: `0 0 12px color-mix(in srgb, ${teamColor} 20%, transparent)`,
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{row.rank === 1 ? (
|
|
160
|
+
<FaTrophy style={{ color: "var(--marker-yellow)" }} />
|
|
161
|
+
) : row.rank === 2 ? (
|
|
162
|
+
<FaTrophy style={{ color: "var(--marker-blue)" }} />
|
|
163
|
+
) : row.rank === 3 ? (
|
|
164
|
+
<FaTrophy style={{ color: "var(--marker-orange)" }} />
|
|
165
|
+
) : null}
|
|
166
|
+
<span>#{row.rank}</span>
|
|
167
|
+
</span>
|
|
168
|
+
</td>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
+
function TeamCell({ row }: { row: any }) {
|
|
174
|
+
return (
|
|
175
|
+
<td>
|
|
176
|
+
<div className="flex items-center gap-2">
|
|
177
|
+
<span className="font-semibold">{row.name}</span>
|
|
178
|
+
</div>
|
|
179
|
+
</td>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
|
+
function OverallCell({ row }: { row: any }) {
|
|
185
|
+
return <td className="font-semibold">{formatLedgerNumber(row.points)}</td>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
189
|
+
function GapCell({ row }: { row: any }) {
|
|
190
|
+
return (
|
|
191
|
+
<td>
|
|
192
|
+
<span
|
|
193
|
+
className="inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-[13px] font-black"
|
|
194
|
+
style={{
|
|
195
|
+
background:
|
|
196
|
+
typeof row.gapToNext === "number" && row.gapToNext > 0
|
|
197
|
+
? "color-mix(in srgb, var(--marker-yellow) 18%, var(--panel-strong))"
|
|
198
|
+
: "transparent",
|
|
199
|
+
color: "var(--ink)",
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
{typeof row.gapToNext === "number" && row.gapToNext > 0
|
|
203
|
+
? `+${formatLedgerNumber(row.gapToNext)}`
|
|
204
|
+
: "-"}
|
|
205
|
+
</span>
|
|
206
|
+
</td>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
211
|
+
function LatestCell({ row, isTop }: { row: any; isTop: boolean }) {
|
|
212
|
+
return (
|
|
213
|
+
<td>
|
|
214
|
+
<LatestBadge pts={row.latestPoints ?? 0} isTop={isTop} />
|
|
215
|
+
</td>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function MovesCell({
|
|
220
|
+
shift,
|
|
221
|
+
shiftAmount,
|
|
222
|
+
}: {
|
|
223
|
+
shift: string;
|
|
224
|
+
shiftAmount: number;
|
|
225
|
+
}) {
|
|
226
|
+
return (
|
|
227
|
+
<td>
|
|
228
|
+
{shift === "up" ? (
|
|
229
|
+
<span
|
|
230
|
+
className="inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[13px] font-black"
|
|
231
|
+
style={{
|
|
232
|
+
borderColor:
|
|
233
|
+
"color-mix(in srgb, var(--marker-green) 40%, var(--line))",
|
|
234
|
+
background:
|
|
235
|
+
"color-mix(in srgb, var(--marker-green) 16%, var(--panel-strong))",
|
|
236
|
+
color: "var(--ink)",
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
<FaArrowTrendUp />
|
|
240
|
+
<span>+{shiftAmount}</span>
|
|
241
|
+
</span>
|
|
242
|
+
) : shift === "down" ? (
|
|
243
|
+
<span
|
|
244
|
+
className="inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[13px] font-black"
|
|
245
|
+
style={{
|
|
246
|
+
borderColor:
|
|
247
|
+
"color-mix(in srgb, var(--marker-pink) 40%, var(--line))",
|
|
248
|
+
background:
|
|
249
|
+
"color-mix(in srgb, var(--marker-pink) 16%, var(--panel-strong))",
|
|
250
|
+
color: "var(--ink)",
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
<FaArrowDown />
|
|
254
|
+
<span>-{shiftAmount}</span>
|
|
255
|
+
</span>
|
|
256
|
+
) : shift === "same" ? (
|
|
257
|
+
<span
|
|
258
|
+
className="inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[13px] font-black"
|
|
259
|
+
style={{
|
|
260
|
+
borderColor:
|
|
261
|
+
"color-mix(in srgb, var(--marker-blue) 40%, var(--line))",
|
|
262
|
+
background:
|
|
263
|
+
"color-mix(in srgb, var(--marker-blue) 16%, var(--panel-strong))",
|
|
264
|
+
color: "var(--ink)",
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
<FaMinus />
|
|
268
|
+
<span>same</span>
|
|
269
|
+
</span>
|
|
270
|
+
) : (
|
|
271
|
+
<span className="text-(--ink-soft)">-</span>
|
|
272
|
+
)}
|
|
273
|
+
</td>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
278
|
+
function TransfersCell({ row }: { row: any }) {
|
|
279
|
+
return (
|
|
280
|
+
<td>
|
|
281
|
+
{typeof row.transfersLeft === "number" ? (
|
|
282
|
+
<span
|
|
283
|
+
className="inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[13px] font-bold"
|
|
284
|
+
style={{
|
|
285
|
+
borderColor:
|
|
286
|
+
"color-mix(in srgb, var(--accent-lime) 28%, var(--line))",
|
|
287
|
+
background:
|
|
288
|
+
"color-mix(in srgb, var(--accent-lime) 16%, var(--panel-strong))",
|
|
289
|
+
color: "var(--ink)",
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<FaCircle
|
|
293
|
+
className="text-[0.45rem]"
|
|
294
|
+
style={{ color: "var(--marker-green)" }}
|
|
295
|
+
/>
|
|
296
|
+
<span>{row.transfersLeft}</span>
|
|
297
|
+
</span>
|
|
298
|
+
) : (
|
|
299
|
+
"-"
|
|
300
|
+
)}
|
|
301
|
+
</td>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function BoosterCell({ boosterCount }: { boosterCount: number }) {
|
|
306
|
+
return (
|
|
307
|
+
<td>
|
|
308
|
+
{boosterCount > 0 ? (
|
|
309
|
+
<span
|
|
310
|
+
className="inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[13px] font-bold text-(--ink)"
|
|
311
|
+
style={{
|
|
312
|
+
borderColor:
|
|
313
|
+
"color-mix(in srgb, var(--accent-magenta) 28%, var(--line))",
|
|
314
|
+
background:
|
|
315
|
+
"color-mix(in srgb, var(--accent-magenta) 16%, var(--panel-strong) 84%)",
|
|
316
|
+
}}
|
|
317
|
+
>
|
|
318
|
+
<span className="flex items-center">
|
|
319
|
+
{Array.from({ length: boosterCount }).map((_, dotIndex) => (
|
|
320
|
+
<FaCircle
|
|
321
|
+
key={`booster-${dotIndex}`}
|
|
322
|
+
className="text-[0.42rem] -ml-0.5 first:ml-0"
|
|
323
|
+
style={{
|
|
324
|
+
color: getTeamColor(dotIndex, 8),
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
327
|
+
))}
|
|
328
|
+
</span>
|
|
329
|
+
<span>{boosterCount}</span>
|
|
330
|
+
</span>
|
|
331
|
+
) : (
|
|
332
|
+
<span className="inline-flex items-center text-(--ink-soft)">
|
|
333
|
+
<FaCircle className="text-[0.38rem] -ml-0.5 first:ml-0 text-slate-400" />
|
|
334
|
+
<FaCircle className="text-[0.38rem] -ml-0.5 text-slate-400" />
|
|
335
|
+
<FaCircle className="text-[0.38rem] -ml-0.5 text-slate-400" />
|
|
336
|
+
</span>
|
|
337
|
+
)}
|
|
338
|
+
</td>
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
343
|
+
function EfficiencyCell({ row }: { row: any }) {
|
|
344
|
+
return (
|
|
345
|
+
<td>
|
|
346
|
+
<span
|
|
347
|
+
className="inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-[13px] font-black"
|
|
348
|
+
style={{
|
|
349
|
+
background:
|
|
350
|
+
typeof row.efficiency === "number" && row.efficiency >= 8
|
|
351
|
+
? "color-mix(in srgb, var(--marker-green) 20%, var(--panel-strong))"
|
|
352
|
+
: typeof row.efficiency === "number" && row.efficiency >= 6
|
|
353
|
+
? "color-mix(in srgb, var(--marker-yellow) 20%, var(--panel-strong))"
|
|
354
|
+
: "color-mix(in srgb, var(--marker-pink) 20%, var(--panel-strong))",
|
|
355
|
+
color:
|
|
356
|
+
typeof row.efficiency === "number" && row.efficiency >= 8
|
|
357
|
+
? "var(--ink)"
|
|
358
|
+
: typeof row.efficiency === "number" && row.efficiency >= 6
|
|
359
|
+
? "var(--ink)"
|
|
360
|
+
: "var(--ink)",
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
<FaCircle className="text-[0.38rem]" />
|
|
364
|
+
<span>
|
|
365
|
+
{typeof row.efficiency === "number" ? row.efficiency.toFixed(1) : "-"}
|
|
366
|
+
</span>
|
|
367
|
+
</span>
|
|
368
|
+
</td>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
|
+
function getRankShift(row: any): string {
|
|
374
|
+
if (typeof row.previousRank !== "number") return "none";
|
|
375
|
+
if (row.previousRank > row.rank) return "up";
|
|
376
|
+
if (row.previousRank < row.rank) return "down";
|
|
377
|
+
return "same";
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
|
+
function getShiftAmount(row: any): number {
|
|
382
|
+
return typeof row.previousRank === "number"
|
|
383
|
+
? Math.abs(row.previousRank - row.rank)
|
|
384
|
+
: 0;
|
|
385
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { motion } from "framer-motion";
|
|
2
|
+
import type { SectionCardProps } from "../../types/dashboard";
|
|
3
|
+
|
|
4
|
+
export function SectionCard({
|
|
5
|
+
title,
|
|
6
|
+
note,
|
|
7
|
+
children,
|
|
8
|
+
className = "",
|
|
9
|
+
index,
|
|
10
|
+
icon: Icon,
|
|
11
|
+
accent = "var(--accent-cyan)",
|
|
12
|
+
}: SectionCardProps) {
|
|
13
|
+
return (
|
|
14
|
+
<motion.section
|
|
15
|
+
className={`paper-card section-card ${className}`}
|
|
16
|
+
initial={{ opacity: 0, y: 22, rotate: index % 2 === 0 ? -0.5 : 0.7 }}
|
|
17
|
+
whileInView={{ opacity: 1, y: 0, rotate: 0 }}
|
|
18
|
+
viewport={{ once: true, margin: "-60px" }}
|
|
19
|
+
transition={{ duration: 0.45, delay: index * 0.04 }}
|
|
20
|
+
>
|
|
21
|
+
<div className="section-tape" aria-hidden="true" />
|
|
22
|
+
<div className="section-head">
|
|
23
|
+
<div className="flex items-start gap-3">
|
|
24
|
+
<span
|
|
25
|
+
className="inline-flex h-11 w-11 items-center justify-center rounded-full border shadow-[0_10px_20px_var(--shadow)]"
|
|
26
|
+
style={{
|
|
27
|
+
color: "var(--ink)",
|
|
28
|
+
borderColor: `color-mix(in srgb, ${accent} 34%, var(--border))`,
|
|
29
|
+
background: `radial-gradient(circle at 30% 30%, color-mix(in srgb, ${accent} 40%, var(--panel-strong) 60%), color-mix(in srgb, ${accent} 82%, transparent))`,
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<Icon className="text-lg" />
|
|
33
|
+
</span>
|
|
34
|
+
<div>
|
|
35
|
+
<h2 className="section-title">{title}</h2>
|
|
36
|
+
<p className="ledger-note">{note}</p>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<span
|
|
40
|
+
className="pencil-mark inline-flex items-center gap-1 rounded-full px-2 py-1"
|
|
41
|
+
style={{
|
|
42
|
+
background: `color-mix(in srgb, ${accent} 16%, transparent)`,
|
|
43
|
+
color: accent,
|
|
44
|
+
}}
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
>
|
|
47
|
+
<FaBolt className="text-[0.7rem]" />
|
|
48
|
+
<span className="text-[12px] font-black uppercase tracking-[0.2em]">
|
|
49
|
+
spotlight
|
|
50
|
+
</span>
|
|
51
|
+
</span>
|
|
52
|
+
</div>
|
|
53
|
+
{children}
|
|
54
|
+
</motion.section>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Import required icon
|
|
59
|
+
import { FaBolt } from "react-icons/fa6";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StickyMiniProps } from "../../types/dashboard";
|
|
2
|
+
|
|
3
|
+
export function StickyMini({
|
|
4
|
+
title,
|
|
5
|
+
value,
|
|
6
|
+
note,
|
|
7
|
+
variant,
|
|
8
|
+
icon: Icon,
|
|
9
|
+
}: StickyMiniProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className={`sticky-mini sticky-mini-${variant} hover-jitter`}>
|
|
12
|
+
<p className="sticky-mini-title flex items-center gap-2">
|
|
13
|
+
<Icon className="text-sm" />
|
|
14
|
+
<span>{title}</span>
|
|
15
|
+
</p>
|
|
16
|
+
<p className="sticky-mini-value">{value}</p>
|
|
17
|
+
<p className="sticky-mini-note">{note}</p>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { SectionCard } from "./SectionCard";
|
|
2
|
+
export { StickyMini } from "./StickyMini";
|
|
3
|
+
export { ChartBoard } from "./ChartBoard";
|
|
4
|
+
export { CaptainBoard } from "./CaptainBoard";
|
|
5
|
+
export { LedgerTable } from "./LedgerTable";
|
|
6
|
+
export { LatestBadge } from "./LatestBadge";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
export function DashboardChartFrame({
|
|
6
|
+
children,
|
|
7
|
+
chartHeight,
|
|
8
|
+
}: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
chartHeight: number;
|
|
11
|
+
isMobile: boolean;
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className="min-w-0 p-2.5 sm:p-3 hover-jitter relative overflow-hidden"
|
|
16
|
+
style={{
|
|
17
|
+
height: chartHeight,
|
|
18
|
+
width: "100%",
|
|
19
|
+
margin: "0",
|
|
20
|
+
border:
|
|
21
|
+
"2px solid color-mix(in srgb, var(--accent-cyan) 24%, var(--line))",
|
|
22
|
+
borderRadius: "24px 26px 18px 22px",
|
|
23
|
+
background:
|
|
24
|
+
"linear-gradient(180deg, color-mix(in srgb, var(--paper) 92%, var(--panel-strong) 8%), color-mix(in srgb, var(--paper-2) 84%, var(--panel) 16%))",
|
|
25
|
+
boxShadow:
|
|
26
|
+
"inset 0 0 0 1px color-mix(in srgb, var(--chalk) 30%, transparent), 0 16px 30px color-mix(in srgb, var(--shadow) 20%, transparent)",
|
|
27
|
+
position: "relative",
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
{/* Notebook margin line */}
|
|
31
|
+
<div
|
|
32
|
+
className="absolute left-3 top-0 bottom-0 pointer-events-none"
|
|
33
|
+
style={{
|
|
34
|
+
width: "1px",
|
|
35
|
+
background:
|
|
36
|
+
"linear-gradient(180deg, color-mix(in srgb, var(--marker-pink) 40%, transparent) 0%, transparent 100%)",
|
|
37
|
+
opacity: 0.3,
|
|
38
|
+
zIndex: 1,
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
{/* Coffee stain doodle */}
|
|
43
|
+
<div
|
|
44
|
+
className="absolute pointer-events-none wobbly"
|
|
45
|
+
style={{
|
|
46
|
+
top: "6%",
|
|
47
|
+
right: "4%",
|
|
48
|
+
width: 28,
|
|
49
|
+
height: 16,
|
|
50
|
+
borderRadius: "50%",
|
|
51
|
+
background:
|
|
52
|
+
"color-mix(in srgb, var(--marker-yellow) 18%, transparent)",
|
|
53
|
+
zIndex: 1,
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
{/* Corner fold doodle - top right */}
|
|
58
|
+
<div
|
|
59
|
+
className="absolute top-0 right-0 pointer-events-none wobbly"
|
|
60
|
+
style={{
|
|
61
|
+
width: 0,
|
|
62
|
+
height: 0,
|
|
63
|
+
borderStyle: "solid",
|
|
64
|
+
borderWidth: "0 16px 16px 0",
|
|
65
|
+
borderColor:
|
|
66
|
+
"transparent color-mix(in srgb, var(--marker-yellow) 30%, transparent) transparent transparent",
|
|
67
|
+
zIndex: 2,
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
{children}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export function DoodleSpinner() {
|
|
4
|
+
return (
|
|
5
|
+
<svg className="doodle-spinner" viewBox="0 0 48 48">
|
|
6
|
+
<circle cx="24" cy="24" r="20" />
|
|
7
|
+
<circle cx="24" cy="24" r="14" strokeDasharray="60" />
|
|
8
|
+
<circle cx="24" cy="24" r="8" strokeDasharray="30" />
|
|
9
|
+
{/* hand-drawn center dot */}
|
|
10
|
+
<circle cx="24" cy="24" r="3" fill="var(--ink-soft)" opacity="0.4" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
type TeamPill = {
|
|
4
|
+
label: string;
|
|
5
|
+
fill: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default function TeamPills({
|
|
9
|
+
items,
|
|
10
|
+
max = 4,
|
|
11
|
+
}: {
|
|
12
|
+
items: TeamPill[];
|
|
13
|
+
max?: number;
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<div className="flex flex-wrap gap-2">
|
|
17
|
+
{items.slice(0, max).map((item, i) => (
|
|
18
|
+
<span
|
|
19
|
+
key={item.label}
|
|
20
|
+
className="inline-flex items-center gap-2 border px-3 py-1 text-[12px] font-black uppercase tracking-[0.16em]"
|
|
21
|
+
style={{
|
|
22
|
+
color: "var(--ink)",
|
|
23
|
+
border: "1.5px solid",
|
|
24
|
+
borderRadius: `999px ${920 + i * 28}px ${980 - i * 24}px 999px`,
|
|
25
|
+
borderColor: `color-mix(in srgb, ${item.fill} 40%, var(--line))`,
|
|
26
|
+
background: `color-mix(in srgb, ${item.fill} 18%, var(--panel-strong))`,
|
|
27
|
+
boxShadow: `2px 3px 0 color-mix(in srgb, ${item.fill} 28%, var(--shadow))`,
|
|
28
|
+
transform: `rotate(${i % 2 === 0 ? -0.8 + i * 0.4 : 0.6 - i * 0.3}deg)`,
|
|
29
|
+
fontFamily: "var(--font-note), cursive",
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<span
|
|
33
|
+
className="h-2.5 w-2.5 rounded-full"
|
|
34
|
+
style={{ background: item.fill }}
|
|
35
|
+
/>
|
|
36
|
+
<span>{item.label}</span>
|
|
37
|
+
</span>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export const MATCH_POINTS: { teamId: number; matchId: number; points: number }[] = [
|
|
2
|
+
{"teamId":1,"matchId":1,"points":727},{"teamId":1,"matchId":2,"points":229},{"teamId":1,"matchId":3,"points":163.5},{"teamId":1,"matchId":4,"points":120.5},{"teamId":1,"matchId":5,"points":137.5},{"teamId":1,"matchId":6,"points":472},{"teamId":1,"matchId":7,"points":120},{"teamId":1,"matchId":8,"points":107},{"teamId":1,"matchId":9,"points":498},{"teamId":1,"matchId":10,"points":225.5},{"teamId":1,"matchId":11,"points":323.5},{"teamId":1,"matchId":12,"points":65},{"teamId":1,"matchId":13,"points":309.5},{"teamId":1,"matchId":14,"points":268},{"teamId":1,"matchId":15,"points":168},{"teamId":1,"matchId":16,"points":442},{"teamId":1,"matchId":17,"points":407},{"teamId":1,"matchId":18,"points":360},{"teamId":1,"matchId":19,"points":239},{"teamId":1,"matchId":20,"points":368.5},{"teamId":1,"matchId":21,"points":324.5},{"teamId":1,"matchId":22,"points":159},{"teamId":1,"matchId":23,"points":467},{"teamId":1,"matchId":24,"points":95},{"teamId":1,"matchId":25,"points":275},{"teamId":1,"matchId":26,"points":273.5},{"teamId":1,"matchId":27,"points":312.5},{"teamId":1,"matchId":28,"points":334},{"teamId":1,"matchId":29,"points":558.5},{"teamId":1,"matchId":30,"points":540},{"teamId":1,"matchId":31,"points":565},{"teamId":1,"matchId":32,"points":157},{"teamId":1,"matchId":33,"points":403.5},{"teamId":1,"matchId":34,"points":214.5},{"teamId":1,"matchId":35,"points":736.5},{"teamId":1,"matchId":36,"points":461},{"teamId":1,"matchId":37,"points":308.5},{"teamId":1,"matchId":38,"points":101.5},{"teamId":1,"matchId":39,"points":101},{"teamId":1,"matchId":40,"points":345},{"teamId":1,"matchId":41,"points":471.5},{"teamId":1,"matchId":42,"points":249},{"teamId":1,"matchId":43,"points":282.5},{"teamId":1,"matchId":44,"points":102.5},{"teamId":1,"matchId":45,"points":203},{"teamId":1,"matchId":46,"points":210},{"teamId":1,"matchId":47,"points":889},{"teamId":1,"matchId":48,"points":269},{"teamId":1,"matchId":49,"points":250.5},{"teamId":1,"matchId":50,"points":461},{"teamId":2,"matchId":1,"points":535},{"teamId":2,"matchId":2,"points":337.5},{"teamId":2,"matchId":3,"points":165.5},{"teamId":2,"matchId":4,"points":196.5},{"teamId":2,"matchId":5,"points":108.5},{"teamId":2,"matchId":6,"points":141},{"teamId":2,"matchId":7,"points":307},{"teamId":2,"matchId":8,"points":257.5},{"teamId":2,"matchId":9,"points":307},{"teamId":2,"matchId":10,"points":238.5},{"teamId":2,"matchId":11,"points":302.5},{"teamId":2,"matchId":12,"points":65},{"teamId":2,"matchId":13,"points":241},{"teamId":2,"matchId":14,"points":198.5},{"teamId":2,"matchId":15,"points":168},{"teamId":2,"matchId":16,"points":352.5},{"teamId":2,"matchId":17,"points":109},{"teamId":2,"matchId":18,"points":332},{"teamId":2,"matchId":19,"points":282.5},{"teamId":2,"matchId":20,"points":212.5},{"teamId":2,"matchId":21,"points":322.5},{"teamId":2,"matchId":22,"points":305},{"teamId":2,"matchId":23,"points":218},{"teamId":2,"matchId":24,"points":244.5},{"teamId":2,"matchId":25,"points":271.5},{"teamId":2,"matchId":26,"points":93},{"teamId":2,"matchId":27,"points":158.5},{"teamId":2,"matchId":28,"points":232.5},{"teamId":2,"matchId":29,"points":159},{"teamId":2,"matchId":30,"points":135},{"teamId":2,"matchId":31,"points":283},{"teamId":2,"matchId":32,"points":159},{"teamId":2,"matchId":33,"points":148},{"teamId":2,"matchId":34,"points":242},{"teamId":2,"matchId":35,"points":649},{"teamId":2,"matchId":36,"points":511},{"teamId":2,"matchId":37,"points":259},{"teamId":2,"matchId":38,"points":146.5},{"teamId":2,"matchId":39,"points":88.5},{"teamId":2,"matchId":40,"points":339},{"teamId":2,"matchId":41,"points":320.5},{"teamId":2,"matchId":42,"points":324.5},{"teamId":2,"matchId":43,"points":247.5},{"teamId":2,"matchId":44,"points":263.5},{"teamId":2,"matchId":45,"points":135},{"teamId":2,"matchId":46,"points":253},{"teamId":2,"matchId":47,"points":293},{"teamId":2,"matchId":48,"points":295},{"teamId":2,"matchId":49,"points":659},{"teamId":2,"matchId":50,"points":301},{"teamId":3,"matchId":1,"points":295},{"teamId":3,"matchId":2,"points":297},{"teamId":3,"matchId":3,"points":155},{"teamId":3,"matchId":4,"points":252.5},{"teamId":3,"matchId":5,"points":208},{"teamId":3,"matchId":6,"points":217},{"teamId":3,"matchId":7,"points":266},{"teamId":3,"matchId":8,"points":148.5},{"teamId":3,"matchId":9,"points":349},{"teamId":3,"matchId":10,"points":78},{"teamId":3,"matchId":11,"points":179.5},{"teamId":3,"matchId":12,"points":42.5},{"teamId":3,"matchId":13,"points":242},{"teamId":3,"matchId":14,"points":170.5},{"teamId":3,"matchId":15,"points":249},{"teamId":3,"matchId":16,"points":150},{"teamId":3,"matchId":17,"points":263},{"teamId":3,"matchId":18,"points":401},{"teamId":3,"matchId":19,"points":327},{"teamId":3,"matchId":20,"points":264},{"teamId":3,"matchId":21,"points":263.5},{"teamId":3,"matchId":22,"points":161},{"teamId":3,"matchId":23,"points":235},{"teamId":3,"matchId":24,"points":399.5},{"teamId":3,"matchId":25,"points":410.5},{"teamId":3,"matchId":26,"points":239},{"teamId":3,"matchId":27,"points":280.5},{"teamId":3,"matchId":28,"points":241.5},{"teamId":3,"matchId":29,"points":309},{"teamId":3,"matchId":30,"points":78},{"teamId":3,"matchId":31,"points":186.5},{"teamId":3,"matchId":32,"points":119},{"teamId":3,"matchId":33,"points":316.5},{"teamId":3,"matchId":34,"points":596},{"teamId":3,"matchId":35,"points":766.5},{"teamId":3,"matchId":36,"points":283},{"teamId":3,"matchId":37,"points":347},{"teamId":3,"matchId":38,"points":29},{"teamId":3,"matchId":39,"points":83.5},{"teamId":3,"matchId":40,"points":414},{"teamId":3,"matchId":41,"points":183.5},{"teamId":3,"matchId":42,"points":258.5},{"teamId":3,"matchId":43,"points":191.5},{"teamId":3,"matchId":44,"points":179.5},{"teamId":3,"matchId":45,"points":218},{"teamId":3,"matchId":46,"points":233.5},{"teamId":3,"matchId":47,"points":456.5},{"teamId":3,"matchId":48,"points":231.5},{"teamId":3,"matchId":49,"points":921},{"teamId":3,"matchId":50,"points":406},{"teamId":4,"matchId":1,"points":386},{"teamId":4,"matchId":2,"points":401.5},{"teamId":4,"matchId":3,"points":149},{"teamId":4,"matchId":4,"points":296},{"teamId":4,"matchId":5,"points":67.5},{"teamId":4,"matchId":6,"points":296},{"teamId":4,"matchId":7,"points":240.5},{"teamId":4,"matchId":8,"points":221.5},{"teamId":4,"matchId":9,"points":182.5},{"teamId":4,"matchId":10,"points":368.5},{"teamId":4,"matchId":11,"points":107},{"teamId":4,"matchId":12,"points":77},{"teamId":4,"matchId":13,"points":-1},{"teamId":4,"matchId":14,"points":226},{"teamId":4,"matchId":15,"points":294.5},{"teamId":4,"matchId":16,"points":319},{"teamId":4,"matchId":17,"points":205.5},{"teamId":4,"matchId":18,"points":572},{"teamId":4,"matchId":19,"points":178},{"teamId":4,"matchId":20,"points":161},{"teamId":4,"matchId":21,"points":48.5},{"teamId":4,"matchId":22,"points":255},{"teamId":4,"matchId":23,"points":279},{"teamId":4,"matchId":24,"points":167.5},{"teamId":4,"matchId":25,"points":189.5},{"teamId":4,"matchId":26,"points":408},{"teamId":4,"matchId":27,"points":357.5},{"teamId":4,"matchId":28,"points":248.5},{"teamId":4,"matchId":29,"points":172.5},{"teamId":4,"matchId":30,"points":73},{"teamId":4,"matchId":31,"points":411},{"teamId":4,"matchId":32,"points":135},{"teamId":4,"matchId":33,"points":345.5},{"teamId":4,"matchId":34,"points":113},{"teamId":4,"matchId":35,"points":678},{"teamId":4,"matchId":36,"points":507},{"teamId":4,"matchId":37,"points":170.5},{"teamId":4,"matchId":38,"points":185},{"teamId":4,"matchId":39,"points":99},{"teamId":4,"matchId":40,"points":368},{"teamId":4,"matchId":41,"points":517.5},{"teamId":4,"matchId":42,"points":260},{"teamId":4,"matchId":43,"points":321.5},{"teamId":4,"matchId":44,"points":175.5},{"teamId":4,"matchId":45,"points":319},{"teamId":4,"matchId":46,"points":327.5},{"teamId":4,"matchId":47,"points":299},{"teamId":4,"matchId":48,"points":393},{"teamId":4,"matchId":49,"points":819},{"teamId":4,"matchId":50,"points":167.5},{"teamId":5,"matchId":1,"points":510},{"teamId":5,"matchId":2,"points":376.5},{"teamId":5,"matchId":3,"points":108},{"teamId":5,"matchId":4,"points":174},{"teamId":5,"matchId":5,"points":162.5},{"teamId":5,"matchId":6,"points":317},{"teamId":5,"matchId":7,"points":203.5},{"teamId":5,"matchId":8,"points":159.5},{"teamId":5,"matchId":9,"points":403},{"teamId":5,"matchId":10,"points":65.5},{"teamId":5,"matchId":11,"points":188},{"teamId":5,"matchId":12,"points":32.5},{"teamId":5,"matchId":13,"points":166},{"teamId":5,"matchId":14,"points":130},{"teamId":5,"matchId":15,"points":181.5},{"teamId":5,"matchId":16,"points":322},{"teamId":5,"matchId":17,"points":219},{"teamId":5,"matchId":18,"points":172},{"teamId":5,"matchId":19,"points":242},{"teamId":5,"matchId":20,"points":328.5},{"teamId":5,"matchId":21,"points":28.5},{"teamId":5,"matchId":22,"points":232},{"teamId":5,"matchId":23,"points":316},{"teamId":5,"matchId":24,"points":258},{"teamId":5,"matchId":25,"points":294},{"teamId":5,"matchId":26,"points":192},{"teamId":5,"matchId":27,"points":136},{"teamId":5,"matchId":28,"points":78.5},{"teamId":5,"matchId":29,"points":319},{"teamId":5,"matchId":30,"points":127},{"teamId":5,"matchId":31,"points":551},{"teamId":5,"matchId":32,"points":290},{"teamId":5,"matchId":33,"points":455.5},{"teamId":5,"matchId":34,"points":452.5},{"teamId":5,"matchId":35,"points":662},{"teamId":5,"matchId":36,"points":465},{"teamId":5,"matchId":37,"points":350.5},{"teamId":5,"matchId":38,"points":51},{"teamId":5,"matchId":39,"points":258.5},{"teamId":5,"matchId":40,"points":367},{"teamId":5,"matchId":41,"points":254},{"teamId":5,"matchId":42,"points":344.5},{"teamId":5,"matchId":43,"points":245.5},{"teamId":5,"matchId":44,"points":276.5},{"teamId":5,"matchId":45,"points":135},{"teamId":5,"matchId":46,"points":73.5},{"teamId":5,"matchId":47,"points":287},{"teamId":5,"matchId":48,"points":287},{"teamId":5,"matchId":49,"points":186.5},{"teamId":5,"matchId":50,"points":174},{"teamId":6,"matchId":1,"points":530.5},{"teamId":6,"matchId":2,"points":415.5},{"teamId":6,"matchId":3,"points":241.5},{"teamId":6,"matchId":4,"points":169},{"teamId":6,"matchId":5,"points":47},{"teamId":6,"matchId":6,"points":331},{"teamId":6,"matchId":7,"points":197},{"teamId":6,"matchId":8,"points":99.5},{"teamId":6,"matchId":9,"points":471.5},{"teamId":6,"matchId":10,"points":240},{"teamId":6,"matchId":11,"points":146},{"teamId":6,"matchId":12,"points":35.5},{"teamId":6,"matchId":13,"points":265.5},{"teamId":6,"matchId":14,"points":82},{"teamId":6,"matchId":15,"points":217.5},{"teamId":6,"matchId":16,"points":135.5},{"teamId":6,"matchId":17,"points":188},{"teamId":6,"matchId":18,"points":101.5},{"teamId":6,"matchId":19,"points":155.5},{"teamId":6,"matchId":20,"points":226.5},{"teamId":6,"matchId":21,"points":46},{"teamId":6,"matchId":22,"points":115},{"teamId":6,"matchId":23,"points":297.5},{"teamId":6,"matchId":24,"points":169.5},{"teamId":6,"matchId":25,"points":275},{"teamId":6,"matchId":26,"points":325},{"teamId":6,"matchId":27,"points":245.5},{"teamId":6,"matchId":28,"points":280.5},{"teamId":6,"matchId":29,"points":402.5},{"teamId":6,"matchId":30,"points":75.5},{"teamId":6,"matchId":31,"points":322},{"teamId":6,"matchId":32,"points":303},{"teamId":6,"matchId":33,"points":308.5},{"teamId":6,"matchId":34,"points":322},{"teamId":6,"matchId":35,"points":455},{"teamId":6,"matchId":36,"points":326.5},{"teamId":6,"matchId":37,"points":524},{"teamId":6,"matchId":38,"points":131},{"teamId":6,"matchId":39,"points":152.5},{"teamId":6,"matchId":40,"points":100},{"teamId":6,"matchId":41,"points":497.5},{"teamId":6,"matchId":42,"points":313.5},{"teamId":6,"matchId":43,"points":197.5},{"teamId":6,"matchId":44,"points":224},{"teamId":6,"matchId":45,"points":181},{"teamId":6,"matchId":46,"points":259},{"teamId":6,"matchId":47,"points":827},{"teamId":6,"matchId":48,"points":144},{"teamId":6,"matchId":49,"points":210.5},{"teamId":6,"matchId":50,"points":387},{"teamId":7,"matchId":1,"points":343},{"teamId":7,"matchId":2,"points":374.5},{"teamId":7,"matchId":3,"points":140.5},{"teamId":7,"matchId":4,"points":242.5},{"teamId":7,"matchId":5,"points":0},{"teamId":7,"matchId":6,"points":175},{"teamId":7,"matchId":7,"points":239},{"teamId":7,"matchId":8,"points":10},{"teamId":7,"matchId":9,"points":277},{"teamId":7,"matchId":10,"points":76},{"teamId":7,"matchId":11,"points":75.5},{"teamId":7,"matchId":12,"points":41.5},{"teamId":7,"matchId":13,"points":114},{"teamId":7,"matchId":14,"points":149.5},{"teamId":7,"matchId":15,"points":78},{"teamId":7,"matchId":16,"points":124},{"teamId":7,"matchId":17,"points":314},{"teamId":7,"matchId":18,"points":40},{"teamId":7,"matchId":19,"points":12},{"teamId":7,"matchId":20,"points":246},{"teamId":7,"matchId":21,"points":206},{"teamId":7,"matchId":22,"points":104},{"teamId":7,"matchId":23,"points":111},{"teamId":7,"matchId":24,"points":460},{"teamId":7,"matchId":25,"points":53},{"teamId":7,"matchId":26,"points":16},{"teamId":7,"matchId":27,"points":58.5},{"teamId":7,"matchId":28,"points":105},{"teamId":7,"matchId":29,"points":103},{"teamId":7,"matchId":30,"points":184},{"teamId":7,"matchId":31,"points":175.5},{"teamId":7,"matchId":32,"points":130},{"teamId":7,"matchId":33,"points":136.5},{"teamId":7,"matchId":34,"points":85},{"teamId":7,"matchId":35,"points":246},{"teamId":7,"matchId":36,"points":279},{"teamId":7,"matchId":37,"points":125},{"teamId":7,"matchId":38,"points":0},{"teamId":7,"matchId":39,"points":86.5},{"teamId":7,"matchId":40,"points":203},{"teamId":7,"matchId":41,"points":84},{"teamId":7,"matchId":42,"points":296},{"teamId":7,"matchId":43,"points":220},{"teamId":7,"matchId":44,"points":57},{"teamId":7,"matchId":45,"points":60},{"teamId":7,"matchId":46,"points":132.5},{"teamId":7,"matchId":47,"points":0},{"teamId":7,"matchId":48,"points":315},{"teamId":7,"matchId":49,"points":261.5},{"teamId":7,"matchId":50,"points":384},{"teamId":8,"matchId":1,"points":667},{"teamId":8,"matchId":2,"points":257},{"teamId":8,"matchId":3,"points":0},{"teamId":8,"matchId":4,"points":0},{"teamId":8,"matchId":5,"points":0},{"teamId":8,"matchId":6,"points":259},{"teamId":8,"matchId":7,"points":0},{"teamId":8,"matchId":8,"points":206},{"teamId":8,"matchId":9,"points":0},{"teamId":8,"matchId":10,"points":209},{"teamId":8,"matchId":11,"points":268},{"teamId":8,"matchId":12,"points":0},{"teamId":8,"matchId":13,"points":42},{"teamId":8,"matchId":14,"points":0},{"teamId":8,"matchId":15,"points":0},{"teamId":8,"matchId":16,"points":168},{"teamId":8,"matchId":17,"points":66},{"teamId":8,"matchId":18,"points":0},{"teamId":8,"matchId":19,"points":0},{"teamId":8,"matchId":20,"points":289},{"teamId":8,"matchId":21,"points":175},{"teamId":8,"matchId":22,"points":0},{"teamId":8,"matchId":23,"points":207},{"teamId":8,"matchId":24,"points":5},{"teamId":8,"matchId":25,"points":0},{"teamId":8,"matchId":26,"points":155},{"teamId":8,"matchId":27,"points":260},{"teamId":8,"matchId":28,"points":0},{"teamId":8,"matchId":29,"points":0},{"teamId":8,"matchId":30,"points":75.5},{"teamId":8,"matchId":31,"points":186},{"teamId":8,"matchId":32,"points":0},{"teamId":8,"matchId":33,"points":107.5},{"teamId":8,"matchId":34,"points":75},{"teamId":8,"matchId":35,"points":0},{"teamId":8,"matchId":36,"points":197},{"teamId":8,"matchId":37,"points":0},{"teamId":8,"matchId":38,"points":0},{"teamId":8,"matchId":39,"points":144},{"teamId":8,"matchId":40,"points":0},{"teamId":8,"matchId":41,"points":205},{"teamId":8,"matchId":42,"points":144},{"teamId":8,"matchId":43,"points":0},{"teamId":8,"matchId":44,"points":82.5},{"teamId":8,"matchId":45,"points":51},{"teamId":8,"matchId":46,"points":0},{"teamId":8,"matchId":47,"points":270},{"teamId":8,"matchId":48,"points":0},{"teamId":8,"matchId":49,"points":219},{"teamId":8,"matchId":50,"points":183},
|
|
3
|
+
];
|